]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/minifram.cpp
guard code for mac / quickdraw
[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
WS
15#include "wx/minifram.h"
16
17#ifndef WX_PRECOMP
b41b2a05 18 #include "wx/settings.h"
0416c418
PC
19 #include "wx/dcclient.h"
20 #include "wx/image.h"
11dbb4bf 21#endif
b2b3ccc5 22
cca410b3 23#include <gtk/gtk.h>
83624f79 24
32a95f9f
RR
25//-----------------------------------------------------------------------------
26// data
27//-----------------------------------------------------------------------------
28
8480b297
RR
29extern bool g_blockEventsOnDrag;
30extern bool g_blockEventsOnScroll;
32a95f9f 31
32a95f9f
RR
32//-----------------------------------------------------------------------------
33// "expose_event" of m_mainWidget
34//-----------------------------------------------------------------------------
35
6d976005
RR
36// StepColour() it a utility function that simply darkens
37// or lightens a color, based on the specified percentage
38static 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
46static 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
865bb325 58extern "C" {
6d727f6c 59static gboolean gtk_window_own_expose_callback(GtkWidget* widget, GdkEventExpose* gdk_event, wxMiniFrame* win)
32a95f9f 60{
6d727f6c
PC
61 if (!win->m_hasVMT || gdk_event->count > 0)
62 return false;
c2fa61e8 63
67b73b9a 64 gtk_paint_shadow (widget->style,
cca410b3 65 widget->window,
67b73b9a
MR
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);
b9a535f5 71
00e4ffbc 72 int style = win->GetWindowStyle();
b41b2a05 73
85a0a12a 74 wxClientDC dc(win);
cca410b3 75
ab171e95
RR
76#if wxUSE_NEW_DC
77 wxImplDC *impl = dc.GetImpl();
78 wxGTKClientImplDC *client_impl = wxDynamicCast( impl, wxGTKClientImplDC );
79 // Hack alert
cca410b3 80 client_impl->m_window = widget->window;
ab171e95 81#else
85a0a12a 82 // Hack alert
cca410b3 83 dc.m_window = widget->window;
ab171e95 84#endif
b41b2a05 85
85a0a12a
RR
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
340bfb43 93 if (!win->GetTitle().empty() &&
00e4ffbc
RR
94 ((style & wxCAPTION) ||
95 (style & wxTINY_CAPTION_HORIZ) ||
96 (style & wxTINY_CAPTION_VERT)))
b9a535f5 97 {
ba718523 98 dc.SetFont( *wxSMALL_FONT );
340bfb43 99
6d976005
RR
100 wxBrush brush( LightContrastColour( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT) ) );
101 dc.SetBrush( brush );
102 dc.SetPen( *wxTRANSPARENT_PEN );
39db4477
RR
103 dc.DrawRectangle( win->m_miniEdge-1,
104 win->m_miniEdge-1,
105 win->m_width - (2*(win->m_miniEdge-1)),
106 15 );
ba718523 107
ba718523 108 dc.SetTextForeground( *wxWHITE );
39db4477 109 dc.DrawText( win->GetTitle(), 6, 4 );
00e4ffbc
RR
110
111 if (style & wxCLOSE_BOX)
39db4477 112 dc.DrawBitmap( win->m_closeButton, win->m_width-18, 3, true );
b9a535f5 113 }
39db4477 114
6d727f6c 115 return false;
32a95f9f 116}
865bb325 117}
32a95f9f 118
32a95f9f
RR
119//-----------------------------------------------------------------------------
120// "button_press_event" of m_mainWidget
121//-----------------------------------------------------------------------------
122
865bb325 123extern "C" {
32a95f9f
RR
124static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxMiniFrame *win )
125{
a2053b27 126 if (!win->m_hasVMT) return FALSE;
32a95f9f
RR
127 if (g_blockEventsOnDrag) return TRUE;
128 if (g_blockEventsOnScroll) return TRUE;
129
130 if (win->m_isDragging) return TRUE;
131
85a0a12a
RR
132 int style = win->GetWindowStyle();
133
00e4ffbc
RR
134 int y = (int)gdk_event->y;
135 int x = (int)gdk_event->x;
b41b2a05 136
85a0a12a
RR
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
cca410b3 142 GdkWindow *source = widget->window;
85a0a12a
RR
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);
b41b2a05 154
85a0a12a
RR
155 return TRUE;
156 }
00e4ffbc 157
00e4ffbc
RR
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 }
b41b2a05 167
39db4477 168 if (y > win->m_miniEdge-1 + 15) return TRUE;
340bfb43 169
4dcaf11a 170 gdk_window_raise( win->m_widget->window );
c2fa61e8 171
32a95f9f
RR
172 gdk_pointer_grab( widget->window, FALSE,
173 (GdkEventMask)
174 (GDK_BUTTON_PRESS_MASK |
175 GDK_BUTTON_RELEASE_MASK |
f03fc89f 176 GDK_POINTER_MOTION_MASK |
32a95f9f 177 GDK_POINTER_MOTION_HINT_MASK |
f03fc89f 178 GDK_BUTTON_MOTION_MASK |
32a95f9f
RR
179 GDK_BUTTON1_MOTION_MASK),
180 (GdkWindow *) NULL,
181 (GdkCursor *) NULL,
7941ba11 182 (unsigned int) GDK_CURRENT_TIME );
c2fa61e8 183
00e4ffbc
RR
184 win->m_diffX = x;
185 win->m_diffY = y;
32a95f9f
RR
186 win->m_oldX = 0;
187 win->m_oldY = 0;
c2fa61e8 188
340bfb43 189 win->m_isDragging = true;
32a95f9f
RR
190
191 return TRUE;
192}
865bb325 193}
32a95f9f
RR
194
195//-----------------------------------------------------------------------------
196// "button_release_event" of m_mainWidget
197//-----------------------------------------------------------------------------
198
865bb325 199extern "C" {
32a95f9f
RR
200static gint gtk_window_button_release_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxMiniFrame *win )
201{
a2053b27 202 if (!win->m_hasVMT) return FALSE;
32a95f9f
RR
203 if (g_blockEventsOnDrag) return TRUE;
204 if (g_blockEventsOnScroll) return TRUE;
205
206 if (!win->m_isDragging) return TRUE;
c2fa61e8 207
11dbb4bf 208 win->m_isDragging = false;
c2fa61e8 209
32a95f9f
RR
210 int x = (int)gdk_event->x;
211 int y = (int)gdk_event->y;
c2fa61e8 212
13111b2a 213 gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
c2fa61e8 214 int org_x = 0;
32a95f9f
RR
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;
121a3581
RR
219 win->m_x = x;
220 win->m_y = y;
9adb9063 221 gtk_window_move( GTK_WINDOW(win->m_widget), x, y );
32a95f9f
RR
222
223 return TRUE;
224}
865bb325 225}
32a95f9f 226
85a0a12a
RR
227//-----------------------------------------------------------------------------
228// "leave_notify_event" of m_mainWidget
229//-----------------------------------------------------------------------------
230
231extern "C" {
232static gboolean
e4161a2a
VZ
233gtk_window_leave_callback(GtkWidget *widget,
234 GdkEventCrossing * WXUNUSED(gdk_event),
235 wxMiniFrame *win)
85a0a12a 236{
85a0a12a
RR
237 if (!win->m_hasVMT) return FALSE;
238 if (g_blockEventsOnDrag) return FALSE;
239
240 gdk_window_set_cursor( widget->window, NULL );
b41b2a05 241
85a0a12a
RR
242 return FALSE;
243}
244}
245
32a95f9f
RR
246//-----------------------------------------------------------------------------
247// "motion_notify_event" of m_mainWidget
248//-----------------------------------------------------------------------------
249
865bb325 250extern "C" {
b41b2a05 251static gint
85a0a12a 252gtk_window_motion_notify_callback( GtkWidget *widget, GdkEventMotion *gdk_event, wxMiniFrame *win )
32a95f9f 253{
a2053b27 254 if (!win->m_hasVMT) return FALSE;
32a95f9f
RR
255 if (g_blockEventsOnDrag) return TRUE;
256 if (g_blockEventsOnScroll) return TRUE;
257
32a95f9f
RR
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
85a0a12a 269 int style = win->GetWindowStyle();
b41b2a05 270
d02d8b4c
RR
271 int x = (int)gdk_event->x;
272 int y = (int)gdk_event->y;
b41b2a05 273
85a0a12a
RR
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 }
b41b2a05 285
85a0a12a
RR
286 win->m_oldX = x - win->m_diffX;
287 win->m_oldY = y - win->m_diffY;
288
d02d8b4c
RR
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
32a95f9f
RR
298 return TRUE;
299}
865bb325 300}
32a95f9f 301
b2b3ccc5
RR
302//-----------------------------------------------------------------------------
303// wxMiniFrame
304//-----------------------------------------------------------------------------
305
00e4ffbc
RR
306static 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
5d5b3a40 311
b2b3ccc5
RR
312IMPLEMENT_DYNAMIC_CLASS(wxMiniFrame,wxFrame)
313
314bool wxMiniFrame::Create( wxWindow *parent, wxWindowID id, const wxString &title,
315 const wxPoint &pos, const wxSize &size,
316 long style, const wxString &name )
317{
b9a535f5 318 if ((style & wxCAPTION) || (style & wxTINY_CAPTION_HORIZ) || (style & wxTINY_CAPTION_VERT))
00e4ffbc 319 m_miniTitle = 16;
c2fa61e8 320
6d976005 321 if (style & wxRESIZE_BORDER)
85a0a12a 322 m_miniEdge = 4;
6d976005
RR
323 else
324 m_miniEdge = 3;
340bfb43 325 m_isDragging = false;
b2b3ccc5
RR
326 m_oldX = -1;
327 m_oldY = -1;
328 m_diffX = 0;
329 m_diffY = 0;
c2fa61e8 330
b2b3ccc5
RR
331 wxFrame::Create( parent, id, title, pos, size, style, name );
332
3f4049ff
PC
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,
cca410b3 337 GDK_POINTER_MOTION_MASK |
3f4049ff
PC
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);
cca410b3
PC
349
350 m_gdkDecor = 0;
351 m_gdkFunc = 0;
352 if (style & wxRESIZE_BORDER)
353 m_gdkFunc = GDK_FUNC_RESIZE;
354
29ae3766
PC
355 // need to reset default size after changing m_gdkDecor
356 gtk_window_set_default_size(GTK_WINDOW(m_widget), m_width, m_height);
0ab0d0e1 357 m_decorSize.Set(0, 0);
29ae3766 358
3f4049ff
PC
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
2c990ec0
RR
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 }
340bfb43 369
00e4ffbc 370 if ((style & wxCLOSE_BOX) &&
b9a535f5
RR
371 ((style & wxCAPTION) || (style & wxTINY_CAPTION_HORIZ) || (style & wxTINY_CAPTION_VERT)))
372 {
00e4ffbc 373 wxImage img = wxBitmap((const char*)close_bits, 16, 16).ConvertToImage();
c4d39711 374 img.Replace(0,0,0,123,123,123);
00e4ffbc
RR
375 img.SetMaskColour(123,123,123);
376 m_closeButton = wxBitmap( img );
b9a535f5 377 }
c2fa61e8 378
32a95f9f 379 /* these are called when the borders are drawn */
3f4049ff 380 g_signal_connect_after(eventbox, "expose_event",
9fa72bd2 381 G_CALLBACK (gtk_window_own_expose_callback), this );
b2b3ccc5 382
32a95f9f 383 /* these are required for dragging the mini frame around */
3f4049ff 384 g_signal_connect (eventbox, "button_press_event",
9fa72bd2 385 G_CALLBACK (gtk_window_button_press_callback), this);
3f4049ff 386 g_signal_connect (eventbox, "button_release_event",
9fa72bd2 387 G_CALLBACK (gtk_window_button_release_callback), this);
3f4049ff 388 g_signal_connect (eventbox, "motion_notify_event",
9fa72bd2 389 G_CALLBACK (gtk_window_motion_notify_callback), this);
3f4049ff 390 g_signal_connect (eventbox, "leave_notify_event",
85a0a12a 391 G_CALLBACK (gtk_window_leave_callback), this);
340bfb43 392 return true;
b2b3ccc5 393}
dcf924a3 394
cca410b3
PC
395void 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
3f4049ff
PC
410// Keep min size at least as large as decorations
411void 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
400be137
RR
420void wxMiniFrame::SetTitle( const wxString &title )
421{
422 wxFrame::SetTitle( title );
340bfb43 423
3f4049ff
PC
424 GtkWidget* widget = GTK_BIN(m_widget)->child;
425 if (widget->window)
426 gdk_window_invalidate_rect(widget->window, NULL, false);
400be137
RR
427}
428
11dbb4bf 429#endif // wxUSE_MINIFRAME