1 /***************************************************************************
2     SignalView.h  -  base class for widgets for views to a signal
3 			     -------------------
4     begin                : Mon Jan 18 2010
5     copyright            : (C) 2010 by Thomas Eschenbacher
6     email                : Thomas.Eschenbacher@gmx.de
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #ifndef SIGNAL_VIEW_H
19 #define SIGNAL_VIEW_H
20 
21 #include "config.h"
22 
23 #include <QtGlobal>
24 #include <QLabel>
25 #include <QList>
26 #include <QObject>
27 #include <QPointer>
28 #include <QPolygon>
29 #include <QSharedPointer>
30 #include <QSize>
31 #include <QString>
32 #include <QTimer>
33 #include <QWidget>
34 
35 #include "libkwave/Sample.h"
36 
37 #include "libgui/MouseMark.h"
38 #include "libgui/ViewItem.h"
39 
40 class QEvent;
41 class QMouseEvent;
42 class QPaintEvent;
43 class QPoint;
44 
45 namespace Kwave
46 {
47 
48     class SignalManager; // forward declaration
49 
50     class Q_DECL_EXPORT SignalView: public QWidget
51     {
52 	Q_OBJECT
53     public:
54 
55 	/** preferred location of the SignalView */
56 	typedef enum {
57 	    UpperDockTop,     /**< upper dock area, top               */
58 	    UpperDockBottom,  /**< upper dock area, bottom            */
59 	    Top,              /**< above all others                   */
60 	    AboveTrackTop,    /**< above the associated track, top    */
61 	    AboveTrackBottom, /**< above the associated track, bottom */
62 	    BelowTrackTop,    /**< below the associated track, top    */
63 	    BelowTrackBottom, /**< below the associated track, bottom */
64 	    Bottom,           /**< below all others                   */
65 	    LowerDockTop,     /**< lower dock area, top               */
66 	    LowerDockBottom   /**< lower dock area, bottom            */
67 	} Location;
68 
69 	/**
70 	 * Constructor
71 	 * @param parent pointer to the parent widget
72 	 * @param controls container widget for associated controls
73 	 * @param signal_manager the signal manager
74 	 * @param preferred_location the location where to insert the view
75 	 * @param track (optional) index of the associated track or -1 if
76 	 *              not related to a specific track (default)
77 	 */
78 	SignalView(QWidget *parent, QWidget *controls,
79 	           Kwave::SignalManager *signal_manager,
80 	           Location preferred_location,
81 	           int track = -1);
82 
83 	/** Destructor */
84         virtual ~SignalView() Q_DECL_OVERRIDE;
85 
86 	/**
87 	 * refresh the content of the view.
88 	 * The default implementation calls repaint().
89 	 * Can be overwritten to trigger more complex refresh scenarios.
90 	 * @see TrackView::refresh() for an example
91 	 */
92 	virtual void refresh();
93 
94 	/** returns the preferred location */
preferredLocation()95 	Location preferredLocation() const {
96 	    return m_preferred_location;
97 	}
98 
99 	/** returns the associated signal manager */
signalManager()100 	inline Kwave::SignalManager *signalManager() const {
101 	    return m_signal_manager;
102 	}
103 
104 	/** returns the index of the associated track (or -1) */
track()105 	inline int track() const {
106 	    return m_track_index;
107 	}
108 
109 	/** returns the current start position */
offset()110 	inline sample_index_t offset() const {
111 	    return m_offset;
112 	}
113 
114 	/** returns the current zoom [pixels/sample] */
zoom()115 	inline double zoom() const {
116 	    return m_zoom;
117 	}
118 
119 	/** returns the current vertical zoom factor */
verticalZoom()120 	inline double verticalZoom() const {
121 	    return m_vertical_zoom;
122 	}
123 
124 	/** returns the index of the first visible sample */
firstVisible()125 	inline sample_index_t firstVisible() const {
126 	    return m_offset;
127 	}
128 
129 	/** returns the index of the last visible sample */
lastVisible()130 	inline sample_index_t lastVisible() const {
131 	    const sample_index_t w = pixels2samples(width());
132 	    return m_offset + ((w) ? (w - 1) : 0);
133 	}
134 
135 	/**
136 	 * converts a number of samples into a number of pixels,
137 	 * based on the current zoom factor
138 	 * @param samples a small number of samples (must be positive)
139 	 * @return number of pixels
140 	 */
141 	int samples2pixels(sample_index_t samples) const;
142 
143 	/**
144 	 * Converts a number of pixels into a number of samples,
145 	 * based on the current zoom factor
146 	 * @param pixels number of pixels (should be positive)
147 	 * @return number of samples
148 	 */
149 	sample_index_t pixels2samples(int pixels) const;
150 
151 	/**
152 	 * Converts a number of samples to a time in milliseconds, based on the
153 	 * current signal rate.
154 	 * @param samples number of samples
155 	 * @return time in milliseconds
156 	 */
157 	double samples2ms(sample_index_t samples);
158 
159 	/**
160 	 * Should be overwritten by subclasses that can display the currently
161 	 * selected range and allow the user to change the selection by mouse.
162 	 * @return true if mouse selection is handled
163 	 */
canHandleSelection()164 	virtual bool canHandleSelection() const { return false; }
165 
166 	/**
167 	 * Tries to find the nearest item that is visible in this view
168 	 * at a given position
169 	 *
170 	 * @param pos position to look at, relative to view [pixels]
171 	 * @return the nearest ViewObject in range
172 	 *         or a null pointer if nothing found
173 	 */
174 	virtual QSharedPointer<Kwave::ViewItem> findItem(const QPoint &pos);
175 
176 	/** slot for mouse moves, used for selection and drag&drop */
177         virtual void mouseMoveEvent(QMouseEvent *e) Q_DECL_OVERRIDE;
178 
179 	/** slot for mouse press, used for selection and drag&drop */
180         virtual void mousePressEvent(QMouseEvent *e) Q_DECL_OVERRIDE;
181 
182 	/** slot for mouse release, used for selection and drag&drop */
183         virtual void mouseReleaseEvent(QMouseEvent *e) Q_DECL_OVERRIDE;
184 
185 	/** slot when the mouse leaves the widget */
186         virtual void leaveEvent(QEvent *e) Q_DECL_OVERRIDE;
187 
188 	/** handles key press events (e.g. the Escape key) */
189         virtual void keyPressEvent(QKeyEvent *e) Q_DECL_OVERRIDE;
190 
191 	/**
192 	 * tolerance in pixel for snapping to a label or selection border
193 	 * @return number of pixels
194 	 */
195 	virtual int selectionTolerance() const;
196 
197 	/**
198 	 * Called when the context menu has been activated over this view
199 	 * @param pos a position in pixel within this widget
200 	 * @param menu pointer to the context menu
201 	 */
202 	virtual void handleContextMenu(const QPoint &pos, QMenu *menu);
203 
204 	/**
205 	 * Adds a widget to the list of associated widgets
206 	 * @param widget a QWidget that is associated to this view
207 	 */
208 	virtual void addSibling(QWidget *widget);
209 
210     signals:
211 
212 	/** emitted whenever the size of the content has changed */
213 	void contentSizeChanged();
214 
215 	/** emitted to request update of the cursor */
216 	void sigCursorChanged(sample_index_t pos);
217 
218 	/**
219 	 * Can be emitted to signal that this view needs to be repainted,
220 	 * probably after synchronizing with other views and additionally
221 	 * throttled to reduce GUI load
222 	 * @param view pointer to the view that needs to be repainted
223 	 */
224 	void sigNeedRepaint(Kwave::SignalView *view);
225 
226 	/** forward a sigCommand to the next layer */
227 	void sigCommand(const QString &command);
228 
229     public slots:
230 
231 	/**
232 	 * changes the association to a track
233 	 * @param track the new track index, or -1 if not associated
234 	 */
235 	virtual void setTrack(int track);
236 
237 	/**
238 	 * sets new zoom factor and offset
239 	 * @param zoom the new zoom factor in pixels/sample
240 	 * @param offset the index of the first visible sample
241 	 */
242 	virtual void setZoomAndOffset(double zoom, sample_index_t offset);
243 
244 	/**
245 	 * sets new vertical zoom factor
246 	 * @param zoom vertical zoom factor
247 	 */
248 	virtual void setVerticalZoom(double zoom);
249 
250 	/**
251 	 * shows the cursor at a given position
252 	 * @param pos current position of the cursor
253 	 */
254 	virtual void showCursor(sample_index_t pos = SAMPLE_INDEX_MAX);
255 
256     protected slots:
257 
258 	/**
259 	 * Shows the current cursor position as a tooltip
260 	 * @param text description of the position
261 	 * @param pos marker position [samples]
262 	 * @param mouse the coordinates of the mouse cursor,
263 	 *              relative to this widget [pixel]
264 	 */
265 	virtual void showPosition(const QString &text, sample_index_t pos,
266 				  const QPoint &mouse);
267 
268 	/**
269 	 * Hide the current position marker
270 	 * @see showPosition
271 	 */
hidePosition()272 	virtual void hidePosition() {
273 	    showPosition(QString(), 0, QPoint(-1,-1));
274 	}
275 
276     protected:
277 
278 	/** @see Qt XDND documentation */
279         virtual void dragEnterEvent(QDragEnterEvent *event) Q_DECL_OVERRIDE;
280 
281 	/** @see Qt XDND documentation */
282         virtual void dragLeaveEvent(QDragLeaveEvent *event) Q_DECL_OVERRIDE;
283 
284 	/** @see Qt XDND documentation */
285         virtual void dropEvent(QDropEvent *event) Q_DECL_OVERRIDE;
286 
287 	/** @see Qt XDND documentation */
288         virtual void dragMoveEvent(QDragMoveEvent *event) Q_DECL_OVERRIDE;
289 
290     protected:
291 
292 	/**
293 	 * Relationship between a screen position and the current selection.
294 	 */
295 	enum SelectionPosEnum {
296 	    None        = 0x0000, /**< not near a border           */
297 	    LeftBorder  = 0x0001, /**< close to start of selection */
298 	    RightBorder = 0x0002, /**< close to end of selection   */
299 	    Selection   = 0x8000  /**< within the selection        */
300 	};
301 	Q_DECLARE_FLAGS(SelectionPos, SelectionPosEnum)
302 
303 	/**
304 	 * Determines the relationship between a screen position and
305 	 * the current selection.
306 	 * @param x screen position
307 	 * @return a SelectionPos
308 	 */
309 	SelectionPos selectionPosition(int x);
310 
311 	/**
312 	 * Checks if a pixel position is within the left and right border
313 	 * of a selection. The tolerance is 2% of the currently
314 	 * visible area.
315 	 * @param x pixel position to be tested
316 	 * @return true if the position is within range
317 	 */
318 	bool isInSelection(int x);
319 
320 	/**
321 	 * Tries to find the nearest item that is visible in this view
322 	 * at a given position and show or hide the corresponding tool tip.
323 	 *
324 	 * @param mouse_pos position to look at, relative to view [pixels]
325 	 * @param active if true an operation (move or drag&drop) is active,
326 	 *               otherwise it is only a hover over an item
327 	 */
328 	void findNewItem(const QPoint &mouse_pos, bool active);
329 
330     protected:
331 
332 	/** widget for displaying associated controls */
333 	QWidget *m_controls;
334 
335 	/** the signal manager */
336 	Kwave::SignalManager *m_signal_manager;
337 
338 	/** the preferred location, as per construction */
339 	Location m_preferred_location;
340 
341 	/** index of the associated track or -1 if no relation to a track */
342 	int m_track_index;
343 
344 	/**
345 	 * Offset from which signal is being displayed. This is equal to
346 	 * the index of the first visible sample.
347 	 */
348 	sample_index_t m_offset;
349 
350 	/** number of samples per pixel */
351 	double m_zoom;
352 
353     private:
354 
355 	class PositionWidget: public QWidget
356 	{
357 	public:
358 	    /** Constructor */
359 	    explicit PositionWidget(QWidget *parent);
360 
361 	    /** Destructor */
362             virtual ~PositionWidget() Q_DECL_OVERRIDE;
363 
364 	    /**
365 	     * set a new label text and alignment
366 	     * @param text the text of the label, can be multiline and rtf/html
367 	     * @param alignment the alignment of the label and the widget,
368 	     *                  can be Qt::AlignLeft, Qt::AlignRight or Qt::AlignHCenter
369 	     */
370 	    virtual void setText(const QString &text, Qt::Alignment alignment);
371 
372 	protected:
373 
374 	    /** paint event: draws the text and the arrow */
375             virtual void paintEvent(QPaintEvent *) Q_DECL_OVERRIDE;
376 
377 	    /**
378 	     * re-creates the mask and the polygon when
379 	     * size/alignment has changed
380 	     */
381 	    virtual void updateMask();
382 
383 	private:
384 
385 	    /** the label that contains the text */
386 	    QLabel *m_label;
387 
388 	    /** alignment of the label / text */
389 	    Qt::Alignment m_alignment;
390 
391 	    /** the radius of the corners [pixel] */
392 	    int m_radius;
393 
394 	    /** the length of the arrows [pixel] */
395 	    int m_arrow_length;
396 
397 	    /** for detecting changes: previous width */
398 	    Qt::Alignment m_last_alignment;
399 
400 	    /** for detecting changes: previous size */
401 	    QSize m_last_size;
402 
403 	    /** polygon used as widget outline */
404 	    QPolygon m_polygon;
405 	};
406 
407     private:
408 
409 	/** zoom factor for vertical size */
410 	double m_vertical_zoom;
411 
412 	/** mode of the mouse cursor */
413 	enum {
414 	    MouseNormal = 0,   /**< over the signal [default]         */
415 	    MouseMoveItem,     /**< while moving an item              */
416 	    MouseDragItem      /**< while dragging an item            */
417 	} m_mouse_mode;
418 
419 	/** selection handler */
420 	Kwave::MouseMark m_mouse_selection;
421 
422 	/**
423 	 * x position where the user clicked the last time, needed for
424 	 * finding out where to start a drag&drop operation [pixel]
425 	 */
426 	int m_mouse_down_x;
427 
428 	/** small widget for showing the mouse cursor position */
429 	PositionWidget m_position_widget;
430 
431 	/** timer for automatic hiding */
432 	QTimer m_position_widget_timer;
433 
434 	/** list of associated widgets, e.g. controls etc */
435 	QList<QPointer<QWidget> > m_siblings;
436 
437 	/** currently selected view item or null */
438 	QSharedPointer<Kwave::ViewItem> m_selected_item;
439     };
440 
441 }
442 
443 #endif /* SIGNAL_VIEW_H */
444 
445 //***************************************************************************
446 //***************************************************************************
447