]> git.saurik.com Git - wxWidgets.git/blame - src/gtk/cursor.cpp
Updated wxWidgets icons in art to use the new official logo.
[wxWidgets.git] / src / gtk / cursor.cpp
CommitLineData
c801d85f 1/////////////////////////////////////////////////////////////////////////////
670f9935 2// Name: src/gtk/cursor.cpp
c801d85f
KB
3// Purpose:
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>
84833214 24
c801d85f
KB
25//-----------------------------------------------------------------------------
26// wxCursor
27//-----------------------------------------------------------------------------
28
8f884a0d 29class wxCursorRefData: public wxGDIRefData
c801d85f 30{
8f884a0d 31public:
8bbe427f 32 wxCursorRefData();
d3c7fc99 33 virtual ~wxCursorRefData();
8bbe427f 34
8f884a0d
VZ
35 virtual bool IsOk() const { return m_cursor != NULL; }
36
c801d85f
KB
37 GdkCursor *m_cursor;
38};
39
8bbe427f 40wxCursorRefData::wxCursorRefData()
c801d85f 41{
d3b9f782 42 m_cursor = NULL;
ff7b1510 43}
c801d85f 44
8bbe427f 45wxCursorRefData::~wxCursorRefData()
c801d85f 46{
bc8b8220 47 if (m_cursor) gdk_cursor_unref( m_cursor );
ff7b1510 48}
c801d85f
KB
49
50//-----------------------------------------------------------------------------
51
5c33522f 52#define M_CURSORDATA static_cast<wxCursorRefData*>(m_refData)
c801d85f 53
0c0f973f 54IMPLEMENT_DYNAMIC_CLASS(wxCursor, wxGDIObject)
c801d85f 55
8bbe427f 56wxCursor::wxCursor()
c801d85f 57{
ff7b1510 58}
c801d85f 59
0ef5b1da 60void wxCursor::InitFromStock( wxStockCursor cursorId )
c801d85f 61{
2d17d68f
RR
62 m_refData = new wxCursorRefData();
63
64 GdkCursorType gdk_cur = GDK_LEFT_PTR;
65 switch (cursorId)
66 {
76471ff7
VZ
67 case wxCURSOR_BLANK:
68 {
0c0f973f
PC
69 const char bits[] = { 0 };
70 const GdkColor color = { 0, 0, 0, 0 };
76471ff7
VZ
71
72 GdkPixmap *pixmap = gdk_bitmap_create_from_data(NULL, bits, 1, 1);
73 M_CURSORDATA->m_cursor = gdk_cursor_new_from_pixmap(pixmap,
74 pixmap,
75 &color,
76 &color,
77 0, 0);
0c0f973f 78 g_object_unref(pixmap);
76471ff7
VZ
79 }
80 return;
81
15dadf31 82 case wxCURSOR_ARROW: // fall through to default
f7bdcdd7 83 case wxCURSOR_DEFAULT: gdk_cur = GDK_LEFT_PTR; break;
15dadf31 84 case wxCURSOR_RIGHT_ARROW: gdk_cur = GDK_RIGHT_PTR; break;
6e63b5c9 85 case wxCURSOR_HAND: gdk_cur = GDK_HAND2; break;
2d17d68f
RR
86 case wxCURSOR_CROSS: gdk_cur = GDK_CROSSHAIR; break;
87 case wxCURSOR_SIZEWE: gdk_cur = GDK_SB_H_DOUBLE_ARROW; break;
88 case wxCURSOR_SIZENS: gdk_cur = GDK_SB_V_DOUBLE_ARROW; break;
83f96286 89 case wxCURSOR_ARROWWAIT:
7c39369e 90 case wxCURSOR_WAIT:
2d17d68f 91 case wxCURSOR_WATCH: gdk_cur = GDK_WATCH; break;
57b10cb4 92 case wxCURSOR_SIZING: gdk_cur = GDK_SIZING; break;
2d17d68f
RR
93 case wxCURSOR_SPRAYCAN: gdk_cur = GDK_SPRAYCAN; break;
94 case wxCURSOR_IBEAM: gdk_cur = GDK_XTERM; break;
95 case wxCURSOR_PENCIL: gdk_cur = GDK_PENCIL; break;
96 case wxCURSOR_NO_ENTRY: gdk_cur = GDK_PIRATE; break;
7c39369e 97 case wxCURSOR_SIZENWSE:
2d17d68f
RR
98 case wxCURSOR_SIZENESW: gdk_cur = GDK_FLEUR; break;
99 case wxCURSOR_QUESTION_ARROW: gdk_cur = GDK_QUESTION_ARROW; break;
7c39369e
VZ
100 case wxCURSOR_PAINT_BRUSH: gdk_cur = GDK_SPRAYCAN; break;
101 case wxCURSOR_MAGNIFIER: gdk_cur = GDK_PLUS; break;
102 case wxCURSOR_CHAR: gdk_cur = GDK_XTERM; break;
2d17d68f
RR
103 case wxCURSOR_LEFT_BUTTON: gdk_cur = GDK_LEFTBUTTON; break;
104 case wxCURSOR_MIDDLE_BUTTON: gdk_cur = GDK_MIDDLEBUTTON; break;
105 case wxCURSOR_RIGHT_BUTTON: gdk_cur = GDK_RIGHTBUTTON; break;
13971833
RD
106 case wxCURSOR_BULLSEYE: gdk_cur = GDK_TARGET; break;
107
957d856c
VZ
108 case wxCURSOR_POINT_LEFT: gdk_cur = GDK_SB_LEFT_ARROW; break;
109 case wxCURSOR_POINT_RIGHT: gdk_cur = GDK_SB_RIGHT_ARROW; break;
c801d85f 110/*
7c39369e
VZ
111 case wxCURSOR_DOUBLE_ARROW: gdk_cur = GDK_DOUBLE_ARROW; break;
112 case wxCURSOR_CROSS_REVERSE: gdk_cur = GDK_CROSS_REVERSE; break;
2d17d68f
RR
113 case wxCURSOR_BASED_ARROW_UP: gdk_cur = GDK_BASED_ARROW_UP; break;
114 case wxCURSOR_BASED_ARROW_DOWN: gdk_cur = GDK_BASED_ARROW_DOWN; break;
c801d85f 115*/
76471ff7 116
7c39369e 117 default:
223d09f6 118 wxFAIL_MSG(wxT("unsupported cursor type"));
7c39369e 119 // will use the standard one
7c39369e 120 break;
2d17d68f 121 }
8bbe427f 122
2d17d68f 123 M_CURSORDATA->m_cursor = gdk_cursor_new( gdk_cur );
ff7b1510 124}
c801d85f 125
0ef5b1da
FM
126
127// used in the following two ctors
c2fa61e8 128extern GtkWidget *wxGetRootWindow();
a1db952f 129
0ef5b1da
FM
130wxCursor::wxCursor(const wxString& cursor_file,
131 wxBitmapType type,
132 int hotSpotX, int hotSpotY)
133{
134 /* TODO: test this code! */
135
136 // Must be an XBM file
137 if (type != wxBITMAP_TYPE_XPM) {
138 wxLogError("Invalid cursor bitmap type '%d'", type);
139 return;
140 }
141
142 // load the XPM
143 GdkBitmap *mask = NULL;
144 GdkBitmap *data = gdk_pixmap_create_from_xpm( wxGetRootWindow()->window,
145 &mask, NULL, cursor_file.mb_str() );
146 if (!data)
147 return;
148
149 // check given hotspot
150 gint w, h;
151 gdk_drawable_get_size( data, &w, &h );
152 if (hotSpotX < 0 || hotSpotX >= w)
153 hotSpotX = 0;
154 if (hotSpotY < 0 || hotSpotY >= h)
155 hotSpotY = 0;
156
157 // create the real cursor
158 m_refData = new wxCursorRefData;
159 M_CURSORDATA->m_cursor =
160 gdk_cursor_new_from_pixmap( data, mask,
161 wxBLACK->GetColor(), wxWHITE->GetColor(),
162 hotSpotX, hotSpotY );
163
164 g_object_unref (data);
165 g_object_unref (mask);
166}
167
168wxCursor::wxCursor(const char bits[], int width, int height,
a1db952f 169 int hotSpotX, int hotSpotY,
f516d986 170 const char maskBits[], const wxColour *fg, const wxColour *bg)
a1db952f
VS
171{
172 if (!maskBits)
173 maskBits = bits;
174 if (!fg)
175 fg = wxBLACK;
176 if (!bg)
177 bg = wxWHITE;
178 if (hotSpotX < 0 || hotSpotX >= width)
179 hotSpotX = 0;
180 if (hotSpotY < 0 || hotSpotY >= height)
181 hotSpotY = 0;
182
c2fa61e8
RD
183 GdkBitmap *data = gdk_bitmap_create_from_data( wxGetRootWindow()->window, (gchar *) bits, width, height );
184 GdkBitmap *mask = gdk_bitmap_create_from_data( wxGetRootWindow()->window, (gchar *) maskBits, width, height);
a1db952f
VS
185
186 m_refData = new wxCursorRefData;
187 M_CURSORDATA->m_cursor = gdk_cursor_new_from_pixmap(
4f558e23 188 data, mask, fg->GetColor(), bg->GetColor(),
a1db952f
VS
189 hotSpotX, hotSpotY );
190
3fe39b0c
MR
191 g_object_unref (data);
192 g_object_unref (mask);
a1db952f
VS
193}
194
7eff657c 195#if wxUSE_IMAGE
0fc5dbf5 196
9f556d1d
VZ
197static void GetHotSpot(const wxImage& image, int& x, int& y)
198{
199 if (image.HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_X))
200 x = image.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X);
201 else
202 x = 0;
203
204 if (image.HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y))
205 y = image.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y);
206 else
207 y = 0;
208
209 if (x < 0 || x >= image.GetWidth())
210 x = 0;
211 if (y < 0 || y >= image.GetHeight())
212 y = 0;
213}
214
7eff657c
JS
215wxCursor::wxCursor( const wxImage & image )
216{
0fc5dbf5
VZ
217 int w = image.GetWidth() ;
218 int h = image.GetHeight();
219 bool bHasMask = image.HasMask();
88051436
PC
220 int hotSpotX, hotSpotY;
221 GetHotSpot(image, hotSpotX, hotSpotY);
222 m_refData = new wxCursorRefData;
223 wxImage image_copy(image);
7eff657c 224
88051436
PC
225 GdkDisplay* display = gdk_drawable_get_display(wxGetRootWindow()->window);
226 if (gdk_display_supports_cursor_color(display))
9f556d1d 227 {
88051436 228 if (!image.HasAlpha())
ff654490 229 {
88051436
PC
230 // add alpha, so wxBitmap will convert to pixbuf format
231 image_copy.InitAlpha();
cb738b8b 232 }
88051436
PC
233 wxBitmap bitmap(image_copy);
234 wxASSERT(bitmap.HasPixbuf());
ff654490
VZ
235 M_CURSORDATA->m_cursor = gdk_cursor_new_from_pixbuf
236 (
88051436
PC
237 display,
238 bitmap.GetPixbuf(),
ff654490
VZ
239 hotSpotX, hotSpotY
240 );
ff654490 241 return;
9f556d1d 242 }
9f556d1d 243
e60f8377
PC
244 unsigned long keyMaskColor = 0;
245 GdkPixmap* mask;
246 if (bHasMask)
247 {
248 keyMaskColor = wxImageHistogram::MakeKey(
249 image.GetMaskRed(), image.GetMaskGreen(), image.GetMaskBlue());
250 // get mask before image is modified
251 wxBitmap bitmap(image, 1);
252 mask = bitmap.GetMask()->GetBitmap();
253 g_object_ref(mask);
254 }
255 else
256 {
257 const int size = ((w + 7) / 8) * h;
258 char* bits = new char[size];
259 memset(bits, 0xff, size);
260 mask = gdk_bitmap_create_from_data(
261 wxGetRootWindow()->window, bits, w, h);
262 delete[] bits;
263 }
264
88051436 265 // modify image so wxBitmap can be used to convert to pixmap
e60f8377 266 image_copy.SetMask(false);
88051436
PC
267 int i, j;
268 wxByte* data = image_copy.GetData();
269 for (j = 0; j < h; j++)
0fc5dbf5 270 {
88051436 271 for (i = 0; i < w; i++, data += 3)
0fc5dbf5 272 {
0fc5dbf5 273 //if average value is > mid grey
88051436
PC
274 if (int(data[0]) + data[1] + data[2] >= 3 * 128)
275 {
276 // wxBitmap only converts (255,255,255) to white
277 data[0] = 255;
278 data[1] = 255;
279 data[2] = 255;
280 }
7eff657c 281 }
0fc5dbf5 282 }
88051436
PC
283 wxBitmap bitmap(image_copy, 1);
284
fa1fcc66 285 // find the most frequent color(s)
952ae1e8 286 wxImageHistogram histogram;
fa1fcc66 287 image.ComputeHistogram(histogram);
7eff657c 288
fa1fcc66 289 long colMostFreq = 0;
952ae1e8 290 unsigned long nMost = 0;
fa1fcc66 291 long colNextMostFreq = 0;
952ae1e8 292 unsigned long nNext = 0;
fa1fcc66
VZ
293 for ( wxImageHistogram::iterator entry = histogram.begin();
294 entry != histogram.end();
295 ++entry )
7eff657c 296 {
88051436 297 unsigned long key = entry->first;
952ae1e8 298 if ( !bHasMask || (key != keyMaskColor) )
7eff657c 299 {
88051436 300 unsigned long value = entry->second.value;
952ae1e8 301 if (value > nMost)
7eff657c 302 {
88051436
PC
303 nNext = nMost;
304 colNextMostFreq = colMostFreq;
952ae1e8 305 nMost = value;
fa1fcc66 306 colMostFreq = key;
7eff657c 307 }
952ae1e8 308 else if (value > nNext)
7eff657c 309 {
952ae1e8 310 nNext = value;
fa1fcc66 311 colNextMostFreq = key;
7eff657c
JS
312 }
313 }
314 }
315
fa1fcc66
VZ
316 wxColour fg = wxColour ( (unsigned char)(colMostFreq >> 16),
317 (unsigned char)(colMostFreq >> 8),
318 (unsigned char)(colMostFreq) );
7eff657c 319
fa1fcc66
VZ
320 wxColour bg = wxColour ( (unsigned char)(colNextMostFreq >> 16),
321 (unsigned char)(colNextMostFreq >> 8),
322 (unsigned char)(colNextMostFreq) );
7eff657c 323
624d506b
CE
324 int fg_intensity = fg.Red() + fg.Green() + fg.Blue();
325 int bg_intensity = bg.Red() + bg.Green() + bg.Blue();
86e69fa5
MR
326
327 if (bg_intensity > fg_intensity)
328 {
624d506b
CE
329 //swap fg and bg
330 wxColour tmp = fg;
331 fg = bg;
332 bg = tmp;
86e69fa5 333 }
624d506b 334
fa1fcc66
VZ
335 M_CURSORDATA->m_cursor = gdk_cursor_new_from_pixmap
336 (
88051436 337 bitmap.GetPixmap(),
fa1fcc66
VZ
338 mask,
339 fg.GetColor(), bg.GetColor(),
340 hotSpotX, hotSpotY
341 );
7eff657c 342
3fe39b0c 343 g_object_unref (mask);
7eff657c 344}
0fc5dbf5
VZ
345
346#endif // wxUSE_IMAGE
7eff657c 347
8bbe427f 348wxCursor::~wxCursor()
c801d85f 349{
ff7b1510 350}
c801d85f 351
8f884a0d 352GdkCursor *wxCursor::GetCursor() const
c801d85f 353{
8f884a0d 354 return M_CURSORDATA->m_cursor;
ff7b1510 355}
c801d85f 356
8f884a0d 357wxGDIRefData *wxCursor::CreateGDIRefData() const
c801d85f 358{
8f884a0d
VZ
359 return new wxCursorRefData;
360}
361
362wxGDIRefData *wxCursor::CloneGDIRefData(const wxGDIRefData *data) const
363{
5c33522f 364 return new wxCursorRefData(*static_cast<const wxCursorRefData *>(data));
ff7b1510 365}
c801d85f
KB
366
367//-----------------------------------------------------------------------------
368// busy cursor routines
369//-----------------------------------------------------------------------------
370
b541538f
PC
371/* Current cursor, in order to hang on to
372 * cursor handle when setting the cursor globally */
373wxCursor g_globalCursor;
7c39369e 374
238d735d 375static wxCursor gs_savedCursor;
89a43902 376static int gs_busyCount = 0;
c801d85f 377
f6bcfd97
BP
378const wxCursor &wxBusyCursor::GetStoredCursor()
379{
380 return gs_savedCursor;
381}
382
383const wxCursor wxBusyCursor::GetBusyCursor()
384{
385 return wxCursor(wxCURSOR_WATCH);
386}
387
df8d50a0
PC
388static void InternalIdle(const wxWindowList& list, GdkDisplay*& display)
389{
390 wxWindowList::const_iterator i = list.begin();
391 for (size_t n = list.size(); n--; ++i)
392 {
393 wxWindow* win = *i;
394 if (display == NULL && win->m_widget && win->m_widget->window)
395 display = gdk_drawable_get_display(win->m_widget->window);
396 win->OnInternalIdle();
397 InternalIdle(win->GetChildren(), display);
398 }
399}
400
8bbe427f 401void wxEndBusyCursor()
c801d85f 402{
238d735d 403 if (--gs_busyCount > 0)
89a43902
VZ
404 return;
405
df8d50a0 406 g_globalCursor = gs_savedCursor;
238d735d 407 gs_savedCursor = wxNullCursor;
df8d50a0
PC
408 GdkDisplay* unused = NULL;
409 InternalIdle(wxTopLevelWindows, unused);
ff7b1510 410}
c801d85f 411
df8d50a0 412void wxBeginBusyCursor(const wxCursor* cursor)
c801d85f 413{
238d735d 414 if (gs_busyCount++ > 0)
89a43902
VZ
415 return;
416
238d735d 417 wxASSERT_MSG( !gs_savedCursor.Ok(),
223d09f6 418 wxT("forgot to call wxEndBusyCursor, will leak memory") );
7c39369e 419
238d735d 420 gs_savedCursor = g_globalCursor;
df8d50a0
PC
421 g_globalCursor = *cursor;
422 GdkDisplay* display = NULL;
423 InternalIdle(wxTopLevelWindows, display);
424 if (display)
425 gdk_display_flush(display);
ff7b1510 426}
c801d85f 427
8bbe427f 428bool wxIsBusy()
c801d85f 429{
89a43902 430 return gs_busyCount > 0;
ff7b1510 431}
c801d85f
KB
432
433void wxSetCursor( const wxCursor& cursor )
434{
238d735d 435 g_globalCursor = cursor;
a1abca32 436 wxTheApp->WakeUpIdle();
ff7b1510 437}