]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/cursor.cpp
follow up parent chain to properly support modal dialog parents, see #15383
[wxWidgets.git] / src / gtk / cursor.cpp
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
35 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
45 wxCursorRefData::wxCursorRefData()
46 {
47 m_cursor = NULL;
48 }
49
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
73 wxCursor::wxCursor()
74 {
75 }
76
77 #if wxUSE_IMAGE
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
95 wxCursor::wxCursor(const wxImage& img)
96 {
97 InitFromImage(img);
98 }
99 #endif
100
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)));
114 GdkPixbuf* pixbuf = bitmap.GetPixbuf();
115 if (fg || bg)
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])
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
169 wxCursor::~wxCursor()
170 {
171 }
172
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 M_CURSORDATA->m_cursor = gdk_cursor_new( gdk_cur );
240 }
241
242 #if wxUSE_IMAGE
243
244 void wxCursor::InitFromImage( const wxImage & image )
245 {
246 const int w = image.GetWidth();
247 const int h = image.GetHeight();
248 const guchar* alpha = image.GetAlpha();
249 const bool hasMask = image.HasMask();
250 int hotSpotX = image.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X);
251 int hotSpotY = image.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y);
252 if (hotSpotX < 0 || hotSpotX > w) hotSpotX = 0;
253 if (hotSpotY < 0 || hotSpotY > h) hotSpotY = 0;
254 GdkPixbuf* pixbuf = gdk_pixbuf_new_from_data(image.GetData(), GDK_COLORSPACE_RGB, false, 8, w, h, w * 3, NULL, NULL);
255 if (alpha || hasMask)
256 {
257 guchar r = 0, g = 0, b = 0;
258 if (hasMask)
259 {
260 r = image.GetMaskRed();
261 g = image.GetMaskGreen();
262 b = image.GetMaskBlue();
263 }
264 GdkPixbuf* pixbuf0 = pixbuf;
265 pixbuf = gdk_pixbuf_add_alpha(pixbuf, hasMask, r, g, b);
266 g_object_unref(pixbuf0);
267 if (alpha)
268 {
269 guchar* d = gdk_pixbuf_get_pixels(pixbuf);
270 const int stride = gdk_pixbuf_get_rowstride(pixbuf);
271 for (int j = 0; j < h; j++, d += stride)
272 for (int i = 0; i < w; i++, alpha++)
273 if (d[4 * i + 3])
274 d[4 * i + 3] = *alpha;
275 }
276 }
277 m_refData = new wxCursorRefData;
278 M_CURSORDATA->m_cursor = gdk_cursor_new_from_pixbuf(gtk_widget_get_display(wxGetRootWindow()), pixbuf, hotSpotX, hotSpotY);
279 g_object_unref(pixbuf);
280 }
281
282 #endif // wxUSE_IMAGE
283
284 GdkCursor *wxCursor::GetCursor() const
285 {
286 return M_CURSORDATA->m_cursor;
287 }
288
289 wxGDIRefData *wxCursor::CreateGDIRefData() const
290 {
291 return new wxCursorRefData;
292 }
293
294 wxGDIRefData *
295 wxCursor::CloneGDIRefData(const wxGDIRefData * WXUNUSED(data)) const
296 {
297 // TODO: We can't clone GDK cursors at the moment. To do this we'd need
298 // to remember the original data from which the cursor was created
299 // (i.e. standard cursor type or the bitmap) or use
300 // gdk_cursor_get_cursor_type() (which is in 2.22+ only) and
301 // gdk_cursor_get_image().
302 wxFAIL_MSG( wxS("Cloning cursors is not implemented in wxGTK.") );
303
304 return new wxCursorRefData;
305 }
306
307 //-----------------------------------------------------------------------------
308 // busy cursor routines
309 //-----------------------------------------------------------------------------
310
311 /* Current cursor, in order to hang on to
312 * cursor handle when setting the cursor globally */
313 wxCursor g_globalCursor;
314
315 static wxCursor gs_savedCursor;
316 static int gs_busyCount = 0;
317
318 const wxCursor &wxBusyCursor::GetStoredCursor()
319 {
320 return gs_savedCursor;
321 }
322
323 const wxCursor wxBusyCursor::GetBusyCursor()
324 {
325 return wxCursor(wxCURSOR_WATCH);
326 }
327
328 static void UpdateCursors(GdkDisplay** display)
329 {
330 wxWindowList::const_iterator i = wxTopLevelWindows.begin();
331 for (size_t n = wxTopLevelWindows.size(); n--; ++i)
332 {
333 wxWindow* win = *i;
334 win->GTKUpdateCursor();
335 if (display && *display == NULL && win->m_widget)
336 *display = gtk_widget_get_display(win->m_widget);
337 }
338 }
339
340 void wxEndBusyCursor()
341 {
342 if (--gs_busyCount > 0)
343 return;
344
345 g_globalCursor = gs_savedCursor;
346 gs_savedCursor = wxNullCursor;
347 UpdateCursors(NULL);
348 }
349
350 void wxBeginBusyCursor(const wxCursor* cursor)
351 {
352 if (gs_busyCount++ > 0)
353 return;
354
355 wxASSERT_MSG( !gs_savedCursor.IsOk(),
356 wxT("forgot to call wxEndBusyCursor, will leak memory") );
357
358 gs_savedCursor = g_globalCursor;
359 g_globalCursor = *cursor;
360 GdkDisplay* display = NULL;
361 UpdateCursors(&display);
362 if (display)
363 gdk_display_flush(display);
364 }
365
366 bool wxIsBusy()
367 {
368 return gs_busyCount > 0;
369 }
370
371 void wxSetCursor( const wxCursor& cursor )
372 {
373 g_globalCursor = cursor;
374 UpdateCursors(NULL);
375 }