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 (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();
107 if (style
& wxRESIZE_BORDER
)
109 dc
.SetBrush( *wxGREY_BRUSH
);
110 dc
.SetPen( *wxTRANSPARENT_PEN
);
111 dc
.DrawRectangle( win
->m_width
- 14, win
->m_height
-14, 14, 14 );
114 if (win
->m_miniTitle
&& !win
->GetTitle().empty())
116 dc
.SetFont( *wxSMALL_FONT
);
118 wxBrush
brush( LightContrastColour( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT
) ) );
119 dc
.SetBrush( brush
);
120 dc
.SetPen( *wxTRANSPARENT_PEN
);
121 dc
.DrawRectangle( win
->m_miniEdge
-1,
123 win
->m_width
- (2*(win
->m_miniEdge
-1)),
126 dc
.SetTextForeground( *wxWHITE
);
127 dc
.DrawText( win
->GetTitle(), 6, 4 );
129 if (style
& wxCLOSE_BOX
)
130 dc
.DrawBitmap( win
->m_closeButton
, win
->m_width
-18, 3, true );
137 //-----------------------------------------------------------------------------
138 // "button_press_event" of m_mainWidget
139 //-----------------------------------------------------------------------------
143 gtk_window_button_press_callback(GtkWidget
* widget
, GdkEventButton
* gdk_event
, wxMiniFrame
* win
)
145 if (gdk_event
->window
!= gtk_widget_get_window(widget
))
147 if (g_blockEventsOnDrag
) return TRUE
;
148 if (g_blockEventsOnScroll
) return TRUE
;
150 if (win
->m_isDragging
) return TRUE
;
152 int style
= win
->GetWindowStyle();
154 int y
= (int)gdk_event
->y
;
155 int x
= (int)gdk_event
->x
;
157 if ((style
& wxRESIZE_BORDER
) &&
158 (x
> win
->m_width
-14) && (y
> win
->m_height
-14))
160 GtkWidget
*ancestor
= gtk_widget_get_toplevel( widget
);
162 GdkWindow
*source
= gtk_widget_get_window(widget
);
166 gdk_window_get_origin( source
, &org_x
, &org_y
);
168 gtk_window_begin_resize_drag (GTK_WINDOW (ancestor
),
169 GDK_WINDOW_EDGE_SOUTH_EAST
,
178 if (win
->m_miniTitle
&& (style
& wxCLOSE_BOX
))
180 if ((y
> 3) && (y
< 19) && (x
> win
->m_width
-19) && (x
< win
->m_width
-3))
187 if (y
>= win
->m_miniEdge
+ win
->m_miniTitle
)
190 gdk_window_raise(gtk_widget_get_window(win
->m_widget
));
192 const GdkEventMask mask
= GdkEventMask(
193 GDK_BUTTON_PRESS_MASK
|
194 GDK_BUTTON_RELEASE_MASK
|
195 GDK_POINTER_MOTION_MASK
|
196 GDK_POINTER_MOTION_HINT_MASK
|
197 GDK_BUTTON_MOTION_MASK
|
198 GDK_BUTTON1_MOTION_MASK
);
201 gdk_event
->device
, gdk_event
->window
, GDK_OWNERSHIP_NONE
,
202 false, mask
, NULL
, gdk_event
->time
);
204 gdk_pointer_grab(gdk_event
->window
, false, mask
, NULL
, NULL
, gdk_event
->time
);
212 win
->m_isDragging
= true;
218 //-----------------------------------------------------------------------------
219 // "button_release_event" of m_mainWidget
220 //-----------------------------------------------------------------------------
224 gtk_window_button_release_callback(GtkWidget
* widget
, GdkEventButton
* gdk_event
, wxMiniFrame
* win
)
226 if (gdk_event
->window
!= gtk_widget_get_window(widget
))
228 if (g_blockEventsOnDrag
) return TRUE
;
229 if (g_blockEventsOnScroll
) return TRUE
;
230 if (!win
->m_isDragging
) return TRUE
;
232 win
->m_isDragging
= false;
234 int x
= (int)gdk_event
->x
;
235 int y
= (int)gdk_event
->y
;
238 gdk_device_ungrab(gdk_event
->device
, gdk_event
->time
);
240 gdk_pointer_ungrab(gdk_event
->time
);
244 gdk_window_get_origin(gtk_widget_get_window(widget
), &org_x
, &org_y
);
245 x
+= org_x
- win
->m_diffX
;
246 y
+= org_y
- win
->m_diffY
;
249 gtk_window_move( GTK_WINDOW(win
->m_widget
), x
, y
);
255 //-----------------------------------------------------------------------------
256 // "leave_notify_event" of m_mainWidget
257 //-----------------------------------------------------------------------------
261 gtk_window_leave_callback(GtkWidget
*widget
,
262 GdkEventCrossing
* gdk_event
,
265 if (g_blockEventsOnDrag
) return FALSE
;
266 if (gdk_event
->window
!= gtk_widget_get_window(widget
))
269 gdk_window_set_cursor(gtk_widget_get_window(widget
), NULL
);
275 //-----------------------------------------------------------------------------
276 // "motion_notify_event" of m_mainWidget
277 //-----------------------------------------------------------------------------
281 gtk_window_motion_notify_callback( GtkWidget
*widget
, GdkEventMotion
*gdk_event
, wxMiniFrame
*win
)
283 if (gdk_event
->window
!= gtk_widget_get_window(widget
))
285 if (g_blockEventsOnDrag
) return TRUE
;
286 if (g_blockEventsOnScroll
) return TRUE
;
288 int x
= int(gdk_event
->x
);
289 int y
= int(gdk_event
->y
);
291 if (gdk_event
->is_hint
)
294 gdk_window_get_device_position(gdk_event
->window
, gdk_event
->device
, &x
, &y
, NULL
);
296 gdk_window_get_pointer(gdk_event
->window
, &x
, &y
, NULL
);
300 if (!win
->m_isDragging
)
302 if (win
->GetWindowStyle() & wxRESIZE_BORDER
)
304 if ((x
> win
->m_width
-14) && (y
> win
->m_height
-14))
305 gdk_window_set_cursor(gtk_widget_get_window(widget
), gdk_cursor_new(GDK_BOTTOM_RIGHT_CORNER
));
307 gdk_window_set_cursor(gtk_widget_get_window(widget
), NULL
);
308 win
->GTKUpdateCursor(false);
313 win
->m_oldX
= x
- win
->m_diffX
;
314 win
->m_oldY
= y
- win
->m_diffY
;
318 gdk_window_get_origin(gtk_widget_get_window(widget
), &org_x
, &org_y
);
319 x
+= org_x
- win
->m_diffX
;
320 y
+= org_y
- win
->m_diffY
;
323 gtk_window_move( GTK_WINDOW(win
->m_widget
), x
, y
);
329 //-----------------------------------------------------------------------------
331 //-----------------------------------------------------------------------------
333 static unsigned char close_bits
[]={
334 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xfb, 0xef, 0xdb, 0xed, 0x8b, 0xe8,
335 0x1b, 0xec, 0x3b, 0xee, 0x1b, 0xec, 0x8b, 0xe8, 0xdb, 0xed, 0xfb, 0xef,
336 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
339 IMPLEMENT_DYNAMIC_CLASS(wxMiniFrame
,wxFrame
)
341 wxMiniFrame::~wxMiniFrame()
345 GtkWidget
* eventbox
= gtk_bin_get_child(GTK_BIN(m_widget
));
346 GTKDisconnect(eventbox
);
350 bool wxMiniFrame::Create( wxWindow
*parent
, wxWindowID id
, const wxString
&title
,
351 const wxPoint
&pos
, const wxSize
&size
,
352 long style
, const wxString
&name
)
355 if (style
& wxCAPTION
)
358 if (style
& wxRESIZE_BORDER
)
362 m_isDragging
= false;
368 // don't allow sizing smaller than decorations
369 int minWidth
= 2 * m_miniEdge
;
370 int minHeight
= 2 * m_miniEdge
+ m_miniTitle
;
371 if (m_minWidth
< minWidth
)
372 m_minWidth
= minWidth
;
373 if (m_minHeight
< minHeight
)
374 m_minHeight
= minHeight
;
376 wxFrame::Create( parent
, id
, title
, pos
, size
, style
, name
);
378 // Use a GtkEventBox for the title and borders. Using m_widget for this
379 // almost works, except that setting the resize cursor has no effect.
380 GtkWidget
* eventbox
= gtk_event_box_new();
381 gtk_widget_add_events(eventbox
,
382 GDK_POINTER_MOTION_MASK
|
383 GDK_POINTER_MOTION_HINT_MASK
);
384 gtk_widget_show(eventbox
);
385 // Use a GtkAlignment to position m_mainWidget inside the decorations
386 GtkWidget
* alignment
= gtk_alignment_new(0, 0, 1, 1);
387 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment
),
388 m_miniTitle
+ m_miniEdge
, m_miniEdge
, m_miniEdge
, m_miniEdge
);
389 gtk_widget_show(alignment
);
390 // The GtkEventBox and GtkAlignment go between m_widget and m_mainWidget
391 gtk_widget_reparent(m_mainWidget
, alignment
);
392 gtk_container_add(GTK_CONTAINER(eventbox
), alignment
);
393 gtk_container_add(GTK_CONTAINER(m_widget
), eventbox
);
397 if (style
& wxRESIZE_BORDER
)
398 m_gdkFunc
= GDK_FUNC_RESIZE
;
399 gtk_window_set_default_size(GTK_WINDOW(m_widget
), m_width
, m_height
);
400 m_decorSize
.Set(0, 0);
403 if (m_parent
&& (GTK_IS_WINDOW(m_parent
->m_widget
)))
405 gtk_window_set_transient_for( GTK_WINDOW(m_widget
), GTK_WINDOW(m_parent
->m_widget
) );
408 if (m_miniTitle
&& (style
& wxCLOSE_BOX
))
410 wxImage img
= wxBitmap((const char*)close_bits
, 16, 16).ConvertToImage();
411 img
.Replace(0,0,0,123,123,123);
412 img
.SetMaskColour(123,123,123);
413 m_closeButton
= wxBitmap( img
);
416 /* these are called when the borders are drawn */
418 g_signal_connect_after(eventbox
, "draw", G_CALLBACK(draw
), this);
420 g_signal_connect_after(eventbox
, "expose_event", G_CALLBACK(expose_event
), this);
423 /* these are required for dragging the mini frame around */
424 g_signal_connect (eventbox
, "button_press_event",
425 G_CALLBACK (gtk_window_button_press_callback
), this);
426 g_signal_connect (eventbox
, "button_release_event",
427 G_CALLBACK (gtk_window_button_release_callback
), this);
428 g_signal_connect (eventbox
, "motion_notify_event",
429 G_CALLBACK (gtk_window_motion_notify_callback
), this);
430 g_signal_connect (eventbox
, "leave_notify_event",
431 G_CALLBACK (gtk_window_leave_callback
), this);
435 void wxMiniFrame::DoGetClientSize(int* width
, int* height
) const
437 wxFrame::DoGetClientSize(width
, height
);
439 if (m_useCachedClientSize
)
444 *width
-= 2 * m_miniEdge
;
445 if (*width
< 0) *width
= 0;
449 *height
-= m_miniTitle
+ 2 * m_miniEdge
;
450 if (*height
< 0) *height
= 0;
454 // Keep min size at least as large as decorations
455 void wxMiniFrame::DoSetSizeHints(int minW
, int minH
, int maxW
, int maxH
, int incW
, int incH
)
457 const int w
= 2 * m_miniEdge
;
458 const int h
= 2 * m_miniEdge
+ m_miniTitle
;
459 if (minW
< w
) minW
= w
;
460 if (minH
< h
) minH
= h
;
461 wxFrame::DoSetSizeHints(minW
, minH
, maxW
, maxH
, incW
, incH
);
464 void wxMiniFrame::SetTitle( const wxString
&title
)
466 wxFrame::SetTitle( title
);
468 GdkWindow
* window
= gtk_widget_get_window(gtk_bin_get_child(GTK_BIN(m_widget
)));
470 gdk_window_invalidate_rect(window
, NULL
, false);
473 #endif // wxUSE_MINIFRAME