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