fix setting background color in wxGTK3 with themes which use background images or...
[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 gdk_pointer_grab( gtk_widget_get_window(widget), false,
197 (GdkEventMask)
198 (GDK_BUTTON_PRESS_MASK |
199 GDK_BUTTON_RELEASE_MASK |
200 GDK_POINTER_MOTION_MASK |
201 GDK_POINTER_MOTION_HINT_MASK |
202 GDK_BUTTON_MOTION_MASK |
203 GDK_BUTTON1_MOTION_MASK),
204 NULL,
205 NULL,
206 (unsigned int) GDK_CURRENT_TIME );
207
208 win->m_diffX = x;
209 win->m_diffY = y;
210 win->m_oldX = 0;
211 win->m_oldY = 0;
212
213 win->m_isDragging = true;
214
215 return TRUE;
216 }
217 }
218
219 //-----------------------------------------------------------------------------
220 // "button_release_event" of m_mainWidget
221 //-----------------------------------------------------------------------------
222
223 extern "C" {
224 static gboolean
225 gtk_window_button_release_callback(GtkWidget* widget, GdkEventButton* gdk_event, wxMiniFrame* win)
226 {
227 if (gdk_event->window != gtk_widget_get_window(widget))
228 return false;
229 if (g_blockEventsOnDrag) return TRUE;
230 if (g_blockEventsOnScroll) return TRUE;
231 if (!win->m_isDragging) return TRUE;
232
233 win->m_isDragging = false;
234
235 int x = (int)gdk_event->x;
236 int y = (int)gdk_event->y;
237
238 gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
239 int org_x = 0;
240 int org_y = 0;
241 gdk_window_get_origin(gtk_widget_get_window(widget), &org_x, &org_y);
242 x += org_x - win->m_diffX;
243 y += org_y - win->m_diffY;
244 win->m_x = x;
245 win->m_y = y;
246 gtk_window_move( GTK_WINDOW(win->m_widget), x, y );
247
248 return TRUE;
249 }
250 }
251
252 //-----------------------------------------------------------------------------
253 // "leave_notify_event" of m_mainWidget
254 //-----------------------------------------------------------------------------
255
256 extern "C" {
257 static gboolean
258 gtk_window_leave_callback(GtkWidget *widget,
259 GdkEventCrossing* gdk_event,
260 wxMiniFrame*)
261 {
262 if (g_blockEventsOnDrag) return FALSE;
263 if (gdk_event->window != gtk_widget_get_window(widget))
264 return false;
265
266 gdk_window_set_cursor(gtk_widget_get_window(widget), NULL);
267
268 return FALSE;
269 }
270 }
271
272 //-----------------------------------------------------------------------------
273 // "motion_notify_event" of m_mainWidget
274 //-----------------------------------------------------------------------------
275
276 extern "C" {
277 static gboolean
278 gtk_window_motion_notify_callback( GtkWidget *widget, GdkEventMotion *gdk_event, wxMiniFrame *win )
279 {
280 if (gdk_event->window != gtk_widget_get_window(widget))
281 return false;
282 if (g_blockEventsOnDrag) return TRUE;
283 if (g_blockEventsOnScroll) return TRUE;
284
285 if (gdk_event->is_hint)
286 {
287 int x = 0;
288 int y = 0;
289 GdkModifierType state;
290 gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
291 gdk_event->x = x;
292 gdk_event->y = y;
293 gdk_event->state = state;
294 }
295
296 int x = (int)gdk_event->x;
297 int y = (int)gdk_event->y;
298
299 if (!win->m_isDragging)
300 {
301 #ifndef __WXGTK3__
302 if (win->GetWindowStyle() & wxRESIZE_BORDER)
303 {
304 if ((x > win->m_width-14) && (y > win->m_height-14))
305 gdk_window_set_cursor(gtk_widget_get_window(widget), gdk_cursor_new(GDK_BOTTOM_RIGHT_CORNER));
306 else
307 gdk_window_set_cursor(gtk_widget_get_window(widget), NULL);
308 win->GTKUpdateCursor(false);
309 }
310 #endif
311 return TRUE;
312 }
313
314 win->m_oldX = x - win->m_diffX;
315 win->m_oldY = y - win->m_diffY;
316
317 int org_x = 0;
318 int org_y = 0;
319 gdk_window_get_origin(gtk_widget_get_window(widget), &org_x, &org_y);
320 x += org_x - win->m_diffX;
321 y += org_y - win->m_diffY;
322 win->m_x = x;
323 win->m_y = y;
324 gtk_window_move( GTK_WINDOW(win->m_widget), x, y );
325
326 return TRUE;
327 }
328 }
329
330 //-----------------------------------------------------------------------------
331 // wxMiniFrame
332 //-----------------------------------------------------------------------------
333
334 static unsigned char close_bits[]={
335 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xfb, 0xef, 0xdb, 0xed, 0x8b, 0xe8,
336 0x1b, 0xec, 0x3b, 0xee, 0x1b, 0xec, 0x8b, 0xe8, 0xdb, 0xed, 0xfb, 0xef,
337 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
338
339
340 IMPLEMENT_DYNAMIC_CLASS(wxMiniFrame,wxFrame)
341
342 wxMiniFrame::~wxMiniFrame()
343 {
344 if (m_widget)
345 {
346 GtkWidget* eventbox = gtk_bin_get_child(GTK_BIN(m_widget));
347 GTKDisconnect(eventbox);
348 }
349 }
350
351 bool wxMiniFrame::Create( wxWindow *parent, wxWindowID id, const wxString &title,
352 const wxPoint &pos, const wxSize &size,
353 long style, const wxString &name )
354 {
355 m_miniTitle = 0;
356 if (style & wxCAPTION)
357 m_miniTitle = 16;
358
359 if (style & wxRESIZE_BORDER)
360 m_miniEdge = 4;
361 else
362 m_miniEdge = 3;
363 m_isDragging = false;
364 m_oldX = -1;
365 m_oldY = -1;
366 m_diffX = 0;
367 m_diffY = 0;
368
369 // don't allow sizing smaller than decorations
370 int minWidth = 2 * m_miniEdge;
371 int minHeight = 2 * m_miniEdge + m_miniTitle;
372 if (m_minWidth < minWidth)
373 m_minWidth = minWidth;
374 if (m_minHeight < minHeight)
375 m_minHeight = minHeight;
376
377 wxFrame::Create( parent, id, title, pos, size, style, name );
378
379 // Use a GtkEventBox for the title and borders. Using m_widget for this
380 // almost works, except that setting the resize cursor has no effect.
381 GtkWidget* eventbox = gtk_event_box_new();
382 gtk_widget_add_events(eventbox,
383 GDK_POINTER_MOTION_MASK |
384 GDK_POINTER_MOTION_HINT_MASK);
385 gtk_widget_show(eventbox);
386 // Use a GtkAlignment to position m_mainWidget inside the decorations
387 GtkWidget* alignment = gtk_alignment_new(0, 0, 1, 1);
388 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment),
389 m_miniTitle + m_miniEdge, m_miniEdge, m_miniEdge, m_miniEdge);
390 gtk_widget_show(alignment);
391 // The GtkEventBox and GtkAlignment go between m_widget and m_mainWidget
392 gtk_widget_reparent(m_mainWidget, alignment);
393 gtk_container_add(GTK_CONTAINER(eventbox), alignment);
394 gtk_container_add(GTK_CONTAINER(m_widget), eventbox);
395
396 m_gdkDecor = 0;
397 m_gdkFunc = 0;
398 if (style & wxRESIZE_BORDER)
399 m_gdkFunc = GDK_FUNC_RESIZE;
400 gtk_window_set_default_size(GTK_WINDOW(m_widget), m_width, m_height);
401 m_decorSize.Set(0, 0);
402 m_deferShow = false;
403
404 if (m_parent && (GTK_IS_WINDOW(m_parent->m_widget)))
405 {
406 gtk_window_set_transient_for( GTK_WINDOW(m_widget), GTK_WINDOW(m_parent->m_widget) );
407 }
408
409 if (m_miniTitle && (style & wxCLOSE_BOX))
410 {
411 wxImage img = wxBitmap((const char*)close_bits, 16, 16).ConvertToImage();
412 img.Replace(0,0,0,123,123,123);
413 img.SetMaskColour(123,123,123);
414 m_closeButton = wxBitmap( img );
415 }
416
417 /* these are called when the borders are drawn */
418 #ifdef __WXGTK3__
419 g_signal_connect_after(eventbox, "draw", G_CALLBACK(draw), this);
420 #else
421 g_signal_connect_after(eventbox, "expose_event", G_CALLBACK(expose_event), this);
422 #endif
423
424 /* these are required for dragging the mini frame around */
425 g_signal_connect (eventbox, "button_press_event",
426 G_CALLBACK (gtk_window_button_press_callback), this);
427 g_signal_connect (eventbox, "button_release_event",
428 G_CALLBACK (gtk_window_button_release_callback), this);
429 g_signal_connect (eventbox, "motion_notify_event",
430 G_CALLBACK (gtk_window_motion_notify_callback), this);
431 g_signal_connect (eventbox, "leave_notify_event",
432 G_CALLBACK (gtk_window_leave_callback), this);
433 return true;
434 }
435
436 void wxMiniFrame::DoGetClientSize(int* width, int* height) const
437 {
438 wxFrame::DoGetClientSize(width, height);
439 if (width)
440 {
441 *width -= 2 * m_miniEdge;
442 if (*width < 0) *width = 0;
443 }
444 if (height)
445 {
446 *height -= m_miniTitle + 2 * m_miniEdge;
447 if (*height < 0) *height = 0;
448 }
449 }
450
451 // Keep min size at least as large as decorations
452 void wxMiniFrame::DoSetSizeHints(int minW, int minH, int maxW, int maxH, int incW, int incH)
453 {
454 const int w = 2 * m_miniEdge;
455 const int h = 2 * m_miniEdge + m_miniTitle;
456 if (minW < w) minW = w;
457 if (minH < h) minH = h;
458 wxFrame::DoSetSizeHints(minW, minH, maxW, maxH, incW, incH);
459 }
460
461 void wxMiniFrame::SetTitle( const wxString &title )
462 {
463 wxFrame::SetTitle( title );
464
465 GdkWindow* window = gtk_widget_get_window(gtk_bin_get_child(GTK_BIN(m_widget)));
466 if (window)
467 gdk_window_invalidate_rect(window, NULL, false);
468 }
469
470 #endif // wxUSE_MINIFRAME