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