Precompiled headers support.
[wxWidgets.git] / src / motif / cursor.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: cursor.cpp
3 // Purpose: wxCursor class
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 17/09/98
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
13 #pragma implementation "cursor.h"
14 #endif
15
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
18
19 #include "wx/cursor.h"
20 #include "wx/app.h"
21 #include "wx/utils.h"
22 #include "wx/list.h"
23 #include "wx/window.h"
24 #if wxUSE_IMAGE
25 #include "wx/image.h"
26 #endif
27
28 #ifdef __VMS__
29 #pragma message disable nosimpint
30 #endif
31 #include <Xm/Xm.h>
32 #include <X11/cursorfont.h>
33 #ifdef __VMS__
34 #pragma message enable nosimpint
35 #endif
36
37 #include "wx/motif/private.h"
38
39 // Cursor for one display, so we can choose the correct one for
40 // the current display.
41 class wxXCursor
42 {
43 public:
44 WXDisplay* m_display;
45 WXCursor m_cursor;
46 };
47
48 WX_DECLARE_LIST(wxXCursor, wxXCursorList);
49 #include "wx/listimpl.cpp"
50 WX_DEFINE_LIST(wxXCursorList);
51
52 class WXDLLEXPORT wxCursorRefData: public wxObjectRefData
53 {
54 friend class WXDLLEXPORT wxCursor;
55 public:
56 wxCursorRefData();
57 ~wxCursorRefData();
58
59 wxXCursorList m_cursors; // wxXCursor objects, one per display
60 wxStockCursor m_cursorId; // wxWindows standard cursor id
61 };
62
63 #define M_CURSORDATA ((wxCursorRefData *)m_refData)
64 #define M_CURSORHANDLERDATA ((wxCursorRefData *)bitmap->m_refData)
65
66 IMPLEMENT_DYNAMIC_CLASS(wxCursor, wxObject)
67
68 wxCursorRefData::wxCursorRefData()
69 {
70 m_cursorId = wxCURSOR_NONE;
71 }
72
73 wxCursorRefData::~wxCursorRefData()
74 {
75 wxXCursorList::compatibility_iterator node = m_cursors.GetFirst();
76 while (node)
77 {
78 wxXCursor* c = node->GetData();
79 XFreeCursor((Display*) c->m_display, (Cursor) c->m_cursor);
80 delete c;
81 node = node->GetNext();
82 }
83 }
84
85 wxCursor::wxCursor()
86 {
87 }
88
89 #if wxUSE_IMAGE
90 wxCursor::wxCursor(const wxImage & image)
91 {
92 unsigned char * rgbBits = image.GetData();
93 int w = image.GetWidth() ;
94 int h = image.GetHeight();
95 bool bHasMask = image.HasMask();
96 int imagebitcount = (w*h)/8;
97
98 unsigned char * bits = new unsigned char [imagebitcount];
99 unsigned char * maskBits = new unsigned char [imagebitcount];
100
101 int i, j, i8; unsigned char c, cMask;
102 for (i=0; i<imagebitcount; i++)
103 {
104 bits[i] = 0xff;
105 i8 = i * 8;
106
107 cMask = 0xfe; // 11111110
108 for (j=0; j<8; j++)
109 {
110 // possible overflow if we do the summation first ?
111 c = rgbBits[(i8+j)*3]/3 + rgbBits[(i8+j)*3+1]/3 + rgbBits[(i8+j)*3+2]/3;
112 //if average value is > mid grey
113 if (c>127)
114 bits[i] = bits[i] & cMask;
115 cMask = (cMask << 1) | 1;
116 }
117 }
118
119 if (bHasMask)
120 {
121 unsigned char
122 r = image.GetMaskRed(),
123 g = image.GetMaskGreen(),
124 b = image.GetMaskBlue();
125
126 for (i=0; i<imagebitcount; i++)
127 {
128 maskBits[i] = 0x0;
129 i8 = i * 8;
130
131 cMask = 0x1;
132 for (j=0; j<8; j++)
133 {
134 if (rgbBits[(i8+j)*3] != r || rgbBits[(i8+j)*3+1] != g || rgbBits[(i8+j)*3+2] != b)
135 maskBits[i] = maskBits[i] | cMask;
136 cMask = (cMask << 1);
137 }
138 }
139 }
140 else // no mask
141 {
142 for (i=0; i<imagebitcount; i++)
143 maskBits[i] = 0xFF;
144 }
145
146 int hotSpotX;
147 int hotSpotY;
148
149 if (image.HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_X))
150 hotSpotX = image.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X);
151 else
152 hotSpotX = 0;
153
154 if (image.HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y))
155 hotSpotY = image.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y);
156 else
157 hotSpotY = 0;
158
159 if (hotSpotX < 0 || hotSpotX >= w)
160 hotSpotX = 0;
161 if (hotSpotY < 0 || hotSpotY >= h)
162 hotSpotY = 0;
163
164 Create( (const char*)bits, w, h, hotSpotX, hotSpotY,
165 (const char*)maskBits );
166
167 delete[] bits;
168 delete[] maskBits;
169 }
170 #endif
171
172 void wxCursor::Create(const char bits[], int width, int height,
173 int hotSpotX, int hotSpotY, const char maskBits[])
174 {
175 if( !m_refData )
176 m_refData = new wxCursorRefData;
177
178 Display *dpy = (Display*) wxGetDisplay();
179 int screen_num = DefaultScreen (dpy);
180
181 Pixmap pixmap = XCreatePixmapFromBitmapData (dpy,
182 RootWindow (dpy, screen_num),
183 (char*) bits, width, height,
184 1 , 0 , 1);
185
186 Pixmap mask_pixmap = None;
187 if (maskBits != NULL)
188 {
189 mask_pixmap = XCreatePixmapFromBitmapData (dpy,
190 RootWindow (dpy, screen_num),
191 (char*) maskBits, width, height,
192 1 , 0 , 1);
193 }
194
195 Create( (WXPixmap)pixmap, (WXPixmap)mask_pixmap, hotSpotX, hotSpotY );
196
197 XFreePixmap( dpy, pixmap );
198 if (mask_pixmap != None)
199 {
200 XFreePixmap( dpy, mask_pixmap );
201 }
202 }
203
204 void wxCursor::Create(WXPixmap pixmap, WXPixmap mask_pixmap,
205 int hotSpotX, int hotSpotY)
206 {
207 if( !m_refData )
208 m_refData = new wxCursorRefData;
209
210 Display *dpy = (Display*) wxGetDisplay();
211 int screen_num = DefaultScreen (dpy);
212
213 XColor foreground_color;
214 XColor background_color;
215 foreground_color.pixel = BlackPixel(dpy, screen_num);
216 background_color.pixel = WhitePixel(dpy, screen_num);
217 Colormap cmap = (Colormap) wxTheApp->GetMainColormap((WXDisplay*) dpy);
218 XQueryColor(dpy, cmap, &foreground_color);
219 XQueryColor(dpy, cmap, &background_color);
220
221 Cursor cursor = XCreatePixmapCursor (dpy,
222 (Pixmap)pixmap,
223 (Pixmap)mask_pixmap,
224 &foreground_color,
225 &background_color,
226 hotSpotX ,
227 hotSpotY);
228
229 if (cursor)
230 {
231 wxXCursor *c = new wxXCursor;
232
233 c->m_cursor = (WXCursor) cursor;
234 c->m_display = (WXDisplay*) dpy;
235 M_CURSORDATA->m_cursors.Append(c);
236 }
237 }
238
239 wxCursor::wxCursor(const char bits[], int width, int height,
240 int hotSpotX, int hotSpotY, const char maskBits[])
241 {
242 Create(bits, width, height, hotSpotX, hotSpotY, maskBits);
243 }
244
245 wxCursor::wxCursor(const wxString& name, long flags, int hotSpotX, int hotSpotY)
246 {
247 // Must be an XBM file
248 if (flags != wxBITMAP_TYPE_XBM)
249 return;
250
251 m_refData = new wxCursorRefData;
252
253 int hotX = -1, hotY = -1;
254 unsigned int w, h;
255 Pixmap pixmap = None, mask_pixmap = None;
256
257 Display *dpy = (Display*) wxGetDisplay();
258 int screen_num = DefaultScreen (dpy);
259
260 int value = XReadBitmapFile (dpy, RootWindow (dpy, screen_num),
261 wxConstCast(name.c_str(), char),
262 &w, &h, &pixmap, &hotX, &hotY);
263
264 if (value == BitmapSuccess)
265 {
266 // TODO: how do we determine whether hotX, hotY were read correctly?
267 if (hotX < 0 || hotY < 0)
268 {
269 hotX = hotSpotX;
270 hotY = hotSpotY;
271 }
272 if (hotX < 0 || hotY < 0)
273 {
274 hotX = 0;
275 hotY = 0;
276 }
277
278 Create( (WXPixmap)pixmap, (WXPixmap)mask_pixmap, hotX, hotY );
279
280 XFreePixmap( dpy, pixmap );
281 }
282 }
283
284 // Cursors by stock number
285 wxCursor::wxCursor(wxStockCursor id)
286 {
287 m_refData = new wxCursorRefData;
288 M_CURSORDATA->m_cursorId = id;
289 }
290
291 wxCursor::~wxCursor()
292 {
293 }
294
295 bool wxCursor::Ok() const
296 {
297 return m_refData != NULL;
298 }
299
300 // Motif-specific: create/get a cursor for the current display
301 WXCursor wxCursor::GetXCursor(WXDisplay* display)
302 {
303 if (!M_CURSORDATA)
304 return (WXCursor) 0;
305 wxXCursorList::compatibility_iterator node = M_CURSORDATA->m_cursors.GetFirst();
306 while (node)
307 {
308 wxXCursor* c = node->GetData();
309 if (c->m_display == display)
310 return c->m_cursor;
311 node = node->GetNext();
312 }
313
314 // No cursor for this display, so let's see if we're an id-type cursor.
315
316 if (M_CURSORDATA->m_cursorId != wxCURSOR_NONE)
317 {
318 WXCursor cursor = MakeCursor(display, M_CURSORDATA->m_cursorId);
319 if (cursor)
320 {
321 wxXCursor* c = new wxXCursor;
322 c->m_cursor = cursor;
323 c->m_display = display;
324 M_CURSORDATA->m_cursors.Append(c);
325 return cursor;
326 }
327 else
328 return (WXCursor) 0;
329 }
330
331 // Not an id-type cursor, so we don't know how to create it.
332 return (WXCursor) 0;
333 }
334
335 // Make a cursor from standard id
336 WXCursor wxCursor::MakeCursor(WXDisplay* display, wxStockCursor id)
337 {
338 Display* dpy = (Display*) display;
339 Cursor cursor = (Cursor) 0;
340 int x_cur = -1;
341
342 switch (id)
343 {
344 case wxCURSOR_WAIT: x_cur = XC_watch; break;
345 case wxCURSOR_CROSS: x_cur = XC_crosshair; break;
346 case wxCURSOR_CHAR: return (WXCursor)cursor; break;
347 case wxCURSOR_HAND: x_cur = XC_hand1; break;
348 case wxCURSOR_BULLSEYE: x_cur = XC_target; break;
349 case wxCURSOR_PENCIL: x_cur = XC_pencil; break;
350 case wxCURSOR_MAGNIFIER: x_cur = XC_sizing; break;
351 case wxCURSOR_IBEAM: x_cur = XC_xterm; break;
352 case wxCURSOR_NO_ENTRY: x_cur = XC_pirate; break;
353 case wxCURSOR_LEFT_BUTTON: x_cur = XC_leftbutton; break;
354 case wxCURSOR_RIGHT_BUTTON: x_cur = XC_rightbutton; break;
355 case wxCURSOR_MIDDLE_BUTTON: x_cur = XC_middlebutton; break;
356 case wxCURSOR_QUESTION_ARROW: x_cur = XC_question_arrow; break;
357 case wxCURSOR_SIZING: x_cur = XC_sizing; break;
358 case wxCURSOR_WATCH: x_cur = XC_watch; break;
359 case wxCURSOR_SPRAYCAN: x_cur = XC_spraycan; break;
360 case wxCURSOR_PAINT_BRUSH: x_cur = XC_spraycan; break;
361 case wxCURSOR_SIZENWSE:
362 case wxCURSOR_SIZENESW: x_cur = XC_crosshair; break;
363 case wxCURSOR_SIZEWE: x_cur = XC_sb_h_double_arrow; break;
364 case wxCURSOR_SIZENS: x_cur = XC_sb_v_double_arrow; break;
365 case wxCURSOR_POINT_LEFT: x_cur = XC_sb_left_arrow; break;
366 case wxCURSOR_POINT_RIGHT: x_cur = XC_sb_right_arrow; break;
367 // (JD Huggins) added more stock cursors for X
368 // X-only cursors BEGIN
369 case wxCURSOR_CROSS_REVERSE: x_cur = XC_cross_reverse; break;
370 case wxCURSOR_DOUBLE_ARROW: x_cur = XC_double_arrow; break;
371 case wxCURSOR_BASED_ARROW_UP: x_cur = XC_based_arrow_up; break;
372 case wxCURSOR_BASED_ARROW_DOWN: x_cur = XC_based_arrow_down; break;
373 case wxCURSOR_BLANK:
374 {
375 GC gc;
376 XGCValues gcv;
377 Pixmap empty_pixmap;
378 XColor blank_color;
379
380 empty_pixmap =
381 XCreatePixmap (dpy, RootWindow (dpy, DefaultScreen (dpy)),
382 16, 16, 1);
383 gcv.function = GXxor;
384 gc = XCreateGC (dpy,
385 empty_pixmap,
386 GCFunction,
387 &gcv);
388 XCopyArea (dpy,
389 empty_pixmap,
390 empty_pixmap,
391 gc,
392 0, 0,
393 16, 16,
394 0, 0);
395 XFreeGC (dpy, gc);
396 cursor = XCreatePixmapCursor (dpy,
397 empty_pixmap,
398 empty_pixmap,
399 &blank_color,
400 &blank_color,
401 8, 8);
402
403 break;
404 }
405 case wxCURSOR_ARROW:
406 default: x_cur = XC_top_left_arrow; break;
407 }
408
409 if( x_cur == -1 )
410 return (WXCursor)cursor;
411
412 cursor = XCreateFontCursor (dpy, x_cur);
413 return (WXCursor) cursor;
414 }
415
416 // Global cursor setting
417 void wxSetCursor(const wxCursor& WXUNUSED(cursor))
418 {
419 // Nothing to do for Motif (no global cursor)
420 }
421
422
423 // ----------------------------------------------------------------------------
424 // busy cursor stuff
425 // ----------------------------------------------------------------------------
426
427 static int wxBusyCursorCount = 0;
428
429 // Helper function
430 static void
431 wxXSetBusyCursor (wxWindow * win, wxCursor * cursor)
432 {
433 Display *display = (Display*) win->GetXDisplay();
434
435 Window xwin = (Window) win->GetXWindow();
436 if (!xwin)
437 return;
438
439 XSetWindowAttributes attrs;
440
441 if (cursor)
442 {
443 attrs.cursor = (Cursor) cursor->GetXCursor(display);
444 }
445 else
446 {
447 // Restore old cursor
448 if (win->GetCursor().Ok())
449 attrs.cursor = (Cursor) win->GetCursor().GetXCursor(display);
450 else
451 attrs.cursor = None;
452 }
453 if (xwin)
454 XChangeWindowAttributes (display, xwin, CWCursor, &attrs);
455
456 XFlush (display);
457
458 for(wxWindowList::compatibility_iterator node = win->GetChildren().GetFirst (); node;
459 node = node->GetNext())
460 {
461 wxWindow *child = node->GetData ();
462 wxXSetBusyCursor (child, cursor);
463 }
464 }
465
466 // Set the cursor to the busy cursor for all windows
467 void wxBeginBusyCursor(wxCursor *cursor)
468 {
469 wxBusyCursorCount++;
470 if (wxBusyCursorCount == 1)
471 {
472 for(wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetFirst (); node;
473 node = node->GetNext())
474 {
475 wxWindow *win = node->GetData ();
476 wxXSetBusyCursor (win, cursor);
477 }
478 }
479 }
480
481 // Restore cursor to normal
482 void wxEndBusyCursor()
483 {
484 if (wxBusyCursorCount == 0)
485 return;
486
487 wxBusyCursorCount--;
488 if (wxBusyCursorCount == 0)
489 {
490 for(wxWindowList::compatibility_iterator node = wxTopLevelWindows.GetFirst (); node;
491 node = node->GetNext())
492 {
493 wxWindow *win = node->GetData ();
494 wxXSetBusyCursor (win, NULL);
495 }
496 }
497 }
498
499 // TRUE if we're between the above two calls
500 bool wxIsBusy()
501 {
502 return (wxBusyCursorCount > 0);
503 }