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