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