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