fix for non-PCH builds
[wxWidgets.git] / src / gtk / cursor.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/cursor.cpp
3 // Purpose:
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
25 //-----------------------------------------------------------------------------
26 // wxCursor
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
40 wxCursorRefData::wxCursorRefData()
41 {
42 m_cursor = (GdkCursor *) NULL;
43 }
44
45 wxCursorRefData::~wxCursorRefData()
46 {
47 if (m_cursor) gdk_cursor_unref( m_cursor );
48 }
49
50 //-----------------------------------------------------------------------------
51
52 #define M_CURSORDATA wx_static_cast(wxCursorRefData*, m_refData)
53
54 IMPLEMENT_DYNAMIC_CLASS(wxCursor, wxGDIObject)
55
56 wxCursor::wxCursor()
57 {
58 }
59
60 void wxCursor::InitFromStock( wxStockCursor cursorId )
61 {
62 m_refData = new wxCursorRefData();
63
64 GdkCursorType gdk_cur = GDK_LEFT_PTR;
65 switch (cursorId)
66 {
67 case wxCURSOR_BLANK:
68 {
69 const char bits[] = { 0 };
70 const GdkColor color = { 0, 0, 0, 0 };
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);
78 g_object_unref(pixmap);
79 }
80 return;
81
82 case wxCURSOR_ARROW: // fall through to default
83 case wxCURSOR_DEFAULT: gdk_cur = GDK_LEFT_PTR; break;
84 case wxCURSOR_RIGHT_ARROW: gdk_cur = GDK_RIGHT_PTR; break;
85 case wxCURSOR_HAND: gdk_cur = GDK_HAND2; break;
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;
89 case wxCURSOR_ARROWWAIT:
90 case wxCURSOR_WAIT:
91 case wxCURSOR_WATCH: gdk_cur = GDK_WATCH; break;
92 case wxCURSOR_SIZING: gdk_cur = GDK_SIZING; break;
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;
97 case wxCURSOR_SIZENWSE:
98 case wxCURSOR_SIZENESW: gdk_cur = GDK_FLEUR; break;
99 case wxCURSOR_QUESTION_ARROW: gdk_cur = GDK_QUESTION_ARROW; break;
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;
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;
106 case wxCURSOR_BULLSEYE: gdk_cur = GDK_TARGET; break;
107
108 case wxCURSOR_POINT_LEFT: gdk_cur = GDK_SB_LEFT_ARROW; break;
109 case wxCURSOR_POINT_RIGHT: gdk_cur = GDK_SB_RIGHT_ARROW; break;
110 /*
111 case wxCURSOR_DOUBLE_ARROW: gdk_cur = GDK_DOUBLE_ARROW; break;
112 case wxCURSOR_CROSS_REVERSE: gdk_cur = GDK_CROSS_REVERSE; break;
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;
115 */
116
117 default:
118 wxFAIL_MSG(wxT("unsupported cursor type"));
119 // will use the standard one
120 break;
121 }
122
123 M_CURSORDATA->m_cursor = gdk_cursor_new( gdk_cur );
124 }
125
126
127 // used in the following two ctors
128 extern GtkWidget *wxGetRootWindow();
129
130 wxCursor::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
168 wxCursor::wxCursor(const char bits[], int width, int height,
169 int hotSpotX, int hotSpotY,
170 const char maskBits[], const wxColour *fg, const wxColour *bg)
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
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);
185
186 m_refData = new wxCursorRefData;
187 M_CURSORDATA->m_cursor = gdk_cursor_new_from_pixmap(
188 data, mask, fg->GetColor(), bg->GetColor(),
189 hotSpotX, hotSpotY );
190
191 g_object_unref (data);
192 g_object_unref (mask);
193 }
194
195 #if wxUSE_IMAGE
196
197 static 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
215 wxCursor::wxCursor( const wxImage & image )
216 {
217 int w = image.GetWidth() ;
218 int h = image.GetHeight();
219 bool bHasMask = image.HasMask();
220 int hotSpotX, hotSpotY;
221 GetHotSpot(image, hotSpotX, hotSpotY);
222 m_refData = new wxCursorRefData;
223 wxImage image_copy(image);
224
225 GdkDisplay* display = gdk_drawable_get_display(wxGetRootWindow()->window);
226 if (gdk_display_supports_cursor_color(display))
227 {
228 if (!image.HasAlpha())
229 {
230 // add alpha, so wxBitmap will convert to pixbuf format
231 image_copy.InitAlpha();
232 }
233 wxBitmap bitmap(image_copy);
234 wxASSERT(bitmap.HasPixbuf());
235 M_CURSORDATA->m_cursor = gdk_cursor_new_from_pixbuf
236 (
237 display,
238 bitmap.GetPixbuf(),
239 hotSpotX, hotSpotY
240 );
241 return;
242 }
243
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
265 // modify image so wxBitmap can be used to convert to pixmap
266 image_copy.SetMask(false);
267 int i, j;
268 wxByte* data = image_copy.GetData();
269 for (j = 0; j < h; j++)
270 {
271 for (i = 0; i < w; i++, data += 3)
272 {
273 //if average value is > mid grey
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 }
281 }
282 }
283 wxBitmap bitmap(image_copy, 1);
284
285 // find the most frequent color(s)
286 wxImageHistogram histogram;
287 image.ComputeHistogram(histogram);
288
289 long colMostFreq = 0;
290 unsigned long nMost = 0;
291 long colNextMostFreq = 0;
292 unsigned long nNext = 0;
293 for ( wxImageHistogram::iterator entry = histogram.begin();
294 entry != histogram.end();
295 ++entry )
296 {
297 unsigned long key = entry->first;
298 if ( !bHasMask || (key != keyMaskColor) )
299 {
300 unsigned long value = entry->second.value;
301 if (value > nMost)
302 {
303 nNext = nMost;
304 colNextMostFreq = colMostFreq;
305 nMost = value;
306 colMostFreq = key;
307 }
308 else if (value > nNext)
309 {
310 nNext = value;
311 colNextMostFreq = key;
312 }
313 }
314 }
315
316 wxColour fg = wxColour ( (unsigned char)(colMostFreq >> 16),
317 (unsigned char)(colMostFreq >> 8),
318 (unsigned char)(colMostFreq) );
319
320 wxColour bg = wxColour ( (unsigned char)(colNextMostFreq >> 16),
321 (unsigned char)(colNextMostFreq >> 8),
322 (unsigned char)(colNextMostFreq) );
323
324 int fg_intensity = fg.Red() + fg.Green() + fg.Blue();
325 int bg_intensity = bg.Red() + bg.Green() + bg.Blue();
326
327 if (bg_intensity > fg_intensity)
328 {
329 //swap fg and bg
330 wxColour tmp = fg;
331 fg = bg;
332 bg = tmp;
333 }
334
335 M_CURSORDATA->m_cursor = gdk_cursor_new_from_pixmap
336 (
337 bitmap.GetPixmap(),
338 mask,
339 fg.GetColor(), bg.GetColor(),
340 hotSpotX, hotSpotY
341 );
342
343 g_object_unref (mask);
344 }
345
346 #endif // wxUSE_IMAGE
347
348 wxCursor::~wxCursor()
349 {
350 }
351
352 GdkCursor *wxCursor::GetCursor() const
353 {
354 return M_CURSORDATA->m_cursor;
355 }
356
357 wxGDIRefData *wxCursor::CreateGDIRefData() const
358 {
359 return new wxCursorRefData;
360 }
361
362 wxGDIRefData *wxCursor::CloneGDIRefData(const wxGDIRefData *data) const
363 {
364 return new wxCursorRefData(*wx_static_cast(const wxCursorRefData *, data));
365 }
366
367 //-----------------------------------------------------------------------------
368 // busy cursor routines
369 //-----------------------------------------------------------------------------
370
371 /* Current cursor, in order to hang on to
372 * cursor handle when setting the cursor globally */
373 wxCursor g_globalCursor;
374
375 static wxCursor gs_savedCursor;
376 static int gs_busyCount = 0;
377
378 const wxCursor &wxBusyCursor::GetStoredCursor()
379 {
380 return gs_savedCursor;
381 }
382
383 const wxCursor wxBusyCursor::GetBusyCursor()
384 {
385 return wxCursor(wxCURSOR_WATCH);
386 }
387
388 static 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
401 void wxEndBusyCursor()
402 {
403 if (--gs_busyCount > 0)
404 return;
405
406 g_globalCursor = gs_savedCursor;
407 gs_savedCursor = wxNullCursor;
408 GdkDisplay* unused = NULL;
409 InternalIdle(wxTopLevelWindows, unused);
410 }
411
412 void wxBeginBusyCursor(const wxCursor* cursor)
413 {
414 if (gs_busyCount++ > 0)
415 return;
416
417 wxASSERT_MSG( !gs_savedCursor.Ok(),
418 wxT("forgot to call wxEndBusyCursor, will leak memory") );
419
420 gs_savedCursor = g_globalCursor;
421 g_globalCursor = *cursor;
422 GdkDisplay* display = NULL;
423 InternalIdle(wxTopLevelWindows, display);
424 if (display)
425 gdk_display_flush(display);
426 }
427
428 bool wxIsBusy()
429 {
430 return gs_busyCount > 0;
431 }
432
433 void wxSetCursor( const wxCursor& cursor )
434 {
435 g_globalCursor = cursor;
436 wxTheApp->WakeUpIdle();
437 }