Move GDK_META_MASK definition in the header in which it is also used.
[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 wxFrame::Create( parent, id, title, pos, size, style, name );
337
338 // Use a GtkEventBox for the title and borders. Using m_widget for this
339 // almost works, except that setting the resize cursor has no effect.
340 GtkWidget* eventbox = gtk_event_box_new();
341 gtk_widget_add_events(eventbox,
342 GDK_POINTER_MOTION_MASK |
343 GDK_POINTER_MOTION_HINT_MASK);
344 gtk_widget_show(eventbox);
345 // Use a GtkAlignment to position m_mainWidget inside the decorations
346 GtkWidget* alignment = gtk_alignment_new(0, 0, 1, 1);
347 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment),
348 m_miniTitle + m_miniEdge, m_miniEdge, m_miniEdge, m_miniEdge);
349 gtk_widget_show(alignment);
350 // The GtkEventBox and GtkAlignment go between m_widget and m_mainWidget
351 gtk_widget_reparent(m_mainWidget, alignment);
352 gtk_container_add(GTK_CONTAINER(eventbox), alignment);
353 gtk_container_add(GTK_CONTAINER(m_widget), eventbox);
354
355 m_gdkDecor = 0;
356 m_gdkFunc = 0;
357 if (style & wxRESIZE_BORDER)
358 m_gdkFunc = GDK_FUNC_RESIZE;
359 gtk_window_set_default_size(GTK_WINDOW(m_widget), m_width, m_height);
360 m_decorSize.Set(0, 0);
361 m_deferShow = false;
362
363 // don't allow sizing smaller than decorations
364 GdkGeometry geom;
365 geom.min_width = 2 * m_miniEdge;
366 geom.min_height = 2 * m_miniEdge + m_miniTitle;
367 gtk_window_set_geometry_hints(GTK_WINDOW(m_widget), NULL, &geom, GDK_HINT_MIN_SIZE);
368
369 if (m_parent && (GTK_IS_WINDOW(m_parent->m_widget)))
370 {
371 gtk_window_set_transient_for( GTK_WINDOW(m_widget), GTK_WINDOW(m_parent->m_widget) );
372 }
373
374 if (m_miniTitle && (style & wxCLOSE_BOX))
375 {
376 wxImage img = wxBitmap((const char*)close_bits, 16, 16).ConvertToImage();
377 img.Replace(0,0,0,123,123,123);
378 img.SetMaskColour(123,123,123);
379 m_closeButton = wxBitmap( img );
380 }
381
382 /* these are called when the borders are drawn */
383 g_signal_connect_after(eventbox, "expose_event",
384 G_CALLBACK (gtk_window_own_expose_callback), this );
385
386 /* these are required for dragging the mini frame around */
387 g_signal_connect (eventbox, "button_press_event",
388 G_CALLBACK (gtk_window_button_press_callback), this);
389 g_signal_connect (eventbox, "button_release_event",
390 G_CALLBACK (gtk_window_button_release_callback), this);
391 g_signal_connect (eventbox, "motion_notify_event",
392 G_CALLBACK (gtk_window_motion_notify_callback), this);
393 g_signal_connect (eventbox, "leave_notify_event",
394 G_CALLBACK (gtk_window_leave_callback), this);
395 return true;
396 }
397
398 void wxMiniFrame::DoGetClientSize(int* width, int* height) const
399 {
400 wxFrame::DoGetClientSize(width, height);
401 if (width)
402 {
403 *width -= 2 * m_miniEdge;
404 if (*width < 0) *width = 0;
405 }
406 if (height)
407 {
408 *height -= m_miniTitle + 2 * m_miniEdge;
409 if (*height < 0) *height = 0;
410 }
411 }
412
413 // Keep min size at least as large as decorations
414 void wxMiniFrame::DoSetSizeHints(int minW, int minH, int maxW, int maxH, int incW, int incH)
415 {
416 const int w = 2 * m_miniEdge;
417 const int h = 2 * m_miniEdge + m_miniTitle;
418 if (minW < w) minW = w;
419 if (minH < h) minH = h;
420 wxFrame::DoSetSizeHints(minW, minH, maxW, maxH, incW, incH);
421 }
422
423 void wxMiniFrame::SetTitle( const wxString &title )
424 {
425 wxFrame::SetTitle( title );
426
427 GdkWindow* window = gtk_widget_get_window(gtk_bin_get_child(GTK_BIN(m_widget)));
428 if (window)
429 gdk_window_invalidate_rect(window, NULL, false);
430 }
431
432 #endif // wxUSE_MINIFRAME