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