1 /* GuiWindow.cpp
2 *
3 * Copyright (C) 1993-2018,2020,2021 Paul Boersma, 2013 Tom Naughton
4 *
5 * This code is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or (at
8 * your option) any later version.
9 *
10 * This code is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 * See the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this work. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "GuiP.h"
20 #include "../kar/UnicodeData.h"
21 #include "machine.h"
22 #include <locale.h>
23
24 Thing_implement (GuiWindow, GuiShell, 0);
25
26 #undef iam
27 #define iam(x) x me = (x) void_me
28
29 #if gtk
_GuiWindow_destroyCallback(GuiObject widget,GdkEvent * event,gpointer void_me)30 static gboolean _GuiWindow_destroyCallback (GuiObject widget, GdkEvent *event, gpointer void_me) {
31 (void) widget;
32 iam (GuiWindow);
33 forget (me);
34 return true;
35 }
_GuiWindow_goAwayCallback(GuiObject widget,GdkEvent * event,gpointer void_me)36 static gboolean _GuiWindow_goAwayCallback (GuiObject widget, GdkEvent *event, gpointer void_me) {
37 (void) widget;
38 iam (GuiWindow);
39 if (my d_goAwayCallback) {
40 my d_goAwayCallback (my d_goAwayBoss);
41 }
42 return true;
43 }
_GuiWindow_child_resizeCallback(GtkWidget * childWidget,gpointer data)44 static void _GuiWindow_child_resizeCallback (GtkWidget *childWidget, gpointer data) {
45 GtkAllocation *parentAllocation = (GtkAllocation *) data;
46 GtkWidget *parentWidget = gtk_widget_get_parent (childWidget);
47 Thing_cast (GuiThing, child, _GuiObject_getUserData (childWidget));
48 if (child) {
49 GuiControl control = nullptr;
50 if (Thing_isa (child, classGuiWindow)) {
51 control = static_cast <GuiWindow> (child);
52 //Melder_casual (U"menu bar");
53 } else if (Thing_isa (child, classGuiControl)) {
54 control = static_cast <GuiControl> (child);
55 //Melder_casual (U"control");
56 } else if (Thing_isa (child, classGuiMenu)) {
57 Thing_cast (GuiMenu, menu, child);
58 control = menu -> d_cascadeButton.get();
59 //Melder_casual (U"menu");
60 }
61 if (control) {
62 /*
63 Move and resize.
64 */
65 trace (U"moving child of class ", Thing_className (control));
66 int left = control -> d_left, right = control -> d_right, top = control -> d_top, bottom = control -> d_bottom;
67 if (left < 0) left += parentAllocation -> width; // this replicates structGuiControl :: v_positionInForm ()
68 if (right <= 0) right += parentAllocation -> width;
69 if (top < 0) top += parentAllocation -> height;
70 if (bottom <= 0) bottom += parentAllocation -> height;
71 trace (U"moving child to (", left, U",", top, U") with size ", right - left, U" x ", bottom - top, U".");
72 GtkAllocation childAllocation { parentAllocation -> x + left, parentAllocation -> y + top, right - left, bottom - top };
73 gtk_widget_size_allocate (GTK_WIDGET (childWidget), & childAllocation);
74 trace (U"moved child of class ", Thing_className (control));
75 }
76 }
77 }
_GuiWindow_resizeCallback(GuiObject widget,GtkAllocation * allocation,gpointer void_me)78 static void _GuiWindow_resizeCallback (GuiObject widget, GtkAllocation *allocation, gpointer void_me) {
79 (void) widget;
80 iam (GuiWindow);
81 trace (U"fixed received size allocation: (", allocation -> x, U", ", allocation -> y,
82 U"), ", allocation -> width, U" x ", allocation -> height, U".");
83 /*
84 Apparently, GTK sends the size allocation message both to the shell and to its fixed-container child.
85 we could capture the message either from the shell or from the fixed; we choose to do it from the fixed.
86 */
87 Melder_assert (GTK_IS_FIXED (widget));
88 Melder_assert (GTK_IS_FIXED (my d_widget));
89 /*
90 We move and resize all the children of the fixed.
91 */
92 #if defined (chrome)
93 {
94 GtkAllocation surrogateShellTitleLabelWidgetAllocation { allocation -> x, allocation -> y, allocation -> width, 31 };
95 gtk_widget_size_allocate (GTK_WIDGET (my chrome_surrogateShellTitleLabelWidget), & surrogateShellTitleLabelWidgetAllocation);
96 }
97 #endif
98 gtk_container_foreach (GTK_CONTAINER (widget), _GuiWindow_child_resizeCallback, allocation);
99 //gtk_container_foreach (GTK_CONTAINER (my d_widget), _GuiWindow_child_resizeCallback, allocation);
100 my d_width = allocation -> width;
101 my d_height = allocation -> height;
102 trace (U"end");
103 }
104 #elif motif
_GuiMotifWindow_destroyCallback(GuiObject widget,XtPointer void_me,XtPointer call)105 static void _GuiMotifWindow_destroyCallback (GuiObject widget, XtPointer void_me, XtPointer call) {
106 (void) widget; (void) call;
107 iam (GuiWindow);
108 if (my d_xmMenuBar) {
109 }
110 trace (U"destroying window widget");
111 forget (me);
112 }
_GuiMotifWindow_goAwayCallback(GuiObject widget,XtPointer void_me,XtPointer call)113 static void _GuiMotifWindow_goAwayCallback (GuiObject widget, XtPointer void_me, XtPointer call) {
114 (void) widget; (void) call;
115 iam (GuiWindow);
116 if (my d_goAwayCallback) {
117 //Melder_casual (U"_GuiMotifWindow_goAwayCallback: before");
118 my d_goAwayCallback (my d_goAwayBoss);
119 //Melder_casual (U"_GuiMotifWindow_goAwayCallback: after");
120 }
121 }
122 #endif
123
GuiWindow_create(int x,int y,int width,int height,int minimumWidth,int minimumHeight,conststring32 title,GuiShell_GoAwayCallback goAwayCallback,Thing goAwayBoss,uint32 flags)124 GuiWindow GuiWindow_create (int x, int y, int width, int height, int minimumWidth, int minimumHeight,
125 conststring32 title /* cattable */, GuiShell_GoAwayCallback goAwayCallback, Thing goAwayBoss, uint32 flags)
126 {
127 autoGuiWindow me = Thing_new (GuiWindow);
128 if (Melder_debug == 55)
129 Melder_casual (U"\t", Thing_messageNameAndAddress (me.get()), U" init");
130 my d_parent = nullptr;
131 my d_goAwayCallback = goAwayCallback;
132 my d_goAwayBoss = goAwayBoss;
133 #if gtk
134 (void) flags;
135 GuiGtk_initialize ();
136 my d_gtkWindow = (GtkWindow *) gtk_window_new (GTK_WINDOW_TOPLEVEL);
137 g_signal_connect (G_OBJECT (my d_gtkWindow), "delete-event", goAwayCallback ? G_CALLBACK (_GuiWindow_goAwayCallback) : G_CALLBACK (gtk_widget_hide), me.get());
138 g_signal_connect (G_OBJECT (my d_gtkWindow), "destroy-event", G_CALLBACK (_GuiWindow_destroyCallback), me.get());
139
140 gtk_window_set_default_size (GTK_WINDOW (my d_gtkWindow), width, height);
141 gtk_window_set_resizable (GTK_WINDOW (my d_gtkWindow), true);
142
143 my d_widget = gtk_fixed_new ();
144 _GuiObject_setUserData (my d_widget, me.get());
145 gtk_widget_set_size_request (GTK_WIDGET (my d_widget), minimumWidth, minimumHeight);
146 gtk_container_add (GTK_CONTAINER (my d_gtkWindow), GTK_WIDGET (my d_widget));
147 GdkGeometry geometry = { minimumWidth, minimumHeight, 0, 0, 0, 0, 0, 0, 0, 0, GDK_GRAVITY_NORTH_WEST };
148 gtk_window_set_geometry_hints (my d_gtkWindow, GTK_WIDGET (my d_widget), & geometry, GDK_HINT_MIN_SIZE);
149 g_signal_connect (G_OBJECT (my d_widget), "size-allocate", G_CALLBACK (_GuiWindow_resizeCallback), me.get());
150 //g_signal_connect (G_OBJECT (my d_gtkWindow), "size-allocate", G_CALLBACK (_GuiWindow_resizeCallback), me.get());
151 #if defined (chrome)
152 my chrome_surrogateShellTitleLabelWidget = gtk_label_new (Melder_peek32to8 (title));
153 gtk_label_set_use_markup (GTK_LABEL (my chrome_surrogateShellTitleLabelWidget), true);
154 gtk_widget_set_size_request (GTK_WIDGET (my chrome_surrogateShellTitleLabelWidget), minimumWidth, 31 /*Machine_getTextHeight()*/);
155 gtk_misc_set_alignment (GTK_MISC (my chrome_surrogateShellTitleLabelWidget), 0.5, 0.0);
156 gtk_fixed_put (GTK_FIXED (my d_widget), GTK_WIDGET (my chrome_surrogateShellTitleLabelWidget), 8, 0);
157 gtk_widget_show (GTK_WIDGET (my chrome_surrogateShellTitleLabelWidget));
158 #endif
159 GuiShell_setTitle (me.get(), title);
160 gint rootX, rootY;
161 gtk_window_get_position (my d_gtkWindow, & rootX, & rootY);
162 trace (rootX, U" root ", rootY);
163 #elif motif
164 my d_xmShell = XmCreateShell (nullptr, flags & GuiWindow_FULLSCREEN ? "Praatwulgfullscreen" : "Praatwulg", nullptr, 0);
165 XtVaSetValues (my d_xmShell, XmNdeleteResponse, goAwayCallback ? XmDO_NOTHING : XmUNMAP, nullptr);
166 XtVaSetValues (my d_xmShell, XmNx, x, XmNy, y, XmNwidth, (Dimension) width, XmNheight, (Dimension) height, nullptr);
167 if (goAwayCallback)
168 XmAddWMProtocolCallback (my d_xmShell, 'delw', _GuiMotifWindow_goAwayCallback, (char *) me.get());
169 GuiShell_setTitle (me.get(), title);
170 my d_widget = XmCreateForm (my d_xmShell, "dialog", nullptr, 0);
171 _GuiObject_setUserData (my d_widget, me.get());
172 XtAddCallback (my d_widget, XmNdestroyCallback, _GuiMotifWindow_destroyCallback, me.get());
173 XtVaSetValues (my d_widget, XmNdialogStyle, XmDIALOG_MODELESS, XmNautoUnmanage, False, nullptr);
174 #elif cocoa
175 (void) flags;
176 NSRect rect = { { static_cast<CGFloat>(x), static_cast<CGFloat>(y) }, { static_cast<CGFloat>(width), static_cast<CGFloat>(height) } };
177 my d_cocoaShell = [[GuiCocoaShell alloc]
178 initWithContentRect: rect
179 styleMask: NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask
180 backing: NSBackingStoreBuffered
181 defer: false];
182 if (Melder_debug == 55)
183 Melder_casual (U"\t\tGuiCocoaShell-", Melder_pointer (my d_cocoaShell), U" init in ", Thing_messageNameAndAddress (me.get()));
184 [my d_cocoaShell setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary];
185 [my d_cocoaShell setMinSize: NSMakeSize (minimumWidth, minimumHeight)];
186 GuiShell_setTitle (me.get(), title);
187 [my d_cocoaShell makeKeyAndOrderFront: nil];
188 my d_widget = (GuiObject) [my d_cocoaShell contentView]; // BUG: this d_widget doesn't have the GuiCocoaAny protocol
189 _GuiObject_setUserData (my d_cocoaShell, me.get());
190 //if (! theGuiCocoaWindowDelegate) {
191 // theGuiCocoaWindowDelegate = [[GuiCocoaWindowDelegate alloc] init];
192 //}
193 //[my d_cocoaWindow setDelegate: theGuiCocoaWindowDelegate];
194 #endif
195 my d_width = width;
196 my d_height = height;
197 my d_shell = me.get();
198 return me.releaseToAmbiguousOwner();
199 }
200
201 uinteger theGuiTopLowAccelerators [8];
202
GuiWindow_addMenuBar(GuiWindow me)203 void GuiWindow_addMenuBar (GuiWindow me) {
204 #if gtk
205 my d_gtkMenuBar = (GtkMenuBar *) gtk_menu_bar_new ();
206 _GuiObject_setUserData (my d_gtkMenuBar, me);
207 my v_positionInForm (my d_gtkMenuBar, 0, 0, Machine_getMenuBarTop (), Machine_getMenuBarBottom (), me); // BUG?
208
209 // we need an accelerator group for each window we're creating accelerated menus on
210 GuiObject topwin = gtk_widget_get_toplevel (GTK_WIDGET (my d_widget));
211 Melder_assert (topwin == my d_gtkWindow);
212 GtkAccelGroup *ag = gtk_accel_group_new ();
213 gtk_window_add_accel_group (GTK_WINDOW (topwin), ag);
214 // unfortunately, menu-bars don't fiddle with accel-groups, so we need a way
215 // to pass it to the sub-menus created upon this bar for their items to have
216 // access to the accel-group
217 g_object_set_data (G_OBJECT (my d_gtkMenuBar), "accel-group", ag);
218 gtk_widget_show (GTK_WIDGET (my d_gtkMenuBar));
219 #elif motif
220 my d_xmMenuBar = XmCreateMenuBar (my d_widget, "menuBar", nullptr, 0);
221 XtVaSetValues (my d_xmMenuBar, XmNleftAttachment, XmATTACH_FORM, XmNrightAttachment, XmATTACH_FORM, nullptr);
222 XtManageChild (my d_xmMenuBar);
223 #elif cocoa
224 (void) me; // this is handled elsewhere
225 #endif
226 }
227
GuiWindow_setDirty(GuiWindow me,bool dirty)228 bool GuiWindow_setDirty (GuiWindow me, bool dirty) {
229 #if gtk
230 (void) dirty;
231 return false;
232 #elif motif
233 (void) dirty;
234 return false;
235 #elif cocoa
236 [my d_cocoaShell setDocumentEdited: dirty];
237 return true;
238 #else
239 (void) dirty;
240 return false;
241 #endif
242 }
243
244 /* End of file GuiWindow.cpp */
245