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