1 /////////////////////////////////////////////////////////////////////////////
2 // Name:        src/gtk/cursor.cpp
3 // Purpose:     wxCursor implementation
4 // Author:      Robert Roebling
5 // Copyright:   (c) 1998 Robert Roebling
6 // Licence:     wxWindows licence
7 /////////////////////////////////////////////////////////////////////////////
8 
9 // For compilers that support precompilation, includes "wx.h".
10 #include "wx/wxprec.h"
11 
12 #include "wx/cursor.h"
13 
14 #ifndef WX_PRECOMP
15     #include "wx/window.h"
16     #include "wx/image.h"
17     #include "wx/bitmap.h"
18     #include "wx/log.h"
19 #endif // WX_PRECOMP
20 
21 #include "wx/gtk/private/wrapgtk.h"
22 #include "wx/gtk/private/object.h"
23 
24 GdkWindow* wxGetTopLevelGDK();
25 
26 //-----------------------------------------------------------------------------
27 // wxCursorRefData
28 //-----------------------------------------------------------------------------
29 
30 class wxCursorRefData: public wxGDIRefData
31 {
32 public:
33     wxCursorRefData();
34     virtual ~wxCursorRefData();
35 
IsOk() const36     virtual bool IsOk() const wxOVERRIDE { return m_cursor != NULL; }
37 
38     GdkCursor *m_cursor;
39 
40 private:
41     // There is no way to copy m_cursor so we can't implement a copy ctor
42     // properly.
43     wxDECLARE_NO_COPY_CLASS(wxCursorRefData);
44 };
45 
wxCursorRefData()46 wxCursorRefData::wxCursorRefData()
47 {
48     m_cursor = NULL;
49 }
50 
~wxCursorRefData()51 wxCursorRefData::~wxCursorRefData()
52 {
53     if (m_cursor)
54     {
55 #ifdef __WXGTK3__
56         g_object_unref(m_cursor);
57 #else
58         gdk_cursor_unref(m_cursor);
59 #endif
60     }
61 }
62 
63 //-----------------------------------------------------------------------------
64 // wxCursor
65 //-----------------------------------------------------------------------------
66 
67 #define M_CURSORDATA static_cast<wxCursorRefData*>(m_refData)
68 
69 wxIMPLEMENT_DYNAMIC_CLASS(wxCursor, wxGDIObject);
70 
wxCursor()71 wxCursor::wxCursor()
72 {
73 }
74 
75 #if wxUSE_IMAGE
wxCursor(const wxString & cursor_file,wxBitmapType type,int hotSpotX,int hotSpotY)76 wxCursor::wxCursor(const wxString& cursor_file,
77                    wxBitmapType type,
78                    int hotSpotX, int hotSpotY)
79 {
80     wxImage img;
81     if (!img.LoadFile(cursor_file, type))
82         return;
83 
84     // eventually set the hotspot:
85     if (!img.HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_X))
86         img.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X, hotSpotX);
87     if (!img.HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y))
88         img.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y, hotSpotY);
89 
90     InitFromImage(img);
91 }
92 
wxCursor(const wxImage & img)93 wxCursor::wxCursor(const wxImage& img)
94 {
95     InitFromImage(img);
96 }
97 #endif
98 
wxCursor(const char bits[],int width,int height,int hotSpotX,int hotSpotY,const char maskBits[],const wxColour * fg,const wxColour * bg)99 wxCursor::wxCursor(const char bits[], int width, int height,
100                    int hotSpotX, int hotSpotY,
101                    const char maskBits[], const wxColour *fg, const wxColour *bg)
102 {
103     m_refData = new wxCursorRefData;
104     if (hotSpotX < 0 || hotSpotX >= width)
105         hotSpotX = 0;
106     if (hotSpotY < 0 || hotSpotY >= height)
107         hotSpotY = 0;
108 #ifdef __WXGTK3__
109     wxBitmap bitmap(bits, width, height);
110     if (maskBits)
111         bitmap.SetMask(new wxMask(wxBitmap(maskBits, width, height), *wxWHITE));
112     GdkPixbuf* pixbuf = bitmap.GetPixbuf();
113     if ((fg && *fg != *wxBLACK) || (bg && *bg != *wxWHITE))
114     {
115         const int stride = gdk_pixbuf_get_rowstride(pixbuf);
116         const int n_channels = gdk_pixbuf_get_n_channels(pixbuf);
117         guchar* data = gdk_pixbuf_get_pixels(pixbuf);
118         for (int j = 0; j < height; j++, data += stride)
119         {
120             guchar* p = data;
121             for (int i = 0; i < width; i++, p += n_channels)
122             {
123                 if (p[0] == 0)
124                 {
125                     if (fg)
126                     {
127                         p[0] = fg->Red();
128                         p[1] = fg->Green();
129                         p[2] = fg->Blue();
130                     }
131                 }
132                 else
133                 {
134                     if (bg)
135                     {
136                         p[0] = bg->Red();
137                         p[1] = bg->Green();
138                         p[2] = bg->Blue();
139                     }
140                 }
141             }
142         }
143     }
144     M_CURSORDATA->m_cursor = gdk_cursor_new_from_pixbuf(
145         gdk_window_get_display(wxGetTopLevelGDK()), pixbuf, hotSpotX, hotSpotY);
146 #else
147     if (!maskBits)
148         maskBits = bits;
149     if (!fg)
150         fg = wxBLACK;
151     if (!bg)
152         bg = wxWHITE;
153 
154     GdkBitmap* data = gdk_bitmap_create_from_data(
155         wxGetTopLevelGDK(), const_cast<char*>(bits), width, height);
156     GdkBitmap* mask = gdk_bitmap_create_from_data(
157         wxGetTopLevelGDK(), const_cast<char*>(maskBits), width, height);
158 
159     M_CURSORDATA->m_cursor = gdk_cursor_new_from_pixmap(
160                  data, mask, fg->GetColor(), bg->GetColor(),
161                  hotSpotX, hotSpotY );
162 
163     g_object_unref (data);
164     g_object_unref (mask);
165 #endif
166 }
167 
~wxCursor()168 wxCursor::~wxCursor()
169 {
170 }
171 
GetHotSpot() const172 wxPoint wxCursor::GetHotSpot() const
173 {
174 #if GTK_CHECK_VERSION(2,8,0)
175     if (GetCursor())
176     {
177         if (wx_is_at_least_gtk2(8))
178         {
179             GdkPixbuf *pixbuf = gdk_cursor_get_image(GetCursor());
180             if (pixbuf)
181             {
182                 wxPoint hotSpot = wxDefaultPosition;
183                 const gchar* opt_xhot = gdk_pixbuf_get_option(pixbuf, "x_hot");
184                 const gchar* opt_yhot = gdk_pixbuf_get_option(pixbuf, "y_hot");
185                 if (opt_xhot && opt_yhot)
186                 {
187                     const int xhot = atoi(opt_xhot);
188                     const int yhot = atoi(opt_yhot);
189                     hotSpot = wxPoint(xhot, yhot);
190                 }
191                 g_object_unref(pixbuf);
192                 return hotSpot;
193             }
194         }
195     }
196 #endif
197 
198     return wxDefaultPosition;
199 }
200 
InitFromStock(wxStockCursor cursorId)201 void wxCursor::InitFromStock( wxStockCursor cursorId )
202 {
203     m_refData = new wxCursorRefData();
204 
205     GdkCursorType gdk_cur = GDK_LEFT_PTR;
206     switch (cursorId)
207     {
208 #ifdef __WXGTK3__
209         case wxCURSOR_BLANK:            gdk_cur = GDK_BLANK_CURSOR; break;
210 #else
211         case wxCURSOR_BLANK:
212             {
213                 const char bits[] = { 0 };
214                 const GdkColor color = { 0, 0, 0, 0 };
215 
216                 GdkPixmap *pixmap = gdk_bitmap_create_from_data(NULL, bits, 1, 1);
217                 M_CURSORDATA->m_cursor = gdk_cursor_new_from_pixmap(pixmap,
218                                                                     pixmap,
219                                                                     &color,
220                                                                     &color,
221                                                                     0, 0);
222                 g_object_unref(pixmap);
223             }
224             return;
225 #endif
226         case wxCURSOR_ARROW:            // fall through to default
227         case wxCURSOR_DEFAULT:          gdk_cur = GDK_LEFT_PTR; break;
228         case wxCURSOR_RIGHT_ARROW:      gdk_cur = GDK_RIGHT_PTR; break;
229         case wxCURSOR_HAND:             gdk_cur = GDK_HAND2; break;
230         case wxCURSOR_CROSS:            gdk_cur = GDK_CROSSHAIR; break;
231         case wxCURSOR_SIZEWE:           gdk_cur = GDK_SB_H_DOUBLE_ARROW; break;
232         case wxCURSOR_SIZENS:           gdk_cur = GDK_SB_V_DOUBLE_ARROW; break;
233         case wxCURSOR_ARROWWAIT:
234         case wxCURSOR_WAIT:
235         case wxCURSOR_WATCH:            gdk_cur = GDK_WATCH; break;
236         case wxCURSOR_SIZING:           gdk_cur = GDK_SIZING; break;
237         case wxCURSOR_SPRAYCAN:         gdk_cur = GDK_SPRAYCAN; break;
238         case wxCURSOR_IBEAM:            gdk_cur = GDK_XTERM; break;
239         case wxCURSOR_PENCIL:           gdk_cur = GDK_PENCIL; break;
240         case wxCURSOR_NO_ENTRY:         gdk_cur = GDK_PIRATE; break;
241         case wxCURSOR_SIZENWSE:
242         case wxCURSOR_SIZENESW:         gdk_cur = GDK_FLEUR; break;
243         case wxCURSOR_QUESTION_ARROW:   gdk_cur = GDK_QUESTION_ARROW; break;
244         case wxCURSOR_PAINT_BRUSH:      gdk_cur = GDK_SPRAYCAN; break;
245         case wxCURSOR_MAGNIFIER:        gdk_cur = GDK_PLUS; break;
246         case wxCURSOR_CHAR:             gdk_cur = GDK_XTERM; break;
247         case wxCURSOR_LEFT_BUTTON:      gdk_cur = GDK_LEFTBUTTON; break;
248         case wxCURSOR_MIDDLE_BUTTON:    gdk_cur = GDK_MIDDLEBUTTON; break;
249         case wxCURSOR_RIGHT_BUTTON:     gdk_cur = GDK_RIGHTBUTTON; break;
250         case wxCURSOR_BULLSEYE:         gdk_cur = GDK_TARGET; break;
251 
252         case wxCURSOR_POINT_LEFT:       gdk_cur = GDK_SB_LEFT_ARROW; break;
253         case wxCURSOR_POINT_RIGHT:      gdk_cur = GDK_SB_RIGHT_ARROW; break;
254 /*
255         case wxCURSOR_DOUBLE_ARROW:     gdk_cur = GDK_DOUBLE_ARROW; break;
256         case wxCURSOR_CROSS_REVERSE:    gdk_cur = GDK_CROSS_REVERSE; break;
257         case wxCURSOR_BASED_ARROW_UP:   gdk_cur = GDK_BASED_ARROW_UP; break;
258         case wxCURSOR_BASED_ARROW_DOWN: gdk_cur = GDK_BASED_ARROW_DOWN; break;
259 */
260 
261         default:
262             wxFAIL_MSG(wxT("unsupported cursor type"));
263             // will use the standard one
264             break;
265     }
266 
267     GdkDisplay* display = gdk_window_get_display(wxGetTopLevelGDK());
268     M_CURSORDATA->m_cursor = gdk_cursor_new_for_display(display, gdk_cur);
269 }
270 
271 #if wxUSE_IMAGE
272 
InitFromImage(const wxImage & image)273 void wxCursor::InitFromImage( const wxImage & image )
274 {
275     const int w = image.GetWidth();
276     const int h = image.GetHeight();
277     const guchar* alpha = image.GetAlpha();
278     const bool hasMask = image.HasMask();
279     int hotSpotX = image.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X);
280     int hotSpotY = image.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y);
281     if (hotSpotX < 0 || hotSpotX > w) hotSpotX = 0;
282     if (hotSpotY < 0 || hotSpotY > h) hotSpotY = 0;
283     GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data(image.GetData(), GDK_COLORSPACE_RGB, false, 8, w, h, w * 3, NULL, NULL);
284     if (alpha || hasMask)
285     {
286         guchar r = 0, g = 0, b = 0;
287         if (hasMask)
288         {
289             r = image.GetMaskRed();
290             g = image.GetMaskGreen();
291             b = image.GetMaskBlue();
292         }
293         GdkPixbuf* pixbuf0 = pixbuf;
294         pixbuf = gdk_pixbuf_add_alpha(pixbuf, hasMask, r, g, b);
295         g_object_unref(pixbuf0);
296         if (alpha)
297         {
298             guchar* d = gdk_pixbuf_get_pixels(pixbuf);
299             const int stride = gdk_pixbuf_get_rowstride(pixbuf);
300             for (int j = 0; j < h; j++, d += stride)
301                 for (int i = 0; i < w; i++, alpha++)
302                     if (d[4 * i + 3])
303                         d[4 * i + 3] = *alpha;
304         }
305     }
306     m_refData = new wxCursorRefData;
307     M_CURSORDATA->m_cursor = gdk_cursor_new_from_pixbuf(
308         gdk_window_get_display(wxGetTopLevelGDK()), pixbuf, hotSpotX, hotSpotY);
309     g_object_unref(pixbuf);
310 }
311 
312 #endif // wxUSE_IMAGE
313 
GetCursor() const314 GdkCursor *wxCursor::GetCursor() const
315 {
316     GdkCursor* cursor = NULL;
317     if (m_refData)
318         cursor = M_CURSORDATA->m_cursor;
319     return cursor;
320 }
321 
CreateGDIRefData() const322 wxGDIRefData *wxCursor::CreateGDIRefData() const
323 {
324     return new wxCursorRefData;
325 }
326 
327 wxGDIRefData *
CloneGDIRefData(const wxGDIRefData * WXUNUSED (data)) const328 wxCursor::CloneGDIRefData(const wxGDIRefData * WXUNUSED(data)) const
329 {
330     // TODO: We can't clone GDK cursors at the moment. To do this we'd need
331     //       to remember the original data from which the cursor was created
332     //       (i.e. standard cursor type or the bitmap) or use
333     //       gdk_cursor_get_cursor_type() (which is in 2.22+ only) and
334     //       gdk_cursor_get_image().
335     wxFAIL_MSG( wxS("Cloning cursors is not implemented in wxGTK.") );
336 
337     return new wxCursorRefData;
338 }
339 
340 //-----------------------------------------------------------------------------
341 // busy cursor routines
342 //-----------------------------------------------------------------------------
343 
344 wxCursor g_globalCursor;
345 wxCursor g_busyCursor;
346 static wxCursor gs_storedCursor;
347 static int       gs_busyCount = 0;
348 
GetStoredCursor()349 const wxCursor& wxBusyCursor::GetStoredCursor()
350 {
351     return gs_storedCursor;
352 }
353 
GetBusyCursor()354 const wxCursor wxBusyCursor::GetBusyCursor()
355 {
356     return g_busyCursor;
357 }
358 
UpdateCursors(wxWindow * win,bool isBusyOrGlobalCursor)359 static void UpdateCursors(wxWindow* win, bool isBusyOrGlobalCursor)
360 {
361     win->GTKUpdateCursor(isBusyOrGlobalCursor);
362     const wxWindowList& children = win->GetChildren();
363     wxWindowList::const_iterator i = children.begin();
364     for (size_t n = children.size(); n--; ++i)
365         UpdateCursors(*i, isBusyOrGlobalCursor);
366 }
367 
SetGlobalCursor(const wxCursor & cursor)368 static void SetGlobalCursor(const wxCursor& cursor)
369 {
370     GdkCursor* gdk_cursor = cursor.GetCursor();
371     GdkDisplay* display = NULL;
372     wxWindowList::const_iterator i = wxTopLevelWindows.begin();
373     for (size_t n = wxTopLevelWindows.size(); n--; ++i)
374     {
375         wxWindow* win = *i;
376         GdkWindow* window;
377         if (win->m_widget && (window = gtk_widget_get_window(win->m_widget)))
378         {
379             gdk_window_set_cursor(window, gdk_cursor);
380             UpdateCursors(win, gdk_cursor != NULL);
381             if (display == NULL)
382                 display = gdk_window_get_display(window);
383         }
384     }
385     if (display)
386         gdk_display_flush(display);
387 }
388 
wxBeginBusyCursor(const wxCursor * cursor)389 void wxBeginBusyCursor(const wxCursor* cursor)
390 {
391     if (gs_busyCount++ == 0)
392     {
393         g_busyCursor = *cursor;
394         gs_storedCursor = g_globalCursor;
395         SetGlobalCursor(*cursor);
396     }
397 }
398 
wxEndBusyCursor()399 void wxEndBusyCursor()
400 {
401     if (gs_busyCount && --gs_busyCount == 0)
402     {
403         g_globalCursor = gs_storedCursor;
404         gs_storedCursor =
405         g_busyCursor = wxCursor();
406         SetGlobalCursor(g_globalCursor);
407     }
408 }
409 
wxIsBusy()410 bool wxIsBusy()
411 {
412     return gs_busyCount > 0;
413 }
414 
wxSetCursor(const wxCursor & cursor)415 void wxSetCursor( const wxCursor& cursor )
416 {
417     if (cursor.IsOk() || g_globalCursor.IsOk())
418     {
419         g_globalCursor = cursor;
420         SetGlobalCursor(cursor);
421     }
422 }
423