1 #ifndef _FunctionEditor_h_
2 #define _FunctionEditor_h_
3 /* FunctionEditor.h
4  *
5  * Copyright (C) 1992-2021 Paul Boersma
6  *
7  * This code is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or (at
10  * your option) any later version.
11  *
12  * This code is distributed in the hope that it will be useful, but
13  * WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15  * See the GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this work. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
21 #include "Editor.h"
22 #include "Graphics.h"
23 #include "Function.h"
24 
25 struct FunctionEditor_picture {
26 	/* KEEP IN SYNC WITH PREFS. */
27 	bool garnish;
28 };
29 
Thing_define(FunctionEditor,Editor)30 Thing_define (FunctionEditor, Editor) {
31 	/*
32 		Inherited attributes:
33 			data: must be a Function.
34 	*/
35 	Function & function() { return * reinterpret_cast <Function *> (& our data); }
36 
37 	/*
38 		Subclasses may change the following attributes,
39 		but have to respect the invariants,
40 		and have to call FunctionEditor_marksChanged ()
41 		immediately after making those changes.
42 		Invariants:
43 			tmin <= startWindow < endWindow <= tmax;
44 			tmin <= startSelection <= endSelection <= tmax;
45 	*/
46 	double tmin, tmax, startWindow, endWindow;
47 	double startSelection, endSelection;   // markers
48 
49 	autoGraphics graphics;   // used in the expose callback
50 	/*
51 		The Normalized Device cordinates are in "pixelettes", which are a bit smaller than pixels.
52 		The purpose of this is optimal look and feel.
53 		The extent to which a pixelette is smaller than a pixel depends on the size of the drawing area:
54 		for smaller drawing areas, texts are a bit more cramped in their rectangles
55 		than for larger drawing areas.
56 	*/
57 	double width_pxlt, height_pxlt;   // size of drawing area in pixelettes
58 	double _functionViewerLeft, _functionViewerRight;   // location of function viewer in pixelettes
59 	double _selectionViewerLeft, _selectionViewerRight;   // location of selection viewer in pixelettes
60 	void updateGeometry (int width_pixels, int height_pixels) {
61 		Graphics_setWsViewport (our graphics.get(), 0.0, width_pixels, 0.0, height_pixels);
62 		our width_pxlt = width_pixels + 21;   // the +21 means that the horizontal margin becomes a tiny bit larger when the window grows
63 		our height_pxlt = height_pixels + 111;   // the +111 means that the vertical margins become a bit larger when the window grows
64 		Graphics_setWsWindow (our graphics.get(), 0.0, our width_pxlt, 0.0, our height_pxlt);
65 		//my viewAllAsPixelettes ();
66 		/*
67 			Put the function viewer at the left and the selection viewer at the right.
68 		*/
69 		our _functionViewerLeft = 0;
70 		our _functionViewerRight = ( our p_showSelectionViewer ? our width_pxlt * (2.0/3.0) : our width_pxlt );
71 		our _selectionViewerLeft = our _functionViewerRight;
72 		our _selectionViewerRight = our width_pxlt;
73 	}
74 	bool isInSelectionViewer (double x_pxlt) const {
75 		return x_pxlt > our _selectionViewerLeft;
76 	}
77 	void viewAllAsPixelettes () const {
78 		Graphics_setViewport (our graphics.get(), 0.0, our width_pxlt, 0.0, our height_pxlt);
79 		Graphics_setWindow (our graphics.get(), 0.0, our width_pxlt, 0.0, our height_pxlt);
80 	}
81 	void viewFunctionViewerAsPixelettes () const {
82 		Graphics_setViewport (our graphics.get(), our _functionViewerLeft, our _functionViewerRight, 0.0, our height_pxlt);
83 		Graphics_setWindow (our graphics.get(), our _functionViewerLeft, our _functionViewerRight, 0.0, our height_pxlt);
84 	}
85 	void viewSelectionViewerAsPixelettes () const {
86 		Graphics_setViewport (our graphics.get(), our _selectionViewerLeft, our _selectionViewerRight, 0.0, our height_pxlt);
87 		Graphics_setWindow (our graphics.get(), our _selectionViewerLeft, our _selectionViewerRight, 0.0, our height_pxlt);
88 	}
89 	constexpr static double space = 30.0;
90 	constexpr static double MARGIN = 107.0;
91 	constexpr static double BOTTOM_MARGIN = 2.0;
92 	constexpr static double TOP_MARGIN = 3.0;
93 	double dataLeft_pxlt () const { return our _functionViewerLeft + our MARGIN; }
94 	double dataRight_pxlt () const { return our _functionViewerRight - our MARGIN; }
95 	double dataBottom_pxlt () const { return our BOTTOM_MARGIN + our space * 3; }
96 	double dataTop_pxlt () const { return our height_pxlt - (our TOP_MARGIN + our space); }
97 	void viewDataAsWorldByFraction () const {
98 		Graphics_setViewport (our graphics.get(), our dataLeft_pxlt(), our dataRight_pxlt(), our dataBottom_pxlt(), our dataTop_pxlt());
99 		Graphics_setWindow (our graphics.get(), our startWindow, our endWindow, 0.0, 1.0);
100 	}
101 	void viewTallDataAsWorldByPixelettes () const {
102 		Graphics_setViewport (our graphics.get(), our dataLeft_pxlt(), our dataRight_pxlt(), 0.0, our height_pxlt);
103 		Graphics_setWindow (our graphics.get(), our startWindow, our endWindow, 0.0, our height_pxlt);
104 	}
105 	constexpr static double SELECTION_VIEWER_MARGIN = 0.0;
106 	void viewInnerSelectionViewerAsFractionByFraction () const {
107 		Graphics_setViewport (our graphics.get(), our _selectionViewerLeft + our MARGIN, our _selectionViewerRight - our MARGIN,
108 				our BOTTOM_MARGIN + our space * 3, our height_pxlt - (our TOP_MARGIN + our space));
109 		Graphics_setViewport (our graphics.get(),
110 			our _selectionViewerLeft + our SELECTION_VIEWER_MARGIN, our _selectionViewerRight - our SELECTION_VIEWER_MARGIN,
111 			our SELECTION_VIEWER_MARGIN, our height_pxlt - our space - our SELECTION_VIEWER_MARGIN
112 		);
113 		Graphics_setWindow (our graphics.get(), 0.0, 1.0, 0.0, 1.0);
114 	}
115 
116 	GuiText textArea;   // optional text at top
117 	bool clickWasModifiedByShiftKey;   // information for drag-and-drop and for start of play
118 	bool duringPlay, backgroundIsUpToDate;
119 	struct FunctionEditor_picture picture;
120 
121 	/* Private: */
122 	GuiDrawingArea drawingArea;
123 	GuiScrollBar scrollBar;
124 	GuiCheckButton groupButton;
125 	GuiObject bottomArea;
126 	bool group, enableUpdates;
127 	int nrect;
128 	struct { double left, right, bottom, top; } rect [8];
129 	double marker [1 + 3], playCursor, startZoomHistory, endZoomHistory;
130 	int numberOfMarkers;
131 
132 	void v_destroy () noexcept
133 		override;
134 	void v_info ()
135 		override;
136 	void v_createMenus ()
137 		override;
138 	void v_createMenuItems_file (EditorMenu)
139 		override;
140 	void v_createMenuItems_query (EditorMenu)
141 		override;
142 	void v_createChildren ()
143 		override;
144 	void v_createHelpMenuItems (EditorMenu)
145 		override;
146 	void v_dataChanged ()
147 		override;
148 
149 	virtual void v_draw () { }
150 	virtual bool v_hasSelectionViewer () { return false; }
151 	virtual void v_drawSelectionViewer () { }
152 	virtual void v_drawRealTimeSelectionViewer (double /* time */) { }
153 	virtual void v_prepareDraw () { }   // for less flashing
154 	virtual conststring32 v_domainName () { return U"time"; }
155 	virtual conststring32 v_selectionViewerName () { return U"selection viewer"; }
156 	virtual conststring32 v_format_domain () { return U"Time domain:"; }
157 	virtual const char *v_format_short () { return u8"%.3f"; }
158 	virtual const char *v_format_long () { return u8"%f"; }
159 	virtual conststring32 v_format_units_long () { return U"seconds"; }
160 	virtual conststring32 v_format_units_short () { return U"s"; }
161 	virtual const char *v_format_totalDuration () { return u8"Total duration %f seconds"; }
162 	virtual const char *v_format_window () { return u8"Visible part %f seconds"; }
163 	virtual const char *v_format_selection () { return u8"%f (%.3f / s)"; }
164 	virtual int v_fixedPrecision_long () { return 6; }
165 	virtual bool v_hasText () { return false; }
166 	virtual void v_play (double /* startTime */, double /* endTime */) { }
167 	virtual bool v_mouseInWideDataView (GuiDrawingArea_MouseEvent event, double x_world, double y_fraction);
168 		/*
169 			Message: "they clicked in the data part of the window, or in the left or right margin."
170 			'event' is the mouse event, with still relevant info on phase and modifier keys;
171 			'x_world' is the time (or another world unit);
172 			'y_fraction' is a value between 0.0 (bottom) and 1.0 (top);
173 			Behaviour of structFunctionEditor::v_mouseInWideDataView ():
174 				moves the cursor to 'x_world', drags to create a selection, or extends the selection.
175 		*/
176 	virtual void v_clickSelectionViewer (double x_fraction, double y_fraction);
177 	virtual int v_playCallback (int phase, double startTime, double endTime, double currentTime);
178 	virtual void v_updateText () { }
179 	virtual void v_prefs_addFields (EditorCommand) { }
180 	virtual void v_prefs_setValues (EditorCommand) { }
181 	virtual void v_prefs_getValues (EditorCommand) { }
182 	virtual void v_createMenuItems_file_draw (EditorMenu) { }
183 	virtual void v_createMenuItems_file_extract (EditorMenu) { }
184 	virtual void v_createMenuItems_file_write (EditorMenu) { }
185 	virtual void v_createMenuItems_view (EditorMenu);
186 	virtual void v_createMenuItems_view_timeDomain (EditorMenu);
187 	virtual void v_createMenuItems_view_audio (EditorMenu);
188 	virtual void v_highlightSelection (double left, double right, double bottom, double top);
189 	virtual double v_getBottomOfSoundArea () { return 0.0; }
190 	virtual double v_getBottomOfSoundAndAnalysisArea () { return 0.0; }
191 	virtual void v_form_pictureSelection (EditorCommand);
192 	virtual void v_ok_pictureSelection (EditorCommand);
193 	virtual void v_do_pictureSelection (EditorCommand);
194 
195     #include "FunctionEditor_prefs.h"
196 };
197 
198 int theFunctionEditor_playCallback (FunctionEditor me, int phase, double startTime, double endTime, double currentTime);
199 
200 #define FunctionEditor_UPDATE_NEEDED  true
201 #define FunctionEditor_NO_UPDATE_NEEDED  false
202 
203 void FunctionEditor_init (FunctionEditor me, conststring32 title, Function data);
204 /*
205 	Function:
206 		creates an Editor with a drawing area, a scroll bar and some buttons.
207 	Postconditions:
208 		my drawingArea is attached to the form at all sides,
209 		my scrollBar only to the bottom, left and right sides.
210 */
211 
212 void FunctionEditor_marksChanged (FunctionEditor me, bool needsUpdateGroup);
213 /*
214 	Function:
215 		update optional text field, the scroll bar, the drawing area and the buttons,
216 		from the current total time, window, cursor, and selection,
217 		and redraw the contents.
218 		If needsUpdateGroup is true, this will be done for all the editors in the group.
219 	Usage:
220 		call this after a change in any of the markers or in the duration of the data.
221 */
222 
223 void FunctionEditor_shift (FunctionEditor me, double shift, bool needsUpdateGroup);
224 /*
225 	Function:
226 		shift (scroll) the window through time, keeping the window length constant.
227 	Usage:
228 		call this after a search.
229 */
230 
231 void FunctionEditor_scrollToView (FunctionEditor me, double t);
232 
233 void FunctionEditor_updateText (FunctionEditor me);
234 /*
235 	Function:
236 		update the optional text widget.
237 	Usage:
238 		call this after moving the cursor, if that would have to change the text.
239 		The generic FunctionEditor also calls this if one of the other marks have changed.
240 	Behaviour:
241 		we just call the updateText method, which the inheritor will have to modify,
242 		since FunctionEditor::updateText does nothing.
243 */
244 
245 void FunctionEditor_redraw (FunctionEditor me);
246 /*
247 	Function:
248 		update the drawing area of a single editor.
249 	Usage:
250 		call this after she changes a view option (font, scaling, hide/show xx)
251 		or after any of the data have changed. In the latter case, also call Editor_broadcastChange.
252 	Behaviour:
253 		we just call Graphics_updateWs (my graphics).
254 */
255 
256 void FunctionEditor_enableUpdates (FunctionEditor me, bool enable);
257 /*
258 	Function:
259 		temporarily disable update event to cause 'draw' messages.
260 	Usage:
261 		If you call from your 'draw' method functions that may trigger expose events,
262 		you should bracket those routines between
263 			FunctionEditor_enableUpdates (me, false);
264 		and
265 			FunctionEditor_enableUpdates (me, true);
266 		This may happen if you call an analysis routine which calls Melder_progress.
267 */
268 
269 void FunctionEditor_ungroup (Daata data);
270 /*
271 	Function:
272 		force all editors containing `data` out of the group.
273 	Usage:
274 		Start cut or paste methods by calling this function,
275 		as the grouped editors will not be synchronized
276 		after either of those actions. Worse, the selection
277 		may get outside the common interval of the editors.
278 */
279 
280 /*
281 	Some functions to enforce a common look to all function editors.
282 	The x axis of the window is supposed to have been set to [my startWindow, my endWindow].
283 	Preconditions:
284 		default line type, default line width.
285 	Postconditions:
286 		default line type, default line width, undefined colour, undefined text alignment.
287 */
288 void FunctionEditor_drawRangeMark (FunctionEditor me, double yWC, conststring32 yWC_string, conststring32 units, int verticalAlignment);
289 void FunctionEditor_drawCursorFunctionValue (FunctionEditor me, double yWC, conststring32 yWC_string, conststring32 units);
290 void FunctionEditor_insertCursorFunctionValue (FunctionEditor me, double yWC, conststring32 yWC_string, conststring32 units, double minimum, double maximum);
291 void FunctionEditor_drawHorizontalHair (FunctionEditor me, double yWC, conststring32 yWC_string, conststring32 units);
292 void FunctionEditor_drawGridLine (FunctionEditor me, double yWC);
293 
294 void FunctionEditor_garnish (FunctionEditor me);   // optionally selection times and selection hairs
295 
296 /* End of file FunctionEditor.h */
297 #endif
298