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