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