]> git.saurik.com Git - wxWidgets.git/blob - src/motif/cursor.cpp
If -1 is used for width/height, use best size.
[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/gdicmn.h"
18 #include "wx/icon.h"
19 #include "wx/app.h"
20 #include "wx/utils.h"
21 #if wxUSE_IMAGE
22 #include "wx/image.h"
23 #endif
24
25 #ifdef __VMS__
26 #pragma message disable nosimpint
27 #endif
28 #include <Xm/Xm.h>
29 #include <X11/cursorfont.h>
30 #ifdef __VMS__
31 #pragma message enable nosimpint
32 #endif
33
34 #include "wx/motif/private.h"
35
36 IMPLEMENT_DYNAMIC_CLASS(wxCursor, wxBitmap)
37 IMPLEMENT_DYNAMIC_CLASS(wxXCursor, wxObject)
38
39 wxCursorRefData::wxCursorRefData()
40 {
41 m_width = 32; m_height = 32;
42 m_cursorId = wxCURSOR_NONE;
43 }
44
45 wxCursorRefData::~wxCursorRefData()
46 {
47 wxNode* node = m_cursors.First();
48 while (node)
49 {
50 wxXCursor* c = (wxXCursor*) node->Data();
51 // TODO: how to delete cursor?
52 // XDestroyCursor((Display*) c->m_display, (Cursor) c->m_cursor); // ??
53 delete c;
54 node = node->Next();
55 }
56 }
57
58 wxCursor::wxCursor()
59 {
60 }
61
62 #if wxUSE_IMAGE
63 wxCursor::wxCursor(const wxImage & image)
64 {
65 unsigned char * rgbBits = image.GetData();
66 int w = image.GetWidth() ;
67 int h = image.GetHeight();
68 bool bHasMask = image.HasMask();
69 int imagebitcount = (w*h)/8;
70
71 unsigned char * bits = new unsigned char [imagebitcount];
72 unsigned char * maskBits = new unsigned char [imagebitcount];
73
74 int i, j, i8; unsigned char c, cMask;
75 for (i=0; i<imagebitcount; i++)
76 {
77 bits[i] = 0;
78 i8 = i * 8;
79
80 cMask = 1;
81 for (j=0; j<8; j++)
82 {
83 // possible overflow if we do the summation first ?
84 c = rgbBits[(i8+j)*3]/3 + rgbBits[(i8+j)*3+1]/3 + rgbBits[(i8+j)*3+2]/3;
85 //if average value is > mid grey
86 if (c>127)
87 bits[i] = bits[i] | cMask;
88 cMask = cMask * 2;
89 }
90 }
91
92 unsigned long keyMaskColor;
93 if (bHasMask)
94 {
95 unsigned char
96 r = image.GetMaskRed(),
97 g = image.GetMaskGreen(),
98 b = image.GetMaskBlue();
99
100 for (i=0; i<imagebitcount; i++)
101 {
102 maskBits[i] = 0x0;
103 i8 = i * 8;
104
105 cMask = 1;
106 for (j=0; j<8; j++)
107 {
108 if (rgbBits[(i8+j)*3] != r || rgbBits[(i8+j)*3+1] != g || rgbBits[(i8+j)*3+2] != b)
109 maskBits[i] = maskBits[i] | cMask;
110 cMask = cMask * 2;
111 }
112 }
113
114 keyMaskColor = (r << 16) | (g << 8) | b;
115 }
116 else // no mask
117 {
118 for (i=0; i<imagebitcount; i++)
119 maskBits[i] = 0xFF;
120
121 // init it to avoid compiler warnings
122 keyMaskColor = 0;
123 }
124 /*
125 // find the most frequent color(s)
126 wxImageHistogram histogram;
127 image.ComputeHistogram(histogram);
128
129 // colors as rrggbb
130 unsigned long key;
131 unsigned long value;
132
133 long colMostFreq = 0;
134 unsigned long nMost = 0;
135 long colNextMostFreq = 0;
136 unsigned long nNext = 0;
137 for ( wxImageHistogram::iterator entry = histogram.begin();
138 entry != histogram.end();
139 ++entry )
140 {
141 value = entry->second.value;
142 key = entry->first;
143 if ( !bHasMask || (key != keyMaskColor) )
144 {
145 if (value > nMost)
146 {
147 nMost = value;
148 colMostFreq = key;
149 }
150 else if (value > nNext)
151 {
152 nNext = value;
153 colNextMostFreq = key;
154 }
155 }
156 }
157
158 wxColour fg = wxColour ( (unsigned char)(colMostFreq >> 16),
159 (unsigned char)(colMostFreq >> 8),
160 (unsigned char)(colMostFreq) );
161
162 wxColour bg = wxColour ( (unsigned char)(colNextMostFreq >> 16),
163 (unsigned char)(colNextMostFreq >> 8),
164 (unsigned char)(colNextMostFreq) );
165 end of color code
166 */
167 int hotSpotX;
168 int hotSpotY;
169
170 if (image.HasOption(wxCUR_HOTSPOT_X))
171 hotSpotX = image.GetOptionInt(wxCUR_HOTSPOT_X);
172 else
173 hotSpotX = 0;
174
175 if (image.HasOption(wxCUR_HOTSPOT_Y))
176 hotSpotY = image.GetOptionInt(wxCUR_HOTSPOT_Y);
177 else
178 hotSpotY = 0;
179
180 if (hotSpotX < 0 || hotSpotX >= w)
181 hotSpotX = 0;
182 if (hotSpotY < 0 || hotSpotY >= h)
183 hotSpotY = 0;
184
185 m_refData = new wxCursorRefData;
186
187 Display *dpy = (Display*) wxGetDisplay();
188 int screen_num = DefaultScreen (dpy);
189
190 Pixmap pixmap = XCreatePixmapFromBitmapData (dpy,
191 RootWindow (dpy, DefaultScreen(dpy)),
192 (char*) bits, w, h,
193 1 , 0 , 1);
194
195 Pixmap mask_pixmap = None;
196 if (maskBits != NULL)
197 {
198 mask_pixmap = XCreatePixmapFromBitmapData (dpy,
199 RootWindow (dpy, DefaultScreen(dpy)),
200 (char*) maskBits, w, h,
201 1 , 0 , 1);
202 }
203
204 XColor foreground_color;
205 XColor background_color;
206 foreground_color.pixel = BlackPixel(dpy, screen_num);
207 background_color.pixel = WhitePixel(dpy, screen_num);
208 Colormap cmap = (Colormap) wxTheApp->GetMainColormap((WXDisplay*) dpy);
209 XQueryColor(dpy, cmap, &foreground_color);
210 XQueryColor(dpy, cmap, &background_color);
211
212 Cursor cursor = XCreatePixmapCursor (dpy,
213 pixmap,
214 mask_pixmap,
215 &foreground_color,
216 &background_color,
217 hotSpotX ,
218 hotSpotY);
219
220 XFreePixmap( dpy, pixmap );
221 if (mask_pixmap != None)
222 {
223 XFreePixmap( dpy, mask_pixmap );
224 }
225
226 if (cursor)
227 {
228 wxXCursor *c = new wxXCursor;
229
230 c->m_cursor = (WXCursor) cursor;
231 c->m_display = (WXDisplay*) dpy;
232 M_CURSORDATA->m_cursors.Append(c);
233 M_CURSORDATA->m_ok = TRUE;
234 }
235 else
236 {
237 M_CURSORDATA->m_ok = TRUE;
238 }
239
240 }
241 #endif
242
243 wxCursor::wxCursor(const char bits[], int width, int height,
244 int hotSpotX, int hotSpotY, const char maskBits[])
245 {
246 m_refData = new wxCursorRefData;
247
248 Display *dpy = (Display*) wxGetDisplay();
249 int screen_num = DefaultScreen (dpy);
250
251 Pixmap pixmap = XCreatePixmapFromBitmapData (dpy,
252 RootWindow (dpy, DefaultScreen(dpy)),
253 (char*) bits, width, height,
254 1 , 0 , 1);
255
256 Pixmap mask_pixmap = None;
257 if (maskBits != NULL)
258 {
259 mask_pixmap = XCreatePixmapFromBitmapData (dpy,
260 RootWindow (dpy, DefaultScreen(dpy)),
261 (char*) maskBits, width, height,
262 1 , 0 , 1);
263 }
264
265 XColor foreground_color;
266 XColor background_color;
267 foreground_color.pixel = BlackPixel(dpy, screen_num);
268 background_color.pixel = WhitePixel(dpy, screen_num);
269 Colormap cmap = (Colormap) wxTheApp->GetMainColormap((WXDisplay*) dpy);
270 XQueryColor(dpy, cmap, &foreground_color);
271 XQueryColor(dpy, cmap, &background_color);
272
273 Cursor cursor = XCreatePixmapCursor (dpy,
274 pixmap,
275 mask_pixmap,
276 &foreground_color,
277 &background_color,
278 hotSpotX ,
279 hotSpotY);
280
281 XFreePixmap( dpy, pixmap );
282 if (mask_pixmap != None)
283 {
284 XFreePixmap( dpy, mask_pixmap );
285 }
286
287 if (cursor)
288 {
289 wxXCursor *c = new wxXCursor;
290
291 c->m_cursor = (WXCursor) cursor;
292 c->m_display = (WXDisplay*) dpy;
293 M_CURSORDATA->m_cursors.Append(c);
294 M_CURSORDATA->m_ok = TRUE;
295 }
296 else
297 {
298 M_CURSORDATA->m_ok = TRUE;
299 }
300 }
301
302 wxCursor::wxCursor(const wxString& name, long flags, int hotSpotX, int hotSpotY)
303 {
304 // Must be an XBM file
305 if (flags != wxBITMAP_TYPE_XBM)
306 return;
307
308 m_refData = new wxCursorRefData;
309
310 int hotX = -1, hotY = -1;
311 unsigned int w, h;
312 Pixmap pixmap;
313
314 Display *dpy = (Display*) wxGetDisplay();
315 int screen_num = DefaultScreen (dpy);
316
317 int value = XReadBitmapFile (dpy, RootWindow (dpy, DefaultScreen (dpy)),
318 (char*) (const char*) name, &w, &h, &pixmap, &hotX, &hotY);
319
320 M_BITMAPDATA->m_width = w;
321 M_BITMAPDATA->m_height = h;
322 M_BITMAPDATA->m_depth = 1;
323
324 if ((value == BitmapFileInvalid) ||
325 (value == BitmapOpenFailed) ||
326 (value == BitmapNoMemory))
327 {
328 }
329 else
330 {
331 XColor foreground_color;
332 XColor background_color;
333 foreground_color.pixel = BlackPixel(dpy, screen_num);
334 background_color.pixel = WhitePixel(dpy, screen_num);
335 Colormap cmap = (Colormap) wxTheApp->GetMainColormap((WXDisplay*) dpy);
336 XQueryColor(dpy, cmap, &foreground_color);
337 XQueryColor(dpy, cmap, &background_color);
338
339 // TODO: how do we determine whether hotX, hotY were read correctly?
340 if (hotX < 0 || hotY < 0)
341 {
342 hotX = hotSpotX;
343 hotY = hotSpotY;
344 }
345 if (hotX < 0 || hotY < 0)
346 {
347 hotX = 0;
348 hotY = 0;
349 }
350
351 Pixmap mask_pixmap = None;
352 Cursor cursor = XCreatePixmapCursor (dpy,
353 pixmap,
354 mask_pixmap,
355 &foreground_color,
356 &background_color,
357 hotX,
358 hotY);
359
360 XFreePixmap( dpy, pixmap );
361 if (cursor)
362 {
363 wxXCursor *c = new wxXCursor;
364
365 c->m_cursor = (WXCursor) cursor;
366 c->m_display = (WXDisplay*) dpy;
367 M_CURSORDATA->m_cursors.Append(c);
368 M_CURSORDATA->m_ok = TRUE;
369 }
370 }
371
372 }
373
374 // Cursors by stock number
375 wxCursor::wxCursor(wxStockCursor id)
376 {
377 m_refData = new wxCursorRefData;
378 M_CURSORDATA->m_cursorId = id;
379 M_CURSORDATA->m_ok = TRUE;
380
381 WXDisplay* display = wxGetDisplay();
382 if (!display)
383 return;
384
385 WXCursor cursor = GetXCursor(display);
386 if (cursor)
387 {
388 wxXCursor* c = new wxXCursor;
389 c->m_cursor = cursor;
390 c->m_display = wxGetDisplay();
391 M_CURSORDATA->m_cursors.Append(c);
392 M_CURSORDATA->m_ok = TRUE;
393 }
394 }
395
396 wxCursor::~wxCursor()
397 {
398 }
399
400 // Motif-specific: create/get a cursor for the current display
401 WXCursor wxCursor::GetXCursor(WXDisplay* display)
402 {
403 if (!M_CURSORDATA)
404 return (WXCursor) 0;
405 wxNode* node = M_CURSORDATA->m_cursors.First();
406 while (node)
407 {
408 wxXCursor* c = (wxXCursor*) node->Data();
409 if (c->m_display == display)
410 return c->m_cursor;
411 node = node->Next();
412 }
413
414 // No cursor for this display, so let's see if we're an id-type cursor.
415
416 if (M_CURSORDATA->m_cursorId != wxCURSOR_NONE)
417 {
418 WXCursor cursor = MakeCursor(display, M_CURSORDATA->m_cursorId);
419 if (cursor)
420 {
421 wxXCursor* c = new wxXCursor;
422 c->m_cursor = cursor;
423 c->m_display = display;
424 M_CURSORDATA->m_cursors.Append(c);
425 return cursor;
426 }
427 else
428 return (WXCursor) 0;
429 }
430
431 // Not an id-type cursor, so we don't know how to create it.
432 return (WXCursor) 0;
433 }
434
435 // Make a cursor from standard id
436 WXCursor wxCursor::MakeCursor(WXDisplay* display, wxStockCursor id)
437 {
438 Display* dpy = (Display*) display;
439 Cursor cursor = (Cursor) 0;
440
441 switch (id)
442 {
443 case wxCURSOR_WAIT:
444 {
445 cursor = XCreateFontCursor (dpy, XC_watch);
446 break;
447 }
448 case wxCURSOR_CROSS:
449 {
450 cursor = XCreateFontCursor (dpy, XC_crosshair);
451 break;
452 }
453 case wxCURSOR_CHAR:
454 {
455 // Nothing
456 break;
457 }
458 case wxCURSOR_HAND:
459 {
460 cursor = XCreateFontCursor (dpy, XC_hand1);
461 break;
462 }
463 case wxCURSOR_BULLSEYE:
464 {
465 cursor = XCreateFontCursor (dpy, XC_target);
466 break;
467 }
468 case wxCURSOR_PENCIL:
469 {
470 cursor = XCreateFontCursor (dpy, XC_pencil);
471 break;
472 }
473 case wxCURSOR_MAGNIFIER:
474 {
475 cursor = XCreateFontCursor (dpy, XC_sizing);
476 break;
477 }
478 case wxCURSOR_IBEAM:
479 {
480 cursor = XCreateFontCursor (dpy, XC_xterm);
481 break;
482 }
483 case wxCURSOR_NO_ENTRY:
484 {
485 cursor = XCreateFontCursor (dpy, XC_pirate);
486 break;
487 }
488 case wxCURSOR_LEFT_BUTTON:
489 {
490 cursor = XCreateFontCursor (dpy, XC_leftbutton);
491 break;
492 }
493 case wxCURSOR_RIGHT_BUTTON:
494 {
495 cursor = XCreateFontCursor (dpy, XC_rightbutton);
496 break;
497 }
498 case wxCURSOR_MIDDLE_BUTTON:
499 {
500 cursor = XCreateFontCursor (dpy, XC_middlebutton);
501 break;
502 }
503 case wxCURSOR_QUESTION_ARROW:
504 {
505 cursor = XCreateFontCursor (dpy, XC_question_arrow);
506 break;
507 }
508 case wxCURSOR_SIZING:
509 {
510 cursor = XCreateFontCursor (dpy, XC_sizing);
511 break;
512 }
513 case wxCURSOR_WATCH:
514 {
515 cursor = XCreateFontCursor (dpy, XC_watch);
516 break;
517 }
518 case wxCURSOR_SPRAYCAN:
519 {
520 cursor = XCreateFontCursor (dpy, XC_spraycan);
521 break;
522 }
523 case wxCURSOR_PAINT_BRUSH:
524 {
525 cursor = XCreateFontCursor (dpy, XC_spraycan);
526 break;
527 }
528 case wxCURSOR_SIZENWSE:
529 case wxCURSOR_SIZENESW:
530 {
531 // Not available in X
532 cursor = XCreateFontCursor (dpy, XC_crosshair);
533 break;
534 }
535 case wxCURSOR_SIZEWE:
536 {
537 cursor = XCreateFontCursor (dpy, XC_sb_h_double_arrow);
538 break;
539 }
540 case wxCURSOR_SIZENS:
541 {
542 cursor = XCreateFontCursor (dpy, XC_sb_v_double_arrow);
543 break;
544 }
545 case wxCURSOR_POINT_LEFT:
546 {
547 cursor = XCreateFontCursor (dpy, XC_sb_left_arrow);
548 break;
549 }
550 case wxCURSOR_POINT_RIGHT:
551 {
552 cursor = XCreateFontCursor (dpy, XC_sb_right_arrow);
553 break;
554 }
555 // (JD Huggins) added more stock cursors for X
556 // X-only cursors BEGIN
557 case wxCURSOR_CROSS_REVERSE:
558 {
559 cursor = XCreateFontCursor(dpy, XC_cross_reverse);
560 break;
561 }
562 case wxCURSOR_DOUBLE_ARROW:
563 {
564 cursor = XCreateFontCursor(dpy, XC_double_arrow);
565 break;
566 }
567 case wxCURSOR_BASED_ARROW_UP:
568 {
569 cursor = XCreateFontCursor(dpy, XC_based_arrow_up);
570 break;
571 }
572 case wxCURSOR_BASED_ARROW_DOWN:
573 {
574 cursor = XCreateFontCursor(dpy, XC_based_arrow_down);
575 break;
576 }
577 default:
578 case wxCURSOR_ARROW:
579 {
580 cursor = XCreateFontCursor (dpy, XC_top_left_arrow);
581 break;
582 }
583 case wxCURSOR_BLANK:
584 {
585 GC gc;
586 XGCValues gcv;
587 Pixmap empty_pixmap;
588 XColor blank_color;
589
590 empty_pixmap = XCreatePixmap (dpy, RootWindow (dpy, DefaultScreen (dpy)),
591 16, 16, 1);
592 gcv.function = GXxor;
593 gc = XCreateGC (dpy,
594 empty_pixmap,
595 GCFunction,
596 &gcv);
597 XCopyArea (dpy,
598 empty_pixmap,
599 empty_pixmap,
600 gc,
601 0, 0,
602 16, 16,
603 0, 0);
604 XFreeGC (dpy, gc);
605 cursor = XCreatePixmapCursor (dpy,
606 empty_pixmap,
607 empty_pixmap,
608 &blank_color,
609 &blank_color,
610 8, 8);
611
612 break;
613 }
614 }
615 return (WXCursor) cursor;
616 }
617
618 // Global cursor setting
619 void wxSetCursor(const wxCursor& WXUNUSED(cursor))
620 {
621 // Nothing to do for Motif (no global cursor)
622 }
623
624
625 // ----------------------------------------------------------------------------
626 // busy cursor stuff
627 // ----------------------------------------------------------------------------
628
629 static int wxBusyCursorCount = 0;
630
631 // Helper function
632 static void
633 wxXSetBusyCursor (wxWindow * win, wxCursor * cursor)
634 {
635 Display *display = (Display*) win->GetXDisplay();
636
637 Window xwin = (Window) win->GetXWindow();
638 if (!xwin)
639 return;
640
641 XSetWindowAttributes attrs;
642
643 if (cursor)
644 {
645 attrs.cursor = (Cursor) cursor->GetXCursor(display);
646 }
647 else
648 {
649 // Restore old cursor
650 if (win->GetCursor().Ok())
651 attrs.cursor = (Cursor) win->GetCursor().GetXCursor(display);
652 else
653 attrs.cursor = None;
654 }
655 if (xwin)
656 XChangeWindowAttributes (display, xwin, CWCursor, &attrs);
657
658 XFlush (display);
659
660 for(wxNode *node = win->GetChildren().First (); node; node = node->Next())
661 {
662 wxWindow *child = (wxWindow *) node->Data ();
663 wxXSetBusyCursor (child, cursor);
664 }
665 }
666
667 // Set the cursor to the busy cursor for all windows
668 void wxBeginBusyCursor(wxCursor *cursor)
669 {
670 wxBusyCursorCount++;
671 if (wxBusyCursorCount == 1)
672 {
673 for(wxNode *node = wxTopLevelWindows.First (); node; node = node->Next())
674 {
675 wxWindow *win = (wxWindow *) node->Data ();
676 wxXSetBusyCursor (win, cursor);
677 }
678 }
679 }
680
681 // Restore cursor to normal
682 void wxEndBusyCursor()
683 {
684 if (wxBusyCursorCount == 0)
685 return;
686
687 wxBusyCursorCount--;
688 if (wxBusyCursorCount == 0)
689 {
690 for(wxNode *node = wxTopLevelWindows.First (); node; node = node->Next())
691 {
692 wxWindow *win = (wxWindow *) node->Data ();
693 wxXSetBusyCursor (win, NULL);
694 }
695 }
696 }
697
698 // TRUE if we're between the above two calls
699 bool wxIsBusy()
700 {
701 return (wxBusyCursorCount > 0);
702 }