1/////////////////////////////////////////////////////////////////////////////
2// Name:        src/gtk/cursor.cpp
3// Purpose:
4// Author:      Robert Roebling
5// Id:          $Id: cursor.cpp 62102 2009-09-24 20:41:08Z JS $
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#include "wx/cursor.h"
14
15#ifndef WX_PRECOMP
16    #include "wx/window.h"
17    #include "wx/image.h"
18    #include "wx/bitmap.h"
19#endif // WX_PRECOMP
20
21#include "wx/gtk/private.h" //for idle stuff
22
23//-----------------------------------------------------------------------------
24// wxCursor
25//-----------------------------------------------------------------------------
26
27class wxCursorRefData: public wxObjectRefData
28{
29  public:
30
31    wxCursorRefData();
32    virtual ~wxCursorRefData();
33
34    GdkCursor *m_cursor;
35};
36
37wxCursorRefData::wxCursorRefData()
38{
39    m_cursor = (GdkCursor *) NULL;
40}
41
42wxCursorRefData::~wxCursorRefData()
43{
44    if (m_cursor) gdk_cursor_unref( m_cursor );
45}
46
47//-----------------------------------------------------------------------------
48
49#define M_CURSORDATA ((wxCursorRefData *)m_refData)
50
51IMPLEMENT_DYNAMIC_CLASS(wxCursor,wxObject)
52
53wxCursor::wxCursor()
54{
55
56}
57
58wxCursor::wxCursor( int cursorId )
59{
60    m_refData = new wxCursorRefData();
61
62    GdkCursorType gdk_cur = GDK_LEFT_PTR;
63    switch (cursorId)
64    {
65        case wxCURSOR_BLANK:
66            {
67                static const gchar bits[] = { 0 };
68                static /* const -- not in GTK1 */ GdkColor color = { 0, 0, 0, 0 };
69
70                GdkPixmap *pixmap = gdk_bitmap_create_from_data(NULL, bits, 1, 1);
71                M_CURSORDATA->m_cursor = gdk_cursor_new_from_pixmap(pixmap,
72                                                                    pixmap,
73                                                                    &color,
74                                                                    &color,
75                                                                    0, 0);
76                g_object_unref(pixmap);
77            }
78            return;
79
80        case wxCURSOR_ARROW:            // fall through to default
81        case wxCURSOR_DEFAULT:          gdk_cur = GDK_LEFT_PTR; break;
82        case wxCURSOR_RIGHT_ARROW:      gdk_cur = GDK_RIGHT_PTR; break;
83        case wxCURSOR_HAND:             gdk_cur = GDK_HAND2; break;
84        case wxCURSOR_CROSS:            gdk_cur = GDK_CROSSHAIR; break;
85        case wxCURSOR_SIZEWE:           gdk_cur = GDK_SB_H_DOUBLE_ARROW; break;
86        case wxCURSOR_SIZENS:           gdk_cur = GDK_SB_V_DOUBLE_ARROW; break;
87        case wxCURSOR_ARROWWAIT:
88        case wxCURSOR_WAIT:
89        case wxCURSOR_WATCH:            gdk_cur = GDK_WATCH; break;
90        case wxCURSOR_SIZING:           gdk_cur = GDK_FLEUR; break;
91        case wxCURSOR_SPRAYCAN:         gdk_cur = GDK_SPRAYCAN; break;
92        case wxCURSOR_IBEAM:            gdk_cur = GDK_XTERM; break;
93        case wxCURSOR_PENCIL:           gdk_cur = GDK_PENCIL; break;
94        case wxCURSOR_NO_ENTRY:         gdk_cur = GDK_PIRATE; break;
95        case wxCURSOR_SIZENWSE:
96        case wxCURSOR_SIZENESW:         gdk_cur = GDK_FLEUR; break;
97        case wxCURSOR_QUESTION_ARROW:   gdk_cur = GDK_QUESTION_ARROW; break;
98        case wxCURSOR_PAINT_BRUSH:      gdk_cur = GDK_SPRAYCAN; break;
99        case wxCURSOR_MAGNIFIER:        gdk_cur = GDK_PLUS; break;
100        case wxCURSOR_CHAR:             gdk_cur = GDK_XTERM; break;
101        case wxCURSOR_LEFT_BUTTON:      gdk_cur = GDK_LEFTBUTTON; break;
102        case wxCURSOR_MIDDLE_BUTTON:    gdk_cur = GDK_MIDDLEBUTTON; break;
103        case wxCURSOR_RIGHT_BUTTON:     gdk_cur = GDK_RIGHTBUTTON; break;
104        case wxCURSOR_BULLSEYE:         gdk_cur = GDK_TARGET; break;
105
106        case wxCURSOR_POINT_LEFT:       gdk_cur = GDK_SB_LEFT_ARROW; break;
107        case wxCURSOR_POINT_RIGHT:      gdk_cur = GDK_SB_RIGHT_ARROW; break;
108/*
109        case wxCURSOR_DOUBLE_ARROW:     gdk_cur = GDK_DOUBLE_ARROW; break;
110        case wxCURSOR_CROSS_REVERSE:    gdk_cur = GDK_CROSS_REVERSE; break;
111        case wxCURSOR_BASED_ARROW_UP:   gdk_cur = GDK_BASED_ARROW_UP; break;
112        case wxCURSOR_BASED_ARROW_DOWN: gdk_cur = GDK_BASED_ARROW_DOWN; break;
113*/
114
115        default:
116            wxFAIL_MSG(wxT("unsupported cursor type"));
117            // will use the standard one
118            break;
119    }
120
121    M_CURSORDATA->m_cursor = gdk_cursor_new( gdk_cur );
122}
123
124extern GtkWidget *wxGetRootWindow();
125
126wxCursor::wxCursor(const char bits[], int width, int  height,
127                   int hotSpotX, int hotSpotY,
128                   const char maskBits[], const wxColour *fg, const wxColour *bg)
129{
130    if (!maskBits)
131        maskBits = bits;
132    if (!fg)
133        fg = wxBLACK;
134    if (!bg)
135        bg = wxWHITE;
136    if (hotSpotX < 0 || hotSpotX >= width)
137        hotSpotX = 0;
138    if (hotSpotY < 0 || hotSpotY >= height)
139        hotSpotY = 0;
140
141    GdkBitmap *data = gdk_bitmap_create_from_data( wxGetRootWindow()->window, (gchar *) bits, width, height );
142    GdkBitmap *mask = gdk_bitmap_create_from_data( wxGetRootWindow()->window, (gchar *) maskBits, width, height);
143
144    m_refData = new wxCursorRefData;
145    M_CURSORDATA->m_cursor = gdk_cursor_new_from_pixmap(
146                 data, mask, fg->GetColor(), bg->GetColor(),
147                 hotSpotX, hotSpotY );
148
149    g_object_unref (data);
150    g_object_unref (mask);
151}
152
153#if wxUSE_IMAGE
154
155static void GetHotSpot(const wxImage& image, int& x, int& y)
156{
157    if (image.HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_X))
158        x = image.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X);
159    else
160        x = 0;
161
162    if (image.HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y))
163        y = image.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y);
164    else
165        y = 0;
166
167    if (x < 0 || x >= image.GetWidth())
168        x = 0;
169    if (y < 0 || y >= image.GetHeight())
170        y = 0;
171}
172
173wxCursor::wxCursor( const wxImage & image )
174{
175    int w = image.GetWidth() ;
176    int h = image.GetHeight();
177    bool bHasMask = image.HasMask();
178    int hotSpotX, hotSpotY;
179    GetHotSpot(image, hotSpotX, hotSpotY);
180    m_refData = new wxCursorRefData;
181    wxImage image_copy(image);
182
183    GdkDisplay* display = gdk_drawable_get_display(wxGetRootWindow()->window);
184    if (gdk_display_supports_cursor_color(display))
185    {
186        if (!image.HasAlpha())
187        {
188            // add alpha, so wxBitmap will convert to pixbuf format
189            image_copy.InitAlpha();
190        }
191        wxBitmap bitmap(image_copy);
192        wxASSERT(bitmap.HasPixbuf());
193        M_CURSORDATA->m_cursor = gdk_cursor_new_from_pixbuf
194                             (
195                              display,
196                              bitmap.GetPixbuf(),
197                              hotSpotX, hotSpotY
198                             );
199        return;
200    }
201
202    unsigned long keyMaskColor = 0;
203    GdkPixmap* mask;
204    if (bHasMask)
205    {
206        keyMaskColor = wxImageHistogram::MakeKey(
207            image.GetMaskRed(), image.GetMaskGreen(), image.GetMaskBlue());
208        // get mask before image is modified
209        wxBitmap bitmap(image, 1);
210        mask = bitmap.GetMask()->GetBitmap();
211        g_object_ref(mask);
212    }
213    else
214    {
215        const int size = ((w + 7) / 8) * h;
216        char* bits = new char[size];
217        memset(bits, 0xff, size);
218        mask = gdk_bitmap_create_from_data(
219            wxGetRootWindow()->window, bits, w, h);
220        delete[] bits;
221    }
222
223    // modify image so wxBitmap can be used to convert to pixmap
224    image_copy.SetMask(false);
225    int i, j;
226    wxByte* data = image_copy.GetData();
227    for (j = 0; j < h; j++)
228    {
229        for (i = 0; i < w; i++, data += 3)
230        {
231            //if average value is > mid grey
232            if (int(data[0]) + data[1] + data[2] >= 3 * 128)
233            {
234                // wxBitmap only converts (255,255,255) to white
235                data[0] = 255;
236                data[1] = 255;
237                data[2] = 255;
238            }
239        }
240    }
241    wxBitmap bitmap(image_copy, 1);
242
243    // find the most frequent color(s)
244    wxImageHistogram histogram;
245    image.ComputeHistogram(histogram);
246
247    long colMostFreq = 0;
248    unsigned long nMost = 0;
249    long colNextMostFreq = 0;
250    unsigned long nNext = 0;
251    for ( wxImageHistogram::iterator entry = histogram.begin();
252          entry != histogram.end();
253          ++entry )
254    {
255        unsigned long key = entry->first;
256        if ( !bHasMask || (key != keyMaskColor) )
257        {
258            unsigned long value = entry->second.value;
259            if (value > nMost)
260            {
261                nNext = nMost;
262                colNextMostFreq = colMostFreq;
263                nMost = value;
264                colMostFreq = key;
265            }
266            else if (value > nNext)
267            {
268                nNext = value;
269                colNextMostFreq = key;
270            }
271        }
272    }
273
274    wxColour fg = wxColour ( (unsigned char)(colMostFreq >> 16),
275                             (unsigned char)(colMostFreq >> 8),
276                             (unsigned char)(colMostFreq) );
277
278    wxColour bg = wxColour ( (unsigned char)(colNextMostFreq >> 16),
279                             (unsigned char)(colNextMostFreq >> 8),
280                             (unsigned char)(colNextMostFreq) );
281
282    int fg_intensity = fg.Red() + fg.Green() + fg.Blue();
283    int bg_intensity = bg.Red() + bg.Green() + bg.Blue();
284
285    if (bg_intensity > fg_intensity)
286    {
287        //swap fg and bg
288        wxColour tmp = fg;
289        fg = bg;
290        bg = tmp;
291    }
292
293    M_CURSORDATA->m_cursor = gdk_cursor_new_from_pixmap
294                             (
295                                bitmap.GetPixmap(),
296                                mask,
297                                fg.GetColor(), bg.GetColor(),
298                                hotSpotX, hotSpotY
299                             );
300
301    g_object_unref (mask);
302}
303
304#endif // wxUSE_IMAGE
305
306wxCursor::~wxCursor()
307{
308}
309
310bool wxCursor::IsOk() const
311{
312    return (m_refData != NULL);
313}
314
315GdkCursor *wxCursor::GetCursor() const
316{
317    return M_CURSORDATA->m_cursor;
318}
319
320//-----------------------------------------------------------------------------
321// busy cursor routines
322//-----------------------------------------------------------------------------
323
324extern wxCursor g_globalCursor;
325
326static wxCursor  gs_savedCursor;
327static int       gs_busyCount = 0;
328
329const wxCursor &wxBusyCursor::GetStoredCursor()
330{
331    return gs_savedCursor;
332}
333
334const wxCursor wxBusyCursor::GetBusyCursor()
335{
336    return wxCursor(wxCURSOR_WATCH);
337}
338
339static void InternalIdle(const wxWindowList& list)
340{
341    wxWindowList::const_iterator i = list.begin();
342    for (size_t n = list.size(); n--; ++i)
343    {
344        wxWindow* win = *i;
345        win->OnInternalIdle();
346        InternalIdle(win->GetChildren());
347    }
348}
349
350void wxEndBusyCursor()
351{
352    if (--gs_busyCount > 0)
353        return;
354
355    g_globalCursor = gs_savedCursor;
356    gs_savedCursor = wxNullCursor;
357    InternalIdle(wxTopLevelWindows);
358}
359
360void wxBeginBusyCursor(const wxCursor* cursor)
361{
362    if (gs_busyCount++ > 0)
363        return;
364
365    wxASSERT_MSG( !gs_savedCursor.Ok(),
366                  wxT("forgot to call wxEndBusyCursor, will leak memory") );
367
368    gs_savedCursor = g_globalCursor;
369    g_globalCursor = *cursor;
370    InternalIdle(wxTopLevelWindows);
371    gdk_flush();
372}
373
374bool wxIsBusy()
375{
376    return gs_busyCount > 0;
377}
378
379void wxSetCursor( const wxCursor& cursor )
380{
381    if (g_isIdle)
382        wxapp_install_idle_handler();
383
384    g_globalCursor = cursor;
385}
386