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