Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtmail / MotifApp / MainWindow.C
1 /* $TOG: MainWindow.C /main/13 1998/04/06 13:22:40 mgreess $ */
2 /*
3  *+SNOTICE
4  *
5  *      $TOG: MainWindow.C /main/13 1998/04/06 13:22:40 mgreess $
6  *
7  *      RESTRICTED CONFIDENTIAL INFORMATION:
8  *      
9  *      The information in this document is subject to special
10  *      restrictions in a confidential disclosure agreement bertween
11  *      HP, IBM, Sun, USL, SCO and Univel.  Do not distribute this
12  *      document outside HP, IBM, Sun, USL, SCO, or Univel wihtout
13  *      Sun's specific written approval.  This documment and all copies
14  *      and derivative works thereof must be returned or destroyed at
15  *      Sun's request.
16  *
17  *      Copyright 1993, 1994, 1995 Sun Microsystems, Inc.  All rights reserved.
18  *
19  *+ENOTICE
20  */
21
22 ///////////////////////////////////////////////////////////////////////////////
23 //////////////////////////////////////////////////////////////////////////////
24 //         This example code is from the book:
25 //
26 //           Object-Oriented Programming with C++ and OSF/Motif
27 //         by
28 //           Douglas Young
29 //           Prentice Hall, 1992
30 //           ISBN 0-13-630252-1 
31 //
32 //         Copyright 1991 by Prentice Hall
33 //         All Rights Reserved
34 //
35 //  Permission to use, copy, modify, and distribute this software for 
36 //  any purpose except publication and without fee is hereby granted, provided 
37 //  that the above copyright notice appear in all copies of the software.
38 ///////////////////////////////////////////////////////////////////////////////
39 //////////////////////////////////////////////////////////////////////////////
40
41
42 ////////////////////////////////////////////////////////////////////
43 // MainWindow.C: Support a toplevel window
44 ////////////////////////////////////////////////////////////////////
45 #include <assert.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <Xm/Protocols.h>
49 #include <Xm/AtomMgr.h>
50 #include <Xm/MainW.h>
51 #include <Dt/Wsm.h>
52 #include <Dt/Session.h>
53 #include <DtMail/IO.hh>
54 #include <X11/Shell.h>
55 #include <X11/Xmu/Editres.h>
56 #include "Application.h"
57 #include "MainWindow.h"
58 #include "Help.hh"
59
60 // The following headers are private to CDE and should NOT be required
61 // but unfortunately are.
62 //
63 extern "C" {
64 #include <Dt/HourGlass.h>
65 }
66 #include <Dt/Icon.h>
67 #include <Dt/IconP.h>
68 #include <Dt/IconFile.h>
69
70 // This is a private CDE function that should be public, but is not,
71 // and does not even have a prototype in a header. Yes, it is required.
72 //
73 extern "C" Pixmap _DtGetMask(Screen * screen, char * image_name);
74
75 #if 0
76 static const char * DefaultIcon = "Dtablnk";
77 #endif
78 static const unsigned long FLASH_INTERVAL = 250; // milliseconds
79
80 MainWindow::MainWindow( char *name, Boolean allowResize ) : UIComponent ( name )
81 {
82     _workArea = NULL;
83     _flashing = 0;
84     _icon_invert = NULL;
85     _window_invert = NULL;
86     _icon = 0;
87     _allow_resize = allowResize;
88
89     assert ( theApplication != NULL ); // Application object must exist
90     // before any MainWindow object
91     theApplication->registerWindow ( this );
92 }
93
94 void
95 MainWindow::initialize( )
96 {
97     char *appWorkspaces;
98
99     // All toplevel windows in the MotifApp framework are 
100     // implemented as a popup shell off the Application's
101     // base widget.
102     //
103     // XmNdeleteResponse is being set to DO_NOTHING so 
104     // that the user can Cancel their close request.
105
106     _w = XtVaCreatePopupShell ( _name, 
107                                 topLevelShellWidgetClass,
108                                 theApplication->baseWidget(),
109                                 XmNdeleteResponse, XmDO_NOTHING,
110                                 XmNallowShellResize, _allow_resize,
111                                 NULL, 0 );
112
113 #ifdef USE_EDITRES
114     XtAddEventHandler(
115                 _w, (EventMask) 0, True,
116                 (XtEventHandler) _XEditResCheckMessages, NULL);
117 #endif
118
119     installDestroyHandler();
120     
121     // Use a Motif XmMainWindow widget to handle window layout
122     
123     _main = XtCreateManagedWidget ( "mainWindow", 
124                                    xmMainWindowWidgetClass,
125                                    _w, 
126                                    NULL, 0 );
127     printHelpId("_main", _main);
128     /* install callback */
129     // XtAddCallback(_main, XmNhelpCallback, HelpCB, helpId);
130     XtAddCallback(_main, XmNhelpCallback, 
131                             HelpCB, "_HOMETOPIC");
132     
133     // Called derived class to create the work area
134     
135     _workArea = createWorkArea ( _main );  
136     assert ( _workArea != NULL );
137     
138     // Designate the _workArea widget as the XmMainWindow
139     // widget's XmNworkWindow widget
140     
141     XtVaSetValues ( _main, 
142                    XmNworkWindow, _workArea,
143                    NULL );
144
145     Atom WM_DELETE_WINDOW=XmInternAtom( XtDisplay( _w ),
146                                         "WM_DELETE_WINDOW",
147                                         False );
148
149     XmAddWMProtocolCallback( _w,
150                              WM_DELETE_WINDOW,
151                              ( XtCallbackProc ) quitCallback,
152                              this );
153
154 #if 0
155     // Why are we setting the icon to Dtablnk.  This is simply going to
156     // be overriden by some other function setting it to the appropriate 
157     // icon.
158     setIconName(DefaultIcon);
159 #endif
160
161     _window_invert = NULL;
162     _last_state = 0;
163     _flash_owin = (Window) NULL;
164     _flash_iwin = (Window) NULL;
165     memset((char*) &(this->_window_attributes), 0, sizeof(XWindowAttributes));
166
167     // Manage the work area if the derived class hasn't already.
168     
169     if ( !XtIsManaged ( _workArea ) )
170         XtManageChild ( _workArea ); 
171
172     XtRealizeWidget(_w);
173     appWorkspaces = theApplication->getAppWorkspaceList();
174     setWorkspacesOccupied(appWorkspaces);
175 }
176
177 MainWindow::~MainWindow( )
178 {
179     // Unregister this window with the Application object
180
181     if (_w) {
182         Atom WM_DELETE_WINDOW=XmInternAtom( XtDisplay( _w ),
183                                             "WM_DELETE_WINDOW",
184                                             False );
185         XmRemoveWMProtocolCallback( _w,
186                                     WM_DELETE_WINDOW,
187                                     ( XtCallbackProc ) quitCallback,
188                                     NULL );
189
190         if (_icon_invert) XFreeGC(XtDisplay(_w), _icon_invert);
191         if (_window_invert) XFreeGC(XtDisplay(_w), _window_invert);
192         if (_flash_iwin != (Window) NULL)
193           XDestroyWindow( XtDisplay(_w), _flash_iwin );
194         if (_flash_owin != (Window) NULL)
195           XDestroyWindow( XtDisplay(_w), _flash_owin );
196     }
197     
198     theApplication->unregisterWindow ( this );
199 }
200
201 void
202 MainWindow::enableWorkAreaResize()
203 {
204     XtVaSetValues(_workArea, XmNresizePolicy, XmRESIZE_ANY, NULL);
205 }
206
207 void
208 MainWindow::disableWorkAreaResize()
209 {
210     XtVaSetValues(_workArea, XmNresizePolicy, XmRESIZE_NONE, NULL);
211 }
212
213 void
214 MainWindow::manage()
215 {
216     assert ( _w != NULL );
217     XtPopup ( _w, XtGrabNone );
218     
219     // Map the window, in case the window is iconified
220
221     if ( XtIsRealized ( _w ) ) 
222         XMapRaised ( XtDisplay ( _w ), XtWindow ( _w ) );
223 }
224
225 void
226 MainWindow::unmanage()
227 {
228     assert ( _w != NULL );
229     XtPopdown ( _w );
230 }
231
232 void
233 MainWindow::iconify()
234 {
235     assert ( _w != NULL );
236     
237     // Set the widget to have an initial iconic state
238     // in case the base widget has not yet been realized
239     
240     XtVaSetValues ( _w, XmNiconic, TRUE, NULL );
241     
242     // If the widget has already been realized,
243     // iconify the window
244     
245     if ( XtIsRealized ( _w ) )
246         XIconifyWindow ( XtDisplay ( _w ), XtWindow ( _w ), 0 );
247 }
248
249 void
250 MainWindow::setIconTitle(const char * title)
251 {
252     XtVaSetValues(_w, XmNiconName, title, NULL);
253 }
254
255 void
256 MainWindow::setIconName(const char * path)
257 {
258     char * icon_filename = XmGetIconFileName(XtScreen(_w),
259                                              NULL,
260                                              (char *)path, // Bug!
261                                              NULL,
262                                              DtLARGE);
263
264     if (icon_filename == NULL) {
265         return;
266     }
267
268     Pixel fg = 0, bg = 0;
269
270     getIconColors(fg, bg);
271
272     _icon = XmGetPixmap(XtScreen(_w),
273                         icon_filename,
274                         fg, bg);
275
276     Pixmap icon_mask_map = _DtGetMask(XtScreen(_w), icon_filename);
277
278     if (!_icon || !icon_mask_map) {
279         return;
280     }
281
282     XtVaSetValues(_w,
283                   XmNiconPixmap, _icon,
284                   XmNiconMask, icon_mask_map,
285                   NULL);
286
287     // Build the inverted icon mask for flashing.
288     //
289     if (_icon_invert) {
290         XFreeGC(XtDisplay(_w), _icon_invert);
291     }
292
293     XGCValues   gc_vals;
294
295     gc_vals.foreground = bg;
296     gc_vals.function = GXxor;
297     _icon_invert = XCreateGC(XtDisplay(_w), _icon, GCForeground | GCFunction,
298                              &gc_vals);
299
300     XtFree(icon_filename);
301 }
302
303 void
304 MainWindow::busyCursor()
305 {
306     // Do nothing if the widget has not been realized
307
308     if (XtIsRealized(_w)) {
309         _DtTurnOnHourGlass(_w);
310     }
311 }
312
313 void
314 MainWindow::normalCursor()
315 {
316     // Do nothing if the widget has not been realized
317     
318     if (XtIsRealized ( _w ))
319     {
320         _DtTurnOffHourGlass(_w);
321     }
322 }
323
324 void
325 MainWindow::setStatus(const char *)
326 {
327     // Noop in our case.
328 }
329
330 void
331 MainWindow::clearStatus(void)
332 {
333     // Noop in our case.
334 }
335
336 void
337 MainWindow::title(const char *text )
338 {
339     XtVaSetValues ( _w, XmNtitle, (char *)text, NULL );
340 }
341
342 void
343 MainWindow::quitCallback( Widget,
344                           XtPointer clientData,
345                           XmAnyCallbackStruct *)
346 {
347     MainWindow *window=( MainWindow *) clientData;
348     window->quit();
349 }
350
351 void
352 MainWindow::getIconColors(Pixel & fore, Pixel & back)
353 {
354     XtVaGetValues (_w,
355                    XmNforeground, &fore,
356                    XmNbackground, &back,
357                    NULL);
358 }
359
360 struct WM_STATE {
361     int         state;
362     Window      icon;
363 };
364
365 static int
366 getWindowState(Widget w)
367 {
368     Atom        wmStateAtom, actualType;
369     int         actualFormat;
370     int         retval;
371     unsigned long nitems, leftover;
372     WM_STATE *wmState;
373  
374     /*  Getting the WM_STATE property to see if iconified or not */
375     wmStateAtom = XInternAtom(XtDisplay(w), "WM_STATE", False);
376  
377     XGetWindowProperty (XtDisplay(w), XtWindow(w), 
378                         wmStateAtom, 0L,
379                         (long)BUFSIZ, False, wmStateAtom, &actualType,
380                         &actualFormat, &nitems, &leftover,
381                         (unsigned char **) &wmState);
382
383     if (wmState)
384         retval = wmState->state;
385     else
386         retval = 0;
387
388     free((void*) wmState);
389     return retval;
390 }
391
392 void
393 MainWindow::flash(const int count)
394 {
395     XWindowAttributes   window_attributes;
396
397     if (count == 0) return;
398     if (_flashing > 0) return;
399
400     if (_window_invert == NULL) {
401         // Create a GC to flash the window.
402         //
403         XGCValues       gc_vals;
404         Pixel   fg, bg;
405         getIconColors(fg, bg);
406         
407         gc_vals.foreground = bg;
408         gc_vals.function = GXxor;
409         _window_invert = XCreateGC(XtDisplay(_w), XtWindow(_w),
410                                    GCForeground | GCFunction, &gc_vals);
411         XSetSubwindowMode(XtDisplay(_w), _window_invert, IncludeInferiors);
412     }
413
414     _last_state = getWindowState(_w);
415
416     //
417     //  The original method here, to invert the window and timeout
418     //  before inverting back to the original (pixels), breaks when
419     //  the window is left with pixels XOR'd in the flash ON state.
420     //
421     //  One quick fix, uses a transparent window (or windows) on top
422     //  of the window to be flashed.  The temp window(s) are used to
423     //  prevent updates while flash is ON and/or cause a full update
424     //  (expose) after each flash.
425     //
426     //  First, (this part optional) put an "InputOnly" window on top of
427     //  the window to be flashed and ignore all events to this window.
428     //  This has the effect of preventing user input (events) from
429     //  causing application updates to the window.  This temp window
430     //  can be left up (with the wait cursor) until flashing is done.  
431     //
432     //  Next, handle expose events by using a transparent "InputOutput"
433     //  window on top of everything only when flash is ON.  This has
434     //  the effect of preventing expose events from causing application
435     //  updates to the real window when flash is ON.  It especially,
436     //  ensures other problems (e.g. updates to the window caused by
437     //  other application timeout events and overlapping window pixels
438     //  from an expose event) are cleaned up by an expose event when
439     //  this temp window is unmapped or destroyed (between each flash).
440     //
441     
442     XGetWindowAttributes(XtDisplay(_w), XtWindow(_w), &window_attributes);
443
444     if ((Window) NULL != _flash_owin &&
445         (window_attributes.width != _window_attributes.width ||
446          window_attributes.height != _window_attributes.height ||
447          window_attributes.border_width != _window_attributes.border_width))
448     {
449         XDestroyWindow( XtDisplay(_w), _flash_iwin );
450         XDestroyWindow( XtDisplay(_w), _flash_owin );
451         _flash_iwin = (Window) NULL;
452         _flash_owin = (Window) NULL;
453     }
454
455     if ((Window) NULL == _flash_owin)
456     {
457         XSetWindowAttributes    sw_attr;
458
459         memcpy((char*) &(this->_window_attributes), 
460                (char*) &window_attributes,
461                sizeof(window_attributes));
462
463         sw_attr.event_mask = 0;
464         _flash_iwin = XCreateWindow(
465                         XtDisplay(_w), XtWindow(_w), 0, 0,
466                         _window_attributes.width, _window_attributes.height,
467                         _window_attributes.border_width, (int) CopyFromParent,
468                         InputOnly, CopyFromParent,
469                         CWEventMask, &sw_attr );
470         XMapWindow( XtDisplay(_w), _flash_iwin );
471         _flash_owin = XCreateWindow(
472                         XtDisplay(_w), XtWindow(_w), 0, 0,
473                         _window_attributes.width, _window_attributes.height,
474                         _window_attributes.border_width, (int) CopyFromParent,
475                         InputOutput, CopyFromParent,
476                         CWEventMask, &sw_attr );
477     }
478
479     _flashing = count * 2;
480     XtAppAddTimeOut(
481                 XtWidgetToApplicationContext(_w),
482                 FLASH_INTERVAL, flashCallback, this);
483 }
484
485 void
486 MainWindow::flashCallback(XtPointer client_data, XtIntervalId * interval_id)
487 {
488     MainWindow * mw = (MainWindow *)client_data;
489
490     mw->doFlash(interval_id);
491 }
492
493 void
494 MainWindow::doFlash(XtIntervalId *)
495 {
496     static int busy_cursor = 0;
497     int state = getWindowState(_w);
498
499     // We are going to make things flash an even number of times.
500     // to do this, we will lie about the state, and leave it at the
501     // old state for one iteration.
502     if (state != _last_state && (_flashing % 2) != 0)
503       state = _last_state;
504  
505     if (! busy_cursor) {
506         busyCursor();
507         busy_cursor = 1;
508     }
509
510     if (state == IconicState) {
511         Pixmap  image = _icon;
512
513         XFillRectangle(XtDisplay(_w), image, _icon_invert, 0, 0, 48, 48);
514         XtVaSetValues(_w, XmNiconPixmap, NULL, NULL);
515         XtVaSetValues(_w, XmNiconPixmap, image, NULL);
516     }
517     else if (state != 0) {
518
519         // Map temp window to prevent expose updates and other
520         // application event updates ... when flash is on.
521         if ( (_flashing % 2) == 0 )
522           XMapWindow( XtDisplay(_w), _flash_owin );
523
524         XFillRectangle(
525                 XtDisplay(_w), XtWindow(_w),
526                 _window_invert, 0, 0,
527                 _window_attributes.width, _window_attributes.height);
528
529         // Remove temp window to update display when flash is off.
530         if ( (_flashing % 2) != 0 )
531           XUnmapWindow( XtDisplay(_w), _flash_owin );
532     }
533
534     _flashing -= 1;
535
536     if (_flashing > 0) {
537         XtAppAddTimeOut(
538                         XtWidgetToApplicationContext(_w),
539                         FLASH_INTERVAL, flashCallback, this);
540         _last_state = state;
541     }
542     else {
543         XUnmapWindow( XtDisplay(_w), _flash_iwin );
544         XUnmapWindow( XtDisplay(_w), _flash_owin );
545         normalCursor();
546         busy_cursor = 0;
547     }
548 }
549
550 Boolean
551 MainWindow::isIconified()
552 {
553  
554     Atom        wmStateAtom, actualType;
555     int         actualFormat;
556     unsigned long nitems, leftover;
557     WM_STATE *wmState;
558     Boolean retval = FALSE;
559  
560     assert ( _w != NULL );
561
562     /*  Getting the WM_STATE property to see if iconified or not */
563     wmStateAtom = XInternAtom(XtDisplay(_w), "WM_STATE", False);
564  
565     XGetWindowProperty (XtDisplay(_w), XtWindow(_w), 
566                         wmStateAtom, 0L,
567                         (long)BUFSIZ, False, wmStateAtom, &actualType,
568                         &actualFormat, &nitems, &leftover,
569                         (unsigned char **) &wmState);
570
571
572     if (wmState && wmState->state == IconicState)
573       retval = TRUE;
574     
575     free((void*) wmState);
576     return retval;
577 }
578
579 /************************************************************************
580  * MbStrchr -
581  ************************************************************************/
582 char *
583 MainWindow::MbStrchr(char *str, int ch)
584 {
585     size_t mbCurMax = MB_CUR_MAX;
586     wchar_t targetChar, curChar;
587     char tmpChar;
588     int i, numBytes, byteLen;
589
590     if(mbCurMax <= 1) return strchr(str, ch);
591
592     tmpChar = (char)ch;
593     mbtowc(&targetChar, &tmpChar, mbCurMax);
594     for(i = 0, numBytes = 0, byteLen = strlen(str); i < byteLen; i += numBytes)
595     {
596         numBytes = mbtowc(&curChar, &str[i], mbCurMax);
597         if(curChar == targetChar) return &str[i];
598     }
599     return (char *)NULL;
600 }
601
602 void
603 MainWindow::setWorkspacesOccupied(char *workspaces)
604 {
605     char        *ptr;
606     Atom        *workspace_atoms = NULL;
607     int         nworkspaces=0;
608
609     if (workspaces)
610     {
611         do
612         {
613             ptr = MbStrchr (workspaces, ' ');
614
615             if (ptr != NULL) *ptr = '\0';
616
617             workspace_atoms = (Atom*) XtRealloc(
618                                                 (char *) workspace_atoms,
619                                                 sizeof(Atom)*(nworkspaces+1));
620             workspace_atoms[nworkspaces] = XmInternAtom(
621                                                 XtDisplay(_w),
622                                                 workspaces, True);
623             nworkspaces++;
624
625             if (ptr != NULL)
626             {
627                 *ptr = ' ';
628                 workspaces = ptr + 1;
629             }
630         } while (ptr != NULL);
631
632         DtWsmSetWorkspacesOccupied(
633                                 XtDisplay(_w), XtWindow (_w), 
634                                 workspace_atoms, nworkspaces);
635
636         XtFree ((char *) workspace_atoms);
637         workspace_atoms = NULL;
638     }
639     else
640     {
641         Window  rootWindow;
642         Atom    pCurrent;
643         Screen  *currentScreen;
644         int     screen;
645
646         screen = XDefaultScreen(XtDisplay(_w));
647         currentScreen = XScreenOfDisplay(XtDisplay(_w), screen);
648         rootWindow = RootWindowOfScreen(currentScreen);
649
650         if (DtWsmGetCurrentWorkspace(
651                                 XtDisplay(_w),
652                                 rootWindow,
653                                 &pCurrent) == Success)
654           DtWsmSetWorkspacesOccupied(
655                                 XtDisplay(_w),
656                                 XtWindow(_w), 
657                                 &pCurrent, 1);
658     }
659 }