use "event" signal emission hook to install idle handler for many events
[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/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 // don't need to install idle handler, its done from "event" signal
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 // don't need to install idle handler, its done from "event" signal
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 // don't need to install idle handler, its done from "event" signal
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 // don't need to install idle handler, its done from "event" signal
246
247 if (!win->m_hasVMT) return FALSE;
248 if (g_blockEventsOnDrag) return FALSE;
249
250 gdk_window_set_cursor( widget->window, NULL );
251
252 return FALSE;
253 }
254 }
255
256 //-----------------------------------------------------------------------------
257 // "motion_notify_event" of m_mainWidget
258 //-----------------------------------------------------------------------------
259
260 extern "C" {
261 static gint
262 gtk_window_motion_notify_callback( GtkWidget *widget, GdkEventMotion *gdk_event, wxMiniFrame *win )
263 {
264 // don't need to install idle handler, its done from "event" signal
265
266 if (!win->m_hasVMT) return FALSE;
267 if (g_blockEventsOnDrag) return TRUE;
268 if (g_blockEventsOnScroll) return TRUE;
269
270 if (gdk_event->is_hint)
271 {
272 int x = 0;
273 int y = 0;
274 GdkModifierType state;
275 gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
276 gdk_event->x = x;
277 gdk_event->y = y;
278 gdk_event->state = state;
279 }
280
281 int style = win->GetWindowStyle();
282
283 int x = (int)gdk_event->x;
284 int y = (int)gdk_event->y;
285
286 if (!win->m_isDragging)
287 {
288 if (style & wxRESIZE_BORDER)
289 {
290 if ((x > win->m_width-14) && (y > win->m_height-14))
291 gdk_window_set_cursor( widget->window, gdk_cursor_new( GDK_BOTTOM_RIGHT_CORNER ) );
292 else
293 gdk_window_set_cursor( widget->window, NULL );
294 }
295 return TRUE;
296 }
297
298 win->m_oldX = x - win->m_diffX;
299 win->m_oldY = y - win->m_diffY;
300
301 int org_x = 0;
302 int org_y = 0;
303 gdk_window_get_origin( widget->window, &org_x, &org_y );
304 x += org_x - win->m_diffX;
305 y += org_y - win->m_diffY;
306 win->m_x = x;
307 win->m_y = y;
308 gtk_window_move( GTK_WINDOW(win->m_widget), x, y );
309
310
311 return TRUE;
312 }
313 }
314
315 //-----------------------------------------------------------------------------
316 // wxMiniFrame
317 //-----------------------------------------------------------------------------
318
319 static unsigned char close_bits[]={
320 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xfb, 0xef, 0xdb, 0xed, 0x8b, 0xe8,
321 0x1b, 0xec, 0x3b, 0xee, 0x1b, 0xec, 0x8b, 0xe8, 0xdb, 0xed, 0xfb, 0xef,
322 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
323
324
325 IMPLEMENT_DYNAMIC_CLASS(wxMiniFrame,wxFrame)
326
327 bool wxMiniFrame::Create( wxWindow *parent, wxWindowID id, const wxString &title,
328 const wxPoint &pos, const wxSize &size,
329 long style, const wxString &name )
330 {
331 style = style | wxCAPTION;
332
333 if ((style & wxCAPTION) || (style & wxTINY_CAPTION_HORIZ) || (style & wxTINY_CAPTION_VERT))
334 m_miniTitle = 16;
335
336 if (style & wxRESIZE_BORDER)
337 m_miniEdge = 4;
338 else
339 m_miniEdge = 3;
340 m_isDragging = false;
341 m_oldX = -1;
342 m_oldY = -1;
343 m_diffX = 0;
344 m_diffY = 0;
345
346 wxFrame::Create( parent, id, title, pos, size, style, name );
347
348 if (m_parent && (GTK_IS_WINDOW(m_parent->m_widget)))
349 {
350 gtk_window_set_transient_for( GTK_WINDOW(m_widget), GTK_WINDOW(m_parent->m_widget) );
351 }
352
353 if ((style & wxCLOSE_BOX) &&
354 ((style & wxCAPTION) || (style & wxTINY_CAPTION_HORIZ) || (style & wxTINY_CAPTION_VERT)))
355 {
356 wxImage img = wxBitmap((const char*)close_bits, 16, 16).ConvertToImage();
357 img.Replace(0,0,0,123,123,123);
358 img.SetMaskColour(123,123,123);
359 m_closeButton = wxBitmap( img );
360 }
361
362 /* these are called when the borders are drawn */
363 g_signal_connect (m_mainWidget, "expose_event",
364 G_CALLBACK (gtk_window_own_expose_callback), this );
365
366 /* these are required for dragging the mini frame around */
367 g_signal_connect (m_mainWidget, "button_press_event",
368 G_CALLBACK (gtk_window_button_press_callback), this);
369 g_signal_connect (m_mainWidget, "button_release_event",
370 G_CALLBACK (gtk_window_button_release_callback), this);
371 g_signal_connect (m_mainWidget, "motion_notify_event",
372 G_CALLBACK (gtk_window_motion_notify_callback), this);
373 g_signal_connect (m_mainWidget, "leave_notify_event",
374 G_CALLBACK (gtk_window_leave_callback), this);
375 return true;
376 }
377
378 void wxMiniFrame::SetTitle( const wxString &title )
379 {
380 wxFrame::SetTitle( title );
381
382 if (GTK_PIZZA(m_mainWidget)->bin_window)
383 gdk_window_invalidate_rect( GTK_PIZZA(m_mainWidget)->bin_window, NULL, true );
384 }
385
386 #endif // wxUSE_MINIFRAME