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