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