Added resize support to wxMiniFrame.
[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/settings.h"
17
18 #ifndef WX_PRECOMP
19 #include "wx/dcscreen.h"
20 #endif
21
22 #include "gtk/gtk.h"
23 #include "wx/gtk/win_gtk.h"
24 #include "wx/gtk/private.h"
25
26 #include <gdk/gdk.h>
27 #include <gdk/gdkprivate.h>
28 #include <gdk/gdkx.h>
29
30 //-----------------------------------------------------------------------------
31 // data
32 //-----------------------------------------------------------------------------
33
34 extern bool g_blockEventsOnDrag;
35 extern bool g_blockEventsOnScroll;
36 extern GtkWidget *wxGetRootWindow();
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 static void gtk_window_own_expose_callback( GtkWidget *widget, GdkEventExpose *gdk_event, wxMiniFrame *win )
66 {
67 if (g_isIdle) wxapp_install_idle_handler();
68
69 if (!win->m_hasVMT) return;
70 if (gdk_event->count > 0) return;
71
72 GtkPizza *pizza = GTK_PIZZA(widget);
73
74 gtk_paint_shadow (widget->style,
75 pizza->bin_window,
76 GTK_STATE_NORMAL,
77 GTK_SHADOW_OUT,
78 NULL, NULL, NULL, // FIXME: No clipping?
79 0, 0,
80 win->m_width, win->m_height);
81
82 int style = win->GetWindowStyle();
83
84 wxClientDC dc(win);
85 // Hack alert
86 dc.m_window = pizza->bin_window;
87
88 if (style & wxRESIZE_BORDER)
89 {
90 dc.SetBrush( *wxGREY_BRUSH );
91 dc.SetPen( *wxTRANSPARENT_PEN );
92 dc.DrawRectangle( win->m_width - 14, win->m_height-14, 14, 14 );
93 }
94
95 if (!win->GetTitle().empty() &&
96 ((style & wxCAPTION) ||
97 (style & wxTINY_CAPTION_HORIZ) ||
98 (style & wxTINY_CAPTION_VERT)))
99 {
100 dc.SetFont( *wxSMALL_FONT );
101 int height = dc.GetCharHeight();
102
103 wxBrush brush( LightContrastColour( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT) ) );
104 dc.SetBrush( brush );
105 dc.SetPen( *wxTRANSPARENT_PEN );
106 dc.DrawRectangle( 3, 3, win->m_width - 7, height-2 );
107
108 dc.SetTextForeground( *wxWHITE );
109 dc.DrawText( win->GetTitle(), 6, 3 );
110
111 if (style & wxCLOSE_BOX)
112 dc.DrawBitmap( win->m_closeButton, win->m_width-19, 3, true );
113 }
114 }
115 }
116
117 //-----------------------------------------------------------------------------
118 // "button_press_event" of m_mainWidget
119 //-----------------------------------------------------------------------------
120
121 extern "C" {
122 static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxMiniFrame *win )
123 {
124 if (g_isIdle) wxapp_install_idle_handler();
125
126 if (!win->m_hasVMT) return FALSE;
127 if (g_blockEventsOnDrag) return TRUE;
128 if (g_blockEventsOnScroll) return TRUE;
129
130 if (win->m_isDragging) return TRUE;
131
132 GtkPizza *pizza = GTK_PIZZA(widget);
133 if (gdk_event->window != pizza->bin_window) return TRUE;
134
135 int style = win->GetWindowStyle();
136
137 int y = (int)gdk_event->y;
138 int x = (int)gdk_event->x;
139
140 if ((style & wxRESIZE_BORDER) &&
141 (x > win->m_width-14) && (y > win->m_height-14))
142 {
143 GtkWidget *ancestor = gtk_widget_get_toplevel( widget );
144
145 GdkWindow *source = GTK_PIZZA(widget)->bin_window;
146
147 int org_x = 0;
148 int org_y = 0;
149 gdk_window_get_origin( source, &org_x, &org_y );
150
151 gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
152 GDK_WINDOW_EDGE_SOUTH_EAST,
153 1,
154 org_x + x,
155 org_y + y,
156 0);
157
158 return TRUE;
159 }
160
161 if ((style & wxCLOSE_BOX) &&
162 ((style & wxCAPTION) || (style & wxTINY_CAPTION_HORIZ) || (style & wxTINY_CAPTION_VERT)))
163 {
164 if ((y > 3) && (y < 19) && (x > win->m_width-19) && (x < win->m_width-3))
165 {
166 win->Close();
167 return TRUE;
168 }
169 }
170
171 wxClientDC dc(win);
172 dc.SetFont( *wxSMALL_FONT );
173 int height = dc.GetCharHeight() + 1;
174
175
176 if (y > height) return TRUE;
177
178 gdk_window_raise( win->m_widget->window );
179
180 gdk_pointer_grab( widget->window, FALSE,
181 (GdkEventMask)
182 (GDK_BUTTON_PRESS_MASK |
183 GDK_BUTTON_RELEASE_MASK |
184 GDK_POINTER_MOTION_MASK |
185 GDK_POINTER_MOTION_HINT_MASK |
186 GDK_BUTTON_MOTION_MASK |
187 GDK_BUTTON1_MOTION_MASK),
188 (GdkWindow *) NULL,
189 (GdkCursor *) NULL,
190 (unsigned int) GDK_CURRENT_TIME );
191
192 win->m_diffX = x;
193 win->m_diffY = y;
194 win->m_oldX = 0;
195 win->m_oldY = 0;
196
197 win->m_isDragging = true;
198
199 return TRUE;
200 }
201 }
202
203 //-----------------------------------------------------------------------------
204 // "button_release_event" of m_mainWidget
205 //-----------------------------------------------------------------------------
206
207 extern "C" {
208 static gint gtk_window_button_release_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxMiniFrame *win )
209 {
210 if (g_isIdle) wxapp_install_idle_handler();
211
212 if (!win->m_hasVMT) return FALSE;
213 if (g_blockEventsOnDrag) return TRUE;
214 if (g_blockEventsOnScroll) return TRUE;
215
216 if (!win->m_isDragging) return TRUE;
217
218 win->m_isDragging = false;
219
220 int x = (int)gdk_event->x;
221 int y = (int)gdk_event->y;
222
223 gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
224 int org_x = 0;
225 int org_y = 0;
226 gdk_window_get_origin( widget->window, &org_x, &org_y );
227 x += org_x - win->m_diffX;
228 y += org_y - win->m_diffY;
229 win->m_x = x;
230 win->m_y = y;
231 gtk_window_move( GTK_WINDOW(win->m_widget), x, y );
232
233 return TRUE;
234 }
235 }
236
237 //-----------------------------------------------------------------------------
238 // "leave_notify_event" of m_mainWidget
239 //-----------------------------------------------------------------------------
240
241 extern "C" {
242 static gboolean
243 gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_event, wxMiniFrame *win )
244 {
245 if (g_isIdle)
246 wxapp_install_idle_handler();
247
248 if (!win->m_hasVMT) return FALSE;
249 if (g_blockEventsOnDrag) return FALSE;
250
251 gdk_window_set_cursor( widget->window, NULL );
252
253 return FALSE;
254 }
255 }
256
257 //-----------------------------------------------------------------------------
258 // "motion_notify_event" of m_mainWidget
259 //-----------------------------------------------------------------------------
260
261 extern "C" {
262 static gint
263 gtk_window_motion_notify_callback( GtkWidget *widget, GdkEventMotion *gdk_event, wxMiniFrame *win )
264 {
265 if (g_isIdle)
266 wxapp_install_idle_handler();
267
268 if (!win->m_hasVMT) return FALSE;
269 if (g_blockEventsOnDrag) return TRUE;
270 if (g_blockEventsOnScroll) return TRUE;
271
272 if (gdk_event->is_hint)
273 {
274 int x = 0;
275 int y = 0;
276 GdkModifierType state;
277 gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
278 gdk_event->x = x;
279 gdk_event->y = y;
280 gdk_event->state = state;
281 }
282
283 int style = win->GetWindowStyle();
284
285 int x = (int)gdk_event->x;
286 int y = (int)gdk_event->y;
287
288 if (!win->m_isDragging)
289 {
290 if (style & wxRESIZE_BORDER)
291 {
292 if ((x > win->m_width-14) && (y > win->m_height-14))
293 gdk_window_set_cursor( widget->window, gdk_cursor_new( GDK_BOTTOM_RIGHT_CORNER ) );
294 else
295 gdk_window_set_cursor( widget->window, NULL );
296 }
297 return TRUE;
298 }
299
300 win->m_oldX = x - win->m_diffX;
301 win->m_oldY = y - win->m_diffY;
302
303 int org_x = 0;
304 int org_y = 0;
305 gdk_window_get_origin( widget->window, &org_x, &org_y );
306 x += org_x - win->m_diffX;
307 y += org_y - win->m_diffY;
308 win->m_x = x;
309 win->m_y = y;
310 gtk_window_move( GTK_WINDOW(win->m_widget), x, y );
311
312
313 return TRUE;
314 }
315 }
316
317 //-----------------------------------------------------------------------------
318 // wxMiniFrame
319 //-----------------------------------------------------------------------------
320
321 static unsigned char close_bits[]={
322 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xfb, 0xef, 0xdb, 0xed, 0x8b, 0xe8,
323 0x1b, 0xec, 0x3b, 0xee, 0x1b, 0xec, 0x8b, 0xe8, 0xdb, 0xed, 0xfb, 0xef,
324 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
325
326
327 IMPLEMENT_DYNAMIC_CLASS(wxMiniFrame,wxFrame)
328
329 bool wxMiniFrame::Create( wxWindow *parent, wxWindowID id, const wxString &title,
330 const wxPoint &pos, const wxSize &size,
331 long style, const wxString &name )
332 {
333 style = style | wxCAPTION;
334
335 if ((style & wxCAPTION) || (style & wxTINY_CAPTION_HORIZ) || (style & wxTINY_CAPTION_VERT))
336 m_miniTitle = 16;
337
338 if (style & wxRESIZE_BORDER)
339 m_miniEdge = 4;
340 else
341 m_miniEdge = 3;
342 m_isDragging = false;
343 m_oldX = -1;
344 m_oldY = -1;
345 m_diffX = 0;
346 m_diffY = 0;
347
348 wxFrame::Create( parent, id, title, pos, size, style, name );
349
350 if (m_parent && (GTK_IS_WINDOW(m_parent->m_widget)))
351 {
352 gtk_window_set_transient_for( GTK_WINDOW(m_widget), GTK_WINDOW(m_parent->m_widget) );
353 }
354
355 if ((style & wxCLOSE_BOX) &&
356 ((style & wxCAPTION) || (style & wxTINY_CAPTION_HORIZ) || (style & wxTINY_CAPTION_VERT)))
357 {
358 wxImage img = wxBitmap((const char*)close_bits, 16, 16).ConvertToImage();
359 img.Replace(0,0,0,123,123,123);
360 img.SetMaskColour(123,123,123);
361 m_closeButton = wxBitmap( img );
362 }
363
364 /* these are called when the borders are drawn */
365 g_signal_connect (m_mainWidget, "expose_event",
366 G_CALLBACK (gtk_window_own_expose_callback), this );
367
368 /* these are required for dragging the mini frame around */
369 g_signal_connect (m_mainWidget, "button_press_event",
370 G_CALLBACK (gtk_window_button_press_callback), this);
371 g_signal_connect (m_mainWidget, "button_release_event",
372 G_CALLBACK (gtk_window_button_release_callback), this);
373 g_signal_connect (m_mainWidget, "motion_notify_event",
374 G_CALLBACK (gtk_window_motion_notify_callback), this);
375 g_signal_connect (m_mainWidget, "leave_notify_event",
376 G_CALLBACK (gtk_window_leave_callback), this);
377 return true;
378 }
379
380 void wxMiniFrame::SetTitle( const wxString &title )
381 {
382 wxFrame::SetTitle( title );
383
384 if (GTK_PIZZA(m_mainWidget)->bin_window)
385 gdk_window_invalidate_rect( GTK_PIZZA(m_mainWidget)->bin_window, NULL, true );
386 }
387
388 #endif // wxUSE_MINIFRAME