1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/minifram.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Copyright: (c) 1998 Robert Roebling
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11
12 #if wxUSE_MINIFRAME
13
14 #include "wx/minifram.h"
15
16 #ifndef WX_PRECOMP
17 #include "wx/settings.h"
18 #include "wx/dcclient.h"
19 #endif
20
21 #ifdef __WXGTK3__
22 #include "wx/gtk/dc.h"
23 #else
24 #include "wx/gtk/dcclient.h"
25 #endif
26
27 #include "wx/gtk/private/wrapgtk.h"
28 #include "wx/gtk/private/gtk3-compat.h"
29
30 //-----------------------------------------------------------------------------
31 // data
32 //-----------------------------------------------------------------------------
33
34 extern bool g_blockEventsOnDrag;
35 extern bool g_blockEventsOnScroll;
36
37 //-----------------------------------------------------------------------------
38 // "expose_event" of m_mainWidget
39 //-----------------------------------------------------------------------------
40
41 extern "C" {
42 #ifdef __WXGTK3__
draw(GtkWidget * widget,cairo_t * cr,wxMiniFrame * win)43 static gboolean draw(GtkWidget* widget, cairo_t* cr, wxMiniFrame* win)
44 #else
45 static gboolean expose_event(GtkWidget* widget, GdkEventExpose* gdk_event, wxMiniFrame* win)
46 #endif
47 {
48 #ifdef __WXGTK3__
49 if (!gtk_cairo_should_draw_window(cr, gtk_widget_get_window(widget)))
50 return false;
51
52 GtkStyleContext* sc = gtk_widget_get_style_context(widget);
53 gtk_style_context_save(sc);
54 gtk_style_context_add_class(sc, GTK_STYLE_CLASS_BUTTON);
55 gtk_render_frame(sc, cr, 0, 0, win->m_width, win->m_height);
56 gtk_style_context_restore(sc);
57
58 wxGTKCairoDC dc(cr, win);
59 #else
60 if (gdk_event->count > 0 ||
61 gdk_event->window != gtk_widget_get_window(widget))
62 {
63 return false;
64 }
65
66 gtk_paint_shadow (gtk_widget_get_style(widget),
67 gtk_widget_get_window(widget),
68 GTK_STATE_NORMAL,
69 GTK_SHADOW_OUT,
70 NULL, NULL, NULL, // FIXME: No clipping?
71 0, 0,
72 win->m_width, win->m_height);
73
74 wxClientDC dc(win);
75
76 wxDCImpl *impl = dc.GetImpl();
77 wxClientDCImpl *gtk_impl = wxDynamicCast( impl, wxClientDCImpl );
78 gtk_impl->m_gdkwindow = gtk_widget_get_window(widget); // Hack alert
79 #endif
80
81 int style = win->GetWindowStyle();
82
83 if (style & wxRESIZE_BORDER)
84 {
85 dc.SetBrush( *wxGREY_BRUSH );
86 dc.SetPen( *wxTRANSPARENT_PEN );
87 dc.DrawRectangle(win->m_width - 14, win->m_height - win->m_miniEdge, 14, win->m_miniEdge);
88 dc.DrawRectangle(win->m_width - win->m_miniEdge, win->m_height - 14, win->m_miniEdge, 14);
89 }
90
91 if (win->m_miniTitle && !win->GetTitle().empty())
92 {
93 dc.SetFont( *wxSMALL_FONT );
94
95 wxBrush brush(wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT));
96 dc.SetBrush( brush );
97 dc.SetPen( *wxTRANSPARENT_PEN );
98 dc.DrawRectangle( win->m_miniEdge-1,
99 win->m_miniEdge-1,
100 win->m_width - (2*(win->m_miniEdge-1)),
101 15 );
102
103 const wxColour textColor = wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHTTEXT);
104 dc.SetTextForeground(textColor);
105 dc.DrawText( win->GetTitle(), 6, 4 );
106
107 if (style & wxCLOSE_BOX)
108 {
109 dc.SetTextBackground(textColor);
110 dc.DrawBitmap( win->m_closeButton, win->m_width-18, 3, true );
111 }
112 }
113
114 return false;
115 }
116 }
117
118 //-----------------------------------------------------------------------------
119 // "button_press_event" of m_mainWidget
120 //-----------------------------------------------------------------------------
121
122 extern "C" {
123 static gboolean
gtk_window_button_press_callback(GtkWidget * widget,GdkEventButton * gdk_event,wxMiniFrame * win)124 gtk_window_button_press_callback(GtkWidget* widget, GdkEventButton* gdk_event, wxMiniFrame* win)
125 {
126 if (gdk_event->window != gtk_widget_get_window(widget))
127 return false;
128 if (win->m_isDragMove || g_blockEventsOnDrag || g_blockEventsOnScroll)
129 return true;
130
131 int style = win->GetWindowStyle();
132
133 int y = (int)gdk_event->y;
134 int x = (int)gdk_event->x;
135
136 if ((style & wxRESIZE_BORDER) &&
137 (x > win->m_width-14) && (y > win->m_height-14))
138 {
139 gtk_window_begin_resize_drag(GTK_WINDOW(win->m_widget),
140 GDK_WINDOW_EDGE_SOUTH_EAST,
141 gdk_event->button,
142 int(gdk_event->x_root), int(gdk_event->y_root),
143 gdk_event->time);
144
145 return TRUE;
146 }
147
148 if (win->m_miniTitle && (style & wxCLOSE_BOX))
149 {
150 if ((y > 3) && (y < 19) && (x > win->m_width-19) && (x < win->m_width-3))
151 {
152 win->Close();
153 return TRUE;
154 }
155 }
156
157 if (y >= win->m_miniEdge + win->m_miniTitle)
158 return true;
159
160 gdk_window_raise(gtk_widget_get_window(win->m_widget));
161
162 #ifdef __WXGTK3__
163 #ifndef __WXGTK4__
164 GdkDisplay* display = gdk_window_get_display(gdk_event->window);
165 if (strcmp("GdkWaylandDisplay", g_type_name(G_TYPE_FROM_INSTANCE(display))) == 0)
166 #endif
167 {
168 gtk_window_begin_move_drag(GTK_WINDOW(win->m_widget),
169 gdk_event->button,
170 int(gdk_event->x_root), int(gdk_event->y_root),
171 gdk_event->time);
172
173 return true;
174 }
175 #endif
176 #ifndef __WXGTK4__
177 const GdkEventMask mask = GdkEventMask(
178 GDK_BUTTON_PRESS_MASK |
179 GDK_BUTTON_RELEASE_MASK |
180 GDK_POINTER_MOTION_MASK |
181 GDK_BUTTON_MOTION_MASK);
182 #ifdef __WXGTK3__
183 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
184 gdk_device_grab(
185 gdk_event->device, gdk_event->window, GDK_OWNERSHIP_NONE,
186 false, mask, NULL, gdk_event->time);
187 wxGCC_WARNING_RESTORE()
188 #else
189 gdk_pointer_grab(gdk_event->window, false, mask, NULL, NULL, gdk_event->time);
190 #endif
191
192 win->m_dragOffset.Set(int(gdk_event->x), int(gdk_event->y));
193 win->m_isDragMove = true;
194
195 return TRUE;
196 #endif // !__WXGTK4__
197 }
198 }
199
200 #ifndef __WXGTK4__
201 //-----------------------------------------------------------------------------
202 // "button-release-event"
203 //-----------------------------------------------------------------------------
204
205 extern "C" {
206 static gboolean
button_release_event(GtkWidget * widget,GdkEventButton * gdk_event,wxMiniFrame * win)207 button_release_event(GtkWidget* widget, GdkEventButton* gdk_event, wxMiniFrame* win)
208 {
209 if (gdk_event->window != gtk_widget_get_window(widget))
210 return false;
211 if (!win->m_isDragMove || g_blockEventsOnDrag || g_blockEventsOnScroll)
212 return true;
213
214 win->m_isDragMove = false;
215
216 #ifdef __WXGTK3__
217 wxGCC_WARNING_SUPPRESS(deprecated-declarations)
218 gdk_device_ungrab(gdk_event->device, gdk_event->time);
219 wxGCC_WARNING_RESTORE()
220 #else
221 gdk_pointer_ungrab(gdk_event->time);
222 #endif
223
224 return true;
225 }
226 }
227 #endif // !__WXGTK4__
228
229 //-----------------------------------------------------------------------------
230 // "leave_notify_event" of m_mainWidget
231 //-----------------------------------------------------------------------------
232
233 extern "C" {
234 static gboolean
gtk_window_leave_callback(GtkWidget * widget,GdkEventCrossing * gdk_event,wxMiniFrame *)235 gtk_window_leave_callback(GtkWidget *widget,
236 GdkEventCrossing* gdk_event,
237 wxMiniFrame*)
238 {
239 if (g_blockEventsOnDrag) return FALSE;
240 if (gdk_event->window != gtk_widget_get_window(widget))
241 return false;
242
243 gdk_window_set_cursor(gtk_widget_get_window(widget), NULL);
244
245 return FALSE;
246 }
247 }
248
249 //-----------------------------------------------------------------------------
250 // "motion_notify_event" of m_mainWidget
251 //-----------------------------------------------------------------------------
252
253 extern "C" {
254 static gboolean
gtk_window_motion_notify_callback(GtkWidget * widget,GdkEventMotion * gdk_event,wxMiniFrame * win)255 gtk_window_motion_notify_callback( GtkWidget *widget, GdkEventMotion *gdk_event, wxMiniFrame *win )
256 {
257 if (gdk_event->window != gtk_widget_get_window(widget))
258 return false;
259 if (g_blockEventsOnDrag) return TRUE;
260 if (g_blockEventsOnScroll) return TRUE;
261
262 #ifndef __WXGTK4__
263 if (win->m_isDragMove)
264 {
265 gtk_window_move(GTK_WINDOW(win->m_widget),
266 int(gdk_event->x_root) - win->m_dragOffset.x,
267 int(gdk_event->y_root) - win->m_dragOffset.y);
268
269 return true;
270 }
271 #endif
272 {
273 if (win->GetWindowStyle() & wxRESIZE_BORDER)
274 {
275 const int x = int(gdk_event->x);
276 const int y = int(gdk_event->y);
277
278 GdkCursor* cursor = NULL;
279 GdkWindow* window = gtk_widget_get_window(widget);
280 if ((x > win->m_width-14) && (y > win->m_height-14))
281 {
282 GdkDisplay* display = gdk_window_get_display(window);
283 cursor = gdk_cursor_new_for_display(display, GDK_BOTTOM_RIGHT_CORNER);
284 }
285 gdk_window_set_cursor(window, cursor);
286 if (cursor)
287 {
288 #ifdef __WXGTK3__
289 g_object_unref(cursor);
290 #else
291 gdk_cursor_unref(cursor);
292 #endif
293 }
294 }
295 }
296
297 return TRUE;
298 }
299 }
300
301 //-----------------------------------------------------------------------------
302 // wxMiniFrame
303 //-----------------------------------------------------------------------------
304
305 static unsigned char close_bits[]={
306 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xfb, 0xef, 0xdb, 0xed, 0x8b, 0xe8,
307 0x1b, 0xec, 0x3b, 0xee, 0x1b, 0xec, 0x8b, 0xe8, 0xdb, 0xed, 0xfb, 0xef,
308 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
309
310
311 wxIMPLEMENT_DYNAMIC_CLASS(wxMiniFrame, wxFrame);
312
~wxMiniFrame()313 wxMiniFrame::~wxMiniFrame()
314 {
315 if (m_widget)
316 {
317 GtkWidget* eventbox = gtk_bin_get_child(GTK_BIN(m_widget));
318 GTKDisconnect(eventbox);
319 }
320 }
321
Create(wxWindow * parent,wxWindowID id,const wxString & title,const wxPoint & pos,const wxSize & size,long style,const wxString & name)322 bool wxMiniFrame::Create( wxWindow *parent, wxWindowID id, const wxString &title,
323 const wxPoint &pos, const wxSize &size,
324 long style, const wxString &name )
325 {
326 m_isDragMove = false;
327 m_miniTitle = 0;
328 if (style & wxCAPTION)
329 m_miniTitle = 16;
330
331 if (style & wxRESIZE_BORDER)
332 m_miniEdge = 4;
333 else
334 m_miniEdge = 3;
335
336 // don't allow sizing smaller than decorations
337 int minWidth = 2 * m_miniEdge;
338 int minHeight = 2 * m_miniEdge + m_miniTitle;
339 if (m_minWidth < minWidth)
340 m_minWidth = minWidth;
341 if (m_minHeight < minHeight)
342 m_minHeight = minHeight;
343
344 wxFrame::Create( parent, id, title, pos, size, style, name );
345
346 // Use a GtkEventBox for the title and borders. Using m_widget for this
347 // almost works, except that setting the resize cursor has no effect.
348 GtkWidget* eventbox = gtk_event_box_new();
349 gtk_widget_add_events(eventbox, GDK_POINTER_MOTION_MASK);
350 gtk_widget_show(eventbox);
351 #ifdef __WXGTK3__
352 g_object_ref(m_mainWidget);
353 gtk_container_remove(GTK_CONTAINER(m_widget), m_mainWidget);
354 gtk_container_add(GTK_CONTAINER(eventbox), m_mainWidget);
355 g_object_unref(m_mainWidget);
356
357 gtk_widget_set_margin_start(m_mainWidget, m_miniEdge);
358 gtk_widget_set_margin_end(m_mainWidget, m_miniEdge);
359 gtk_widget_set_margin_top(m_mainWidget, m_miniTitle + m_miniEdge);
360 gtk_widget_set_margin_bottom(m_mainWidget, m_miniEdge);
361 #else
362 // Use a GtkAlignment to position m_mainWidget inside the decorations
363 GtkWidget* alignment = gtk_alignment_new(0, 0, 1, 1);
364 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment),
365 m_miniTitle + m_miniEdge, m_miniEdge, m_miniEdge, m_miniEdge);
366 gtk_widget_show(alignment);
367 // The GtkEventBox and GtkAlignment go between m_widget and m_mainWidget
368 gtk_widget_reparent(m_mainWidget, alignment);
369 gtk_container_add(GTK_CONTAINER(eventbox), alignment);
370 #endif
371 gtk_container_add(GTK_CONTAINER(m_widget), eventbox);
372
373 m_gdkDecor = 0;
374 gtk_window_set_decorated(GTK_WINDOW(m_widget), false);
375 m_gdkFunc = GDK_FUNC_MOVE;
376 if (style & wxRESIZE_BORDER)
377 m_gdkFunc |= GDK_FUNC_RESIZE;
378 gtk_window_set_default_size(GTK_WINDOW(m_widget), m_width, m_height);
379
380 // Reset m_decorSize, wxMiniFrame manages its own decorations
381 m_decorSize = DecorSize();
382 m_deferShow = false;
383
384 if (m_parent && (GTK_IS_WINDOW(m_parent->m_widget)))
385 {
386 gtk_window_set_transient_for( GTK_WINDOW(m_widget), GTK_WINDOW(m_parent->m_widget) );
387 }
388
389 if (m_miniTitle && (style & wxCLOSE_BOX))
390 {
391 m_closeButton = wxBitmap((const char*)close_bits, 16, 16);
392 m_closeButton.SetMask(new wxMask(m_closeButton));
393 }
394
395 /* these are called when the borders are drawn */
396 #ifdef __WXGTK3__
397 g_signal_connect_after(eventbox, "draw", G_CALLBACK(draw), this);
398 #else
399 g_signal_connect_after(eventbox, "expose_event", G_CALLBACK(expose_event), this);
400 #endif
401
402 /* these are required for dragging the mini frame around */
403 g_signal_connect (eventbox, "button_press_event",
404 G_CALLBACK (gtk_window_button_press_callback), this);
405 #ifndef __WXGTK4__
406 g_signal_connect(eventbox, "button-release-event",
407 G_CALLBACK(button_release_event), this);
408 #endif
409 g_signal_connect (eventbox, "motion_notify_event",
410 G_CALLBACK (gtk_window_motion_notify_callback), this);
411 g_signal_connect (eventbox, "leave_notify_event",
412 G_CALLBACK (gtk_window_leave_callback), this);
413 return true;
414 }
415
DoGetClientSize(int * width,int * height) const416 void wxMiniFrame::DoGetClientSize(int* width, int* height) const
417 {
418 wxFrame::DoGetClientSize(width, height);
419
420 if (m_useCachedClientSize)
421 return;
422
423 if (width)
424 {
425 *width -= 2 * m_miniEdge;
426 if (*width < 0) *width = 0;
427 }
428 if (height)
429 {
430 *height -= m_miniTitle + 2 * m_miniEdge;
431 if (*height < 0) *height = 0;
432 }
433 }
434
435 // Keep min size at least as large as decorations
DoSetSizeHints(int minW,int minH,int maxW,int maxH,int incW,int incH)436 void wxMiniFrame::DoSetSizeHints(int minW, int minH, int maxW, int maxH, int incW, int incH)
437 {
438 const int w = 2 * m_miniEdge;
439 const int h = 2 * m_miniEdge + m_miniTitle;
440 if (minW < w) minW = w;
441 if (minH < h) minH = h;
442 wxFrame::DoSetSizeHints(minW, minH, maxW, maxH, incW, incH);
443 }
444
SetTitle(const wxString & title)445 void wxMiniFrame::SetTitle( const wxString &title )
446 {
447 wxFrame::SetTitle( title );
448
449 GdkWindow* window = gtk_widget_get_window(gtk_bin_get_child(GTK_BIN(m_widget)));
450 if (window)
451 gdk_window_invalidate_rect(window, NULL, false);
452 }
453
454 #endif // wxUSE_MINIFRAME
455