Merge branch 'master' into cde-next
[oweals/cde.git] / cde / programs / dtwm / WmWinState.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 libraries 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 /* 
24  * (c) Copyright 1989, 1990, 1991, 1992 OPEN SOFTWARE FOUNDATION, INC. 
25  * ALL RIGHTS RESERVED 
26 */ 
27 /* 
28  * Motif Release 1.2.1
29 */
30 /*
31  * (c) Copyright 1987, 1988, 1989, 1990 HEWLETT-PACKARD COMPANY */
32
33 /*
34  * Included Files:
35  */
36
37 #include "WmGlobal.h"
38 #include "WmICCC.h"
39 #include "WmProtocol.h"
40 #include "WmIPC.h"
41
42
43 /*
44  * include extern functions
45  */
46
47 #include "WmCDecor.h"
48 #include "WmCDInfo.h"
49 #include "WmFunction.h"
50 #include "WmIDecor.h"
51 #include "WmIPlace.h"
52 #include "WmIconBox.h"
53 #include "WmKeyFocus.h"
54 #include "WmPanelP.h"  /* for typedef in WmManage.h */
55 #include "WmManage.h"
56 #include "WmProperty.h"
57 #include "WmWinInfo.h"
58 #include "WmWinList.h"
59 #include "WmWrkspace.h"
60
61
62 /*
63  * Function Declarations:
64  */
65
66 #include "WmMultiHead.h"
67 #include "WmWinState.h"
68 static void SlideWindowOut (ClientData *pCD);
69 static void UnmapClients (ClientData *pCD, unsigned int event_mask);
70 static void SetupWindowStateWithEventMask (ClientData *pCD, int newState, Time setTime, unsigned int event_mask);
71
72
73
74 /*
75  * Global Variables:
76  */
77 extern int firstTime;
78
79 \f
80 /******************************<->*************************************
81  *
82  *  SetClientState (pCD, newState, setTime)
83  *
84  *
85  *  Description:
86  *  -----------
87  *  This function is used to change the state of a client window (between
88  *  withdrawn, normal, minimized, maximized).
89  *
90  *
91  *  Inputs:
92  *  ------
93  *  pCD = This is a pointer to the window data for the window that
94  *        is to have its state changed. The fields that are used
95  *        are clientState, ...
96  *
97  *  newState = This is the state that the client window is to be changed to.
98  *
99  *  setTime = timestamp for state setting operations
100  *
101  * 
102  *  Outputs:
103  *  -------
104  *  pCD.clientState = new client state
105  *
106  ******************************<->***********************************/
107
108 void SetClientState (ClientData *pCD, int newState, Time setTime)
109 {
110         SetClientStateWithEventMask(pCD, newState, setTime, (unsigned int)0);
111 } /* END OF FUNCTION SetClientState */
112
113 void SetClientStateWithEventMask (ClientData *pCD, int newState, Time setTime, unsigned int event_mask)
114 {
115     ClientData *pcdLeader;
116     int currentState;
117     WmScreenData *pSD = PSD_FOR_CLIENT(pCD);
118     Boolean notShowing = (newState & UNSEEN_STATE);
119
120     currentState = pCD->clientState;
121     if (currentState == newState)
122     {
123         /* no change in state */
124         return;
125     }
126
127
128     /*
129      * Undo the old state and setup the new state.  If this is a transient
130      * window then insure that it is put in a state that is compatible
131      * with its transient leader (e.g., it cannot be minimized separately).
132      */
133
134     pcdLeader = (pCD->transientLeader) ? FindTransientTreeLeader (pCD) : pCD;
135     SetClientWsIndex (pCD);
136
137     if (pCD->transientLeader)
138     {
139         if ((pcdLeader->clientState == MINIMIZED_STATE) &&
140             (newState != WITHDRAWN_STATE))
141         {
142             newState = MINIMIZED_STATE;
143             if (notShowing)
144             {
145                 newState |= UNSEEN_STATE;
146             }
147         }
148         else if ((newState == MINIMIZED_STATE) &&
149                  (pcdLeader->clientState != MINIMIZED_STATE))
150         {
151             if (currentState == WITHDRAWN_STATE)
152             {
153                 newState = NORMAL_STATE;
154             if (notShowing)
155             {
156                 newState |= UNSEEN_STATE;
157             }
158             }
159             else
160             {
161                 newState = currentState;
162             if (notShowing)
163             {
164                 newState |= UNSEEN_STATE;
165             }
166             }
167         }
168         if (newState == currentState)
169         {
170             return;
171         }
172     }
173
174     switch (newState)
175     {
176
177         case UNSEEN_STATE | WITHDRAWN_STATE:
178         {
179             /*
180              * Free window manager resources (frame and icon).  The
181              * WM_STATE property is set in WithdrawWindow.
182              */
183
184             UnManageWindow (pCD);
185             break;
186         }
187
188         case NORMAL_STATE:
189         case MAXIMIZED_STATE:
190         {
191             SetupWindowStateWithEventMask (pCD, newState, setTime, event_mask);
192             XMapWindow (DISPLAY, pCD->client);
193             XMapWindow (DISPLAY, pCD->clientFrameWin);
194             WmStopWaiting();   /* in WmIPC.c */
195             break;
196         }
197
198         case MINIMIZED_STATE:
199         {
200             Boolean clientHasFocus;
201
202             /*
203              * Transient windows are minimized with the rest of the transient
204              * tree, including the transient leader.
205              */
206
207             if ((pCD->clientState == NORMAL_STATE) ||
208                 (pCD->clientState == MAXIMIZED_STATE))
209             {
210                 if ((wmGD.keyboardFocus == pCD) ||
211                     (pCD->transientChildren && wmGD.keyboardFocus &&
212                      (pCD == FindTransientTreeLeader (wmGD.keyboardFocus))))
213                 {
214                     clientHasFocus = True;
215                 }
216                 else
217                 {
218                     clientHasFocus = False;
219                 }
220
221                 if (clientHasFocus ||
222                   ((wmGD.nextKeyboardFocus == pCD) ||
223                    (pCD->transientChildren && wmGD.keyboardFocus &&
224                     (pCD == FindTransientTreeLeader (wmGD.nextKeyboardFocus)))))
225                 {
226                     /*
227                      * Give up the keyboard focus when minimized (including
228                      * the case in which an associated transient window has
229                      * the focus).  Immediately remove the focus indication
230                      * from the window being minimized.
231                      */
232
233                     if (wmGD.autoKeyFocus &&
234                         (wmGD.keyboardFocusPolicy == KEYBOARD_FOCUS_EXPLICIT))
235                     {
236                         AutoResetKeyFocus (pcdLeader, setTime);
237                     }
238                     else
239                     {
240                         Do_Focus_Key (NULL, setTime, 
241                                 ALWAYS_SET_FOCUS | WORKSPACE_IF_NULL);
242                     }
243
244                     if (clientHasFocus)
245                     {
246                         SetKeyboardFocus (NULL, 0);
247                     }
248                 }
249
250                 /* unmap main client and all transients */
251                 UnmapClients (pCD, event_mask);
252             }
253
254             /*
255              * Display the icon for the minimized client.
256              */
257
258             if (ICON_FRAME_WIN(pCD)) 
259             {
260                 if (pCD->clientState & UNSEEN_STATE)
261                 {
262                     if (pCD->iconWindow)
263                     {
264                         XMapWindow (DISPLAY, pCD->iconWindow);
265                     }
266                     XMapWindow (DISPLAY, ICON_FRAME_WIN(pCD));
267                 }
268
269                 ShowAllIconsForMinimizedClient (pCD);
270             }
271
272             SetClientWMState (pCD, IconicState, MINIMIZED_STATE);
273
274             if ((pSD->useIconBox) && P_ICON_BOX(pCD))
275             {
276                 if ((pCD->clientFlags & ICON_BOX) && ACTIVE_ICON_TEXT_WIN)
277                 {
278                     /*
279                      * Hide active icon text window and reparent it to
280                      * root
281                      */
282                     HideActiveIconText((WmScreenData *)NULL);
283                     pSD->activeLabelParent = ACTIVE_ROOT;
284                     XReparentWindow(DISPLAY, ACTIVE_ICON_TEXT_WIN , 
285                                 ACTIVE_ROOT, 0, 0 );
286                 }
287                 if (ICON_FRAME_WIN(pCD))
288                 {
289                     /* 
290                      * force icon appearance in icon box to change 
291                      */
292                     IconExposureProc (pCD, True);
293                 }
294             }
295             break;
296         }
297
298
299         case UNSEEN_STATE | NORMAL_STATE:
300         case UNSEEN_STATE | MAXIMIZED_STATE:
301         case UNSEEN_STATE | MINIMIZED_STATE:
302         {
303             if (wmGD.keyboardFocus == pCD)
304             {
305                 /*
306                  * Give up the keyboard focus 
307                  */
308                 Do_Focus_Key ((ClientData *)NULL, 
309                         CurrentTime, ALWAYS_SET_FOCUS);
310                 SetKeyboardFocus (NULL, 0);
311             }
312
313             if (!(pCD->clientState & UNSEEN_STATE) &&
314                  (((pCD->clientState & ~UNSEEN_STATE) == NORMAL_STATE) ||
315                   ((pCD->clientState & ~UNSEEN_STATE) == MAXIMIZED_STATE)))
316             {
317                 /* unmap main client and all transients */
318                 UnmapClients (pcdLeader, event_mask);
319
320             }
321       
322             if (pCD->clientFrameWin) 
323             {
324                 if (!P_ICON_BOX(pCD))
325                 {
326                     if (ICON_FRAME_WIN(pCD))
327                     {
328                         XUnmapWindow (DISPLAY, ICON_FRAME_WIN(pCD));
329                     }
330                     if (pCD->iconWindow)
331                         XUnmapWindow (DISPLAY, pCD->iconWindow);
332                 } 
333             }
334
335             switch (newState & ~UNSEEN_STATE)
336             {
337             case MINIMIZED_STATE:
338                 SetClientWMState (pCD, IconicState, newState);
339                 break;
340
341             case NORMAL_STATE:
342             case MAXIMIZED_STATE:
343             default:
344                 SetClientWMState (pCD, NormalState, newState);
345                 break;
346             }
347         }
348         break;
349     }
350
351 } /* END OF FUNCTION SetClientStateWithEventMask */
352
353
354 \f
355 /*************************************<->*************************************
356  *
357  *  SetupWindowStateWithEventMask (pCD, newState, setTime, event_mask)
358  *
359  *
360  *  Description:
361  *  -----------
362  *  This function is used to setup a client window in the Normal or Maximized
363  *  state.
364  *
365  *
366  *  Inputs:
367  *  ------
368  *  pCD = This is a pointer to the window data for the window that
369  *        is to have its state changed.
370  *
371  *  newState = This is the state that the client window is to be changed to.
372  *
373  *  setTime = timestamp for state setting operations
374  *
375  *  event_mask = what to grab to prevent stray events going somewhere
376  * 
377  *  Outputs:
378  *  -------
379  *  pCD.clientState = new client state
380  *
381  *************************************<->***********************************/
382
383 static void SetupWindowStateWithEventMask (ClientData *pCD, int newState, 
384         Time setTime, unsigned int event_mask)
385 {
386     int currentState;
387     int wsI, iplace;
388     WmWorkspaceData *pWS_i;
389     WmScreenData *pSD = PSD_FOR_CLIENT(pCD);
390
391     currentState = pCD->clientState;
392
393     /*
394      * A transient window is not restored or maximized if the transient leader
395      * is minimized.
396      */
397
398     if (newState == NORMAL_STATE)
399     {
400         if (pCD->maxConfig == True)
401         {
402             /*
403              * The configuration function uses maxConfig to determine
404              * what the current configuration is (and then resets
405              * maxConfig) and uses the state paramenter to determine
406              * what the new configuration is.
407              */
408
409             ConfigureNewState (pCD); 
410         }
411     }
412     else /* MAXIMIZED_STATE */
413     {
414         if (pCD->maxConfig == False)
415         {
416             ConfigureNewState (pCD); 
417         }
418     }
419
420     if (currentState == MINIMIZED_STATE)
421     {
422         Boolean clearIconFocus;
423
424         /*
425          * give up keyboard focus 
426          */
427
428         if ((wmGD.keyboardFocus == pCD) ||
429             (wmGD.nextKeyboardFocus == pCD))
430         {
431             Do_Focus_Key (NULL, setTime, ALWAYS_SET_FOCUS | WORKSPACE_IF_NULL);
432         }
433
434         if (wmGD.keyboardFocus == pCD)
435         {
436             clearIconFocus = True;
437         }
438         else
439         {
440             clearIconFocus = False;
441         }
442
443         /*
444          * The wm icon frame window and the client icon window
445          * (if it is being used) are mapped and the client window and
446          * client frame are unmapped.
447          */
448
449         if (ICON_FRAME_WIN(pCD))
450         {
451             if (pSD->useIconBox && P_ICON_BOX(pCD) && 
452                 !(pCD->clientFlags & ICON_BOX))
453             {
454                 ShowClientIconState(pCD, newState);
455             }
456             else 
457             {
458                 Boolean doGrab = False;
459                 if (event_mask)
460                 doGrab = (Success == XGrabPointer 
461                         (DISPLAY, DefaultRootWindow(DISPLAY),
462                         False, event_mask, GrabModeAsync, GrabModeAsync,
463                         None, None, CurrentTime));
464                 XUnmapWindow (DISPLAY, ICON_FRAME_WIN(pCD));
465                 if (pCD->iconWindow)
466                 {
467                     XUnmapWindow (DISPLAY, pCD->iconWindow);
468                 }
469                 if (event_mask && doGrab)
470                 {
471                         XEvent event;
472                         XMaskEvent(DISPLAY, event_mask, &event);
473                         XUngrabPointer(DISPLAY,CurrentTime);
474                 }
475                 if (wmGD.iconAutoPlace) 
476                 {
477                     for (wsI = 0; wsI < pCD->numInhabited; wsI++)
478                     {
479                         iplace = pCD->pWsList[wsI].iconPlace;
480                         if (iplace != NO_ICON_PLACE)
481                         {
482                             pWS_i = GetWorkspaceData (pCD->pSD,
483                                                 pCD->pWsList[wsI].wsID);
484                             pWS_i->IPData.placeList[iplace].pCD = 
485                                     NULL;
486                         }
487                     }
488                 }
489             }
490
491             if (clearIconFocus)
492             {
493                 ClearFocusIndication (pCD, False /*no refresh*/);
494                 wmGD.keyboardFocus = NULL;
495             }
496         }
497     }
498     if ((currentState != NORMAL_STATE) && (currentState != MAXIMIZED_STATE))
499     {
500         /*
501          * Note that maximized state is considered a NormalState in
502          * the ICCC.  SetClientWMState also sets the state in the
503          * client data.
504          */
505
506         if (currentState == MINIMIZED_STATE)
507         {
508             /*
509              * Raise the window(s) when they are deiconified.
510              */
511
512             pCD->clientState = newState;
513                     wmGD.bSuspendSecondaryRestack = True;
514             F_Raise (NULL, pCD, NULL);
515                     wmGD.bSuspendSecondaryRestack = False;
516         }
517
518         if ( (!(pCD->clientFlags & ICON_BOX)) || 
519              ((pCD->clientFlags & ICON_BOX) && (!(firstTime))) )
520         {
521           if ((currentState == WITHDRAWN_STATE) && 
522               (pCD->dtwmBehaviors & DtWM_BEHAVIOR_SUBPANEL) &&
523               !(pCD->transientChildren))
524           {
525               if (pCD->dtwmBehaviors & DtWM_BEHAVIOR_SUB_RESTORED)
526               {
527                   pCD->dtwmBehaviors &= ~DtWM_BEHAVIOR_SUB_RESTORED;
528                   pCD->dtwmBehaviors &= ~DtWM_BEHAVIOR_SUBPANEL;
529                   XMapWindow (DISPLAY, pCD->client);
530                   XMapWindow (DISPLAY, pCD->clientFrameWin);
531               }
532               else
533               {
534                   SlideWindowOut (pCD);
535               }
536           }
537           else
538             MapClientWindows (pCD);
539         }
540
541
542         /*
543          * Set the WM_STATE property of the window and any associated
544          * transients, along with the clientState value.  The call
545          * is made with an indication of NORMAL_STATE to insure
546          * that transient window clientState values are setup
547          * correctly.  The top-level window clientState is set later.
548          */
549
550         SetClientWMState (pCD, NormalState, NORMAL_STATE);
551     }
552     pCD->clientState = newState;
553
554     if ((wmGD.keyboardFocusPolicy == KEYBOARD_FOCUS_EXPLICIT) &&
555         (currentState == MINIMIZED_STATE) && wmGD.deiconifyKeyFocus)
556     {
557         ClientData *pcdFocus;
558
559         pcdFocus = FindTransientFocus (pCD);
560         if (pcdFocus)
561         {
562             Do_Focus_Key (pcdFocus, setTime, ALWAYS_SET_FOCUS);
563         }
564     }
565
566     if ( pSD->useIconBox &&  P_ICON_BOX(pCD) &&
567          (!(pCD->clientFlags & ICON_BOX)) && (ICON_FRAME_WIN(pCD)))
568     {
569         /* 
570          * force icon appearance in icon box to change 
571          */
572
573         IconExposureProc (pCD, True);
574     }
575
576 } /* END OF FUNCTION SetupWindowStateWithEventMask */
577
578
579
580 \f
581 /*************************************<->*************************************
582  *
583  *  ConfigureNewState (pcd)
584  *
585  *
586  *  Description:
587  *  -----------
588  *  Configure the window to a new state
589  *
590  *
591  *  Inputs:
592  *  ------
593  *  pcd         - pointer to client data
594  * 
595  *  Outputs:
596  *  -------
597  *
598  *
599  *  Comments:
600  *  --------
601  *  o This is only good for going between NORMAL and MAXIMIZED state.
602  * 
603  *************************************<->***********************************/
604
605 void ConfigureNewState (ClientData *pcd)
606 {
607     WmHeadInfo_t *WmHI = NULL;
608
609     if (pcd->maxConfig)
610     {
611         pcd->maxConfig = FALSE;
612         RegenerateClientFrame(pcd);
613         XResizeWindow (DISPLAY, pcd->client,
614                            (unsigned int) pcd->clientWidth, 
615                            (unsigned int) pcd->clientHeight);
616     }
617     else
618     {
619     /*
620      * Update client config to reflect underlying head, if MultiHead is active
621      */
622     if (WmHI = GetHeadInfo(pcd)) {
623         FrameToClient(pcd, &WmHI->x_org, &WmHI->y_org,
624                 &WmHI->width, &WmHI->height);
625         pcd->maxX = WmHI->x_org;
626         pcd->maxY = WmHI->y_org;
627         pcd->maxWidth = WmHI->width;
628         pcd->maxHeight = WmHI->height;
629     }
630
631         XResizeWindow (DISPLAY, pcd->client,
632                            (unsigned int) pcd->maxWidth, 
633                            (unsigned int) pcd->maxHeight);
634         pcd->maxConfig = TRUE;
635         RegenerateClientFrame(pcd);
636     }
637     SendConfigureNotify (pcd);
638
639     /*
640      * Force repaint if size doesn't change to update frame appearance.
641      */
642
643     if ((pcd->clientWidth == pcd->maxWidth) &&
644         (pcd->clientHeight == pcd->maxHeight))
645     {
646         FrameExposureProc (pcd);
647     }
648
649 } /* END OF FUNCTION ConfigureNewState */
650
651
652 \f
653 /*************************************<->*************************************
654  *
655  *  UnmapClients (pCD, event_mask)
656  *
657  *
658  *  Description:
659  *  -----------
660  *  Unmap the window(s).  The indicated client may be the head of a transient
661  *  tree - if it is unmap all windows in the transient tree.
662  *
663  *
664  *  Inputs:
665  *  ------
666  *  pCD = pointer to client data of window(s) to be unmapped
667  *  event_mask = what to grab to prevent stray events going somewhere. Our
668  *      passive grab has just been activated -- but it is dropped when the
669  *      window is unmapped and the ButtonRelease event can go to the window
670  *      now exposed. Avoid this by grabbing the ButtonRelease before the unmap
671  *      and swallowing it.
672  *      Also done for icon being unmapped.
673  *
674  *************************************<->***********************************/
675
676 static void UnmapClients (ClientData *pCD, unsigned int event_mask)
677 {
678     ClientData *pNext;
679     Boolean doGrab = False;
680
681     pNext = pCD->transientChildren;
682     while (pNext)
683     {
684         /* unmap all children first */
685         if (pNext->transientChildren)
686             UnmapClients (pNext, (unsigned int) 0);
687
688         /* then unmap all siblings at this level */
689         XUnmapWindow (DISPLAY, pNext->clientFrameWin);
690         XUnmapWindow (DISPLAY, pNext->client);
691         pNext->wmUnmapCount++;
692         pNext = pNext->transientSiblings;
693     }
694
695     if (event_mask)
696         doGrab = (Success == XGrabPointer (DISPLAY, DefaultRootWindow(DISPLAY),
697                 False, event_mask, GrabModeAsync, GrabModeAsync,
698                 None, None, CurrentTime));
699     /* unmap this primary window */
700     XUnmapWindow (DISPLAY, pCD->clientFrameWin); 
701     XUnmapWindow (DISPLAY, pCD->client);
702     if (event_mask && doGrab)
703         {
704         XEvent event;
705         XMaskEvent(DISPLAY, event_mask, &event);
706         XUngrabPointer(DISPLAY,CurrentTime);
707         }
708     pCD->wmUnmapCount++;
709
710 } /* END OF FUNCTION UnmapClients */
711
712
713 \f
714 /*************************************<->*************************************
715  *
716  *  SetClientWMState (pCD, wmState, mwmState)
717  *
718  *
719  *  Description:
720  *  -----------
721  *  Set a new window manage state for a client window or a tree of transient
722  *  client windows.
723  *
724  *  Inputs:
725  *  ------
726  *  pCD = pointer to  client data
727  *
728  *  wmState = new state for WM_STATE property
729  *
730  *  mwmState = mwm client state
731  *
732  *************************************<->***********************************/
733
734 void SetClientWMState (ClientData *pCD, int wmState, int mwmState)
735 {
736     ClientData *pNext;
737     Boolean bToUnseen;
738
739     bToUnseen = (mwmState & UNSEEN_STATE) != 0;
740     mwmState &= ~UNSEEN_STATE;
741
742     SetClientWsIndex (pCD);
743     pNext = pCD->transientChildren;
744     while (pNext)
745     {
746         if (pNext->transientChildren)
747         {
748             SetClientWMState (pNext, wmState, mwmState);
749         }
750
751         SetClientWsIndex (pNext);
752         SetWMState (pNext->client, wmState, ICON_FRAME_WIN(pNext));
753         if (pNext->maxConfig && mwmState == NORMAL_STATE)
754         {
755             pNext->clientState = MAXIMIZED_STATE;
756         }
757         else if (!pNext->maxConfig && mwmState == MAXIMIZED_STATE)
758         {
759             pNext->clientState = NORMAL_STATE;
760         }
761         else
762         {
763             pNext->clientState = mwmState;
764         }
765         if (bToUnseen)
766             pNext->clientState |= UNSEEN_STATE;
767         pNext = pNext->transientSiblings;
768     }
769
770     SetWMState (pCD->client, wmState, ICON_FRAME_WIN(pCD));
771     pCD->clientState = mwmState;
772     if (bToUnseen)
773         pCD->clientState |= UNSEEN_STATE;
774
775 } /* END OF FUNCTION SetClientWMState */
776
777 #define SLIDE_UP_PERCENTAGE     5
778 #define SLIDE_UP_DIVISOR        (100/SLIDE_UP_PERCENTAGE)
779 #define SLIDE_UP_INTERVAL       15
780
781 /******************************<->*************************************
782  *
783  * void SlideOutTimerProc (client_data, id)
784  *
785  *  Description:
786  *  -----------
787  *  An XtTimerCallbackProc to process slide up mapping of a panel
788  *
789  *  Inputs:
790  *  ------
791  *  client_data = pointer to a SlideOutRec
792  * 
793  *  Outputs:
794  *  -------
795  *  
796  *
797  *  Comments:
798  *  --------
799  ******************************<->***********************************/
800 void
801 SlideOutTimerProc ( XtPointer client_data, XtIntervalId *id)
802 {
803     SlideOutRec *pSOR = (SlideOutRec *) client_data;
804     Boolean bDone = False;
805
806     if (pSOR)
807     {
808         /*
809          * compute next increment;
810          */
811         switch (pSOR->direction)
812         {
813             case SLIDE_NORTH:
814                 if (pSOR->mapping)
815                 {
816                     pSOR->currY -= pSOR->incHeight;
817                     pSOR->currHeight += pSOR->incHeight;
818                     if ((pSOR->currY < pSOR->pCD->frameInfo.y) ||
819                         (pSOR->currHeight > pSOR->pCD->frameInfo.height))
820                     {
821                         pSOR->currY = pSOR->pCD->frameInfo.y;
822                         pSOR->currHeight = pSOR->pCD->frameInfo.height;
823                     }
824                     bDone = (pSOR->currY == pSOR->pCD->frameInfo.y);
825                 }
826                 else 
827                 {
828                     pSOR->currY += pSOR->incHeight;
829                     if (pSOR->incHeight >= pSOR->currHeight)
830                     {
831                         pSOR->currHeight = 0;
832                         bDone = True;
833                     }
834                     else
835                     {
836                         pSOR->currHeight -= pSOR->incHeight;
837                     }
838                 }
839                 break;
840
841             case SLIDE_SOUTH:
842                 if (pSOR->mapping)
843                 {
844                     pSOR->currHeight += pSOR->incHeight;
845                     if (pSOR->currHeight > pSOR->pCD->frameInfo.height)
846                     {
847                         pSOR->currHeight = pSOR->pCD->frameInfo.height;
848                     }
849                     bDone = 
850                       (pSOR->currHeight == pSOR->pCD->frameInfo.height);
851                 }
852                 else
853                 {
854                     if (pSOR->incHeight >= pSOR->currHeight)
855                     {
856                         pSOR->currHeight = 0;
857                         bDone = True;
858                     }
859                     else
860                     {
861                         pSOR->currHeight -= pSOR->incHeight;
862                     }
863                 }
864                 break;
865         }
866
867         /*
868          * do next slide-up
869          */
870         if (pSOR->currHeight > 0)
871         {
872             XMoveResizeWindow (DISPLAY, pSOR->coverWin, 
873                 pSOR->currX, pSOR->currY,
874                 pSOR->currWidth, pSOR->currHeight);
875
876             XMoveResizeWindow (DISPLAY, pSOR->pCD->clientFrameWin, 
877                 pSOR->currX, pSOR->currY, 
878                 pSOR->currWidth, pSOR->currHeight);
879         }
880         
881         /*
882          * See if we need to continue
883          */
884         if (bDone)
885         {
886             if (!pSOR->mapping)
887             {
888                 /* Time to really unmanage the slide-up */
889                 XtUnmanageChild (pSOR->wSubpanel);
890             }
891             else
892             {
893                 WmSubpanelPosted (DISPLAY1, pSOR->pCD->client);
894                 SendConfigureNotify(pSOR->pCD);
895             }
896
897             /* done! clean up */
898             XDestroyWindow (DISPLAY, pSOR->coverWin);
899             pSOR->pCD->pSOR = NULL;
900             XtFree ((char *)pSOR);
901             wmGD.iSlideUpsInProgress -= 1;
902         }
903         else
904         {
905             /* re-arm the timer */
906             XtAppAddTimeOut(wmGD.mwmAppContext, pSOR->interval,
907                             SlideOutTimerProc, (XtPointer)pSOR);
908             XSync (DISPLAY, False);
909         }
910     }
911
912 } /* END OF FUNCTION SlideOutTimerProc */
913
914
915 \f
916 /*************************************<->*************************************
917  *
918  *  SlideWindowOut (pCD)
919  *
920  *
921  *  Description:
922  *  -----------
923  *  Maps a window with a slide-out effect.
924  *
925  *
926  *  Inputs:
927  *  ------
928  *  pCD = pointer to  client data
929  *
930  *  Comment:
931  *  -------
932  *  Only supports slide-up or slide-down 
933  * 
934  *************************************<->***********************************/
935
936 static void
937 SlideWindowOut (ClientData *pCD)
938 {
939     SlideOutRec *pSOR;
940
941     if (pCD->pSOR)
942     {
943         pSOR = pCD->pSOR;
944
945         /*
946          *  Hmmm. We're already sliding this window.
947          *  If we're supposed to go in the other direction,
948          *  then turn it around.
949          */
950         if (pSOR->mapping == True)
951         {
952             /*
953              * We're already mapping this guy, ignore this
954              * and finish what we've already got going.
955              */
956             return;
957         }
958         else
959         {
960             /*
961              * We're not mapping this guy. Reverse course!!
962              */
963             pSOR->mapping = True;
964
965             /* insure the client window is mapped */
966             XMapWindow (DISPLAY, pCD->client);
967
968             /* handle the rest on the next timeout */
969             return;
970         }
971     }
972
973     /* map the primary window */
974     XMapWindow (DISPLAY, pCD->client);
975     pSOR = (SlideOutRec *) XtMalloc (sizeof(SlideOutRec));
976     if (pSOR)
977     {
978         /*
979          * Compute this ahead of time so we can check against
980          * the window size. If the window is short, we'll
981          * just map it, avoiding a lot of processing.
982          */
983         pSOR->incHeight = (Dimension) (DisplayHeight(DISPLAY, 
984                         SCREEN_FOR_CLIENT(pCD))/SLIDE_UP_DIVISOR);
985     }
986
987     if ((pCD->slideDirection != SLIDE_NOT) && pSOR &&
988         (pSOR->incHeight < pCD->frameInfo.height))
989     {
990         XSetWindowAttributes window_attribs;
991         XWindowChanges window_changes;
992         unsigned long mask;
993
994         /* 
995          * Set up data for processing slide up
996          */
997         pSOR->pCD = pCD;
998         pSOR->interval = SLIDE_UP_INTERVAL;
999         pSOR->direction = pCD->slideDirection;
1000         pSOR->mapping = True;
1001         pSOR->wSubpanel = NULL;
1002         pSOR->pCD->pSOR = pSOR;
1003         
1004         switch (pSOR->direction)
1005         {
1006             case SLIDE_NORTH:
1007                 pSOR->incWidth = 0;
1008                 pSOR->currWidth = pCD->frameInfo.width;
1009                 pSOR->currHeight = pSOR->incHeight;
1010                 pSOR->currX = pCD->frameInfo.x;
1011                 pSOR->currY = pCD->frameInfo.y + 
1012                     (pCD->frameInfo.height - pSOR->currHeight);
1013                 break;
1014
1015             case SLIDE_SOUTH:
1016                 pSOR->incWidth = 0;
1017                 pSOR->currWidth = pCD->frameInfo.width;
1018                 pSOR->currHeight = pSOR->incHeight;
1019                 pSOR->currX = pCD->frameInfo.x;
1020                 pSOR->currY = pCD->frameInfo.y;
1021                 break;
1022         }
1023
1024         /*
1025          * Create screening window to hide the slide-up from button
1026          * events until it is all the way up.
1027          */
1028         mask = CWOverrideRedirect;
1029         window_attribs.override_redirect = True;
1030         pSOR->coverWin = XCreateWindow(DISPLAY,
1031                             RootWindow (DISPLAY, SCREEN_FOR_CLIENT(pCD)),
1032                             pSOR->currX, pSOR->currY, 
1033                             pSOR->currWidth, pSOR->currHeight, 0,
1034                             CopyFromParent,InputOnly,CopyFromParent,
1035                             mask, &window_attribs);
1036
1037         /* 
1038          * Put screen window above the slide-up client
1039          */
1040         mask = CWStackMode | CWSibling;
1041         window_changes.stack_mode = Above;
1042         window_changes.sibling = pCD->clientFrameWin;
1043         XConfigureWindow (DISPLAY, pSOR->coverWin, mask, &window_changes);
1044
1045         /*
1046          * Start slide-up processing
1047          */ 
1048         XMoveResizeWindow (DISPLAY, pSOR->coverWin, pSOR->currX, pSOR->currY,
1049             pSOR->currWidth, pSOR->currHeight);
1050         XMoveResizeWindow (DISPLAY, pCD->clientFrameWin, 
1051             pSOR->currX, pSOR->currY, pSOR->currWidth, pSOR->currHeight);
1052         XMapWindow (DISPLAY, pSOR->coverWin);
1053         XMapWindow (DISPLAY, pCD->clientFrameWin);
1054         XSync (DISPLAY, False);
1055
1056         XtAppAddTimeOut(wmGD.mwmAppContext, pSOR->interval,
1057                         SlideOutTimerProc, (XtPointer)pSOR);
1058         
1059         wmGD.iSlideUpsInProgress += 1;
1060
1061     }
1062     else
1063     {
1064         /*
1065          * Not sliding because no direction specified or our window
1066          * is just a little guy.
1067          */
1068         XMapWindow (DISPLAY, pCD->clientFrameWin);
1069         if (pSOR) 
1070         {
1071             XtFree ((char *) pSOR);
1072             pCD->pSOR = NULL;
1073         }
1074     }
1075
1076 } /* END OF FUNCTION SlideOutWindow */
1077
1078
1079 \f
1080 /*************************************<->*************************************
1081  *
1082  *  SlideSubpanelBackIn (pCD, wSubpanel)
1083  *
1084  *
1085  *  Description:
1086  *  -----------
1087  *  Slides a subpanel back in
1088  *
1089  *
1090  *  Inputs:
1091  *  ------
1092  *  pCD = pointer to  client data
1093  *  wSubpanel = subpanel widget to unmanage
1094  *
1095  *  Comment:
1096  *  -------
1097  * 
1098  *************************************<->***********************************/
1099
1100 void
1101 SlideSubpanelBackIn (ClientData *pCD, Widget wSubpanel)
1102 {
1103     SlideOutRec *pSOR;
1104
1105     if (pCD->pSOR)
1106     {
1107         pSOR = pCD->pSOR;
1108
1109         /*
1110          *  Hmmm. We're already sliding this window.
1111          *  If we're supposed to go in the other direction,
1112          *  then turn it around.
1113          */
1114         if (pSOR->mapping == False)
1115         {
1116             /*
1117              * We're already unmapping this guy, ignore this
1118              * and finish what we've already got going.
1119              */
1120             return;
1121         }
1122         else
1123         {
1124             /*
1125              * We're mapping this guy. Reverse course!!
1126              */
1127             pSOR->mapping = False;
1128             pSOR->wSubpanel = wSubpanel;
1129
1130             /* handle the rest on the next timeout */
1131             return;
1132         }
1133     }
1134
1135     pSOR = (SlideOutRec *) XtMalloc (sizeof(SlideOutRec));
1136     if (pSOR)
1137     {
1138         /*
1139          * Compute this ahead of time to check if our window
1140          * is short. If it is, we'll just unmap it, avoiding
1141          * a lot of extra work.
1142          */
1143         pSOR->incHeight = (Dimension) (DisplayHeight(DISPLAY, 
1144                 SCREEN_FOR_CLIENT(pCD))/SLIDE_UP_DIVISOR);
1145     }
1146
1147     if ((pCD->slideDirection != SLIDE_NOT) && pSOR &&
1148         (pSOR->incHeight < pCD->frameInfo.height))
1149     {
1150         XSetWindowAttributes window_attribs;
1151         XWindowChanges window_changes;
1152         unsigned long mask;
1153
1154         /* 
1155          * Set up data for processing slide up
1156          */
1157         pSOR->pCD = pCD;
1158         pSOR->interval = SLIDE_UP_INTERVAL;
1159         pSOR->direction = pCD->slideDirection;
1160         pSOR->mapping = False;
1161         pSOR->wSubpanel = wSubpanel;
1162         pSOR->pCD->pSOR = pSOR;
1163         
1164         pSOR->incWidth = 0;
1165         pSOR->currWidth = pCD->frameInfo.width;
1166         pSOR->currHeight = pCD->frameInfo.height;
1167         pSOR->currX = pCD->frameInfo.x;
1168         pSOR->currY = pCD->frameInfo.y;
1169
1170         switch (pSOR->direction)
1171         {
1172             case SLIDE_NORTH:
1173                 pSOR->currHeight -= pSOR->incHeight;
1174                 pSOR->currY += pSOR->incHeight;
1175                 break;
1176
1177             case SLIDE_SOUTH:
1178                 pSOR->currHeight -= pSOR->incHeight;
1179                 break;
1180         }
1181
1182         /*
1183          * Create screening window to hide the slide-up from button
1184          * events until it is all the way up.
1185          */
1186         mask = CWOverrideRedirect;
1187         window_attribs.override_redirect = True;
1188         pSOR->coverWin = XCreateWindow(DISPLAY,
1189                             RootWindow (DISPLAY, SCREEN_FOR_CLIENT(pCD)),
1190                             pSOR->currX, pSOR->currY, 
1191                             pSOR->currWidth, pSOR->currHeight, 0,
1192                             CopyFromParent,InputOnly,CopyFromParent,
1193                             mask, &window_attribs);
1194
1195         /* 
1196          * Put screen window above the slide-up client
1197          */
1198         mask = CWStackMode | CWSibling;
1199         window_changes.stack_mode = Above;
1200         window_changes.sibling = pCD->clientFrameWin;
1201         XConfigureWindow (DISPLAY, pSOR->coverWin, mask, &window_changes);
1202
1203         /*
1204          * Start slide-up processing
1205          */ 
1206         XMapWindow (DISPLAY, pSOR->coverWin);
1207
1208         if (pSOR->currHeight > 0)
1209         {
1210             XMoveResizeWindow (DISPLAY, pCD->clientFrameWin, 
1211                 pSOR->currX, pSOR->currY, 
1212                 pSOR->currWidth, pSOR->currHeight);
1213
1214             XMoveResizeWindow (DISPLAY, pSOR->coverWin, 
1215                 pSOR->currX, pSOR->currY, 
1216                 pSOR->currWidth, pSOR->currHeight);
1217
1218             XSync (DISPLAY, False);
1219         }
1220
1221         XtAppAddTimeOut(wmGD.mwmAppContext, pSOR->interval,
1222                         SlideOutTimerProc, (XtPointer)pSOR);
1223
1224         wmGD.iSlideUpsInProgress += 1;
1225
1226     }
1227     else
1228     {
1229         /*
1230          * Not sliding because no direction specified or our window
1231          * is just a little guy.
1232          */
1233         /* Just unmanage the slide-up */
1234         XtUnmanageChild (wSubpanel);
1235         if (pSOR) 
1236         {
1237             XtFree ((char *) pSOR);
1238             pCD->pSOR = NULL;
1239         }
1240     }
1241
1242 } /* END OF FUNCTION SlideOutWindow */
1243
1244 \f
1245 /*************************************<->*************************************
1246  *
1247  *  MapClientWindows (pCD)
1248  *
1249  *
1250  *  Description:
1251  *  -----------
1252  *  Maps the window.  If this is a transient tree then all the windows in
1253  *  the transient tree are mapped.
1254  *
1255  *
1256  *  Inputs:
1257  *  ------
1258  *  pCD = pointer to  client data
1259  * 
1260  *************************************<->***********************************/
1261
1262 void MapClientWindows (ClientData *pCD)
1263 {
1264     ClientData *pNext;
1265
1266
1267     pNext = pCD->transientChildren;
1268     while (pNext)
1269     {
1270         /* map all transient children first */
1271         if (pNext->transientChildren)
1272         {
1273             MapClientWindows (pNext);
1274         }
1275
1276         /* then map all siblings at this level */
1277         XMapWindow (DISPLAY, pNext->client);
1278         XMapWindow (DISPLAY, pNext->clientFrameWin);
1279
1280         pNext = pNext->transientSiblings;
1281     }
1282
1283     /* map the primary window */
1284     XMapWindow (DISPLAY, pCD->client);
1285     XMapWindow (DISPLAY, pCD->clientFrameWin);
1286
1287 } /* END OF FUNCTION MapClientWindows */
1288
1289
1290 \f
1291 /*************************************<->*************************************
1292  *
1293  *  ShowIconForMinimizedClient (pWS, pCD)
1294  *
1295  *
1296  *  Description:
1297  *  -----------
1298  *  This function shows the icon for the specified client.  If the icon
1299  *  is in an icon box then the "minimized" icon is displayed.  If the icon
1300  *  is on the root window it is mapped.
1301  * 
1302  *
1303  *  Inputs:
1304  *  ------
1305  *  pWS = pointer to workspace data
1306  *  pCD = pointer to  client data
1307  *
1308  *************************************<->***********************************/
1309
1310 void ShowIconForMinimizedClient (WmWorkspaceData *pWS, ClientData *pCD)
1311 {
1312     WmScreenData *pSD = PSD_FOR_CLIENT(pCD);
1313
1314     /*
1315      * Handle auto-placement for root icons (icons not in an icon
1316      * box).
1317      */
1318     if (wmGD.iconAutoPlace && !P_ICON_BOX(pCD))
1319     {
1320         if ((ICON_PLACE(pCD) == NO_ICON_PLACE) ||
1321             ((pWS->IPData.placeList[ICON_PLACE(pCD)].pCD) &&
1322              (pWS->IPData.placeList[ICON_PLACE(pCD)].pCD != pCD)))
1323         {
1324             /*
1325              * Icon place not defined or occupied by another client,
1326              * find a free place to put the icon.
1327              */
1328
1329             if ((ICON_PLACE(pCD) = GetNextIconPlace (&pWS->IPData)) 
1330                 == NO_ICON_PLACE)
1331             {
1332                 ICON_PLACE(pCD) = 
1333                     CvtIconPositionToPlace (&pWS->IPData,
1334                                                          pCD->clientX,
1335                                                          pCD->clientY);
1336             }
1337             CvtIconPlaceToPosition (&pWS->IPData, ICON_PLACE(pCD), 
1338                                     &ICON_X(pCD), &ICON_Y(pCD));
1339
1340         }
1341
1342         pWS->IPData.placeList[ICON_PLACE(pCD)].pCD = pCD;
1343     }
1344
1345     /*
1346      * If icon on root window and this workspace is active, the
1347      * make sure it's in the right place.
1348      */
1349     if ((pWS == pSD->pActiveWS) && !P_ICON_BOX(pCD))
1350     {
1351         XMoveWindow (DISPLAY, ICON_FRAME_WIN(pCD), 
1352             ICON_X(pCD), ICON_Y(pCD));
1353     }
1354
1355     if (pCD->iconWindow)
1356     {
1357         XMapWindow (DISPLAY, pCD->iconWindow);
1358     }
1359
1360     if ((pSD->useIconBox) && P_ICON_BOX(pCD))
1361     {
1362         ShowClientIconState (pCD, MINIMIZED_STATE );
1363     }
1364     else
1365     {
1366         XWindowChanges windowChanges;
1367
1368         /*
1369          * Map the icon on the screen at the appropriate place in the 
1370          * window stack.
1371          */
1372
1373         if (wmGD.lowerOnIconify)
1374         {
1375             if ((&pCD->iconEntry != pSD->lastClient) &&
1376                 (pSD->lastClient))
1377             {
1378                 if (pSD->lastClient->type == MINIMIZED_STATE)
1379                 {
1380                     windowChanges.sibling = 
1381                         ICON_FRAME_WIN(pSD->lastClient->pCD);
1382                 }
1383                 else
1384                 {
1385                     windowChanges.sibling =
1386                         pSD->lastClient->pCD->clientFrameWin;
1387                 }
1388                 windowChanges.stack_mode = Below;
1389                 XConfigureWindow (DISPLAY, ICON_FRAME_WIN(pCD),
1390                                   (CWSibling | CWStackMode), &windowChanges);
1391                 MoveEntryInList (pWS, &pCD->iconEntry, 
1392                     False /*on bottom*/, NULL);
1393             }
1394         }
1395         else
1396         {
1397             windowChanges.sibling = pCD->clientFrameWin;
1398             windowChanges.stack_mode = Below;
1399             XConfigureWindow (DISPLAY, ICON_FRAME_WIN(pCD),
1400                               (CWSibling | CWStackMode), &windowChanges);
1401             MoveEntryInList (pWS, &pCD->iconEntry, False /*below*/,
1402                              &pCD->clientEntry);
1403         }
1404
1405         if (pWS == pSD->pActiveWS)
1406         {
1407             XMapWindow (DISPLAY, ICON_FRAME_WIN(pCD));
1408         }
1409     }
1410
1411 } /* END OF FUNCTION ShowIconForMinimizedClient */
1412
1413 /*************************************<->*************************************
1414  *
1415  *  ShowAllIconsForMinimizedClient (pCD)
1416  *
1417  *
1418  *  Description:
1419  *  -----------
1420  *  This function places icons in all the workspaces for the minimized
1421  *  client. Since there is only one clientState per client (not per
1422  *  workspace), this loops over all workspace in which the client
1423  *  resides and places an icon in each.
1424  * 
1425  *
1426  *  Inputs:
1427  *  ------
1428  *  pCD = pointer to  client data
1429  *
1430  *  Comments:
1431  *  ---------
1432  *  This operates by setting up the currentWsc index for each workspace
1433  *  and calling ShowIconForMinimizedClient, which makes heavy use of
1434  *  the macros that use the currentWsc index.
1435  *
1436  *************************************<->***********************************/
1437
1438 void ShowAllIconsForMinimizedClient (ClientData *pCD)
1439 {
1440     int saveWsc = pCD->currentWsc;
1441     int tmpWsc;
1442     WmWorkspaceData *pWS;
1443
1444     for (tmpWsc = 0; tmpWsc < pCD->numInhabited; tmpWsc++)
1445     {
1446         pCD->currentWsc = tmpWsc;
1447         pWS = GetWorkspaceData (PSD_FOR_CLIENT(pCD),
1448                                     pCD->pWsList[tmpWsc].wsID);
1449         ShowIconForMinimizedClient(pWS, pCD);
1450     }
1451     
1452     pCD->currentWsc = saveWsc;
1453
1454 } /* END OF FUNCTION ShowAllIconsForMinimizedClient */