]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/cursor.cpp
new file added
[wxWidgets.git] / src / gtk / cursor.cpp
CommitLineData
c801d85f 1/////////////////////////////////////////////////////////////////////////////
670f9935 2// Name: src/gtk/cursor.cpp
d6764050 3// Purpose: wxCursor implementation
c801d85f 4// Author: Robert Roebling
dbf858b5 5// Id: $Id$
01111366 6// Copyright: (c) 1998 Robert Roebling
65571936 7// Licence: wxWindows licence
c801d85f
KB
8/////////////////////////////////////////////////////////////////////////////
9
14f355c2
VS
10// For compilers that support precompilation, includes "wx.h".
11#include "wx/wxprec.h"
12
c801d85f 13#include "wx/cursor.h"
670f9935
WS
14
15#ifndef WX_PRECOMP
df8d50a0 16 #include "wx/window.h"
670f9935 17 #include "wx/app.h"
0416c418 18 #include "wx/image.h"
88051436 19 #include "wx/bitmap.h"
cfaf8bd1 20 #include "wx/log.h"
670f9935
WS
21#endif // WX_PRECOMP
22
a1abca32 23#include <gtk/gtk.h>
833fb475 24#include "wx/gtk/private/object.h"
385e8575 25#include "wx/gtk/private/gtk2-compat.h"
84833214 26
c801d85f 27//-----------------------------------------------------------------------------
d6764050 28// wxCursorRefData
c801d85f
KB
29//-----------------------------------------------------------------------------
30
8f884a0d 31class wxCursorRefData: public wxGDIRefData
c801d85f 32{
8f884a0d 33public:
8bbe427f 34 wxCursorRefData();
d3c7fc99 35 virtual ~wxCursorRefData();
8bbe427f 36
8f884a0d
VZ
37 virtual bool IsOk() const { return m_cursor != NULL; }
38
c801d85f 39 GdkCursor *m_cursor;
51bf928d
VZ
40
41private:
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);
c801d85f
KB
45};
46
8bbe427f 47wxCursorRefData::wxCursorRefData()
c801d85f 48{
d3b9f782 49 m_cursor = NULL;
ff7b1510 50}
c801d85f 51
8bbe427f 52wxCursorRefData::~wxCursorRefData()
c801d85f 53{
bc8b8220 54 if (m_cursor) gdk_cursor_unref( m_cursor );
ff7b1510 55}
c801d85f 56
d6764050
FM
57
58//-----------------------------------------------------------------------------
59// wxCursor
c801d85f
KB
60//-----------------------------------------------------------------------------
61
5c33522f 62#define M_CURSORDATA static_cast<wxCursorRefData*>(m_refData)
c801d85f 63
0c0f973f 64IMPLEMENT_DYNAMIC_CLASS(wxCursor, wxGDIObject)
c801d85f 65
d6764050
FM
66// used in the following two ctors
67extern GtkWidget *wxGetRootWindow();
68
69
8bbe427f 70wxCursor::wxCursor()
c801d85f 71{
ff7b1510 72}
c801d85f 73
d6764050
FM
74#if wxUSE_IMAGE
75wxCursor::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;
03647350 82
d6764050 83 // eventually set the hotspot:
a4afdcf3 84 if (!img.HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_X))
d6764050 85 img.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X, hotSpotX);
a4afdcf3 86 if (!img.HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y))
d6764050 87 img.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y, hotSpotY);
03647350 88
d6764050
FM
89 InitFromImage(img);
90}
91
92wxCursor::wxCursor(const wxImage& img)
93{
94 InitFromImage(img);
95}
96#endif
97
98wxCursor::wxCursor(const char bits[], int width, int height,
99 int hotSpotX, int hotSpotY,
100 const char maskBits[], const wxColour *fg, const wxColour *bg)
101{
9dc44eff
PC
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
d6764050
FM
145 if (!maskBits)
146 maskBits = bits;
147 if (!fg)
148 fg = wxBLACK;
149 if (!bg)
150 bg = wxWHITE;
d6764050 151
385e8575
PC
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);
d6764050 156
d6764050
FM
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);
9dc44eff 163#endif
d6764050
FM
164}
165
166wxCursor::~wxCursor()
167{
168}
169
0ef5b1da 170void wxCursor::InitFromStock( wxStockCursor cursorId )
c801d85f 171{
2d17d68f
RR
172 m_refData = new wxCursorRefData();
173
174 GdkCursorType gdk_cur = GDK_LEFT_PTR;
175 switch (cursorId)
176 {
9dc44eff
PC
177#ifdef __WXGTK3__
178 case wxCURSOR_BLANK: gdk_cur = GDK_BLANK_CURSOR; break;
179#else
76471ff7
VZ
180 case wxCURSOR_BLANK:
181 {
0c0f973f
PC
182 const char bits[] = { 0 };
183 const GdkColor color = { 0, 0, 0, 0 };
76471ff7
VZ
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);
0c0f973f 191 g_object_unref(pixmap);
76471ff7
VZ
192 }
193 return;
9dc44eff 194#endif
15dadf31 195 case wxCURSOR_ARROW: // fall through to default
f7bdcdd7 196 case wxCURSOR_DEFAULT: gdk_cur = GDK_LEFT_PTR; break;
15dadf31 197 case wxCURSOR_RIGHT_ARROW: gdk_cur = GDK_RIGHT_PTR; break;
6e63b5c9 198 case wxCURSOR_HAND: gdk_cur = GDK_HAND2; break;
2d17d68f
RR
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;
83f96286 202 case wxCURSOR_ARROWWAIT:
7c39369e 203 case wxCURSOR_WAIT:
2d17d68f 204 case wxCURSOR_WATCH: gdk_cur = GDK_WATCH; break;
57b10cb4 205 case wxCURSOR_SIZING: gdk_cur = GDK_SIZING; break;
2d17d68f
RR
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;
7c39369e 210 case wxCURSOR_SIZENWSE:
2d17d68f
RR
211 case wxCURSOR_SIZENESW: gdk_cur = GDK_FLEUR; break;
212 case wxCURSOR_QUESTION_ARROW: gdk_cur = GDK_QUESTION_ARROW; break;
7c39369e
VZ
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;
2d17d68f
RR
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;
13971833
RD
219 case wxCURSOR_BULLSEYE: gdk_cur = GDK_TARGET; break;
220
957d856c
VZ
221 case wxCURSOR_POINT_LEFT: gdk_cur = GDK_SB_LEFT_ARROW; break;
222 case wxCURSOR_POINT_RIGHT: gdk_cur = GDK_SB_RIGHT_ARROW; break;
c801d85f 223/*
7c39369e
VZ
224 case wxCURSOR_DOUBLE_ARROW: gdk_cur = GDK_DOUBLE_ARROW; break;
225 case wxCURSOR_CROSS_REVERSE: gdk_cur = GDK_CROSS_REVERSE; break;
2d17d68f
RR
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;
c801d85f 228*/
76471ff7 229
7c39369e 230 default:
223d09f6 231 wxFAIL_MSG(wxT("unsupported cursor type"));
7c39369e 232 // will use the standard one
7c39369e 233 break;
2d17d68f 234 }
8bbe427f 235
2d17d68f 236 M_CURSORDATA->m_cursor = gdk_cursor_new( gdk_cur );
ff7b1510 237}
c801d85f 238
7eff657c 239#if wxUSE_IMAGE
0fc5dbf5 240
d6764050 241void wxCursor::InitFromImage( const wxImage & image )
7eff657c 242{
9dc44eff
PC
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)
9f556d1d 253 {
9dc44eff
PC
254 guchar r = 0, g = 0, b = 0;
255 if (hasMask)
833fb475 256 {
9dc44eff
PC
257 r = image.GetMaskRed();
258 g = image.GetMaskGreen();
259 b = image.GetMaskBlue();
833fb475 260 }
9dc44eff
PC
261 GdkPixbuf* pixbuf0 = pixbuf;
262 pixbuf = gdk_pixbuf_add_alpha(pixbuf, hasMask, r, g, b);
263 g_object_unref(pixbuf0);
264 if (alpha)
7eff657c 265 {
9dc44eff
PC
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;
833fb475 272 }
833fb475 273 }
9dc44eff
PC
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);
7eff657c 277}
0fc5dbf5
VZ
278
279#endif // wxUSE_IMAGE
7eff657c 280
8f884a0d 281GdkCursor *wxCursor::GetCursor() const
c801d85f 282{
8f884a0d 283 return M_CURSORDATA->m_cursor;
ff7b1510 284}
c801d85f 285
8f884a0d 286wxGDIRefData *wxCursor::CreateGDIRefData() const
c801d85f 287{
8f884a0d
VZ
288 return new wxCursorRefData;
289}
290
51bf928d
VZ
291wxGDIRefData *
292wxCursor::CloneGDIRefData(const wxGDIRefData * WXUNUSED(data)) const
8f884a0d 293{
51bf928d
VZ
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;
ff7b1510 302}
c801d85f
KB
303
304//-----------------------------------------------------------------------------
305// busy cursor routines
306//-----------------------------------------------------------------------------
307
b541538f
PC
308/* Current cursor, in order to hang on to
309 * cursor handle when setting the cursor globally */
310wxCursor g_globalCursor;
7c39369e 311
238d735d 312static wxCursor gs_savedCursor;
89a43902 313static int gs_busyCount = 0;
c801d85f 314
f6bcfd97
BP
315const wxCursor &wxBusyCursor::GetStoredCursor()
316{
317 return gs_savedCursor;
318}
319
320const wxCursor wxBusyCursor::GetBusyCursor()
321{
322 return wxCursor(wxCURSOR_WATCH);
323}
324
2e952cd1 325static void UpdateCursors(GdkDisplay** display)
df8d50a0 326{
2e952cd1
PC
327 wxWindowList::const_iterator i = wxTopLevelWindows.begin();
328 for (size_t n = wxTopLevelWindows.size(); n--; ++i)
df8d50a0
PC
329 {
330 wxWindow* win = *i;
2e952cd1
PC
331 win->GTKUpdateCursor();
332 if (display && *display == NULL && win->m_widget)
333 *display = gtk_widget_get_display(win->m_widget);
df8d50a0
PC
334 }
335}
336
8bbe427f 337void wxEndBusyCursor()
c801d85f 338{
238d735d 339 if (--gs_busyCount > 0)
89a43902
VZ
340 return;
341
df8d50a0 342 g_globalCursor = gs_savedCursor;
238d735d 343 gs_savedCursor = wxNullCursor;
2e952cd1 344 UpdateCursors(NULL);
ff7b1510 345}
c801d85f 346
df8d50a0 347void wxBeginBusyCursor(const wxCursor* cursor)
c801d85f 348{
238d735d 349 if (gs_busyCount++ > 0)
89a43902
VZ
350 return;
351
a1b806b9 352 wxASSERT_MSG( !gs_savedCursor.IsOk(),
223d09f6 353 wxT("forgot to call wxEndBusyCursor, will leak memory") );
7c39369e 354
238d735d 355 gs_savedCursor = g_globalCursor;
df8d50a0
PC
356 g_globalCursor = *cursor;
357 GdkDisplay* display = NULL;
2e952cd1 358 UpdateCursors(&display);
df8d50a0
PC
359 if (display)
360 gdk_display_flush(display);
ff7b1510 361}
c801d85f 362
8bbe427f 363bool wxIsBusy()
c801d85f 364{
89a43902 365 return gs_busyCount > 0;
ff7b1510 366}
c801d85f
KB
367
368void wxSetCursor( const wxCursor& cursor )
369{
238d735d 370 g_globalCursor = cursor;
2e952cd1 371 UpdateCursors(NULL);
ff7b1510 372}