1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/minifram.cpp
4 // Author: Robert Roebling
5 // Copyright: (c) 1998 Robert Roebling
6 // Licence: wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
14 #include "wx/minifram.h"
17 #include "wx/settings.h"
18 #include "wx/dcclient.h"
23 #include "wx/gtk/dc.h"
25 #include "wx/gtk/dcclient.h"
29 #include "wx/gtk/private/gtk2-compat.h"
31 //-----------------------------------------------------------------------------
33 //-----------------------------------------------------------------------------
35 extern bool g_blockEventsOnDrag
;
36 extern bool g_blockEventsOnScroll
;
38 //-----------------------------------------------------------------------------
39 // "expose_event" of m_mainWidget
40 //-----------------------------------------------------------------------------
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
)
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));
52 static wxColor
LightContrastColour(const wxColour
& c
)
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)
61 return StepColour(c
, amount
);
66 static gboolean
draw(GtkWidget
* widget
, cairo_t
* cr
, wxMiniFrame
* win
)
68 static gboolean
expose_event(GtkWidget
* widget
, GdkEventExpose
* gdk_event
, wxMiniFrame
* win
)
72 if (!gtk_cairo_should_draw_window(cr
, gtk_widget_get_window(widget
)))
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
);
83 if (gdk_event
->count
> 0 ||
84 gdk_event
->window
!= gtk_widget_get_window(widget
))
89 gtk_paint_shadow (gtk_widget_get_style(widget
),
90 gtk_widget_get_window(widget
),
93 NULL
, NULL
, NULL
, // FIXME: No clipping?
95 win
->m_width
, win
->m_height
);
99 wxDCImpl
*impl
= dc
.GetImpl();
100 wxClientDCImpl
*gtk_impl
= wxDynamicCast( impl
, wxClientDCImpl
);
101 gtk_impl
->m_gdkwindow
= gtk_widget_get_window(widget
); // Hack alert
104 int style
= win
->GetWindowStyle();
106 if (style
& wxRESIZE_BORDER
)
108 dc
.SetBrush( *wxGREY_BRUSH
);
109 dc
.SetPen( *wxTRANSPARENT_PEN
);
110 dc
.DrawRectangle( win
->m_width
- 14, win
->m_height
-14, 14, 14 );
113 if (win
->m_miniTitle
&& !win
->GetTitle().empty())
115 dc
.SetFont( *wxSMALL_FONT
);
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,
122 win
->m_width
- (2*(win
->m_miniEdge
-1)),
125 dc
.SetTextForeground( *wxWHITE
);
126 dc
.DrawText( win
->GetTitle(), 6, 4 );
128 if (style
& wxCLOSE_BOX
)
129 dc
.DrawBitmap( win
->m_closeButton
, win
->m_width
-18, 3, true );
136 //-----------------------------------------------------------------------------
137 // "button_press_event" of m_mainWidget
138 //-----------------------------------------------------------------------------
142 gtk_window_button_press_callback(GtkWidget
* widget
, GdkEventButton
* gdk_event
, wxMiniFrame
* win
)
144 if (gdk_event
->window
!= gtk_widget_get_window(widget
))
146 if (g_blockEventsOnDrag
) return TRUE
;
147 if (g_blockEventsOnScroll
) return TRUE
;
149 if (win
->m_isDragging
) return TRUE
;
151 int style
= win
->GetWindowStyle();
153 int y
= (int)gdk_event
->y
;
154 int x
= (int)gdk_event
->x
;
156 if ((style
& wxRESIZE_BORDER
) &&
157 (x
> win
->m_width
-14) && (y
> win
->m_height
-14))
159 GtkWidget
*ancestor
= gtk_widget_get_toplevel( widget
);
161 GdkWindow
*source
= gtk_widget_get_window(widget
);
165 gdk_window_get_origin( source
, &org_x
, &org_y
);
167 gtk_window_begin_resize_drag (GTK_WINDOW (ancestor
),
168 GDK_WINDOW_EDGE_SOUTH_EAST
,
177 if (win
->m_miniTitle
&& (style
& wxCLOSE_BOX
))
179 if ((y
> 3) && (y
< 19) && (x
> win
->m_width
-19) && (x
< win
->m_width
-3))
186 if (y
>= win
->m_miniEdge
+ win
->m_miniTitle
)
189 gdk_window_raise(gtk_widget_get_window(win
->m_widget
));
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
);
200 gdk_event
->device
, gdk_event
->window
, GDK_OWNERSHIP_NONE
,
201 false, mask
, NULL
, gdk_event
->time
);
203 gdk_pointer_grab(gdk_event
->window
, false, mask
, NULL
, NULL
, gdk_event
->time
);
211 win
->m_isDragging
= true;
217 //-----------------------------------------------------------------------------
218 // "button_release_event" of m_mainWidget
219 //-----------------------------------------------------------------------------
223 gtk_window_button_release_callback(GtkWidget
* widget
, GdkEventButton
* gdk_event
, wxMiniFrame
* win
)
225 if (gdk_event
->window
!= gtk_widget_get_window(widget
))
227 if (g_blockEventsOnDrag
) return TRUE
;
228 if (g_blockEventsOnScroll
) return TRUE
;
229 if (!win
->m_isDragging
) return TRUE
;
231 win
->m_isDragging
= false;
233 int x
= (int)gdk_event
->x
;
234 int y
= (int)gdk_event
->y
;
237 gdk_device_ungrab(gdk_event
->device
, gdk_event
->time
);
239 gdk_pointer_ungrab(gdk_event
->time
);
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
;
248 gtk_window_move( GTK_WINDOW(win
->m_widget
), x
, y
);
254 //-----------------------------------------------------------------------------
255 // "leave_notify_event" of m_mainWidget
256 //-----------------------------------------------------------------------------
260 gtk_window_leave_callback(GtkWidget
*widget
,
261 GdkEventCrossing
* gdk_event
,
264 if (g_blockEventsOnDrag
) return FALSE
;
265 if (gdk_event
->window
!= gtk_widget_get_window(widget
))
268 gdk_window_set_cursor(gtk_widget_get_window(widget
), NULL
);
274 //-----------------------------------------------------------------------------
275 // "motion_notify_event" of m_mainWidget
276 //-----------------------------------------------------------------------------
280 gtk_window_motion_notify_callback( GtkWidget
*widget
, GdkEventMotion
*gdk_event
, wxMiniFrame
*win
)
282 if (gdk_event
->window
!= gtk_widget_get_window(widget
))
284 if (g_blockEventsOnDrag
) return TRUE
;
285 if (g_blockEventsOnScroll
) return TRUE
;
287 int x
= int(gdk_event
->x
);
288 int y
= int(gdk_event
->y
);
290 if (gdk_event
->is_hint
)
293 gdk_window_get_device_position(gdk_event
->window
, gdk_event
->device
, &x
, &y
, NULL
);
295 gdk_window_get_pointer(gdk_event
->window
, &x
, &y
, NULL
);
299 if (!win
->m_isDragging
)
301 if (win
->GetWindowStyle() & wxRESIZE_BORDER
)
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
));
306 gdk_window_set_cursor(gtk_widget_get_window(widget
), NULL
);
307 win
->GTKUpdateCursor(false);
312 win
->m_oldX
= x
- win
->m_diffX
;
313 win
->m_oldY
= y
- win
->m_diffY
;
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
;
322 gtk_window_move( GTK_WINDOW(win
->m_widget
), x
, y
);
328 //-----------------------------------------------------------------------------
330 //-----------------------------------------------------------------------------
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 };
338 IMPLEMENT_DYNAMIC_CLASS(wxMiniFrame
,wxFrame
)
340 wxMiniFrame::~wxMiniFrame()
344 GtkWidget
* eventbox
= gtk_bin_get_child(GTK_BIN(m_widget
));
345 GTKDisconnect(eventbox
);
349 bool wxMiniFrame::Create( wxWindow
*parent
, wxWindowID id
, const wxString
&title
,
350 const wxPoint
&pos
, const wxSize
&size
,
351 long style
, const wxString
&name
)
354 if (style
& wxCAPTION
)
357 if (style
& wxRESIZE_BORDER
)
361 m_isDragging
= false;
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
;
375 wxFrame::Create( parent
, id
, title
, pos
, size
, style
, name
);
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
);
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
));
402 if (m_parent
&& (GTK_IS_WINDOW(m_parent
->m_widget
)))
404 gtk_window_set_transient_for( GTK_WINDOW(m_widget
), GTK_WINDOW(m_parent
->m_widget
) );
407 if (m_miniTitle
&& (style
& wxCLOSE_BOX
))
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
);
415 /* these are called when the borders are drawn */
417 g_signal_connect_after(eventbox
, "draw", G_CALLBACK(draw
), this);
419 g_signal_connect_after(eventbox
, "expose_event", G_CALLBACK(expose_event
), this);
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);
434 void wxMiniFrame::DoGetClientSize(int* width
, int* height
) const
436 wxFrame::DoGetClientSize(width
, height
);
438 if (m_useCachedClientSize
)
443 *width
-= 2 * m_miniEdge
;
444 if (*width
< 0) *width
= 0;
448 *height
-= m_miniTitle
+ 2 * m_miniEdge
;
449 if (*height
< 0) *height
= 0;
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
)
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
);
463 void wxMiniFrame::SetTitle( const wxString
&title
)
465 wxFrame::SetTitle( title
);
467 GdkWindow
* window
= gtk_widget_get_window(gtk_bin_get_child(GTK_BIN(m_widget
)));
469 gdk_window_invalidate_rect(window
, NULL
, false);
472 #endif // wxUSE_MINIFRAME