1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/gtk/minifram.cpp
3// Purpose:
4// Author:      Robert Roebling
5// Id:          $Id: minifram.cpp 49712 2007-11-07 10:23:21Z RR $
6// Copyright:   (c) 1998 Robert Roebling
7// Licence:     wxWindows licence
8/////////////////////////////////////////////////////////////////////////////
9
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
13#if wxUSE_MINIFRAME
14
15#include "wx/minifram.h"
16
17#ifndef WX_PRECOMP
18    #include "wx/settings.h"
19    #include "wx/dcclient.h"
20    #include "wx/image.h"
21#endif
22
23#include "wx/gtk/win_gtk.h"
24#include "wx/gtk/private.h"
25
26//-----------------------------------------------------------------------------
27// data
28//-----------------------------------------------------------------------------
29
30extern bool        g_blockEventsOnDrag;
31extern bool        g_blockEventsOnScroll;
32extern GtkWidget  *wxGetRootWindow();
33
34//-----------------------------------------------------------------------------
35// "expose_event" of m_mainWidget
36//-----------------------------------------------------------------------------
37
38// StepColour() it a utility function that simply darkens
39// or lightens a color, based on the specified percentage
40static wxColor StepColour(const wxColor& c, int percent)
41{
42    int r = c.Red(), g = c.Green(), b = c.Blue();
43    return wxColour((unsigned char)wxMin((r*percent)/100,255),
44                    (unsigned char)wxMin((g*percent)/100,255),
45                    (unsigned char)wxMin((b*percent)/100,255));
46}
47
48static wxColor LightContrastColour(const wxColour& c)
49{
50    int amount = 120;
51
52    // if the color is especially dark, then
53    // make the contrast even lighter
54    if (c.Red() < 128 && c.Green() < 128 && c.Blue() < 128)
55        amount = 160;
56
57    return StepColour(c, amount);
58}
59
60extern "C" {
61static gboolean gtk_window_own_expose_callback(GtkWidget* widget, GdkEventExpose* gdk_event, wxMiniFrame* win)
62{
63    // don't need to install idle handler, its done from "event" signal
64
65    if (!win->m_hasVMT || gdk_event->count > 0)
66        return false;
67
68    GtkPizza *pizza = GTK_PIZZA(widget);
69
70    gtk_paint_shadow (widget->style,
71                      pizza->bin_window,
72                      GTK_STATE_NORMAL,
73                      GTK_SHADOW_OUT,
74                      NULL, NULL, NULL, // FIXME: No clipping?
75                      0, 0,
76                      win->m_width, win->m_height);
77
78    int style = win->GetWindowStyle();
79
80    wxClientDC dc(win);
81    // Hack alert
82    dc.m_window = pizza->bin_window;
83
84    if (style & wxRESIZE_BORDER)
85    {
86        dc.SetBrush( *wxGREY_BRUSH );
87        dc.SetPen( *wxTRANSPARENT_PEN );
88        dc.DrawRectangle( win->m_width - 14, win->m_height-14, 14, 14 );
89    }
90
91    if (!win->GetTitle().empty() &&
92        ((style & wxCAPTION) ||
93         (style & wxTINY_CAPTION_HORIZ) ||
94         (style & wxTINY_CAPTION_VERT)))
95    {
96        dc.SetFont( *wxSMALL_FONT );
97
98        wxBrush brush( LightContrastColour( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT) ) );
99        dc.SetBrush( brush );
100        dc.SetPen( *wxTRANSPARENT_PEN );
101        dc.DrawRectangle( win->m_miniEdge-1,
102                          win->m_miniEdge-1,
103                          win->m_width - (2*(win->m_miniEdge-1)),
104                          15  );
105
106        dc.SetTextForeground( *wxWHITE );
107        dc.DrawText( win->GetTitle(), 6, 4 );
108
109        if (style & wxCLOSE_BOX)
110            dc.DrawBitmap( win->m_closeButton, win->m_width-18, 3, true );
111    }
112    return false;
113}
114}
115
116//-----------------------------------------------------------------------------
117// "button_press_event" of m_mainWidget
118//-----------------------------------------------------------------------------
119
120extern "C" {
121static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxMiniFrame *win )
122{
123    // don't need to install idle handler, its done from "event" signal
124
125    if (!win->m_hasVMT) return FALSE;
126    if (g_blockEventsOnDrag) return TRUE;
127    if (g_blockEventsOnScroll) return TRUE;
128
129    if (win->m_isDragging) return TRUE;
130
131    GtkPizza *pizza = GTK_PIZZA(widget);
132    if (gdk_event->window != pizza->bin_window) return TRUE;
133
134    int style = win->GetWindowStyle();
135
136    int y = (int)gdk_event->y;
137    int x = (int)gdk_event->x;
138
139    if ((style & wxRESIZE_BORDER) &&
140        (x > win->m_width-14) && (y > win->m_height-14))
141    {
142        GtkWidget *ancestor = gtk_widget_get_toplevel( widget );
143
144        GdkWindow *source = GTK_PIZZA(widget)->bin_window;
145
146        int org_x = 0;
147        int org_y = 0;
148        gdk_window_get_origin( source, &org_x, &org_y );
149
150        gtk_window_begin_resize_drag (GTK_WINDOW (ancestor),
151                                  GDK_WINDOW_EDGE_SOUTH_EAST,
152                                  1,
153                                  org_x + x,
154                                  org_y + y,
155                                  0);
156
157        return TRUE;
158    }
159
160    if ((style & wxCLOSE_BOX) &&
161        ((style & wxCAPTION) || (style & wxTINY_CAPTION_HORIZ) || (style & wxTINY_CAPTION_VERT)))
162    {
163        if ((y > 3) && (y < 19) && (x > win->m_width-19) && (x < win->m_width-3))
164        {
165            win->Close();
166            return TRUE;
167        }
168    }
169
170    if (y > win->m_miniEdge-1 + 15) return TRUE;
171
172    gdk_window_raise( win->m_widget->window );
173
174    gdk_pointer_grab( widget->window, FALSE,
175                      (GdkEventMask)
176                         (GDK_BUTTON_PRESS_MASK |
177                          GDK_BUTTON_RELEASE_MASK |
178                          GDK_POINTER_MOTION_MASK        |
179                          GDK_POINTER_MOTION_HINT_MASK  |
180                          GDK_BUTTON_MOTION_MASK        |
181                          GDK_BUTTON1_MOTION_MASK),
182                      (GdkWindow *) NULL,
183                      (GdkCursor *) NULL,
184                      (unsigned int) GDK_CURRENT_TIME );
185
186    win->m_diffX = x;
187    win->m_diffY = y;
188    win->m_oldX = 0;
189    win->m_oldY = 0;
190
191    win->m_isDragging = true;
192
193    return TRUE;
194}
195}
196
197//-----------------------------------------------------------------------------
198// "button_release_event" of m_mainWidget
199//-----------------------------------------------------------------------------
200
201extern "C" {
202static gint gtk_window_button_release_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxMiniFrame *win )
203{
204    // don't need to install idle handler, its done from "event" signal
205
206    if (!win->m_hasVMT) return FALSE;
207    if (g_blockEventsOnDrag) return TRUE;
208    if (g_blockEventsOnScroll) return TRUE;
209
210    if (!win->m_isDragging) return TRUE;
211
212    win->m_isDragging = false;
213
214    int x = (int)gdk_event->x;
215    int y = (int)gdk_event->y;
216
217    gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME );
218    int org_x = 0;
219    int org_y = 0;
220    gdk_window_get_origin( widget->window, &org_x, &org_y );
221    x += org_x - win->m_diffX;
222    y += org_y - win->m_diffY;
223    win->m_x = x;
224    win->m_y = y;
225    gtk_window_move( GTK_WINDOW(win->m_widget), x, y );
226
227    return TRUE;
228}
229}
230
231//-----------------------------------------------------------------------------
232// "leave_notify_event" of m_mainWidget
233//-----------------------------------------------------------------------------
234
235extern "C" {
236static gboolean
237gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_event, wxMiniFrame *win )
238{
239    // don't need to install idle handler, its done from "event" signal
240
241    if (!win->m_hasVMT) return FALSE;
242    if (g_blockEventsOnDrag) return FALSE;
243
244    gdk_window_set_cursor( widget->window, NULL );
245
246    return FALSE;
247}
248}
249
250//-----------------------------------------------------------------------------
251// "motion_notify_event" of m_mainWidget
252//-----------------------------------------------------------------------------
253
254extern "C" {
255static gint
256gtk_window_motion_notify_callback( GtkWidget *widget, GdkEventMotion *gdk_event, wxMiniFrame *win )
257{
258    // don't need to install idle handler, its done from "event" signal
259
260    if (!win->m_hasVMT) return FALSE;
261    if (g_blockEventsOnDrag) return TRUE;
262    if (g_blockEventsOnScroll) return TRUE;
263
264    if (gdk_event->is_hint)
265    {
266       int x = 0;
267       int y = 0;
268       GdkModifierType state;
269       gdk_window_get_pointer(gdk_event->window, &x, &y, &state);
270       gdk_event->x = x;
271       gdk_event->y = y;
272       gdk_event->state = state;
273    }
274
275    int style = win->GetWindowStyle();
276
277    int x = (int)gdk_event->x;
278    int y = (int)gdk_event->y;
279
280    if (!win->m_isDragging)
281    {
282        if (style & wxRESIZE_BORDER)
283        {
284            if ((x > win->m_width-14) && (y > win->m_height-14))
285               gdk_window_set_cursor( widget->window, gdk_cursor_new( GDK_BOTTOM_RIGHT_CORNER ) );
286            else
287               gdk_window_set_cursor( widget->window, NULL );
288        }
289        return TRUE;
290    }
291
292    win->m_oldX = x - win->m_diffX;
293    win->m_oldY = y - win->m_diffY;
294
295    int org_x = 0;
296    int org_y = 0;
297    gdk_window_get_origin( widget->window, &org_x, &org_y );
298    x += org_x - win->m_diffX;
299    y += org_y - win->m_diffY;
300    win->m_x = x;
301    win->m_y = y;
302    gtk_window_move( GTK_WINDOW(win->m_widget), x, y );
303
304
305    return TRUE;
306}
307}
308
309//-----------------------------------------------------------------------------
310// wxMiniFrame
311//-----------------------------------------------------------------------------
312
313static unsigned char close_bits[]={
314    0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xfb, 0xef, 0xdb, 0xed, 0x8b, 0xe8,
315    0x1b, 0xec, 0x3b, 0xee, 0x1b, 0xec, 0x8b, 0xe8, 0xdb, 0xed, 0xfb, 0xef,
316    0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
317
318
319IMPLEMENT_DYNAMIC_CLASS(wxMiniFrame,wxFrame)
320
321bool wxMiniFrame::Create( wxWindow *parent, wxWindowID id, const wxString &title,
322      const wxPoint &pos, const wxSize &size,
323      long style, const wxString &name )
324{
325    if ((style & wxCAPTION) || (style & wxTINY_CAPTION_HORIZ) || (style & wxTINY_CAPTION_VERT))
326        m_miniTitle = 16;
327
328    if (style & wxRESIZE_BORDER)
329        m_miniEdge = 4;
330    else
331        m_miniEdge = 3;
332    m_isDragging = false;
333    m_oldX = -1;
334    m_oldY = -1;
335    m_diffX = 0;
336    m_diffY = 0;
337
338    wxFrame::Create( parent, id, title, pos, size, style, name );
339
340    if (m_parent && (GTK_IS_WINDOW(m_parent->m_widget)))
341    {
342        gtk_window_set_transient_for( GTK_WINDOW(m_widget), GTK_WINDOW(m_parent->m_widget) );
343    }
344
345    if ((style & wxCLOSE_BOX) &&
346        ((style & wxCAPTION) || (style & wxTINY_CAPTION_HORIZ) || (style & wxTINY_CAPTION_VERT)))
347    {
348        wxImage img = wxBitmap((const char*)close_bits, 16, 16).ConvertToImage();
349        img.Replace(0,0,0,123,123,123);
350        img.SetMaskColour(123,123,123);
351        m_closeButton = wxBitmap( img );
352    }
353
354    /* these are called when the borders are drawn */
355    g_signal_connect (m_mainWidget, "expose_event",
356                      G_CALLBACK (gtk_window_own_expose_callback), this );
357
358    /* these are required for dragging the mini frame around */
359    g_signal_connect (m_mainWidget, "button_press_event",
360                      G_CALLBACK (gtk_window_button_press_callback), this);
361    g_signal_connect (m_mainWidget, "button_release_event",
362                      G_CALLBACK (gtk_window_button_release_callback), this);
363    g_signal_connect (m_mainWidget, "motion_notify_event",
364                      G_CALLBACK (gtk_window_motion_notify_callback), this);
365    g_signal_connect (m_mainWidget, "leave_notify_event",
366                      G_CALLBACK (gtk_window_leave_callback), this);
367    return true;
368}
369
370void wxMiniFrame::SetTitle( const wxString &title )
371{
372    wxFrame::SetTitle( title );
373
374    if (GTK_PIZZA(m_mainWidget)->bin_window)
375        gdk_window_invalidate_rect( GTK_PIZZA(m_mainWidget)->bin_window, NULL, true );
376}
377
378#endif // wxUSE_MINIFRAME
379