Remove all lines containing cvs/svn "$Id$" keyword.
[wxWidgets.git] / src / motif / utils.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/motif/utils.cpp
3 // Purpose: Various utilities
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 17/09/98
7 // Copyright: (c) Julian Smart
8 // Licence: wxWindows licence
9 /////////////////////////////////////////////////////////////////////////////
10
11 // ============================================================================
12 // declarations
13 // ============================================================================
14
15 // ----------------------------------------------------------------------------
16 // headers
17 // ----------------------------------------------------------------------------
18
19 // For compilers that support precompilation, includes "wx.h".
20 #include "wx/wxprec.h"
21
22 #include "wx/utils.h"
23
24 #ifndef WX_PRECOMP
25 #include "wx/app.h"
26 #include "wx/dcmemory.h"
27 #include "wx/bitmap.h"
28 #endif
29
30 #include "wx/apptrait.h"
31 #include "wx/evtloop.h"
32 #include "wx/private/eventloopsourcesmanager.h"
33 #include "wx/motif/private/timer.h"
34
35 #include <string.h>
36
37 #if (defined(__SUNCC__) || defined(__CLCC__))
38 #include <sysent.h>
39 #endif
40
41 #ifdef __VMS__
42 #pragma message disable nosimpint
43 #endif
44
45 #include <Xm/Xm.h>
46 #include <Xm/Frame.h>
47
48 #include "wx/motif/private.h"
49
50 #include "X11/Xutil.h"
51
52 #ifdef __VMS__
53 #pragma message enable nosimpint
54 #endif
55
56
57 // ============================================================================
58 // implementation
59 // ============================================================================
60
61 // ----------------------------------------------------------------------------
62 // async event processing
63 // ----------------------------------------------------------------------------
64
65 // Consume all events until no more left
66 void wxFlushEvents(WXDisplay* wxdisplay)
67 {
68 Display *display = (Display*)wxdisplay;
69 wxEventLoop evtLoop;
70
71 XSync (display, False);
72
73 while (evtLoop.Pending())
74 {
75 XFlush (display);
76 evtLoop.Dispatch();
77 }
78 }
79
80 #if wxUSE_EVENTLOOP_SOURCE
81
82 extern "C"
83 {
84
85 static
86 void
87 wxMotifInputHandler(XtPointer data,
88 int* WXUNUSED(fd),
89 XtInputId* WXUNUSED(inputId))
90 {
91 wxEventLoopSourceHandler * const
92 handler = static_cast<wxEventLoopSourceHandler *>(data);
93
94 handler->OnReadWaiting();
95 }
96
97 }
98
99 // This class exists just to call XtRemoveInput() in its dtor, the real work of
100 // dispatching events on the file descriptor to the handler is done by
101 // wxMotifInputHandler callback above.
102 class wxMotifEventLoopSource : public wxEventLoopSource
103 {
104 public:
105 wxMotifEventLoopSource(XtInputId inputId,
106 wxEventLoopSourceHandler *handler,
107 int flags)
108 : wxEventLoopSource(handler, flags),
109 m_inputId(inputId)
110 {
111 }
112
113 virtual ~wxMotifEventLoopSource()
114 {
115 XtRemoveInput(m_inputId);
116 }
117
118 private:
119 const XtInputId m_inputId;
120
121 wxDECLARE_NO_COPY_CLASS(wxMotifEventLoopSource);
122 };
123
124 class wxMotifEventLoopSourcesManager : public wxEventLoopSourcesManagerBase
125 {
126 public:
127 wxEventLoopSource *
128 AddSourceForFD(int fd, wxEventLoopSourceHandler* handler, int flags)
129 {
130 wxCHECK_MSG( wxTheApp, NULL, "Must create wxTheApp first" );
131
132 // The XtInputXXXMask values cannot be combined (hence "Mask" is a
133 // complete misnomer), and supporting those would make the code more
134 // complicated and we don't need them for now.
135 wxCHECK_MSG( !(flags & (wxEVENT_SOURCE_OUTPUT |
136 wxEVENT_SOURCE_EXCEPTION)),
137 NULL,
138 "Monitoring FDs for output/errors not supported" );
139
140 wxCHECK_MSG( flags & wxEVENT_SOURCE_INPUT,
141 NULL,
142 "Should be monitoring for input" );
143
144 XtInputId inputId = XtAppAddInput
145 (
146 (XtAppContext) wxTheApp->GetAppContext(),
147 fd,
148 (XtPointer) XtInputReadMask,
149 wxMotifInputHandler,
150 handler
151 );
152 if ( inputId < 0 )
153 return 0;
154
155 return new wxMotifEventLoopSource(inputId, handler, flags);
156 }
157 };
158
159 wxEventLoopSourcesManagerBase* wxGUIAppTraits::GetEventLoopSourcesManager()
160 {
161 static wxMotifEventLoopSourcesManager s_eventLoopSourcesManager;
162
163 return &s_eventLoopSourcesManager;
164 }
165
166 #endif // wxUSE_EVENTLOOP_SOURCE
167
168 // ----------------------------------------------------------------------------
169 // misc
170 // ----------------------------------------------------------------------------
171
172 // Emit a beeeeeep
173 void wxBell()
174 {
175 // Use current setting for the bell
176 XBell (wxGlobalDisplay(), 0);
177 }
178
179 wxPortId wxGUIAppTraits::GetToolkitVersion(int *verMaj, int *verMin) const
180 {
181 // XmVERSION and XmREVISION are defined in Xm/Xm.h
182 if ( verMaj )
183 *verMaj = XmVERSION;
184 if ( verMin )
185 *verMin = XmREVISION;
186
187 return wxPORT_MOTIF;
188 }
189
190 wxEventLoopBase* wxGUIAppTraits::CreateEventLoop()
191 {
192 return new wxEventLoop;
193 }
194
195 wxTimerImpl* wxGUIAppTraits::CreateTimerImpl(wxTimer* timer)
196 {
197 return new wxMotifTimerImpl(timer);
198 }
199
200 // ----------------------------------------------------------------------------
201 // display info
202 // ----------------------------------------------------------------------------
203
204 void wxGetMousePosition( int* x, int* y )
205 {
206 #if wxUSE_NANOX
207 // TODO
208 *x = 0;
209 *y = 0;
210 #else
211 XMotionEvent xev;
212 Window root, child;
213 XQueryPointer(wxGlobalDisplay(),
214 DefaultRootWindow(wxGlobalDisplay()),
215 &root, &child,
216 &(xev.x_root), &(xev.y_root),
217 &(xev.x), &(xev.y),
218 &(xev.state));
219 *x = xev.x_root;
220 *y = xev.y_root;
221 #endif
222 }
223
224 // Return true if we have a colour display
225 bool wxColourDisplay()
226 {
227 return wxDisplayDepth() > 1;
228 }
229
230 // Returns depth of screen
231 int wxDisplayDepth()
232 {
233 Display *dpy = wxGlobalDisplay();
234
235 return DefaultDepth (dpy, DefaultScreen (dpy));
236 }
237
238 // Get size of display
239 void wxDisplaySize(int *width, int *height)
240 {
241 Display *dpy = wxGlobalDisplay();
242
243 if ( width )
244 *width = DisplayWidth (dpy, DefaultScreen (dpy));
245 if ( height )
246 *height = DisplayHeight (dpy, DefaultScreen (dpy));
247 }
248
249 void wxDisplaySizeMM(int *width, int *height)
250 {
251 Display *dpy = wxGlobalDisplay();
252
253 if ( width )
254 *width = DisplayWidthMM(dpy, DefaultScreen (dpy));
255 if ( height )
256 *height = DisplayHeightMM(dpy, DefaultScreen (dpy));
257 }
258
259 // Configurable display in wxX11 and wxMotif
260 static WXDisplay *gs_currentDisplay = NULL;
261 static wxString gs_displayName;
262
263 WXDisplay *wxGetDisplay()
264 {
265 if (gs_currentDisplay)
266 return gs_currentDisplay;
267 else if (wxTheApp)
268 return wxTheApp->GetInitialDisplay();
269 return NULL;
270 }
271
272 bool wxSetDisplay(const wxString& display_name)
273 {
274 gs_displayName = display_name;
275
276 if ( display_name.empty() )
277 {
278 gs_currentDisplay = NULL;
279
280 return true;
281 }
282 else
283 {
284 Cardinal argc = 0;
285
286 Display *display = XtOpenDisplay((XtAppContext) wxTheApp->GetAppContext(),
287 display_name.c_str(),
288 wxTheApp->GetAppName().c_str(),
289 wxTheApp->GetClassName().c_str(),
290 NULL,
291 #if XtSpecificationRelease < 5
292 0, &argc,
293 #else
294 0, (int *)&argc,
295 #endif
296 NULL);
297
298 if (display)
299 {
300 gs_currentDisplay = (WXDisplay*) display;
301 return true;
302 }
303 else
304 return false;
305 }
306 }
307
308 wxString wxGetDisplayName()
309 {
310 return gs_displayName;
311 }
312
313 wxWindow* wxFindWindowAtPoint(const wxPoint& pt)
314 {
315 return wxGenericFindWindowAtPoint(pt);
316 }
317
318 // ----------------------------------------------------------------------------
319 // Some colour manipulation routines
320 // ----------------------------------------------------------------------------
321
322 void wxHSVToXColor(wxHSV *hsv,XColor *rgb)
323 {
324 int h = hsv->h;
325 int s = hsv->s;
326 int v = hsv->v;
327 int r = 0, g = 0, b = 0;
328 int i, f;
329 int p, q, t;
330 s = (s * wxMAX_RGB) / wxMAX_SV;
331 v = (v * wxMAX_RGB) / wxMAX_SV;
332 if (h == 360) h = 0;
333 if (s == 0) { h = 0; r = g = b = v; }
334 i = h / 60;
335 f = h % 60;
336 p = v * (wxMAX_RGB - s) / wxMAX_RGB;
337 q = v * (wxMAX_RGB - s * f / 60) / wxMAX_RGB;
338 t = v * (wxMAX_RGB - s * (60 - f) / 60) / wxMAX_RGB;
339 switch (i)
340 {
341 case 0: r = v, g = t, b = p; break;
342 case 1: r = q, g = v, b = p; break;
343 case 2: r = p, g = v, b = t; break;
344 case 3: r = p, g = q, b = v; break;
345 case 4: r = t, g = p, b = v; break;
346 case 5: r = v, g = p, b = q; break;
347 }
348 rgb->red = (unsigned short)(r << 8);
349 rgb->green = (unsigned short)(g << 8);
350 rgb->blue = (unsigned short)(b << 8);
351 }
352
353 void wxXColorToHSV(wxHSV *hsv,XColor *rgb)
354 {
355 int r = rgb->red >> 8;
356 int g = rgb->green >> 8;
357 int b = rgb->blue >> 8;
358 int maxv = wxMax3(r, g, b);
359 int minv = wxMin3(r, g, b);
360 int h = 0, s, v;
361 v = maxv;
362 if (maxv) s = (maxv - minv) * wxMAX_RGB / maxv;
363 else s = 0;
364 if (s == 0) h = 0;
365 else
366 {
367 int rc, gc, bc, hex = 0;
368 rc = (maxv - r) * wxMAX_RGB / (maxv - minv);
369 gc = (maxv - g) * wxMAX_RGB / (maxv - minv);
370 bc = (maxv - b) * wxMAX_RGB / (maxv - minv);
371 if (r == maxv) { h = bc - gc, hex = 0; }
372 else if (g == maxv) { h = rc - bc, hex = 2; }
373 else if (b == maxv) { h = gc - rc, hex = 4; }
374 h = hex * 60 + (h * 60 / wxMAX_RGB);
375 if (h < 0) h += 360;
376 }
377 hsv->h = h;
378 hsv->s = (s * wxMAX_SV) / wxMAX_RGB;
379 hsv->v = (v * wxMAX_SV) / wxMAX_RGB;
380 }
381
382 void wxAllocNearestColor(Display *d,Colormap cmp,XColor *xc)
383 {
384 #if !wxUSE_NANOX
385 int llp;
386
387 int screen = DefaultScreen(d);
388 int num_colors = DisplayCells(d,screen);
389
390 XColor *color_defs = new XColor[num_colors];
391 for(llp = 0;llp < num_colors;llp++) color_defs[llp].pixel = llp;
392 XQueryColors(d,cmp,color_defs,num_colors);
393
394 wxHSV hsv_defs, hsv;
395 wxXColorToHSV(&hsv,xc);
396
397 int diff, min_diff = 0, pixel = 0;
398
399 for(llp = 0;llp < num_colors;llp++)
400 {
401 wxXColorToHSV(&hsv_defs,&color_defs[llp]);
402 diff = wxSIGN(wxH_WEIGHT * (hsv.h - hsv_defs.h)) +
403 wxSIGN(wxS_WEIGHT * (hsv.s - hsv_defs.s)) +
404 wxSIGN(wxV_WEIGHT * (hsv.v - hsv_defs.v));
405 if (llp == 0) min_diff = diff;
406 if (min_diff > diff) { min_diff = diff; pixel = llp; }
407 if (min_diff == 0) break;
408 }
409
410 xc -> red = color_defs[pixel].red;
411 xc -> green = color_defs[pixel].green;
412 xc -> blue = color_defs[pixel].blue;
413 xc -> flags = DoRed | DoGreen | DoBlue;
414
415 /* FIXME, TODO
416 if (!XAllocColor(d,cmp,xc))
417 cout << "wxAllocNearestColor : Warning : Cannot find nearest color !\n";
418 */
419
420 delete[] color_defs;
421 #endif
422 }
423
424 void wxAllocColor(Display *d,Colormap cmp,XColor *xc)
425 {
426 if (!XAllocColor(d,cmp,xc))
427 {
428 // cout << "wxAllocColor : Warning : cannot allocate color, attempt find nearest !\n";
429 wxAllocNearestColor(d,cmp,xc);
430 }
431 }
432
433 wxString wxGetXEventName(XEvent& event)
434 {
435 #if wxUSE_NANOX
436 wxString str(wxT("(some event)"));
437 return str;
438 #else
439 int type = event.xany.type;
440 static char* event_name[] = {
441 wxMOTIF_STR(""), wxMOTIF_STR("unknown(-)"), // 0-1
442 wxMOTIF_STR("KeyPress"), wxMOTIF_STR("KeyRelease"), wxMOTIF_STR("ButtonPress"), wxMOTIF_STR("ButtonRelease"), // 2-5
443 wxMOTIF_STR("MotionNotify"), wxMOTIF_STR("EnterNotify"), wxMOTIF_STR("LeaveNotify"), wxMOTIF_STR("FocusIn"), // 6-9
444 wxMOTIF_STR("FocusOut"), wxMOTIF_STR("KeymapNotify"), wxMOTIF_STR("Expose"), wxMOTIF_STR("GraphicsExpose"), // 10-13
445 wxMOTIF_STR("NoExpose"), wxMOTIF_STR("VisibilityNotify"), wxMOTIF_STR("CreateNotify"), // 14-16
446 wxMOTIF_STR("DestroyNotify"), wxMOTIF_STR("UnmapNotify"), wxMOTIF_STR("MapNotify"), wxMOTIF_STR("MapRequest"),// 17-20
447 wxMOTIF_STR("ReparentNotify"), wxMOTIF_STR("ConfigureNotify"), wxMOTIF_STR("ConfigureRequest"), // 21-23
448 wxMOTIF_STR("GravityNotify"), wxMOTIF_STR("ResizeRequest"), wxMOTIF_STR("CirculateNotify"), // 24-26
449 wxMOTIF_STR("CirculateRequest"), wxMOTIF_STR("PropertyNotify"), wxMOTIF_STR("SelectionClear"), // 27-29
450 wxMOTIF_STR("SelectionRequest"), wxMOTIF_STR("SelectionNotify"), wxMOTIF_STR("ColormapNotify"), // 30-32
451 wxMOTIF_STR("ClientMessage"), wxMOTIF_STR("MappingNotify"), // 33-34
452 wxMOTIF_STR("unknown(+)")}; // 35
453 type = wxMin(35, type); type = wxMax(1, type);
454 wxString str(event_name[type]);
455 return str;
456 #endif
457 }
458
459 // ----------------------------------------------------------------------------
460 // accelerators
461 // ----------------------------------------------------------------------------
462
463 // Find the letter corresponding to the mnemonic, for Motif
464 char wxFindMnemonic (const char *s)
465 {
466 char mnem = 0;
467 int len = strlen (s);
468 int i;
469
470 for (i = 0; i < len; i++)
471 {
472 if (s[i] == '&')
473 {
474 // Carefully handle &&
475 if ((i + 1) <= len && s[i + 1] == '&')
476 i++;
477 else
478 {
479 mnem = s[i + 1];
480 break;
481 }
482 }
483 }
484 return mnem;
485 }
486
487 char* wxFindAccelerator( const char *s )
488 {
489 #if 1
490 wxUnusedVar(s);
491 // VZ: this function returns incorrect keysym which completely breaks kbd
492 // handling
493 return NULL;
494 #else
495 // The accelerator text is after the \t char.
496 s = strchr( s, '\t' );
497
498 if( !s ) return NULL;
499
500 /*
501 Now we need to format it as X standard:
502
503 input output
504
505 F7 --> <Key>F7
506 Ctrl+N --> Ctrl<Key>N
507 Alt+k --> Meta<Key>k
508 Ctrl+Shift+A --> Ctrl Shift<Key>A
509
510 and handle Ctrl-N & similia
511 */
512
513 static char buf[256];
514
515 buf[0] = '\0';
516 wxString tmp = s + 1; // skip TAB
517 size_t index = 0;
518
519 while( index < tmp.length() )
520 {
521 size_t plus = tmp.find( '+', index );
522 size_t minus = tmp.find( '-', index );
523
524 // neither '+' nor '-', add <Key>
525 if( plus == wxString::npos && minus == wxString::npos )
526 {
527 strcat( buf, "<Key>" );
528 strcat( buf, tmp.c_str() + index );
529
530 return buf;
531 }
532
533 // OK: npos is big and positive
534 size_t sep = wxMin( plus, minus );
535 wxString mod = tmp.substr( index, sep - index );
536
537 // Ctrl -> Ctrl
538 // Shift -> Shift
539 // Alt -> Meta
540 if( mod == "Alt" )
541 mod = "Meta";
542
543 if( buf[0] )
544 strcat( buf, " " );
545
546 strcat( buf, mod.c_str() );
547
548 index = sep + 1;
549 }
550
551 return NULL;
552 #endif
553 }
554
555 XmString wxFindAcceleratorText (const char *s)
556 {
557 #if 1
558 wxUnusedVar(s);
559 // VZ: this function returns incorrect keysym which completely breaks kbd
560 // handling
561 return NULL;
562 #else
563 // The accelerator text is after the \t char.
564 s = strchr( s, '\t' );
565
566 if( !s ) return NULL;
567
568 return wxStringToXmString( s + 1 ); // skip TAB!
569 #endif
570 }
571
572 // Change a widget's foreground and background colours.
573 void wxDoChangeForegroundColour(WXWidget widget, wxColour& foregroundColour)
574 {
575 if (!foregroundColour.IsOk())
576 return;
577
578 // When should we specify the foreground, if it's calculated
579 // by wxComputeColours?
580 // Solution: say we start with the default (computed) foreground colour.
581 // If we call SetForegroundColour explicitly for a control or window,
582 // then the foreground is changed.
583 // Therefore SetBackgroundColour computes the foreground colour, and
584 // SetForegroundColour changes the foreground colour. The ordering is
585 // important.
586
587 XtVaSetValues ((Widget) widget,
588 XmNforeground, foregroundColour.AllocColour(XtDisplay((Widget) widget)),
589 NULL);
590 }
591
592 void wxDoChangeBackgroundColour(WXWidget widget, const wxColour& backgroundColour, bool changeArmColour)
593 {
594 if (!backgroundColour.IsOk())
595 return;
596
597 wxComputeColours (XtDisplay((Widget) widget), & backgroundColour,
598 NULL);
599
600 XtVaSetValues ((Widget) widget,
601 XmNbackground, g_itemColors[wxBACK_INDEX].pixel,
602 XmNtopShadowColor, g_itemColors[wxTOPS_INDEX].pixel,
603 XmNbottomShadowColor, g_itemColors[wxBOTS_INDEX].pixel,
604 XmNforeground, g_itemColors[wxFORE_INDEX].pixel,
605 NULL);
606
607 if (changeArmColour)
608 XtVaSetValues ((Widget) widget,
609 XmNarmColor, g_itemColors[wxSELE_INDEX].pixel,
610 NULL);
611 }
612
613 extern void wxDoChangeFont(WXWidget widget, const wxFont& font)
614 {
615 // Lesstif 0.87 hangs here, but 0.93 does not; MBN: sometimes it does
616 #if !wxCHECK_LESSTIF() // || wxCHECK_LESSTIF_VERSION( 0, 93 )
617 Widget w = (Widget)widget;
618 XtVaSetValues( w,
619 wxFont::GetFontTag(), font.GetFontTypeC( XtDisplay(w) ),
620 NULL );
621 #else
622 wxUnusedVar(widget);
623 wxUnusedVar(font);
624 #endif
625
626 }
627
628 wxString wxXmStringToString( const XmString& xmString )
629 {
630 char *txt;
631 if( XmStringGetLtoR( xmString, XmSTRING_DEFAULT_CHARSET, &txt ) )
632 {
633 wxString str(txt);
634 XtFree (txt);
635 return str;
636 }
637
638 return wxEmptyString;
639 }
640
641 XmString wxStringToXmString( const char* str )
642 {
643 return XmStringCreateLtoR((char *)str, XmSTRING_DEFAULT_CHARSET);
644 }
645
646 // ----------------------------------------------------------------------------
647 // wxBitmap utility functions
648 // ----------------------------------------------------------------------------
649
650 // Creates a bitmap with transparent areas drawn in
651 // the given colour.
652 wxBitmap wxCreateMaskedBitmap(const wxBitmap& bitmap, const wxColour& colour)
653 {
654 wxBitmap newBitmap(bitmap.GetWidth(),
655 bitmap.GetHeight(),
656 bitmap.GetDepth());
657 wxMemoryDC destDC;
658 wxMemoryDC srcDC;
659
660 srcDC.SelectObjectAsSource(bitmap);
661 destDC.SelectObject(newBitmap);
662
663 wxBrush brush(colour, wxSOLID);
664 destDC.SetBackground(brush);
665 destDC.Clear();
666 destDC.Blit(0, 0, bitmap.GetWidth(), bitmap.GetHeight(),
667 &srcDC, 0, 0, wxCOPY, true);
668
669 return newBitmap;
670 }
671
672 // ----------------------------------------------------------------------------
673 // Miscellaneous functions
674 // ----------------------------------------------------------------------------
675
676 WXWidget wxCreateBorderWidget( WXWidget parent, long style )
677 {
678 Widget borderWidget = (Widget)NULL, parentWidget = (Widget)parent;
679
680 if (style & wxSIMPLE_BORDER)
681 {
682 borderWidget = XtVaCreateManagedWidget
683 (
684 "simpleBorder",
685 xmFrameWidgetClass, parentWidget,
686 XmNshadowType, XmSHADOW_ETCHED_IN,
687 XmNshadowThickness, 1,
688 NULL
689 );
690 }
691 else if ((style & wxSUNKEN_BORDER) || (style & wxBORDER_THEME))
692 {
693 borderWidget = XtVaCreateManagedWidget
694 (
695 "sunkenBorder",
696 xmFrameWidgetClass, parentWidget,
697 XmNshadowType, XmSHADOW_IN,
698 NULL
699 );
700 }
701 else if (style & wxRAISED_BORDER)
702 {
703 borderWidget = XtVaCreateManagedWidget
704 (
705 "raisedBorder",
706 xmFrameWidgetClass, parentWidget,
707 XmNshadowType, XmSHADOW_OUT,
708 NULL
709 );
710 }
711
712 return borderWidget;
713 }