]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/minifram.cpp
new file added
[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
8d788b83 84 if (!win->m_hasVMT || 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{
385e8575 147 if (!win->m_hasVMT || 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{
385e8575 227 if (!win->m_hasVMT || 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,
e4161a2a 260 wxMiniFrame *win)
85a0a12a 261{
85a0a12a
RR
262 if (!win->m_hasVMT) return FALSE;
263 if (g_blockEventsOnDrag) return FALSE;
385e8575 264 if (gdk_event->window != gtk_widget_get_window(widget))
8d788b83 265 return false;
85a0a12a 266
385e8575 267 gdk_window_set_cursor(gtk_widget_get_window(widget), NULL);
b41b2a05 268
85a0a12a
RR
269 return FALSE;
270}
271}
272
32a95f9f
RR
273//-----------------------------------------------------------------------------
274// "motion_notify_event" of m_mainWidget
275//-----------------------------------------------------------------------------
276
865bb325 277extern "C" {
8d788b83 278static gboolean
85a0a12a 279gtk_window_motion_notify_callback( GtkWidget *widget, GdkEventMotion *gdk_event, wxMiniFrame *win )
32a95f9f 280{
385e8575 281 if (!win->m_hasVMT || gdk_event->window != gtk_widget_get_window(widget))
8d788b83 282 return false;
32a95f9f
RR
283 if (g_blockEventsOnDrag) return TRUE;
284 if (g_blockEventsOnScroll) return TRUE;
285
32a95f9f
RR
286 if (gdk_event->is_hint)
287 {
288 int x = 0;
289 int y = 0;
290 GdkModifierType state;
291 gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
292 gdk_event->x = x;
293 gdk_event->y = y;
294 gdk_event->state = state;
295 }
296
d02d8b4c
RR
297 int x = (int)gdk_event->x;
298 int y = (int)gdk_event->y;
b41b2a05 299
85a0a12a
RR
300 if (!win->m_isDragging)
301 {
9dc44eff
PC
302#ifndef __WXGTK3__
303 if (win->GetWindowStyle() & wxRESIZE_BORDER)
85a0a12a
RR
304 {
305 if ((x > win->m_width-14) && (y > win->m_height-14))
385e8575 306 gdk_window_set_cursor(gtk_widget_get_window(widget), gdk_cursor_new(GDK_BOTTOM_RIGHT_CORNER));
85a0a12a 307 else
385e8575 308 gdk_window_set_cursor(gtk_widget_get_window(widget), NULL);
c2246a38 309 win->GTKUpdateCursor(false);
85a0a12a 310 }
9dc44eff 311#endif
85a0a12a
RR
312 return TRUE;
313 }
b41b2a05 314
85a0a12a
RR
315 win->m_oldX = x - win->m_diffX;
316 win->m_oldY = y - win->m_diffY;
317
d02d8b4c
RR
318 int org_x = 0;
319 int org_y = 0;
385e8575 320 gdk_window_get_origin(gtk_widget_get_window(widget), &org_x, &org_y);
d02d8b4c
RR
321 x += org_x - win->m_diffX;
322 y += org_y - win->m_diffY;
323 win->m_x = x;
324 win->m_y = y;
325 gtk_window_move( GTK_WINDOW(win->m_widget), x, y );
326
32a95f9f
RR
327 return TRUE;
328}
865bb325 329}
32a95f9f 330
b2b3ccc5
RR
331//-----------------------------------------------------------------------------
332// wxMiniFrame
333//-----------------------------------------------------------------------------
334
00e4ffbc
RR
335static unsigned char close_bits[]={
336 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xfb, 0xef, 0xdb, 0xed, 0x8b, 0xe8,
337 0x1b, 0xec, 0x3b, 0xee, 0x1b, 0xec, 0x8b, 0xe8, 0xdb, 0xed, 0xfb, 0xef,
338 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
339
5d5b3a40 340
b2b3ccc5
RR
341IMPLEMENT_DYNAMIC_CLASS(wxMiniFrame,wxFrame)
342
343bool wxMiniFrame::Create( wxWindow *parent, wxWindowID id, const wxString &title,
344 const wxPoint &pos, const wxSize &size,
345 long style, const wxString &name )
346{
8d788b83
PC
347 m_miniTitle = 0;
348 if (style & wxCAPTION)
00e4ffbc 349 m_miniTitle = 16;
c2fa61e8 350
6d976005 351 if (style & wxRESIZE_BORDER)
85a0a12a 352 m_miniEdge = 4;
6d976005
RR
353 else
354 m_miniEdge = 3;
340bfb43 355 m_isDragging = false;
b2b3ccc5
RR
356 m_oldX = -1;
357 m_oldY = -1;
358 m_diffX = 0;
359 m_diffY = 0;
c2fa61e8 360
3b7067a0
PC
361 // don't allow sizing smaller than decorations
362 int minWidth = 2 * m_miniEdge;
363 int minHeight = 2 * m_miniEdge + m_miniTitle;
364 if (m_minWidth < minWidth)
365 m_minWidth = minWidth;
366 if (m_minHeight < minHeight)
367 m_minHeight = minHeight;
368
b2b3ccc5
RR
369 wxFrame::Create( parent, id, title, pos, size, style, name );
370
3f4049ff
PC
371 // Use a GtkEventBox for the title and borders. Using m_widget for this
372 // almost works, except that setting the resize cursor has no effect.
373 GtkWidget* eventbox = gtk_event_box_new();
374 gtk_widget_add_events(eventbox,
cca410b3 375 GDK_POINTER_MOTION_MASK |
3f4049ff
PC
376 GDK_POINTER_MOTION_HINT_MASK);
377 gtk_widget_show(eventbox);
378 // Use a GtkAlignment to position m_mainWidget inside the decorations
379 GtkWidget* alignment = gtk_alignment_new(0, 0, 1, 1);
380 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment),
381 m_miniTitle + m_miniEdge, m_miniEdge, m_miniEdge, m_miniEdge);
382 gtk_widget_show(alignment);
383 // The GtkEventBox and GtkAlignment go between m_widget and m_mainWidget
384 gtk_widget_reparent(m_mainWidget, alignment);
385 gtk_container_add(GTK_CONTAINER(eventbox), alignment);
386 gtk_container_add(GTK_CONTAINER(m_widget), eventbox);
cca410b3
PC
387
388 m_gdkDecor = 0;
389 m_gdkFunc = 0;
390 if (style & wxRESIZE_BORDER)
391 m_gdkFunc = GDK_FUNC_RESIZE;
fce611e4
PC
392 gtk_window_set_default_size(GTK_WINDOW(m_widget), m_width, m_height);
393 m_decorSize.Set(0, 0);
394 m_deferShow = false;
cca410b3 395
2c990ec0
RR
396 if (m_parent && (GTK_IS_WINDOW(m_parent->m_widget)))
397 {
398 gtk_window_set_transient_for( GTK_WINDOW(m_widget), GTK_WINDOW(m_parent->m_widget) );
399 }
340bfb43 400
8d788b83 401 if (m_miniTitle && (style & wxCLOSE_BOX))
b9a535f5 402 {
00e4ffbc 403 wxImage img = wxBitmap((const char*)close_bits, 16, 16).ConvertToImage();
c4d39711 404 img.Replace(0,0,0,123,123,123);
00e4ffbc
RR
405 img.SetMaskColour(123,123,123);
406 m_closeButton = wxBitmap( img );
b9a535f5 407 }
c2fa61e8 408
32a95f9f 409 /* these are called when the borders are drawn */
9dc44eff
PC
410#ifdef __WXGTK3__
411 g_signal_connect_after(eventbox, "draw", G_CALLBACK(draw), this);
412#else
413 g_signal_connect_after(eventbox, "expose_event", G_CALLBACK(expose_event), this);
414#endif
b2b3ccc5 415
32a95f9f 416 /* these are required for dragging the mini frame around */
3f4049ff 417 g_signal_connect (eventbox, "button_press_event",
9fa72bd2 418 G_CALLBACK (gtk_window_button_press_callback), this);
3f4049ff 419 g_signal_connect (eventbox, "button_release_event",
9fa72bd2 420 G_CALLBACK (gtk_window_button_release_callback), this);
3f4049ff 421 g_signal_connect (eventbox, "motion_notify_event",
9fa72bd2 422 G_CALLBACK (gtk_window_motion_notify_callback), this);
3f4049ff 423 g_signal_connect (eventbox, "leave_notify_event",
85a0a12a 424 G_CALLBACK (gtk_window_leave_callback), this);
340bfb43 425 return true;
b2b3ccc5 426}
dcf924a3 427
cca410b3
PC
428void wxMiniFrame::DoGetClientSize(int* width, int* height) const
429{
430 wxFrame::DoGetClientSize(width, height);
431 if (width)
432 {
433 *width -= 2 * m_miniEdge;
434 if (*width < 0) *width = 0;
435 }
436 if (height)
437 {
438 *height -= m_miniTitle + 2 * m_miniEdge;
439 if (*height < 0) *height = 0;
440 }
441}
442
3f4049ff
PC
443// Keep min size at least as large as decorations
444void wxMiniFrame::DoSetSizeHints(int minW, int minH, int maxW, int maxH, int incW, int incH)
445{
446 const int w = 2 * m_miniEdge;
447 const int h = 2 * m_miniEdge + m_miniTitle;
448 if (minW < w) minW = w;
449 if (minH < h) minH = h;
450 wxFrame::DoSetSizeHints(minW, minH, maxW, maxH, incW, incH);
451}
452
400be137
RR
453void wxMiniFrame::SetTitle( const wxString &title )
454{
455 wxFrame::SetTitle( title );
340bfb43 456
385e8575
PC
457 GdkWindow* window = gtk_widget_get_window(gtk_bin_get_child(GTK_BIN(m_widget)));
458 if (window)
459 gdk_window_invalidate_rect(window, NULL, false);
400be137
RR
460}
461
11dbb4bf 462#endif // wxUSE_MINIFRAME