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