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