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 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 (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 (g_blockEventsOnDrag
) return FALSE
;
263 if (gdk_event
->window
!= gtk_widget_get_window(widget
))
266 gdk_window_set_cursor(gtk_widget_get_window(widget
), NULL
);
272 //-----------------------------------------------------------------------------
273 // "motion_notify_event" of m_mainWidget
274 //-----------------------------------------------------------------------------
278 gtk_window_motion_notify_callback( GtkWidget
*widget
, GdkEventMotion
*gdk_event
, wxMiniFrame
*win
)
280 if (gdk_event
->window
!= gtk_widget_get_window(widget
))
282 if (g_blockEventsOnDrag
) return TRUE
;
283 if (g_blockEventsOnScroll
) return TRUE
;
285 if (gdk_event
->is_hint
)
289 GdkModifierType state
;
290 gdk_window_get_pointer(gdk_event
->window
, &x
, &y
, &state
);
293 gdk_event
->state
= state
;
296 int x
= (int)gdk_event
->x
;
297 int y
= (int)gdk_event
->y
;
299 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);
314 win
->m_oldX
= x
- win
->m_diffX
;
315 win
->m_oldY
= y
- win
->m_diffY
;
319 gdk_window_get_origin(gtk_widget_get_window(widget
), &org_x
, &org_y
);
320 x
+= org_x
- win
->m_diffX
;
321 y
+= org_y
- win
->m_diffY
;
324 gtk_window_move( GTK_WINDOW(win
->m_widget
), x
, y
);
330 //-----------------------------------------------------------------------------
332 //-----------------------------------------------------------------------------
334 static unsigned char close_bits
[]={
335 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xfb, 0xef, 0xdb, 0xed, 0x8b, 0xe8,
336 0x1b, 0xec, 0x3b, 0xee, 0x1b, 0xec, 0x8b, 0xe8, 0xdb, 0xed, 0xfb, 0xef,
337 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
340 IMPLEMENT_DYNAMIC_CLASS(wxMiniFrame
,wxFrame
)
342 wxMiniFrame::~wxMiniFrame()
346 GtkWidget
* eventbox
= gtk_bin_get_child(GTK_BIN(m_widget
));
347 GTKDisconnect(eventbox
);
351 bool wxMiniFrame::Create( wxWindow
*parent
, wxWindowID id
, const wxString
&title
,
352 const wxPoint
&pos
, const wxSize
&size
,
353 long style
, const wxString
&name
)
356 if (style
& wxCAPTION
)
359 if (style
& wxRESIZE_BORDER
)
363 m_isDragging
= false;
369 // don't allow sizing smaller than decorations
370 int minWidth
= 2 * m_miniEdge
;
371 int minHeight
= 2 * m_miniEdge
+ m_miniTitle
;
372 if (m_minWidth
< minWidth
)
373 m_minWidth
= minWidth
;
374 if (m_minHeight
< minHeight
)
375 m_minHeight
= minHeight
;
377 wxFrame::Create( parent
, id
, title
, pos
, size
, style
, name
);
379 // Use a GtkEventBox for the title and borders. Using m_widget for this
380 // almost works, except that setting the resize cursor has no effect.
381 GtkWidget
* eventbox
= gtk_event_box_new();
382 gtk_widget_add_events(eventbox
,
383 GDK_POINTER_MOTION_MASK
|
384 GDK_POINTER_MOTION_HINT_MASK
);
385 gtk_widget_show(eventbox
);
386 // Use a GtkAlignment to position m_mainWidget inside the decorations
387 GtkWidget
* alignment
= gtk_alignment_new(0, 0, 1, 1);
388 gtk_alignment_set_padding(GTK_ALIGNMENT(alignment
),
389 m_miniTitle
+ m_miniEdge
, m_miniEdge
, m_miniEdge
, m_miniEdge
);
390 gtk_widget_show(alignment
);
391 // The GtkEventBox and GtkAlignment go between m_widget and m_mainWidget
392 gtk_widget_reparent(m_mainWidget
, alignment
);
393 gtk_container_add(GTK_CONTAINER(eventbox
), alignment
);
394 gtk_container_add(GTK_CONTAINER(m_widget
), eventbox
);
398 if (style
& wxRESIZE_BORDER
)
399 m_gdkFunc
= GDK_FUNC_RESIZE
;
400 gtk_window_set_default_size(GTK_WINDOW(m_widget
), m_width
, m_height
);
401 m_decorSize
.Set(0, 0);
404 if (m_parent
&& (GTK_IS_WINDOW(m_parent
->m_widget
)))
406 gtk_window_set_transient_for( GTK_WINDOW(m_widget
), GTK_WINDOW(m_parent
->m_widget
) );
409 if (m_miniTitle
&& (style
& wxCLOSE_BOX
))
411 wxImage img
= wxBitmap((const char*)close_bits
, 16, 16).ConvertToImage();
412 img
.Replace(0,0,0,123,123,123);
413 img
.SetMaskColour(123,123,123);
414 m_closeButton
= wxBitmap( img
);
417 /* these are called when the borders are drawn */
419 g_signal_connect_after(eventbox
, "draw", G_CALLBACK(draw
), this);
421 g_signal_connect_after(eventbox
, "expose_event", G_CALLBACK(expose_event
), this);
424 /* these are required for dragging the mini frame around */
425 g_signal_connect (eventbox
, "button_press_event",
426 G_CALLBACK (gtk_window_button_press_callback
), this);
427 g_signal_connect (eventbox
, "button_release_event",
428 G_CALLBACK (gtk_window_button_release_callback
), this);
429 g_signal_connect (eventbox
, "motion_notify_event",
430 G_CALLBACK (gtk_window_motion_notify_callback
), this);
431 g_signal_connect (eventbox
, "leave_notify_event",
432 G_CALLBACK (gtk_window_leave_callback
), this);
436 void wxMiniFrame::DoGetClientSize(int* width
, int* height
) const
438 wxFrame::DoGetClientSize(width
, height
);
441 *width
-= 2 * m_miniEdge
;
442 if (*width
< 0) *width
= 0;
446 *height
-= m_miniTitle
+ 2 * m_miniEdge
;
447 if (*height
< 0) *height
= 0;
451 // Keep min size at least as large as decorations
452 void wxMiniFrame::DoSetSizeHints(int minW
, int minH
, int maxW
, int maxH
, int incW
, int incH
)
454 const int w
= 2 * m_miniEdge
;
455 const int h
= 2 * m_miniEdge
+ m_miniTitle
;
456 if (minW
< w
) minW
= w
;
457 if (minH
< h
) minH
= h
;
458 wxFrame::DoSetSizeHints(minW
, minH
, maxW
, maxH
, incW
, incH
);
461 void wxMiniFrame::SetTitle( const wxString
&title
)
463 wxFrame::SetTitle( title
);
465 GdkWindow
* window
= gtk_widget_get_window(gtk_bin_get_child(GTK_BIN(m_widget
)));
467 gdk_window_invalidate_rect(window
, NULL
, false);
470 #endif // wxUSE_MINIFRAME