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