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