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();
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 (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 const GdkEventMask mask
= GdkEventMask(
197 GDK_BUTTON_PRESS_MASK
|
198 GDK_BUTTON_RELEASE_MASK
|
199 GDK_POINTER_MOTION_MASK
|
200 GDK_POINTER_MOTION_HINT_MASK
|
201 GDK_BUTTON_MOTION_MASK
|
202 GDK_BUTTON1_MOTION_MASK
);
205 gdk_event
->device
, gdk_event
->window
, GDK_OWNERSHIP_NONE
,
206 false, mask
, NULL
, gdk_event
->time
);
208 gdk_pointer_grab(gdk_event
->window
, false, mask
, NULL
, NULL
, gdk_event
->time
);
216 win
->m_isDragging
= true;
222 //-----------------------------------------------------------------------------
223 // "button_release_event" of m_mainWidget
224 //-----------------------------------------------------------------------------
228 gtk_window_button_release_callback(GtkWidget
* widget
, GdkEventButton
* gdk_event
, wxMiniFrame
* win
)
230 if (gdk_event
->window
!= gtk_widget_get_window(widget
))
232 if (g_blockEventsOnDrag
) return TRUE
;
233 if (g_blockEventsOnScroll
) return TRUE
;
234 if (!win
->m_isDragging
) return TRUE
;
236 win
->m_isDragging
= false;
238 int x
= (int)gdk_event
->x
;
239 int y
= (int)gdk_event
->y
;
242 gdk_device_ungrab(gdk_event
->device
, gdk_event
->time
);
244 gdk_pointer_ungrab(gdk_event
->time
);
248 gdk_window_get_origin(gtk_widget_get_window(widget
), &org_x
, &org_y
);
249 x
+= org_x
- win
->m_diffX
;
250 y
+= org_y
- win
->m_diffY
;
253 gtk_window_move( GTK_WINDOW(win
->m_widget
), x
, y
);
259 //-----------------------------------------------------------------------------
260 // "leave_notify_event" of m_mainWidget
261 //-----------------------------------------------------------------------------
265 gtk_window_leave_callback(GtkWidget
*widget
,
266 GdkEventCrossing
* gdk_event
,
269 if (g_blockEventsOnDrag
) return FALSE
;
270 if (gdk_event
->window
!= gtk_widget_get_window(widget
))
273 gdk_window_set_cursor(gtk_widget_get_window(widget
), NULL
);
279 //-----------------------------------------------------------------------------
280 // "motion_notify_event" of m_mainWidget
281 //-----------------------------------------------------------------------------
285 gtk_window_motion_notify_callback( GtkWidget
*widget
, GdkEventMotion
*gdk_event
, wxMiniFrame
*win
)
287 if (gdk_event
->window
!= gtk_widget_get_window(widget
))
289 if (g_blockEventsOnDrag
) return TRUE
;
290 if (g_blockEventsOnScroll
) return TRUE
;
292 int x
= int(gdk_event
->x
);
293 int y
= int(gdk_event
->y
);
295 if (gdk_event
->is_hint
)
298 gdk_window_get_device_position(gdk_event
->window
, gdk_event
->device
, &x
, &y
, NULL
);
300 gdk_window_get_pointer(gdk_event
->window
, &x
, &y
, NULL
);
304 if (!win
->m_isDragging
)
307 if (win
->GetWindowStyle() & wxRESIZE_BORDER
)
309 if ((x
> win
->m_width
-14) && (y
> win
->m_height
-14))
310 gdk_window_set_cursor(gtk_widget_get_window(widget
), gdk_cursor_new(GDK_BOTTOM_RIGHT_CORNER
));
312 gdk_window_set_cursor(gtk_widget_get_window(widget
), NULL
);
313 win
->GTKUpdateCursor(false);
319 win
->m_oldX
= x
- win
->m_diffX
;
320 win
->m_oldY
= y
- win
->m_diffY
;
324 gdk_window_get_origin(gtk_widget_get_window(widget
), &org_x
, &org_y
);
325 x
+= org_x
- win
->m_diffX
;
326 y
+= org_y
- win
->m_diffY
;
329 gtk_window_move( GTK_WINDOW(win
->m_widget
), x
, y
);
335 //-----------------------------------------------------------------------------
337 //-----------------------------------------------------------------------------
339 static unsigned char close_bits
[]={
340 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xfb, 0xef, 0xdb, 0xed, 0x8b, 0xe8,
341 0x1b, 0xec, 0x3b, 0xee, 0x1b, 0xec, 0x8b, 0xe8, 0xdb, 0xed, 0xfb, 0xef,
342 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
345 IMPLEMENT_DYNAMIC_CLASS(wxMiniFrame
,wxFrame
)
347 wxMiniFrame
::~wxMiniFrame()
351 GtkWidget
* eventbox
= gtk_bin_get_child(GTK_BIN(m_widget
));
352 GTKDisconnect(eventbox
);
356 bool wxMiniFrame
::Create( wxWindow
*parent
, wxWindowID id
, const wxString
&title
,
357 const wxPoint
&pos
, const wxSize
&size
,
358 long style
, const wxString
&name
)
361 if (style
& wxCAPTION
)
364 if (style
& wxRESIZE_BORDER
)
368 m_isDragging
= false;
374 // don't allow sizing smaller than decorations
375 int minWidth
= 2 * m_miniEdge
;
376 int minHeight
= 2 * m_miniEdge
+ m_miniTitle
;
377 if (m_minWidth
< minWidth
)
378 m_minWidth
= minWidth
;
379 if (m_minHeight
< minHeight
)
380 m_minHeight
= minHeight
;
382 wxFrame
::Create( parent
, id
, title
, pos
, size
, style
, name
);
384 // Use a GtkEventBox for the title and borders. Using m_widget for this
385 // almost works, except that setting the resize cursor has no effect.
386 GtkWidget
* eventbox
= gtk_event_box_new();
387 gtk_widget_add_events(eventbox
,
388 GDK_POINTER_MOTION_MASK
|
389 GDK_POINTER_MOTION_HINT_MASK
);
390 gtk_widget_show(eventbox
);
391 // Use a GtkAlignment to position m_mainWidget inside the decorations
392 GtkWidget
* alignment
= gtk_alignment_new(0, 0, 1, 1);
393 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment
),
394 m_miniTitle
+ m_miniEdge
, m_miniEdge
, m_miniEdge
, m_miniEdge
);
395 gtk_widget_show(alignment
);
396 // The GtkEventBox and GtkAlignment go between m_widget and m_mainWidget
397 gtk_widget_reparent(m_mainWidget
, alignment
);
398 gtk_container_add(GTK_CONTAINER(eventbox
), alignment
);
399 gtk_container_add(GTK_CONTAINER(m_widget
), eventbox
);
403 if (style
& wxRESIZE_BORDER
)
404 m_gdkFunc
= GDK_FUNC_RESIZE
;
405 gtk_window_set_default_size(GTK_WINDOW(m_widget
), m_width
, m_height
);
406 m_decorSize
.Set(0, 0);
409 if (m_parent
&& (GTK_IS_WINDOW(m_parent
->m_widget
)))
411 gtk_window_set_transient_for( GTK_WINDOW(m_widget
), GTK_WINDOW(m_parent
->m_widget
) );
414 if (m_miniTitle
&& (style
& wxCLOSE_BOX
))
416 wxImage img
= wxBitmap((const char*)close_bits
, 16, 16).ConvertToImage();
417 img
.Replace(0,0,0,123,123,123);
418 img
.SetMaskColour(123,123,123);
419 m_closeButton
= wxBitmap( img
);
422 /* these are called when the borders are drawn */
424 g_signal_connect_after(eventbox
, "draw", G_CALLBACK(draw
), this);
426 g_signal_connect_after(eventbox
, "expose_event", G_CALLBACK(expose_event
), this);
429 /* these are required for dragging the mini frame around */
430 g_signal_connect (eventbox
, "button_press_event",
431 G_CALLBACK (gtk_window_button_press_callback
), this);
432 g_signal_connect (eventbox
, "button_release_event",
433 G_CALLBACK (gtk_window_button_release_callback
), this);
434 g_signal_connect (eventbox
, "motion_notify_event",
435 G_CALLBACK (gtk_window_motion_notify_callback
), this);
436 g_signal_connect (eventbox
, "leave_notify_event",
437 G_CALLBACK (gtk_window_leave_callback
), this);
441 void wxMiniFrame
::DoGetClientSize(int* width
, int* height
) const
443 wxFrame
::DoGetClientSize(width
, height
);
446 *width
-= 2 * m_miniEdge
;
447 if (*width
< 0) *width
= 0;
451 *height
-= m_miniTitle
+ 2 * m_miniEdge
;
452 if (*height
< 0) *height
= 0;
456 // Keep min size at least as large as decorations
457 void wxMiniFrame
::DoSetSizeHints(int minW
, int minH
, int maxW
, int maxH
, int incW
, int incH
)
459 const int w
= 2 * m_miniEdge
;
460 const int h
= 2 * m_miniEdge
+ m_miniTitle
;
461 if (minW
< w
) minW
= w
;
462 if (minH
< h
) minH
= h
;
463 wxFrame
::DoSetSizeHints(minW
, minH
, maxW
, maxH
, incW
, incH
);
466 void wxMiniFrame
::SetTitle( const wxString
&title
)
468 wxFrame
::SetTitle( title
);
470 GdkWindow
* window
= gtk_widget_get_window(gtk_bin_get_child(GTK_BIN(m_widget
)));
472 gdk_window_invalidate_rect(window
, NULL
, false);
475 #endif // wxUSE_MINIFRAME