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