]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/minifram.cpp
missing commit
[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
85a0a12a
RR
107 if (style & wxRESIZE_BORDER)
108 {
109 dc.SetBrush( *wxGREY_BRUSH );
110 dc.SetPen( *wxTRANSPARENT_PEN );
111 dc.DrawRectangle( win->m_width - 14, win->m_height-14, 14, 14 );
112 }
113
8d788b83 114 if (win->m_miniTitle && !win->GetTitle().empty())
b9a535f5 115 {
ba718523 116 dc.SetFont( *wxSMALL_FONT );
340bfb43 117
6d976005
RR
118 wxBrush brush( LightContrastColour( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT) ) );
119 dc.SetBrush( brush );
120 dc.SetPen( *wxTRANSPARENT_PEN );
03647350
VZ
121 dc.DrawRectangle( win->m_miniEdge-1,
122 win->m_miniEdge-1,
39db4477
RR
123 win->m_width - (2*(win->m_miniEdge-1)),
124 15 );
ba718523 125
ba718523 126 dc.SetTextForeground( *wxWHITE );
39db4477 127 dc.DrawText( win->GetTitle(), 6, 4 );
00e4ffbc
RR
128
129 if (style & wxCLOSE_BOX)
39db4477 130 dc.DrawBitmap( win->m_closeButton, win->m_width-18, 3, true );
b9a535f5 131 }
03647350 132
6d727f6c 133 return false;
32a95f9f 134}
865bb325 135}
32a95f9f 136
32a95f9f
RR
137//-----------------------------------------------------------------------------
138// "button_press_event" of m_mainWidget
139//-----------------------------------------------------------------------------
140
865bb325 141extern "C" {
8d788b83
PC
142static gboolean
143gtk_window_button_press_callback(GtkWidget* widget, GdkEventButton* gdk_event, wxMiniFrame* win)
32a95f9f 144{
8ab75332 145 if (gdk_event->window != gtk_widget_get_window(widget))
8d788b83 146 return false;
32a95f9f
RR
147 if (g_blockEventsOnDrag) return TRUE;
148 if (g_blockEventsOnScroll) return TRUE;
149
150 if (win->m_isDragging) return TRUE;
151
85a0a12a
RR
152 int style = win->GetWindowStyle();
153
00e4ffbc
RR
154 int y = (int)gdk_event->y;
155 int x = (int)gdk_event->x;
b41b2a05 156
85a0a12a
RR
157 if ((style & wxRESIZE_BORDER) &&
158 (x > win->m_width-14) && (y > win->m_height-14))
159 {
160 GtkWidget *ancestor = gtk_widget_get_toplevel( widget );
161
385e8575 162 GdkWindow *source = gtk_widget_get_window(widget);
85a0a12a
RR
163
164 int org_x = 0;
165 int org_y = 0;
166 gdk_window_get_origin( source, &org_x, &org_y );
167
168 gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
169 GDK_WINDOW_EDGE_SOUTH_EAST,
170 1,
171 org_x + x,
172 org_y + y,
173 0);
b41b2a05 174
85a0a12a
RR
175 return TRUE;
176 }
00e4ffbc 177
8d788b83 178 if (win->m_miniTitle && (style & wxCLOSE_BOX))
00e4ffbc
RR
179 {
180 if ((y > 3) && (y < 19) && (x > win->m_width-19) && (x < win->m_width-3))
181 {
182 win->Close();
183 return TRUE;
184 }
185 }
b41b2a05 186
8d788b83
PC
187 if (y >= win->m_miniEdge + win->m_miniTitle)
188 return true;
340bfb43 189
385e8575 190 gdk_window_raise(gtk_widget_get_window(win->m_widget));
c2fa61e8 191
c0174166
PC
192 const GdkEventMask mask = GdkEventMask(
193 GDK_BUTTON_PRESS_MASK |
194 GDK_BUTTON_RELEASE_MASK |
195 GDK_POINTER_MOTION_MASK |
196 GDK_POINTER_MOTION_HINT_MASK |
197 GDK_BUTTON_MOTION_MASK |
198 GDK_BUTTON1_MOTION_MASK);
199#ifdef __WXGTK3__
200 gdk_device_grab(
201 gdk_event->device, gdk_event->window, GDK_OWNERSHIP_NONE,
202 false, mask, NULL, gdk_event->time);
203#else
204 gdk_pointer_grab(gdk_event->window, false, mask, NULL, NULL, gdk_event->time);
205#endif
c2fa61e8 206
00e4ffbc
RR
207 win->m_diffX = x;
208 win->m_diffY = y;
32a95f9f
RR
209 win->m_oldX = 0;
210 win->m_oldY = 0;
c2fa61e8 211
340bfb43 212 win->m_isDragging = true;
32a95f9f
RR
213
214 return TRUE;
215}
865bb325 216}
32a95f9f
RR
217
218//-----------------------------------------------------------------------------
219// "button_release_event" of m_mainWidget
220//-----------------------------------------------------------------------------
221
865bb325 222extern "C" {
8d788b83
PC
223static gboolean
224gtk_window_button_release_callback(GtkWidget* widget, GdkEventButton* gdk_event, wxMiniFrame* win)
32a95f9f 225{
8ab75332 226 if (gdk_event->window != gtk_widget_get_window(widget))
8d788b83 227 return false;
32a95f9f
RR
228 if (g_blockEventsOnDrag) return TRUE;
229 if (g_blockEventsOnScroll) return TRUE;
32a95f9f 230 if (!win->m_isDragging) return TRUE;
c2fa61e8 231
11dbb4bf 232 win->m_isDragging = false;
c2fa61e8 233
32a95f9f
RR
234 int x = (int)gdk_event->x;
235 int y = (int)gdk_event->y;
c2fa61e8 236
c0174166
PC
237#ifdef __WXGTK3__
238 gdk_device_ungrab(gdk_event->device, gdk_event->time);
239#else
240 gdk_pointer_ungrab(gdk_event->time);
241#endif
c2fa61e8 242 int org_x = 0;
32a95f9f 243 int org_y = 0;
385e8575 244 gdk_window_get_origin(gtk_widget_get_window(widget), &org_x, &org_y);
32a95f9f
RR
245 x += org_x - win->m_diffX;
246 y += org_y - win->m_diffY;
121a3581
RR
247 win->m_x = x;
248 win->m_y = y;
9adb9063 249 gtk_window_move( GTK_WINDOW(win->m_widget), x, y );
32a95f9f
RR
250
251 return TRUE;
252}
865bb325 253}
32a95f9f 254
85a0a12a
RR
255//-----------------------------------------------------------------------------
256// "leave_notify_event" of m_mainWidget
257//-----------------------------------------------------------------------------
258
259extern "C" {
260static gboolean
e4161a2a 261gtk_window_leave_callback(GtkWidget *widget,
8d788b83 262 GdkEventCrossing* gdk_event,
8ab75332 263 wxMiniFrame*)
85a0a12a 264{
85a0a12a 265 if (g_blockEventsOnDrag) return FALSE;
385e8575 266 if (gdk_event->window != gtk_widget_get_window(widget))
8d788b83 267 return false;
85a0a12a 268
385e8575 269 gdk_window_set_cursor(gtk_widget_get_window(widget), NULL);
b41b2a05 270
85a0a12a
RR
271 return FALSE;
272}
273}
274
32a95f9f
RR
275//-----------------------------------------------------------------------------
276// "motion_notify_event" of m_mainWidget
277//-----------------------------------------------------------------------------
278
865bb325 279extern "C" {
8d788b83 280static gboolean
85a0a12a 281gtk_window_motion_notify_callback( GtkWidget *widget, GdkEventMotion *gdk_event, wxMiniFrame *win )
32a95f9f 282{
8ab75332 283 if (gdk_event->window != gtk_widget_get_window(widget))
8d788b83 284 return false;
32a95f9f
RR
285 if (g_blockEventsOnDrag) return TRUE;
286 if (g_blockEventsOnScroll) return TRUE;
287
c0174166
PC
288 int x = int(gdk_event->x);
289 int y = int(gdk_event->y);
290
32a95f9f
RR
291 if (gdk_event->is_hint)
292 {
c0174166
PC
293#ifdef __WXGTK3__
294 gdk_window_get_device_position(gdk_event->window, gdk_event->device, &x, &y, NULL);
295#else
296 gdk_window_get_pointer(gdk_event->window, &x, &y, NULL);
297#endif
32a95f9f
RR
298 }
299
85a0a12a
RR
300 if (!win->m_isDragging)
301 {
9dc44eff 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
RR
309 }
310 return TRUE;
311 }
b41b2a05 312
85a0a12a
RR
313 win->m_oldX = x - win->m_diffX;
314 win->m_oldY = y - win->m_diffY;
315
d02d8b4c
RR
316 int org_x = 0;
317 int org_y = 0;
385e8575 318 gdk_window_get_origin(gtk_widget_get_window(widget), &org_x, &org_y);
d02d8b4c
RR
319 x += org_x - win->m_diffX;
320 y += org_y - win->m_diffY;
321 win->m_x = x;
322 win->m_y = y;
323 gtk_window_move( GTK_WINDOW(win->m_widget), x, y );
324
32a95f9f
RR
325 return TRUE;
326}
865bb325 327}
32a95f9f 328
b2b3ccc5
RR
329//-----------------------------------------------------------------------------
330// wxMiniFrame
331//-----------------------------------------------------------------------------
332
00e4ffbc
RR
333static unsigned char close_bits[]={
334 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xfb, 0xef, 0xdb, 0xed, 0x8b, 0xe8,
335 0x1b, 0xec, 0x3b, 0xee, 0x1b, 0xec, 0x8b, 0xe8, 0xdb, 0xed, 0xfb, 0xef,
336 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
337
5d5b3a40 338
b2b3ccc5 339IMPLEMENT_DYNAMIC_CLASS(wxMiniFrame,wxFrame)
8ab75332
PC
340
341wxMiniFrame::~wxMiniFrame()
342{
343 if (m_widget)
344 {
345 GtkWidget* eventbox = gtk_bin_get_child(GTK_BIN(m_widget));
346 GTKDisconnect(eventbox);
347 }
348}
b2b3ccc5
RR
349
350bool wxMiniFrame::Create( wxWindow *parent, wxWindowID id, const wxString &title,
351 const wxPoint &pos, const wxSize &size,
352 long style, const wxString &name )
353{
8d788b83
PC
354 m_miniTitle = 0;
355 if (style & wxCAPTION)
00e4ffbc 356 m_miniTitle = 16;
c2fa61e8 357
6d976005 358 if (style & wxRESIZE_BORDER)
85a0a12a 359 m_miniEdge = 4;
6d976005
RR
360 else
361 m_miniEdge = 3;
340bfb43 362 m_isDragging = false;
b2b3ccc5
RR
363 m_oldX = -1;
364 m_oldY = -1;
365 m_diffX = 0;
366 m_diffY = 0;
c2fa61e8 367
3b7067a0
PC
368 // don't allow sizing smaller than decorations
369 int minWidth = 2 * m_miniEdge;
370 int minHeight = 2 * m_miniEdge + m_miniTitle;
371 if (m_minWidth < minWidth)
372 m_minWidth = minWidth;
373 if (m_minHeight < minHeight)
374 m_minHeight = minHeight;
375
b2b3ccc5
RR
376 wxFrame::Create( parent, id, title, pos, size, style, name );
377
3f4049ff
PC
378 // Use a GtkEventBox for the title and borders. Using m_widget for this
379 // almost works, except that setting the resize cursor has no effect.
380 GtkWidget* eventbox = gtk_event_box_new();
381 gtk_widget_add_events(eventbox,
cca410b3 382 GDK_POINTER_MOTION_MASK |
3f4049ff
PC
383 GDK_POINTER_MOTION_HINT_MASK);
384 gtk_widget_show(eventbox);
385 // Use a GtkAlignment to position m_mainWidget inside the decorations
386 GtkWidget* alignment = gtk_alignment_new(0, 0, 1, 1);
387 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment),
388 m_miniTitle + m_miniEdge, m_miniEdge, m_miniEdge, m_miniEdge);
389 gtk_widget_show(alignment);
390 // The GtkEventBox and GtkAlignment go between m_widget and m_mainWidget
391 gtk_widget_reparent(m_mainWidget, alignment);
392 gtk_container_add(GTK_CONTAINER(eventbox), alignment);
393 gtk_container_add(GTK_CONTAINER(m_widget), eventbox);
cca410b3
PC
394
395 m_gdkDecor = 0;
396 m_gdkFunc = 0;
397 if (style & wxRESIZE_BORDER)
398 m_gdkFunc = GDK_FUNC_RESIZE;
fce611e4
PC
399 gtk_window_set_default_size(GTK_WINDOW(m_widget), m_width, m_height);
400 m_decorSize.Set(0, 0);
401 m_deferShow = false;
cca410b3 402
2c990ec0
RR
403 if (m_parent && (GTK_IS_WINDOW(m_parent->m_widget)))
404 {
405 gtk_window_set_transient_for( GTK_WINDOW(m_widget), GTK_WINDOW(m_parent->m_widget) );
406 }
340bfb43 407
8d788b83 408 if (m_miniTitle && (style & wxCLOSE_BOX))
b9a535f5 409 {
00e4ffbc 410 wxImage img = wxBitmap((const char*)close_bits, 16, 16).ConvertToImage();
c4d39711 411 img.Replace(0,0,0,123,123,123);
00e4ffbc
RR
412 img.SetMaskColour(123,123,123);
413 m_closeButton = wxBitmap( img );
b9a535f5 414 }
c2fa61e8 415
32a95f9f 416 /* these are called when the borders are drawn */
9dc44eff
PC
417#ifdef __WXGTK3__
418 g_signal_connect_after(eventbox, "draw", G_CALLBACK(draw), this);
419#else
420 g_signal_connect_after(eventbox, "expose_event", G_CALLBACK(expose_event), this);
421#endif
b2b3ccc5 422
32a95f9f 423 /* these are required for dragging the mini frame around */
3f4049ff 424 g_signal_connect (eventbox, "button_press_event",
9fa72bd2 425 G_CALLBACK (gtk_window_button_press_callback), this);
3f4049ff 426 g_signal_connect (eventbox, "button_release_event",
9fa72bd2 427 G_CALLBACK (gtk_window_button_release_callback), this);
3f4049ff 428 g_signal_connect (eventbox, "motion_notify_event",
9fa72bd2 429 G_CALLBACK (gtk_window_motion_notify_callback), this);
3f4049ff 430 g_signal_connect (eventbox, "leave_notify_event",
85a0a12a 431 G_CALLBACK (gtk_window_leave_callback), this);
340bfb43 432 return true;
b2b3ccc5 433}
dcf924a3 434
cca410b3
PC
435void wxMiniFrame::DoGetClientSize(int* width, int* height) const
436{
437 wxFrame::DoGetClientSize(width, height);
b0d053c1
PC
438
439 if (m_useCachedClientSize)
440 return;
441
cca410b3
PC
442 if (width)
443 {
444 *width -= 2 * m_miniEdge;
445 if (*width < 0) *width = 0;
446 }
447 if (height)
448 {
449 *height -= m_miniTitle + 2 * m_miniEdge;
450 if (*height < 0) *height = 0;
451 }
452}
453
3f4049ff
PC
454// Keep min size at least as large as decorations
455void wxMiniFrame::DoSetSizeHints(int minW, int minH, int maxW, int maxH, int incW, int incH)
456{
457 const int w = 2 * m_miniEdge;
458 const int h = 2 * m_miniEdge + m_miniTitle;
459 if (minW < w) minW = w;
460 if (minH < h) minH = h;
461 wxFrame::DoSetSizeHints(minW, minH, maxW, maxH, incW, incH);
462}
463
400be137
RR
464void wxMiniFrame::SetTitle( const wxString &title )
465{
466 wxFrame::SetTitle( title );
340bfb43 467
385e8575
PC
468 GdkWindow* window = gtk_widget_get_window(gtk_bin_get_child(GTK_BIN(m_widget)));
469 if (window)
470 gdk_window_invalidate_rect(window, NULL, false);
400be137
RR
471}
472
11dbb4bf 473#endif // wxUSE_MINIFRAME