1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/minifram.cpp
4 // Author: Robert Roebling
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
15 #include "wx/minifram.h"
18 #include "wx/settings.h"
19 #include "wx/dcclient.h"
24 #include "wx/gtk/dc.h"
26 #include "wx/gtk/dcclient.h"
30 #include "wx/gtk/private/gtk2-compat.h"
32 //-----------------------------------------------------------------------------
34 //-----------------------------------------------------------------------------
36 extern bool g_blockEventsOnDrag
;
37 extern bool g_blockEventsOnScroll
;
39 //-----------------------------------------------------------------------------
40 // "expose_event" of m_mainWidget
41 //-----------------------------------------------------------------------------
43 // StepColour() it a utility function that simply darkens
44 // or lightens a color, based on the specified percentage
45 static wxColor
StepColour(const wxColor
& c
, int percent
)
47 int r
= c
.Red(), g
= c
.Green(), b
= c
.Blue();
48 return wxColour((unsigned char)wxMin((r
*percent
)/100,255),
49 (unsigned char)wxMin((g
*percent
)/100,255),
50 (unsigned char)wxMin((b
*percent
)/100,255));
53 static wxColor
LightContrastColour(const wxColour
& c
)
57 // if the color is especially dark, then
58 // make the contrast even lighter
59 if (c
.Red() < 128 && c
.Green() < 128 && c
.Blue() < 128)
62 return StepColour(c
, amount
);
67 static gboolean
draw(GtkWidget
* widget
, cairo_t
* cr
, wxMiniFrame
* win
)
69 static gboolean
expose_event(GtkWidget
* widget
, GdkEventExpose
* gdk_event
, wxMiniFrame
* win
)
73 if (!gtk_cairo_should_draw_window(cr
, gtk_widget_get_window(widget
)))
76 GtkStyleContext
* sc
= gtk_widget_get_style_context(widget
);
77 gtk_style_context_save(sc
);
78 gtk_style_context_add_class(sc
, GTK_STYLE_CLASS_BUTTON
);
79 gtk_render_frame(sc
, cr
, 0, 0, win
->m_width
, win
->m_height
);
80 gtk_style_context_restore(sc
);
84 if (!win
->m_hasVMT
|| gdk_event
->count
> 0 ||
85 gdk_event
->window
!= gtk_widget_get_window(widget
))
90 gtk_paint_shadow (gtk_widget_get_style(widget
),
91 gtk_widget_get_window(widget
),
94 NULL
, NULL
, NULL
, // FIXME: No clipping?
96 win
->m_width
, win
->m_height
);
100 wxDCImpl
*impl
= dc
.GetImpl();
101 wxClientDCImpl
*gtk_impl
= wxDynamicCast( impl
, wxClientDCImpl
);
102 gtk_impl
->m_gdkwindow
= gtk_widget_get_window(widget
); // Hack alert
105 int style
= win
->GetWindowStyle();
108 if (style
& wxRESIZE_BORDER
)
110 dc
.SetBrush( *wxGREY_BRUSH
);
111 dc
.SetPen( *wxTRANSPARENT_PEN
);
112 dc
.DrawRectangle( win
->m_width
- 14, win
->m_height
-14, 14, 14 );
116 if (win
->m_miniTitle
&& !win
->GetTitle().empty())
118 dc
.SetFont( *wxSMALL_FONT
);
120 wxBrush
brush( LightContrastColour( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
) ) );
121 dc
.SetBrush( brush
);
122 dc
.SetPen( *wxTRANSPARENT_PEN
);
123 dc
.DrawRectangle( win
->m_miniEdge
-1,
125 win
->m_width
- (2*(win
->m_miniEdge
-1)),
128 dc
.SetTextForeground( *wxWHITE
);
129 dc
.DrawText( win
->GetTitle(), 6, 4 );
131 if (style
& wxCLOSE_BOX
)
132 dc
.DrawBitmap( win
->m_closeButton
, win
->m_width
-18, 3, true );
139 //-----------------------------------------------------------------------------
140 // "button_press_event" of m_mainWidget
141 //-----------------------------------------------------------------------------
145 gtk_window_button_press_callback(GtkWidget
* widget
, GdkEventButton
* gdk_event
, wxMiniFrame
* win
)
147 if (!win
->m_hasVMT
|| gdk_event
->window
!= gtk_widget_get_window(widget
))
149 if (g_blockEventsOnDrag
) return TRUE
;
150 if (g_blockEventsOnScroll
) return TRUE
;
152 if (win
->m_isDragging
) return TRUE
;
154 int style
= win
->GetWindowStyle();
156 int y
= (int)gdk_event
->y
;
157 int x
= (int)gdk_event
->x
;
160 if ((style
& wxRESIZE_BORDER
) &&
161 (x
> win
->m_width
-14) && (y
> win
->m_height
-14))
163 GtkWidget
*ancestor
= gtk_widget_get_toplevel( widget
);
165 GdkWindow
*source
= gtk_widget_get_window(widget
);
169 gdk_window_get_origin( source
, &org_x
, &org_y
);
171 gtk_window_begin_resize_drag (GTK_WINDOW (ancestor
),
172 GDK_WINDOW_EDGE_SOUTH_EAST
,
182 if (win
->m_miniTitle
&& (style
& wxCLOSE_BOX
))
184 if ((y
> 3) && (y
< 19) && (x
> win
->m_width
-19) && (x
< win
->m_width
-3))
191 if (y
>= win
->m_miniEdge
+ win
->m_miniTitle
)
194 gdk_window_raise(gtk_widget_get_window(win
->m_widget
));
196 gdk_pointer_grab( gtk_widget_get_window(widget
), false,
198 (GDK_BUTTON_PRESS_MASK
|
199 GDK_BUTTON_RELEASE_MASK
|
200 GDK_POINTER_MOTION_MASK
|
201 GDK_POINTER_MOTION_HINT_MASK
|
202 GDK_BUTTON_MOTION_MASK
|
203 GDK_BUTTON1_MOTION_MASK
),
206 (unsigned int) GDK_CURRENT_TIME
);
213 win
->m_isDragging
= true;
219 //-----------------------------------------------------------------------------
220 // "button_release_event" of m_mainWidget
221 //-----------------------------------------------------------------------------
225 gtk_window_button_release_callback(GtkWidget
* widget
, GdkEventButton
* gdk_event
, wxMiniFrame
* win
)
227 if (!win
->m_hasVMT
|| gdk_event
->window
!= gtk_widget_get_window(widget
))
229 if (g_blockEventsOnDrag
) return TRUE
;
230 if (g_blockEventsOnScroll
) return TRUE
;
231 if (!win
->m_isDragging
) return TRUE
;
233 win
->m_isDragging
= false;
235 int x
= (int)gdk_event
->x
;
236 int y
= (int)gdk_event
->y
;
238 gdk_pointer_ungrab ( (guint32
)GDK_CURRENT_TIME
);
241 gdk_window_get_origin(gtk_widget_get_window(widget
), &org_x
, &org_y
);
242 x
+= org_x
- win
->m_diffX
;
243 y
+= org_y
- win
->m_diffY
;
246 gtk_window_move( GTK_WINDOW(win
->m_widget
), x
, y
);
252 //-----------------------------------------------------------------------------
253 // "leave_notify_event" of m_mainWidget
254 //-----------------------------------------------------------------------------
258 gtk_window_leave_callback(GtkWidget
*widget
,
259 GdkEventCrossing
* gdk_event
,
262 if (!win
->m_hasVMT
) return FALSE
;
263 if (g_blockEventsOnDrag
) return FALSE
;
264 if (gdk_event
->window
!= gtk_widget_get_window(widget
))
267 gdk_window_set_cursor(gtk_widget_get_window(widget
), NULL
);
273 //-----------------------------------------------------------------------------
274 // "motion_notify_event" of m_mainWidget
275 //-----------------------------------------------------------------------------
279 gtk_window_motion_notify_callback( GtkWidget
*widget
, GdkEventMotion
*gdk_event
, wxMiniFrame
*win
)
281 if (!win
->m_hasVMT
|| gdk_event
->window
!= gtk_widget_get_window(widget
))
283 if (g_blockEventsOnDrag
) return TRUE
;
284 if (g_blockEventsOnScroll
) return TRUE
;
286 if (gdk_event
->is_hint
)
290 GdkModifierType state
;
291 gdk_window_get_pointer(gdk_event
->window
, &x
, &y
, &state
);
294 gdk_event
->state
= state
;
297 int x
= (int)gdk_event
->x
;
298 int y
= (int)gdk_event
->y
;
300 if (!win
->m_isDragging
)
303 if (win
->GetWindowStyle() & wxRESIZE_BORDER
)
305 if ((x
> win
->m_width
-14) && (y
> win
->m_height
-14))
306 gdk_window_set_cursor(gtk_widget_get_window(widget
), gdk_cursor_new(GDK_BOTTOM_RIGHT_CORNER
));
308 gdk_window_set_cursor(gtk_widget_get_window(widget
), NULL
);
309 win
->GTKUpdateCursor(false);
315 win
->m_oldX
= x
- win
->m_diffX
;
316 win
->m_oldY
= y
- win
->m_diffY
;
320 gdk_window_get_origin(gtk_widget_get_window(widget
), &org_x
, &org_y
);
321 x
+= org_x
- win
->m_diffX
;
322 y
+= org_y
- win
->m_diffY
;
325 gtk_window_move( GTK_WINDOW(win
->m_widget
), x
, y
);
331 //-----------------------------------------------------------------------------
333 //-----------------------------------------------------------------------------
335 static unsigned char close_bits
[]={
336 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xfb, 0xef, 0xdb, 0xed, 0x8b, 0xe8,
337 0x1b, 0xec, 0x3b, 0xee, 0x1b, 0xec, 0x8b, 0xe8, 0xdb, 0xed, 0xfb, 0xef,
338 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
341 IMPLEMENT_DYNAMIC_CLASS(wxMiniFrame
,wxFrame
)
343 bool wxMiniFrame::Create( wxWindow
*parent
, wxWindowID id
, const wxString
&title
,
344 const wxPoint
&pos
, const wxSize
&size
,
345 long style
, const wxString
&name
)
348 if (style
& wxCAPTION
)
351 if (style
& wxRESIZE_BORDER
)
355 m_isDragging
= false;
361 // don't allow sizing smaller than decorations
362 int minWidth
= 2 * m_miniEdge
;
363 int minHeight
= 2 * m_miniEdge
+ m_miniTitle
;
364 if (m_minWidth
< minWidth
)
365 m_minWidth
= minWidth
;
366 if (m_minHeight
< minHeight
)
367 m_minHeight
= minHeight
;
369 wxFrame::Create( parent
, id
, title
, pos
, size
, style
, name
);
371 // Use a GtkEventBox for the title and borders. Using m_widget for this
372 // almost works, except that setting the resize cursor has no effect.
373 GtkWidget
* eventbox
= gtk_event_box_new();
374 gtk_widget_add_events(eventbox
,
375 GDK_POINTER_MOTION_MASK
|
376 GDK_POINTER_MOTION_HINT_MASK
);
377 gtk_widget_show(eventbox
);
378 // Use a GtkAlignment to position m_mainWidget inside the decorations
379 GtkWidget
* alignment
= gtk_alignment_new(0, 0, 1, 1);
380 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment
),
381 m_miniTitle
+ m_miniEdge
, m_miniEdge
, m_miniEdge
, m_miniEdge
);
382 gtk_widget_show(alignment
);
383 // The GtkEventBox and GtkAlignment go between m_widget and m_mainWidget
384 gtk_widget_reparent(m_mainWidget
, alignment
);
385 gtk_container_add(GTK_CONTAINER(eventbox
), alignment
);
386 gtk_container_add(GTK_CONTAINER(m_widget
), eventbox
);
390 if (style
& wxRESIZE_BORDER
)
391 m_gdkFunc
= GDK_FUNC_RESIZE
;
392 gtk_window_set_default_size(GTK_WINDOW(m_widget
), m_width
, m_height
);
393 m_decorSize
.Set(0, 0);
396 if (m_parent
&& (GTK_IS_WINDOW(m_parent
->m_widget
)))
398 gtk_window_set_transient_for( GTK_WINDOW(m_widget
), GTK_WINDOW(m_parent
->m_widget
) );
401 if (m_miniTitle
&& (style
& wxCLOSE_BOX
))
403 wxImage img
= wxBitmap((const char*)close_bits
, 16, 16).ConvertToImage();
404 img
.Replace(0,0,0,123,123,123);
405 img
.SetMaskColour(123,123,123);
406 m_closeButton
= wxBitmap( img
);
409 /* these are called when the borders are drawn */
411 g_signal_connect_after(eventbox
, "draw", G_CALLBACK(draw
), this);
413 g_signal_connect_after(eventbox
, "expose_event", G_CALLBACK(expose_event
), this);
416 /* these are required for dragging the mini frame around */
417 g_signal_connect (eventbox
, "button_press_event",
418 G_CALLBACK (gtk_window_button_press_callback
), this);
419 g_signal_connect (eventbox
, "button_release_event",
420 G_CALLBACK (gtk_window_button_release_callback
), this);
421 g_signal_connect (eventbox
, "motion_notify_event",
422 G_CALLBACK (gtk_window_motion_notify_callback
), this);
423 g_signal_connect (eventbox
, "leave_notify_event",
424 G_CALLBACK (gtk_window_leave_callback
), this);
428 void wxMiniFrame::DoGetClientSize(int* width
, int* height
) const
430 wxFrame::DoGetClientSize(width
, height
);
433 *width
-= 2 * m_miniEdge
;
434 if (*width
< 0) *width
= 0;
438 *height
-= m_miniTitle
+ 2 * m_miniEdge
;
439 if (*height
< 0) *height
= 0;
443 // Keep min size at least as large as decorations
444 void wxMiniFrame::DoSetSizeHints(int minW
, int minH
, int maxW
, int maxH
, int incW
, int incH
)
446 const int w
= 2 * m_miniEdge
;
447 const int h
= 2 * m_miniEdge
+ m_miniTitle
;
448 if (minW
< w
) minW
= w
;
449 if (minH
< h
) minH
= h
;
450 wxFrame::DoSetSizeHints(minW
, minH
, maxW
, maxH
, incW
, incH
);
453 void wxMiniFrame::SetTitle( const wxString
&title
)
455 wxFrame::SetTitle( title
);
457 GdkWindow
* window
= gtk_widget_get_window(gtk_bin_get_child(GTK_BIN(m_widget
)));
459 gdk_window_invalidate_rect(window
, NULL
, false);
462 #endif // wxUSE_MINIFRAME