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