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