Use a GtkVBox to do TLW layout. Rework some of the remaining sizing code.
[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 int height = dc.GetCharHeight();
100
101 wxBrush brush( LightContrastColour( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT) ) );
102 dc.SetBrush( brush );
103 dc.SetPen( *wxTRANSPARENT_PEN );
104 dc.DrawRectangle( 3, 3, win->m_width - 7, height );
105
106 dc.SetTextForeground( *wxWHITE );
107 dc.DrawText( win->GetTitle(), 6, 3 );
108
109 if (style & wxCLOSE_BOX)
110 dc.DrawBitmap( win->m_closeButton, win->m_width-19, 2, true );
111 }
112 return false;
113 }
114 }
115
116 //-----------------------------------------------------------------------------
117 // "button_press_event" of m_mainWidget
118 //-----------------------------------------------------------------------------
119
120 extern "C" {
121 static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxMiniFrame *win )
122 {
123 if (!win->m_hasVMT) return FALSE;
124 if (g_blockEventsOnDrag) return TRUE;
125 if (g_blockEventsOnScroll) return TRUE;
126
127 if (win->m_isDragging) return TRUE;
128
129 int style = win->GetWindowStyle();
130
131 int y = (int)gdk_event->y;
132 int x = (int)gdk_event->x;
133
134 if ((style & wxRESIZE_BORDER) &&
135 (x > win->m_width-14) && (y > win->m_height-14))
136 {
137 GtkWidget *ancestor = gtk_widget_get_toplevel( widget );
138
139 GdkWindow *source = widget->window;
140
141 int org_x = 0;
142 int org_y = 0;
143 gdk_window_get_origin( source, &org_x, &org_y );
144
145 gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
146 GDK_WINDOW_EDGE_SOUTH_EAST,
147 1,
148 org_x + x,
149 org_y + y,
150 0);
151
152 return TRUE;
153 }
154
155 if ((style & wxCLOSE_BOX) &&
156 ((style & wxCAPTION) || (style & wxTINY_CAPTION_HORIZ) || (style & wxTINY_CAPTION_VERT)))
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 wxClientDC dc(win);
166 dc.SetFont( *wxSMALL_FONT );
167 int height = dc.GetCharHeight() + 1;
168
169
170 if (y > height) return TRUE;
171
172 gdk_window_raise( win->m_widget->window );
173
174 gdk_pointer_grab( widget->window, FALSE,
175 (GdkEventMask)
176 (GDK_BUTTON_PRESS_MASK |
177 GDK_BUTTON_RELEASE_MASK |
178 GDK_POINTER_MOTION_MASK |
179 GDK_POINTER_MOTION_HINT_MASK |
180 GDK_BUTTON_MOTION_MASK |
181 GDK_BUTTON1_MOTION_MASK),
182 (GdkWindow *) NULL,
183 (GdkCursor *) NULL,
184 (unsigned int) GDK_CURRENT_TIME );
185
186 win->m_diffX = x;
187 win->m_diffY = y;
188 win->m_oldX = 0;
189 win->m_oldY = 0;
190
191 win->m_isDragging = true;
192
193 return TRUE;
194 }
195 }
196
197 //-----------------------------------------------------------------------------
198 // "button_release_event" of m_mainWidget
199 //-----------------------------------------------------------------------------
200
201 extern "C" {
202 static gint gtk_window_button_release_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxMiniFrame *win )
203 {
204 if (!win->m_hasVMT) return FALSE;
205 if (g_blockEventsOnDrag) return TRUE;
206 if (g_blockEventsOnScroll) return TRUE;
207
208 if (!win->m_isDragging) return TRUE;
209
210 win->m_isDragging = false;
211
212 int x = (int)gdk_event->x;
213 int y = (int)gdk_event->y;
214
215 gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
216 int org_x = 0;
217 int org_y = 0;
218 gdk_window_get_origin( widget->window, &org_x, &org_y );
219 x += org_x - win->m_diffX;
220 y += org_y - win->m_diffY;
221 win->m_x = x;
222 win->m_y = y;
223 gtk_window_move( GTK_WINDOW(win->m_widget), x, y );
224
225 return TRUE;
226 }
227 }
228
229 //-----------------------------------------------------------------------------
230 // "leave_notify_event" of m_mainWidget
231 //-----------------------------------------------------------------------------
232
233 extern "C" {
234 static gboolean
235 gtk_window_leave_callback(GtkWidget *widget,
236 GdkEventCrossing * WXUNUSED(gdk_event),
237 wxMiniFrame *win)
238 {
239 if (!win->m_hasVMT) return FALSE;
240 if (g_blockEventsOnDrag) return FALSE;
241
242 gdk_window_set_cursor( widget->window, NULL );
243
244 return FALSE;
245 }
246 }
247
248 //-----------------------------------------------------------------------------
249 // "motion_notify_event" of m_mainWidget
250 //-----------------------------------------------------------------------------
251
252 extern "C" {
253 static gint
254 gtk_window_motion_notify_callback( GtkWidget *widget, GdkEventMotion *gdk_event, wxMiniFrame *win )
255 {
256 if (!win->m_hasVMT) 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 }
285 return TRUE;
286 }
287
288 win->m_oldX = x - win->m_diffX;
289 win->m_oldY = y - win->m_diffY;
290
291 int org_x = 0;
292 int org_y = 0;
293 gdk_window_get_origin( widget->window, &org_x, &org_y );
294 x += org_x - win->m_diffX;
295 y += org_y - win->m_diffY;
296 win->m_x = x;
297 win->m_y = y;
298 gtk_window_move( GTK_WINDOW(win->m_widget), x, y );
299
300 return TRUE;
301 }
302 }
303
304 //-----------------------------------------------------------------------------
305 // "size_allocate" from GtkFixed, parent of m_mainWidget
306 //-----------------------------------------------------------------------------
307
308 extern "C" {
309 static void size_allocate(GtkWidget*, GtkAllocation* alloc, wxMiniFrame* win)
310 {
311 // place m_mainWidget inside of decorations drawn on the GtkFixed
312 GtkAllocation alloc2 = win->m_mainWidget->allocation;
313 alloc2.width = alloc->width - 2 * win->m_miniEdge;
314 alloc2.height = alloc->height - win->m_miniTitle - 2 * win->m_miniEdge;
315 gtk_widget_size_allocate(win->m_mainWidget, &alloc2);
316 }
317 }
318
319 //-----------------------------------------------------------------------------
320 // wxMiniFrame
321 //-----------------------------------------------------------------------------
322
323 static unsigned char close_bits[]={
324 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xfb, 0xef, 0xdb, 0xed, 0x8b, 0xe8,
325 0x1b, 0xec, 0x3b, 0xee, 0x1b, 0xec, 0x8b, 0xe8, 0xdb, 0xed, 0xfb, 0xef,
326 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
327
328
329 IMPLEMENT_DYNAMIC_CLASS(wxMiniFrame,wxFrame)
330
331 bool wxMiniFrame::Create( wxWindow *parent, wxWindowID id, const wxString &title,
332 const wxPoint &pos, const wxSize &size,
333 long style, const wxString &name )
334 {
335 if ((style & wxCAPTION) || (style & wxTINY_CAPTION_HORIZ) || (style & wxTINY_CAPTION_VERT))
336 m_miniTitle = 16;
337
338 if (style & wxRESIZE_BORDER)
339 m_miniEdge = 4;
340 else
341 m_miniEdge = 3;
342 m_isDragging = false;
343 m_oldX = -1;
344 m_oldY = -1;
345 m_diffX = 0;
346 m_diffY = 0;
347
348 wxFrame::Create( parent, id, title, pos, size, style, name );
349
350 // borders and title are on a GtkFixed between m_widget and m_mainWidget
351 GtkWidget* fixed = gtk_fixed_new();
352 gtk_fixed_set_has_window((GtkFixed*)fixed, true);
353 gtk_widget_add_events(fixed,
354 GDK_POINTER_MOTION_MASK |
355 GDK_POINTER_MOTION_HINT_MASK |
356 GDK_BUTTON_MOTION_MASK |
357 GDK_BUTTON_PRESS_MASK |
358 GDK_BUTTON_RELEASE_MASK |
359 GDK_LEAVE_NOTIFY_MASK);
360 gtk_widget_show(fixed);
361 gtk_widget_reparent(m_mainWidget, fixed);
362 gtk_container_add((GtkContainer*)m_widget, fixed);
363 gtk_fixed_move((GtkFixed*)fixed, m_mainWidget, m_miniEdge, m_miniTitle + m_miniEdge);
364 g_signal_connect(fixed, "size_allocate", G_CALLBACK(size_allocate), this);
365
366 m_gdkDecor = 0;
367 m_gdkFunc = 0;
368 if (style & wxRESIZE_BORDER)
369 m_gdkFunc = GDK_FUNC_RESIZE;
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 ((style & wxCLOSE_BOX) &&
377 ((style & wxCAPTION) || (style & wxTINY_CAPTION_HORIZ) || (style & wxTINY_CAPTION_VERT)))
378 {
379 wxImage img = wxBitmap((const char*)close_bits, 16, 16).ConvertToImage();
380 img.Replace(0,0,0,123,123,123);
381 img.SetMaskColour(123,123,123);
382 m_closeButton = wxBitmap( img );
383 }
384
385 /* these are called when the borders are drawn */
386 g_signal_connect_after(fixed, "expose_event",
387 G_CALLBACK (gtk_window_own_expose_callback), this );
388
389 /* these are required for dragging the mini frame around */
390 g_signal_connect (fixed, "button_press_event",
391 G_CALLBACK (gtk_window_button_press_callback), this);
392 g_signal_connect (fixed, "button_release_event",
393 G_CALLBACK (gtk_window_button_release_callback), this);
394 g_signal_connect (fixed, "motion_notify_event",
395 G_CALLBACK (gtk_window_motion_notify_callback), this);
396 g_signal_connect (fixed, "leave_notify_event",
397 G_CALLBACK (gtk_window_leave_callback), this);
398 return true;
399 }
400
401 void wxMiniFrame::DoGetClientSize(int* width, int* height) const
402 {
403 wxFrame::DoGetClientSize(width, height);
404 if (width)
405 {
406 *width -= 2 * m_miniEdge;
407 if (*width < 0) *width = 0;
408 }
409 if (height)
410 {
411 *height -= m_miniTitle + 2 * m_miniEdge;
412 if (*height < 0) *height = 0;
413 }
414 }
415
416 void wxMiniFrame::SetTitle( const wxString &title )
417 {
418 wxFrame::SetTitle( title );
419
420 GtkWidget* fixed = GTK_BIN(m_widget)->child;
421 if (fixed->window)
422 gdk_window_invalidate_rect(fixed->window, NULL, false);
423 }
424
425 #endif // wxUSE_MINIFRAME