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