1 /* GuiDrawingArea.cpp
2 *
3 * Copyright (C) 1993-2018,2020,2021 Paul Boersma,
4 * 2008 Stefan de Konink, 2010 Franz Brausse, 2013 Tom Naughton
5 *
6 * This code is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or (at
9 * your option) any later version.
10 *
11 * This code is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14 * See the GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this work. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "GuiP.h"
21 #if gtk
22 #include "gdk/gdkkeysyms.h"
23 #include <locale.h>
24 #endif
25 #include "GraphicsP.h"
26
27 Thing_implement (GuiDrawingArea, GuiControl, 0);
28
29 #if motif
30 #define iam_drawingarea \
31 Melder_assert (widget -> widgetClass == xmDrawingAreaWidgetClass); \
32 GuiDrawingArea me = (GuiDrawingArea) widget -> userData
33 #else
34 #define iam_drawingarea \
35 GuiDrawingArea me = (GuiDrawingArea) _GuiObject_getUserData (widget)
36 #endif
37
38 #if gtk
39 #pragma mark - GTK CALLBACKS (WITH CAIRO)
_guiGtkDrawingArea_destroyCallback(GuiObject widget,gpointer void_me)40 static void _guiGtkDrawingArea_destroyCallback (GuiObject widget, gpointer void_me) {
41 (void) widget;
42 iam (GuiDrawingArea);
43 forget (me);
44 }
_guiGtkDrawingArea_drawCallback(GuiObject widget,cairo_t * cairoGraphicsContext,gpointer void_me)45 static gboolean _guiGtkDrawingArea_drawCallback (GuiObject widget, cairo_t *cairoGraphicsContext, gpointer void_me) {
46 trace (U"begin");
47 iam (GuiDrawingArea);
48 Melder_assert (me);
49 if (my d_exposeCallback) {
50 Melder_assert (my numberOfGraphicses > 0);
51 structGuiDrawingArea_ExposeEvent event { me, 0 };
52 event. x = 0;
53 event. y = 0;
54 event. width = gtk_widget_get_allocated_width (GTK_WIDGET (widget));
55 event. height = gtk_widget_get_allocated_height (GTK_WIDGET (widget));
56 try {
57 for (int igraphics = 1; igraphics <= my numberOfGraphicses; igraphics ++)
58 ((GraphicsScreen) my graphicses [igraphics]) -> d_cairoGraphicsContext = cairoGraphicsContext;
59 my d_exposeCallback (my d_exposeBoss, & event);
60 for (int igraphics = 1; igraphics <= my numberOfGraphicses; igraphics ++)
61 ((GraphicsScreen) my graphicses [igraphics]) -> d_cairoGraphicsContext = nullptr;
62 } catch (MelderError) {
63 Melder_flushError (U"Redrawing not completed");
64 }
65 trace (U"the draw callback handled drawing");
66 return true;
67 }
68 trace (U"GTK will handle redrawing");
69 return false;
70 }
71 static structGuiDrawingArea_MouseEvent::Phase previousPhase = structGuiDrawingArea_MouseEvent::Phase::DROP;
_guiGtkDrawingArea_mouseDownCallback(GuiObject widget,GdkEvent * e,gpointer void_me)72 static gboolean _guiGtkDrawingArea_mouseDownCallback (GuiObject widget, GdkEvent *e, gpointer void_me) {
73 iam (GuiDrawingArea);
74 if (my mouseCallback) {
75 structGuiDrawingArea_MouseEvent event { me, 0 };
76 event. x = ((GdkEventButton *) e) -> x;
77 event. y = ((GdkEventButton *) e) -> y;
78 event. shiftKeyPressed = (((GdkEventButton *) e) -> state & GDK_SHIFT_MASK) != 0;
79 event. commandKeyPressed = (((GdkEventButton *) e) -> state & GDK_CONTROL_MASK) != 0;
80 event. optionKeyPressed = (((GdkEventButton *) e) -> state & GDK_MOD1_MASK) != 0;
81 if (previousPhase == structGuiDrawingArea_MouseEvent::Phase::CLICK) {
82 /*
83 Apparently a double-click.
84 On other platforms, a mouse-up event is always generated, even within a double-click.
85 On Linux, we generate it ourselves.
86 */
87 try {
88 previousPhase = event. phase = structGuiDrawingArea_MouseEvent::Phase::DROP;
89 my mouseCallback (my mouseBoss, & event);
90 } catch (MelderError) {
91 Melder_flushError (U"Mouse drop not completely handled.");
92 }
93 }
94 try {
95 previousPhase = event. phase = structGuiDrawingArea_MouseEvent::Phase::CLICK;
96 my mouseCallback (my mouseBoss, & event);
97 } catch (MelderError) {
98 Melder_flushError (U"Mouse click not completely handled.");
99 }
100 return true;
101 }
102 return false;
103 }
_guiGtkDrawingArea_mouseDraggedCallback(GuiObject widget,GdkEvent * e,gpointer void_me)104 static gboolean _guiGtkDrawingArea_mouseDraggedCallback (GuiObject widget, GdkEvent *e, gpointer void_me) {
105 iam (GuiDrawingArea);
106 if (my mouseCallback) {
107 structGuiDrawingArea_MouseEvent event { me, 0 };
108 event. x = ((GdkEventButton *) e) -> x;
109 event. y = ((GdkEventButton *) e) -> y;
110 event. shiftKeyPressed = (((GdkEventButton *) e) -> state & GDK_SHIFT_MASK) != 0;
111 event. commandKeyPressed = (((GdkEventButton *) e) -> state & GDK_CONTROL_MASK) != 0;
112 event. optionKeyPressed = (((GdkEventButton *) e) -> state & GDK_MOD1_MASK) != 0;
113 try {
114 previousPhase = event. phase = structGuiDrawingArea_MouseEvent::Phase::DRAG;
115 my mouseCallback (my mouseBoss, & event);
116 } catch (MelderError) {
117 Melder_flushError (U"Mouse drag not completely handled.");
118 }
119 return true;
120 }
121 return false;
122 }
_guiGtkDrawingArea_mouseUpCallback(GuiObject widget,GdkEvent * e,gpointer void_me)123 static gboolean _guiGtkDrawingArea_mouseUpCallback (GuiObject widget, GdkEvent *e, gpointer void_me) {
124 iam (GuiDrawingArea);
125 if (my mouseCallback) {
126 structGuiDrawingArea_MouseEvent event { me, 0 };
127 event. x = ((GdkEventButton *) e) -> x;
128 event. y = ((GdkEventButton *) e) -> y;
129 event. shiftKeyPressed = (((GdkEventButton *) e) -> state & GDK_SHIFT_MASK) != 0;
130 event. commandKeyPressed = (((GdkEventButton *) e) -> state & GDK_CONTROL_MASK) != 0;
131 event. optionKeyPressed = (((GdkEventButton *) e) -> state & GDK_MOD1_MASK) != 0;
132 try {
133 previousPhase = event. phase = structGuiDrawingArea_MouseEvent::Phase::DROP;
134 my mouseCallback (my mouseBoss, & event);
135 } catch (MelderError) {
136 Melder_flushError (U"Mouse drop not completely handled.");
137 }
138 return true;
139 }
140 return false;
141 }
_guiGtkDrawingArea_keyCallback(GuiObject widget,GdkEvent * gevent,gpointer void_me)142 static gboolean _guiGtkDrawingArea_keyCallback (GuiObject widget, GdkEvent *gevent, gpointer void_me) {
143 iam (GuiDrawingArea);
144 trace (U"begin");
145 if (my d_keyCallback && gevent -> type == GDK_KEY_PRESS) {
146 structGuiDrawingArea_KeyEvent event { me, 0 };
147 GdkEventKey *gkeyEvent = (GdkEventKey *) gevent;
148 event. key = gkeyEvent -> keyval;
149 /*
150 * Translate with the help of /usr/include/gtk-2.0/gdk/gdkkeysyms.h
151 */
152 if (event. key == GDK_KEY_Escape) event. key = 27;
153 if (event. key == GDK_KEY_Left) event. key = 0x2190;
154 if (event. key == GDK_KEY_Up) event. key = 0x2191;
155 if (event. key == GDK_KEY_Right) event. key = 0x2192;
156 if (event. key == GDK_KEY_Down) event. key = 0x2193;
157 event. shiftKeyPressed = (gkeyEvent -> state & GDK_SHIFT_MASK) != 0;
158 event. commandKeyPressed = (gkeyEvent -> state & GDK_CONTROL_MASK) != 0;
159 event. optionKeyPressed = (gkeyEvent -> state & GDK_MOD1_MASK) != 0;
160 try {
161 my d_keyCallback (my d_keyBoss, & event);
162 } catch (MelderError) {
163 Melder_flushError (U"Key press not completely handled.");
164 }
165 /*
166 * FIXME: here we should empty the type-ahead buffer
167 */
168 return true;
169 }
170 return false; // if the drawing area has no keyCallback, the system will send the key press to a text field.
171 }
_guiGtkDrawingArea_resizeCallback(GuiObject widget,GtkAllocation * allocation,gpointer void_me)172 static gboolean _guiGtkDrawingArea_resizeCallback (GuiObject widget, GtkAllocation *allocation, gpointer void_me) {
173 iam (GuiDrawingArea);
174 if (my d_resizeCallback) {
175 structGuiDrawingArea_ResizeEvent event { me, 0 };
176 trace (U"drawingArea resized to ", allocation -> width, U" x ", allocation -> height, U".");
177 event. width = allocation -> width;
178 event. height = allocation -> height;
179 //g_debug("%d %d", allocation->width, allocation->height);
180 try {
181 my d_resizeCallback (my d_resizeBoss, & event);
182 } catch (MelderError) {
183 Melder_flushError (U"Window resizing not completely handled.");
184 }
185 return true;
186 }
187 return false;
188 }
_guiGtkDrawingArea_swipeCallback(GuiObject w,GdkEventScroll * event,gpointer void_me)189 static gboolean _guiGtkDrawingArea_swipeCallback (GuiObject w, GdkEventScroll *event, gpointer void_me) {
190 iam (GuiDrawingArea);
191 trace (U"_guiGtkDrawingArea_swipeCallback ", Melder_pointer (my d_horizontalScrollBar), Melder_pointer (my d_verticalScrollBar));
192 if (my d_horizontalScrollBar) {
193 double hv = gtk_range_get_value (GTK_RANGE (my d_horizontalScrollBar -> d_widget));
194 GtkAdjustment *adjustment = gtk_range_get_adjustment (GTK_RANGE (my d_horizontalScrollBar -> d_widget));
195 gdouble hi;
196 g_object_get (adjustment, "step_increment", & hi, nullptr);
197 switch (event -> direction) {
198 case GDK_SCROLL_LEFT:
199 gtk_range_set_value (GTK_RANGE (my d_horizontalScrollBar -> d_widget), hv - hi);
200 break;
201 case GDK_SCROLL_RIGHT:
202 gtk_range_set_value (GTK_RANGE (my d_horizontalScrollBar -> d_widget), hv + hi);
203 break;
204 }
205 }
206 if (my d_verticalScrollBar) {
207 double vv = gtk_range_get_value (GTK_RANGE (my d_verticalScrollBar -> d_widget));
208 GtkAdjustment *adjustment = gtk_range_get_adjustment (GTK_RANGE (my d_verticalScrollBar -> d_widget));
209 gdouble vi;
210 g_object_get (adjustment, "step_increment", & vi, nullptr);
211 switch (event -> direction) {
212 case GDK_SCROLL_UP:
213 gtk_range_set_value (GTK_RANGE (my d_verticalScrollBar -> d_widget), vv - vi);
214 break;
215 case GDK_SCROLL_DOWN:
216 gtk_range_set_value (GTK_RANGE (my d_verticalScrollBar -> d_widget), vv + vi);
217 break;
218 }
219 }
220 return true;
221 }
222 #elif motif
223 #pragma mark - MOTIF CALLBACKS (WITH GDI)
_GuiWinDrawingArea_destroy(GuiObject widget)224 void _GuiWinDrawingArea_destroy (GuiObject widget) {
225 iam_drawingarea;
226 DestroyWindow (widget -> window);
227 forget (me); // NOTE: my widget is not destroyed here
228 }
_GuiWinDrawingArea_update(GuiObject widget)229 void _GuiWinDrawingArea_update (GuiObject widget) {
230 iam_drawingarea;
231 Melder_assert (my numberOfGraphicses > 0);
232 GraphicsScreen graphics = (GraphicsScreen) my graphicses [1];
233 Melder_assert (Thing_isa (graphics, classGraphicsScreen));
234 HDC memoryDC = CreateCompatibleDC (graphics -> d_gdiGraphicsContext);
235 HBITMAP memoryBitmap = CreateCompatibleBitmap (graphics -> d_gdiGraphicsContext, widget -> width, widget -> height);
236 SelectObject (memoryDC, memoryBitmap);
237 SetBkMode (memoryDC, TRANSPARENT); // not the default!
238 SelectPen (memoryDC, GetStockPen (BLACK_PEN));
239 SelectBrush (memoryDC, GetStockBrush (BLACK_BRUSH));
240 SetTextAlign (memoryDC, TA_LEFT | TA_BASELINE | TA_NOUPDATECP); // baseline is not the default!
241 HDC saveContext = graphics -> d_gdiGraphicsContext;
242 for (int igraphics = 1; igraphics <= my numberOfGraphicses; igraphics ++)
243 ((GraphicsScreen) my graphicses [igraphics]) -> d_gdiGraphicsContext = memoryDC;
244 if (my d_exposeCallback) {
245 structGuiDrawingArea_ExposeEvent event { me };
246 try {
247 my d_exposeCallback (my d_exposeBoss, & event);
248 } catch (MelderError) {
249 Melder_flushError (U"Redrawing not completed");
250 }
251 }
252 for (int igraphics = 1; igraphics <= my numberOfGraphicses; igraphics ++)
253 ((GraphicsScreen) my graphicses [igraphics]) -> d_gdiGraphicsContext = saveContext;
254 BitBlt (graphics -> d_gdiGraphicsContext, 0, 0, widget -> width, widget -> height, memoryDC, 0, 0, SRCCOPY);
255 DeleteObject (memoryBitmap);
256 DeleteDC (memoryDC);
257 ValidateRect (widget -> window, nullptr);
258 }
_GuiWinDrawingArea_handleMouse(GuiObject widget,structGuiDrawingArea_MouseEvent::Phase phase,int x,int y)259 void _GuiWinDrawingArea_handleMouse (GuiObject widget, structGuiDrawingArea_MouseEvent::Phase phase, int x, int y) {
260 iam_drawingarea;
261 if (my mouseCallback) {
262 structGuiDrawingArea_MouseEvent event { me, 0 };
263 event. x = x;
264 event. y = y;
265 event. phase = phase;
266 //Melder_casual (U": phase ", (int) phase);
267 event. shiftKeyPressed = GetKeyState (VK_SHIFT) < 0;
268 event. optionKeyPressed = GetKeyState (VK_MENU) < 0;
269 event. commandKeyPressed = GetKeyState (VK_CONTROL) < 0;
270 try {
271 my mouseCallback (my mouseBoss, & event);
272 } catch (MelderError) {
273 switch (phase) {
274 case structGuiDrawingArea_MouseEvent::Phase::CLICK:
275 Melder_flushError (U"Mouse click not completely handled.");
276 break; case structGuiDrawingArea_MouseEvent::Phase::DRAG:
277 Melder_flushError (U"Mouse drag not completely handled.");
278 break; case structGuiDrawingArea_MouseEvent::Phase::DROP:
279 Melder_flushError (U"Mouse drop not completely handled.");
280 break;
281 }
282 }
283 }
284 }
_GuiWinDrawingArea_handleKey(GuiObject widget,TCHAR kar)285 void _GuiWinDrawingArea_handleKey (GuiObject widget, TCHAR kar) { // TODO: event?
286 iam_drawingarea;
287 if (my d_keyCallback) {
288 structGuiDrawingArea_KeyEvent event { me, 0 };
289 event. key = kar;
290 if (event. key == VK_RETURN) event. key = 10;
291 if (event. key == VK_LEFT) event. key = 0x2190;
292 if (event. key == VK_RIGHT) event. key = 0x2192;
293 if (event. key == VK_UP) event. key = 0x2191;
294 if (event. key == VK_DOWN) event. key = 0x2193;
295 event. shiftKeyPressed = GetKeyState (VK_SHIFT) < 0; // TODO: event -> key?
296 event. optionKeyPressed = GetKeyState (VK_MENU) < 0;
297 event. commandKeyPressed = GetKeyState (VK_CONTROL) < 0;
298 try {
299 my d_keyCallback (my d_keyBoss, & event);
300 } catch (MelderError) {
301 Melder_flushError (U"Key press not completely handled.");
302 }
303 }
304 }
_GuiWinDrawingArea_shellResize(GuiObject widget)305 void _GuiWinDrawingArea_shellResize (GuiObject widget) {
306 iam_drawingarea;
307 if (my d_resizeCallback) {
308 structGuiDrawingArea_ResizeEvent event { me };
309 event. width = widget -> width;
310 event. height = widget -> height;
311 try {
312 my d_resizeCallback (my d_resizeBoss, & event);
313 } catch (MelderError) {
314 Melder_flushError (U"Window resizing not completely handled.");
315 }
316 }
317 }
318 #elif cocoa
319 #pragma mark - COCOA CALLBACKS (WITH QUARTZ)
320 @interface GuiCocoaDrawingArea ()
321 @property (nonatomic, assign) BOOL inited;
322 @property (nonatomic, retain) NSTrackingArea *trackingArea;
323 @end
324 @implementation GuiCocoaDrawingArea {
325 GuiDrawingArea d_userData;
326 }
327 - (id) initWithFrame: (NSRect) frame {
328 self = [super initWithFrame: frame];
329 if (self) {
330 _trackingArea = [[[NSTrackingArea alloc]
331 initWithRect: [self visibleRect]
332 options: NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingInVisibleRect | NSTrackingActiveAlways
333 owner: self
334 userInfo: nil]
335 autorelease];
336 [self addTrackingArea: _trackingArea];
337 }
338 return self;
339 }
340 - (void) dealloc { // override
341 GuiDrawingArea me = d_userData;
342 if (Melder_debug == 55)
343 Melder_casual (U"\t\tGuiCocoaDrawingArea-", Melder_pointer (self), U" dealloc for ", Melder_pointer (me));
344 forget (me);
345 [self removeTrackingArea: _trackingArea];
346 trace (U"deleting a drawing area");
347 [super dealloc];
348 }
349 - (GuiThing) getUserData {
350 return d_userData;
351 }
352 - (void) setUserData: (GuiThing) userData {
353 d_userData = static_cast <GuiDrawingArea> (userData);
354 }
355 - (void) resizeCallback: (NSRect) rect {
356 GuiDrawingArea me = (GuiDrawingArea) d_userData;
357 if (me && my d_resizeCallback) {
358 structGuiDrawingArea_ResizeEvent event = { me, 0, 0 };
359 event. width = rect. size. width;
360 event. height = rect. size. height;
361 try {
362 my d_resizeCallback (my d_resizeBoss, & event);
363 } catch (MelderError) {
364 Melder_flushError (U"Window resizing not completely handled.");
365 }
366 }
367 }
368 - (void) drawRect: (NSRect) dirtyRect {
369 trace (U"dirtyRect: ", dirtyRect.origin.x, U", ", dirtyRect.origin.y, U", ", dirtyRect.size.width, U", ", dirtyRect.size.height);
370 GuiDrawingArea me = (GuiDrawingArea) d_userData;
371 if (Melder_debug == 55)
372 Melder_casual (U"\t\tGuiCocoaDrawingArea-", Melder_pointer (self), U" draw to ", Melder_pointer (me));
373 if (! _inited) {
374 // Last chance to do this. Is there a better place?
375 [self resizeCallback: self. frame];
376 _inited = YES;
377 }
378 if (me && my d_exposeCallback) {
379 structGuiDrawingArea_ExposeEvent event = { me, 0, 0, 0, 0 };
380 if (Melder_debug == 55)
381 Melder_casual (U"\t", Thing_messageNameAndAddress (me), U" draw for ", Melder_pointer (my d_exposeBoss));
382 try {
383 Melder_assert (my numberOfGraphicses > 0);
384 for (integer igraphics = 1; igraphics <= my numberOfGraphicses; igraphics ++) {
385 GraphicsScreen graphics = static_cast <GraphicsScreen> (my graphicses [igraphics]);
386 if (graphics -> d_macView) {
387 graphics -> d_macGraphicsContext = Melder_systemVersion < 101400 ?
388 (CGContextRef) [[NSGraphicsContext currentContext] graphicsPort] :
389 [[NSGraphicsContext currentContext] CGContext];
390 Melder_assert (!! graphics -> d_macGraphicsContext);
391 }
392 }
393 my d_exposeCallback (my d_exposeBoss, & event);
394 for (integer igraphics = 1; igraphics <= my numberOfGraphicses; igraphics ++) {
395 GraphicsScreen graphics = static_cast <GraphicsScreen> (my graphicses [igraphics]);
396 if (graphics -> d_macView)
397 graphics -> d_macGraphicsContext = nullptr;
398 }
399 } catch (MelderError) {
400 Melder_flushError (U"Redrawing not completed");
401 }
402 }
403 }
404 - (void) setFrame: (NSRect) rect {
405 [self resizeCallback: rect];
406 [super setFrame: rect];
407 }
408 - (BOOL) acceptsFirstResponder {
409 /*
410 * This overridden method tells the event chain whether the drawing area can accept key events.
411 * It is important that the Demo window and the RunnerMFC window accept key events.
412 * A side effect of accepting key events is that the drawing area obtains the key focus when the user clicks in the drawing area.
413 * It is important, however, that the drawing area of the TextGrid window cannot take away the key focus
414 * from the text field at the top; therefore, that drawing area should not accept key events.
415 * The implementation below is based on the fact that, naturally, the Demo window and the RunnerMFC window
416 * have a key callback, and the drawing area of the TextGrid window has not
417 * (a side effect of this implementation is that the drawing area of the Manual window does not take away
418 * the key focus from the Search field, a situation that cannot hurt).
419 */
420 GuiDrawingArea me = (GuiDrawingArea) d_userData;
421 return !! my d_keyCallback;
422 }
423 - (void) mouseEntered: (NSEvent *) nsEvent {
424 (void) nsEvent;
425 [[NSCursor crosshairCursor] push];
426 }
427 - (void) mouseExited: (NSEvent *) nsEvent {
428 (void) nsEvent;
429 [[NSCursor currentCursor] pop];
430 }
431 - (void) mouse: (NSEvent *) nsEvent inPhase: (structGuiDrawingArea_MouseEvent::Phase) phase {
432 GuiDrawingArea me = (GuiDrawingArea) d_userData;
433 if (me && my mouseCallback) {
434 structGuiDrawingArea_MouseEvent event = { me, 0, 0, phase, false, false, false };
435 NSPoint local_point = [self convertPoint: [nsEvent locationInWindow] fromView: nil];
436 event. x = local_point. x;
437 event. y = local_point. y;
438 NSUInteger modifiers = [nsEvent modifierFlags];
439 event. shiftKeyPressed = modifiers & NSShiftKeyMask;
440 event. optionKeyPressed = modifiers & NSAlternateKeyMask;
441 event. commandKeyPressed = modifiers & NSCommandKeyMask;
442 try {
443 my mouseCallback (my mouseBoss, & event);
444 } catch (MelderError) {
445 switch (phase) {
446 case structGuiDrawingArea_MouseEvent::Phase::CLICK:
447 Melder_flushError (U"Mouse click not completely handled.");
448 break; case structGuiDrawingArea_MouseEvent::Phase::DRAG:
449 Melder_flushError (U"Mouse drag not completely handled.");
450 break; case structGuiDrawingArea_MouseEvent::Phase::DROP:
451 Melder_flushError (U"Mouse drop not completely handled.");
452 break;
453 }
454 }
455 }
456 }
457 - (void) mouseDown: (NSEvent *) nsEvent {
458 [self mouse: nsEvent inPhase: structGuiDrawingArea_MouseEvent::Phase::CLICK];
459 }
460 - (void) mouseDragged: (NSEvent *) nsEvent {
461 [self mouse: nsEvent inPhase: structGuiDrawingArea_MouseEvent::Phase::DRAG];
462 }
463 - (void) mouseUp: (NSEvent *) nsEvent {
464 [self mouse: nsEvent inPhase: structGuiDrawingArea_MouseEvent::Phase::DROP];
465 }
466 - (void) scrollWheel: (NSEvent *) nsEvent {
467 GuiDrawingArea me = (GuiDrawingArea) d_userData;
468 if (me && (my d_horizontalScrollBar || my d_verticalScrollBar)) {
469 if (my d_horizontalScrollBar) {
470 GuiCocoaScrollBar *cocoaScrollBar = (GuiCocoaScrollBar *) my d_horizontalScrollBar -> d_widget;
471 [cocoaScrollBar scrollBy: [nsEvent scrollingDeltaX]];
472 }
473 if (my d_verticalScrollBar) {
474 GuiCocoaScrollBar *cocoaScrollBar = (GuiCocoaScrollBar *) my d_verticalScrollBar -> d_widget;
475 [cocoaScrollBar scrollBy: [nsEvent scrollingDeltaY]];
476 }
477 } else {
478 [super scrollWheel: nsEvent];
479 }
480 }
481 - (void) magnifyWithEvent: (NSEvent *) nsEvent {
482 GuiDrawingArea me = (GuiDrawingArea) d_userData;
483 if (me && (my d_horizontalScrollBar || my d_verticalScrollBar)) {
484 if (my d_horizontalScrollBar) {
485 GuiCocoaScrollBar *cocoaScrollBar = (GuiCocoaScrollBar *) my d_horizontalScrollBar -> d_widget;
486 [cocoaScrollBar magnifyBy: [nsEvent magnification]];
487 }
488 if (my d_verticalScrollBar) {
489 GuiCocoaScrollBar *cocoaScrollBar = (GuiCocoaScrollBar *) my d_verticalScrollBar -> d_widget;
490 [cocoaScrollBar magnifyBy: [nsEvent magnification]];
491 }
492 } else {
493 [super magnifyWithEvent: nsEvent];
494 }
495 }
496 - (BOOL) isFlipped {
497 return YES;
498 }
499 - (void) keyDown: (NSEvent *) nsEvent {
500 Melder_casual (U"key pressed ");
501 GuiDrawingArea me = (GuiDrawingArea) d_userData;
502 if (me && my d_keyCallback) {
503 structGuiDrawingArea_KeyEvent event = { me, U'\0', false, false, false };
504 event. key = [[nsEvent charactersIgnoringModifiers] characterAtIndex: 0];
505 if (event. key == NSLeftArrowFunctionKey) event. key = 0x2190;
506 if (event. key == NSRightArrowFunctionKey) event. key = 0x2192;
507 if (event. key == NSUpArrowFunctionKey) event. key = 0x2191;
508 if (event. key == NSDownArrowFunctionKey) event. key = 0x2193;
509 Melder_casual (U"key ", event. key);
510 NSUInteger modifiers = [nsEvent modifierFlags];
511 event. shiftKeyPressed = modifiers & NSShiftKeyMask;
512 event. optionKeyPressed = modifiers & NSAlternateKeyMask;
513 event. commandKeyPressed = modifiers & NSCommandKeyMask;
514 try {
515 my d_keyCallback (my d_keyBoss, & event);
516 } catch (MelderError) {
517 Melder_flushError (U"Key press not completely handled.");
518 }
519 }
520 }
521 - (void) cancelOperation: (id) sender {
522 Melder_casual (U"escape key pressed");
523 }
524 @end
525 #endif
526
v_destroy()527 void structGuiDrawingArea :: v_destroy () noexcept {
528 if (Melder_debug == 55)
529 Melder_casual (U"\t", Thing_messageNameAndAddress (this), U" v_destroy");
530 #if cocoa
531 if (our d_widget)
532 [our d_widget setUserData: nullptr]; // undangle reference to this
533 #endif
534 GuiDrawingArea_Parent :: v_destroy ();
535 }
536
GuiDrawingArea_create(GuiForm parent,int left,int right,int top,int bottom,GuiDrawingArea_ExposeCallback exposeCallback,GuiDrawingArea_MouseCallback mouseCallback,GuiDrawingArea_KeyCallback keyCallback,GuiDrawingArea_ResizeCallback resizeCallback,Thing boss,uint32)537 GuiDrawingArea GuiDrawingArea_create (GuiForm parent, int left, int right, int top, int bottom,
538 GuiDrawingArea_ExposeCallback exposeCallback,
539 GuiDrawingArea_MouseCallback mouseCallback,
540 GuiDrawingArea_KeyCallback keyCallback,
541 GuiDrawingArea_ResizeCallback resizeCallback, Thing boss,
542 uint32 /* flags */)
543 {
544 autoGuiDrawingArea me = Thing_new (GuiDrawingArea);
545 if (Melder_debug == 55)
546 Melder_casual (U"\t", Thing_messageNameAndAddress (me.get()), U" init in ", Thing_messageNameAndAddress (parent -> d_shell));
547 my d_shell = parent -> d_shell;
548 my d_shell -> drawingArea = me.get();
549 my d_parent = parent;
550 my d_exposeCallback = exposeCallback;
551 my d_exposeBoss = boss;
552 my mouseCallback = mouseCallback;
553 my mouseBoss = boss;
554 my d_keyCallback = keyCallback;
555 my d_keyBoss = boss;
556 my d_resizeCallback = resizeCallback;
557 my d_resizeBoss = boss;
558 #if gtk
559 my d_widget = gtk_drawing_area_new ();
560 GdkEventMask mask = (GdkEventMask) (GDK_EXPOSURE_MASK // receive exposure events
561 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK // receive click events
562 | GDK_BUTTON_MOTION_MASK // receive motion notifies when a button is pressed
563 | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK
564 | GDK_POINTER_MOTION_HINT_MASK); // receive fewer motion notify events (the cb might take time)
565 gtk_widget_set_events (GTK_WIDGET (my d_widget), mask);
566 g_signal_connect (G_OBJECT (my d_widget), "draw", G_CALLBACK (_guiGtkDrawingArea_drawCallback), me.get());
567 g_signal_connect (G_OBJECT (my d_widget), "destroy", G_CALLBACK (_guiGtkDrawingArea_destroyCallback), me.get());
568 g_signal_connect (G_OBJECT (my d_widget), "button-press-event", G_CALLBACK (_guiGtkDrawingArea_mouseDownCallback), me.get());
569 g_signal_connect (G_OBJECT (my d_widget), "button-release-event", G_CALLBACK (_guiGtkDrawingArea_mouseUpCallback), me.get());
570 //g_signal_connect (G_OBJECT (my d_widget), "drag-motion-event", G_CALLBACK (_guiGtkDrawingArea_mouseUpCallback), me.get());
571 g_signal_connect (G_OBJECT (my d_widget), "motion-notify-event", G_CALLBACK (_guiGtkDrawingArea_mouseDraggedCallback), me.get());
572 if (parent) {
573 Melder_assert (parent -> d_widget);
574 g_signal_connect (G_OBJECT (gtk_widget_get_toplevel (GTK_WIDGET (parent -> d_widget))), "key-press-event",
575 G_CALLBACK (_guiGtkDrawingArea_keyCallback), me.get());
576 }
577 g_signal_connect (G_OBJECT (my d_widget), "size-allocate", G_CALLBACK (_guiGtkDrawingArea_resizeCallback), me.get());
578
579 _GuiObject_setUserData (my d_widget, me.get());
580 my v_positionInForm (my d_widget, left, right, top, bottom, parent);
581 #elif motif
582 my d_widget = _Gui_initializeWidget (xmDrawingAreaWidgetClass, parent -> d_widget, U"drawingArea");
583 _GuiObject_setUserData (my d_widget, me.get());
584 my d_widget -> window = CreateWindowEx (0, Melder_peek32toW (_GuiWin_getDrawingAreaClassName ()), L"drawingArea",
585 WS_CHILD | WS_BORDER | WS_CLIPSIBLINGS,
586 my d_widget -> x, my d_widget -> y, my d_widget -> width, my d_widget -> height, my d_widget -> parent -> window, nullptr, theGui.instance, nullptr);
587 SetWindowLongPtr (my d_widget -> window, GWLP_USERDATA, (LONG_PTR) my d_widget);
588 my v_positionInForm (my d_widget, left, right, top, bottom, parent);
589 #elif cocoa
590 GuiCocoaDrawingArea *drawingArea = [[GuiCocoaDrawingArea alloc] init];
591 if (Melder_debug == 55)
592 Melder_casual (U"\t\tGuiCocoaDrawingArea-", Melder_pointer (drawingArea), U" init in ", Thing_messageNameAndAddress (me.get()));
593 my d_widget = (GuiObject) drawingArea;
594 my v_positionInForm (my d_widget, left, right, top, bottom, parent);
595 [drawingArea setUserData: me.get()];
596 if (keyCallback) {
597 [[drawingArea window] makeFirstResponder: drawingArea]; // needed in DemoWindow
598 }
599 #endif
600 return me.releaseToAmbiguousOwner();
601 }
602
GuiDrawingArea_createShown(GuiForm parent,int left,int right,int top,int bottom,GuiDrawingArea_ExposeCallback exposeCallback,GuiDrawingArea_MouseCallback mouseCallback,GuiDrawingArea_KeyCallback keyCallback,GuiDrawingArea_ResizeCallback resizeCallback,Thing boss,uint32 flags)603 GuiDrawingArea GuiDrawingArea_createShown (GuiForm parent, int left, int right, int top, int bottom,
604 GuiDrawingArea_ExposeCallback exposeCallback,
605 GuiDrawingArea_MouseCallback mouseCallback,
606 GuiDrawingArea_KeyCallback keyCallback,
607 GuiDrawingArea_ResizeCallback resizeCallback, Thing boss,
608 uint32 flags)
609 {
610 GuiDrawingArea me = GuiDrawingArea_create (parent, left, right, top, bottom,
611 exposeCallback, mouseCallback,
612 keyCallback, resizeCallback, boss, flags
613 );
614 GuiThing_show (me);
615 return me;
616 }
617
GuiDrawingArea_create(GuiScrolledWindow parent,int width,int height,GuiDrawingArea_ExposeCallback exposeCallback,GuiDrawingArea_MouseCallback mouseCallback,GuiDrawingArea_KeyCallback keyCallback,GuiDrawingArea_ResizeCallback resizeCallback,Thing boss,uint32)618 GuiDrawingArea GuiDrawingArea_create (GuiScrolledWindow parent, int width, int height,
619 GuiDrawingArea_ExposeCallback exposeCallback,
620 GuiDrawingArea_MouseCallback mouseCallback,
621 GuiDrawingArea_KeyCallback keyCallback,
622 GuiDrawingArea_ResizeCallback resizeCallback, Thing boss,
623 uint32 /* flags */)
624 {
625 autoGuiDrawingArea me = Thing_new (GuiDrawingArea);
626 my d_shell = parent -> d_shell;
627 my d_shell -> drawingArea = me.get();
628 my d_parent = parent;
629 my d_exposeCallback = exposeCallback;
630 my d_exposeBoss = boss;
631 my mouseCallback = mouseCallback;
632 my mouseBoss = boss;
633 my d_keyCallback = keyCallback;
634 my d_keyBoss = boss;
635 my d_resizeCallback = resizeCallback;
636 my d_resizeBoss = boss;
637 #if gtk
638 my d_widget = gtk_drawing_area_new ();
639 GdkEventMask mask = (GdkEventMask) (GDK_EXPOSURE_MASK // receive exposure events
640 | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK // receive click events
641 | GDK_BUTTON_MOTION_MASK // receive motion notifies when a button is pressed
642 | GDK_KEY_PRESS_MASK | GDK_KEY_RELEASE_MASK
643 | GDK_POINTER_MOTION_HINT_MASK); // receive fewer motion notify events (the cb might take time)
644 gtk_widget_set_events (GTK_WIDGET (my d_widget), mask);
645 g_signal_connect (G_OBJECT (my d_widget), "draw", G_CALLBACK (_guiGtkDrawingArea_drawCallback), me.get());
646 g_signal_connect (G_OBJECT (my d_widget), "destroy", G_CALLBACK (_guiGtkDrawingArea_destroyCallback), me.get());
647 g_signal_connect (G_OBJECT (my d_widget), "button-press-event", G_CALLBACK (_guiGtkDrawingArea_mouseDownCallback), me.get());
648 g_signal_connect (G_OBJECT (my d_widget), "button-release-event", G_CALLBACK (_guiGtkDrawingArea_mouseUpCallback), me.get());
649 g_signal_connect (G_OBJECT (my d_widget), "motion-notify-event", G_CALLBACK (_guiGtkDrawingArea_mouseDraggedCallback), me.get());
650 if (parent) {
651 g_signal_connect (G_OBJECT (gtk_widget_get_toplevel (GTK_WIDGET (parent -> d_widget))), "key-press-event",
652 G_CALLBACK (_guiGtkDrawingArea_keyCallback), me.get());
653 }
654 g_signal_connect (G_OBJECT (my d_widget), "size-allocate", G_CALLBACK (_guiGtkDrawingArea_resizeCallback), me.get());
655 _GuiObject_setUserData (my d_widget, me.get());
656 my v_positionInScrolledWindow (my d_widget, width, height, parent);
657 #elif motif
658 my d_widget = _Gui_initializeWidget (xmDrawingAreaWidgetClass, parent -> d_widget, U"drawingArea");
659 _GuiObject_setUserData (my d_widget, me.get());
660 my d_widget -> window = CreateWindowEx (0, Melder_peek32toW (_GuiWin_getDrawingAreaClassName ()), L"drawingArea",
661 WS_CHILD | WS_BORDER | WS_CLIPSIBLINGS,
662 0, 0, my d_widget -> width, my d_widget -> height, my d_widget -> parent -> window, nullptr, theGui.instance, nullptr);
663 SetWindowLongPtr (my d_widget -> window, GWLP_USERDATA, (LONG_PTR) my d_widget);
664 my v_positionInScrolledWindow (my d_widget, width, height, parent);
665 #elif cocoa
666 GuiCocoaDrawingArea *drawingArea = [[GuiCocoaDrawingArea alloc] init];
667 my d_widget = (GuiObject) drawingArea;
668 my v_positionInScrolledWindow (my d_widget, width, height, parent);
669 [drawingArea setUserData: me.get()];
670 #endif
671 return me.releaseToAmbiguousOwner();
672 }
673
GuiDrawingArea_createShown(GuiScrolledWindow parent,int width,int height,GuiDrawingArea_ExposeCallback exposeCallback,GuiDrawingArea_MouseCallback mouseCallback,GuiDrawingArea_KeyCallback keyCallback,GuiDrawingArea_ResizeCallback resizeCallback,Thing boss,uint32 flags)674 GuiDrawingArea GuiDrawingArea_createShown (GuiScrolledWindow parent, int width, int height,
675 GuiDrawingArea_ExposeCallback exposeCallback,
676 GuiDrawingArea_MouseCallback mouseCallback,
677 GuiDrawingArea_KeyCallback keyCallback,
678 GuiDrawingArea_ResizeCallback resizeCallback, Thing boss,
679 uint32 flags)
680 {
681 GuiDrawingArea me = GuiDrawingArea_create (parent, width, height,
682 exposeCallback, mouseCallback,
683 keyCallback, resizeCallback, boss, flags
684 );
685 GuiThing_show (me);
686 return me;
687 }
688
GuiDrawingArea_setSwipable(GuiDrawingArea me,GuiScrollBar horizontalScrollBar,GuiScrollBar verticalScrollBar)689 void GuiDrawingArea_setSwipable (GuiDrawingArea me, GuiScrollBar horizontalScrollBar, GuiScrollBar verticalScrollBar) {
690 my d_horizontalScrollBar = horizontalScrollBar;
691 my d_verticalScrollBar = verticalScrollBar;
692 #if gtk
693 gtk_widget_add_events (GTK_WIDGET (my d_widget), GDK_SCROLL_MASK);
694 g_signal_connect (G_OBJECT (my d_widget), "scroll-event", G_CALLBACK (_guiGtkDrawingArea_swipeCallback), me);
695 #endif
696 }
697
GuiDrawingArea_setExposeCallback(GuiDrawingArea me,GuiDrawingArea_ExposeCallback callback,Thing boss)698 void GuiDrawingArea_setExposeCallback (GuiDrawingArea me, GuiDrawingArea_ExposeCallback callback, Thing boss) {
699 my d_exposeCallback = callback;
700 my d_exposeBoss = boss;
701 }
702
GuiDrawingArea_setMouseCallback(GuiDrawingArea me,GuiDrawingArea_MouseCallback callback,Thing boss)703 void GuiDrawingArea_setMouseCallback (GuiDrawingArea me, GuiDrawingArea_MouseCallback callback, Thing boss) {
704 my mouseCallback = callback;
705 my mouseBoss = boss;
706 }
707
GuiDrawingArea_setResizeCallback(GuiDrawingArea me,GuiDrawingArea_ResizeCallback callback,Thing boss)708 void GuiDrawingArea_setResizeCallback (GuiDrawingArea me, GuiDrawingArea_ResizeCallback callback, Thing boss) {
709 my d_resizeCallback = callback;
710 my d_resizeBoss = boss;
711 }
712
713 /* End of file GuiDrawingArea.cpp */
714