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