1 /********************************************************************************
2 *                                                                               *
3 *          M u l t i p l e   D o c u m e n t   C l i e n t   W i n d o w        *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 1998,2021 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or modify          *
9 * it under the terms of the GNU Lesser General Public License as published by   *
10 * the Free Software Foundation; either version 3 of the License, or             *
11 * (at your option) any later version.                                           *
12 *                                                                               *
13 * This library is distributed in the hope that it will be useful,               *
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of                *
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                 *
16 * GNU Lesser General Public License for more details.                           *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public License      *
19 * along with this program.  If not, see <http://www.gnu.org/licenses/>          *
20 ********************************************************************************/
21 #include "xincs.h"
22 #include "fxver.h"
23 #include "fxdefs.h"
24 #include "fxmath.h"
25 #include "FXArray.h"
26 #include "FXHash.h"
27 #include "FXMutex.h"
28 #include "FXStream.h"
29 #include "FXString.h"
30 #include "FXSize.h"
31 #include "FXPoint.h"
32 #include "FXRectangle.h"
33 #include "FXObjectList.h"
34 #include "FXStringDictionary.h"
35 #include "FXSettings.h"
36 #include "FXRegistry.h"
37 #include "FXAccelTable.h"
38 #include "FXEvent.h"
39 #include "FXWindow.h"
40 #include "FXApp.h"
41 #include "FXDCWindow.h"
42 #include "FXImage.h"
43 #include "FXIcon.h"
44 #include "FXFrame.h"
45 #include "FXLabel.h"
46 #include "FXButton.h"
47 #include "FXMenuButton.h"
48 #include "FXComposite.h"
49 #include "FXShell.h"
50 #include "FXPopup.h"
51 #include "FXMenuPane.h"
52 #include "FXMDIButton.h"
53 #include "FXPacker.h"
54 #include "FXHorizontalFrame.h"
55 #include "FXToolBar.h"
56 #include "FXMenuBar.h"
57 #include "FXMDIChild.h"
58 #include "FXMDIClient.h"
59 #include "FXDialogBox.h"
60 #include "FXVerticalFrame.h"
61 #include "FXList.h"
62 
63 
64 /*
65   Notes:
66   - This brings up the question of the cascade design.  Do you want the windows
67     to be cascaded/tiled in the order that they were created, or do you want them
68     to be cascaded in some sort of focus order, with the one that had the focus
69     to be on top,  keeping its focus.
70     In the test app, if you click within any of the child windows, it pops to
71     the top with the focus, with the exception of "TEST3", which has a button.
72     If this button is clicked, that window dos not pop to the top when it
73     gets the focus.  Seems like it should...
74   - Minor problems with mdi.  When a window is maximized and then
75     deleted, the next window that is created is created in "normal" mode.
76     When any adjustment is made to the size of the new window, it pops to
77     "almost" maximized mode.  (The child frame is visible, but as large as
78     possible).  Seems like the child windows should be created maximized if
79     the mdi is in maximized mode.  Also, if there are two windows and one
80     gets maximized and then deleted, the second window pops to "almost"
81     maximixed mode.
82   - We make MDIClient get a first crack at the messages, so that the MDIChild
83     can not shadow any messages really directed at the MDIClient.
84   - Need ``arrange icons'' feature.
85   - When switching active MDIChild windows, we pass the old to the new and vice
86     versa; this allows the MDIChild's target to determine if we switched windows
87     only, or if we switched between one document and another at the same time
88 */
89 
90 #define HORZ_PAD      12
91 #define VERT_PAD      2
92 
93 #define CASCADE_XOFF  24
94 #define CASCADE_YOFF  24
95 
96 #define CLIENT_MIN_WIDTH  16
97 #define CLIENT_MIN_HEIGHT 16
98 
99 
100 using namespace FX;
101 
102 /*******************************************************************************/
103 
104 namespace FX {
105 
106 
107 FXDEFMAP(FXMDIClient) FXMDIClientMap[]={
108   FXMAPFUNC(SEL_PAINT,0,FXMDIClient::onPaint),
109   FXMAPFUNC(SEL_UPDATE,FXWindow::ID_MDI_NEXT,FXMDIClient::onUpdActivateNext),
110   FXMAPFUNC(SEL_UPDATE,FXWindow::ID_MDI_PREV,FXMDIClient::onUpdActivatePrev),
111   FXMAPFUNC(SEL_UPDATE,FXWindow::ID_MDI_TILEHORIZONTAL,FXMDIClient::onUpdTileHorizontal),
112   FXMAPFUNC(SEL_UPDATE,FXWindow::ID_MDI_TILEVERTICAL,FXMDIClient::onUpdTileVertical),
113   FXMAPFUNC(SEL_UPDATE,FXWindow::ID_MDI_CASCADE,FXMDIClient::onUpdCascade),
114   FXMAPFUNC(SEL_UPDATE,FXWindow::ID_MDI_CLOSE,FXMDIClient::onUpdClose),
115   FXMAPFUNC(SEL_UPDATE,FXWindow::ID_MDI_MINIMIZE,FXMDIClient::onUpdMinimize),
116   FXMAPFUNC(SEL_UPDATE,FXWindow::ID_MDI_RESTORE,FXMDIClient::onUpdRestore),
117   FXMAPFUNC(SEL_UPDATE,FXWindow::ID_MDI_MAXIMIZE,FXMDIClient::onUpdMaximize),
118   FXMAPFUNC(SEL_UPDATE,FXWindow::ID_MDI_MENURESTORE,FXMDIClient::onUpdMenuRestore),
119   FXMAPFUNC(SEL_UPDATE,FXWindow::ID_MDI_MENUCLOSE,FXMDIClient::onUpdMenuClose),
120   FXMAPFUNC(SEL_UPDATE,FXWindow::ID_MDI_MENUMINIMIZE,FXMDIClient::onUpdMenuMinimize),
121   FXMAPFUNC(SEL_UPDATE,FXWindow::ID_MDI_MENUWINDOW,FXMDIClient::onUpdMenuWindow),
122   FXMAPFUNC(SEL_UPDATE,FXMDIClient::ID_MDI_ANY,FXMDIClient::onUpdAnyWindows),
123   FXMAPFUNCS(SEL_UPDATE,FXMDIClient::ID_MDI_1,FXMDIClient::ID_MDI_10,FXMDIClient::onUpdWindowSelect),
124   FXMAPFUNCS(SEL_UPDATE,FXMDIClient::ID_MDI_OVER_1,FXMDIClient::ID_MDI_OVER_10,FXMDIClient::onUpdOthersWindows),
125   FXMAPFUNC(SEL_COMMAND,FXWindow::ID_MDI_NEXT,FXMDIClient::onCmdActivateNext),
126   FXMAPFUNC(SEL_COMMAND,FXWindow::ID_MDI_PREV,FXMDIClient::onCmdActivatePrev),
127   FXMAPFUNC(SEL_COMMAND,FXWindow::ID_MDI_TILEHORIZONTAL,FXMDIClient::onCmdTileHorizontal),
128   FXMAPFUNC(SEL_COMMAND,FXWindow::ID_MDI_TILEVERTICAL,FXMDIClient::onCmdTileVertical),
129   FXMAPFUNC(SEL_COMMAND,FXWindow::ID_MDI_CASCADE,FXMDIClient::onCmdCascade),
130   FXMAPFUNCS(SEL_COMMAND,FXMDIClient::ID_MDI_1,FXMDIClient::ID_MDI_10,FXMDIClient::onCmdWindowSelect),
131   FXMAPFUNCS(SEL_COMMAND,FXMDIClient::ID_MDI_OVER_1,FXMDIClient::ID_MDI_OVER_10,FXMDIClient::onCmdOthersWindows),
132   };
133 
134 
135 // Object implementation
FXIMPLEMENT(FXMDIClient,FXComposite,FXMDIClientMap,ARRAYNUMBER (FXMDIClientMap))136 FXIMPLEMENT(FXMDIClient,FXComposite,FXMDIClientMap,ARRAYNUMBER(FXMDIClientMap))
137 
138 
139 // Construct and init
140 FXMDIClient::FXMDIClient(){
141   flags|=FLAG_SHOWN;
142   active=NULL;
143   backImage=NULL;
144   cascadex=CASCADE_XOFF;
145   cascadey=CASCADE_YOFF;
146   }
147 
148 
149 // Construct and init
FXMDIClient(FXComposite * p,FXuint opts,FXint x,FXint y,FXint w,FXint h)150 FXMDIClient::FXMDIClient(FXComposite* p,FXuint opts,FXint x,FXint y,FXint w,FXint h):FXComposite(p,opts,x,y,w,h){
151   flags|=FLAG_SHOWN;
152   backColor=getApp()->getShadowColor();
153   active=NULL;
154   backImage=NULL;
155   cascadex=CASCADE_XOFF;
156   cascadey=CASCADE_YOFF;
157   }
158 
159 
160 // Create window
create()161 void FXMDIClient::create(){
162   FXComposite::create();
163   if(backImage) backImage->create();
164   }
165 
166 
167 // Get width
getDefaultWidth()168 FXint FXMDIClient::getDefaultWidth(){
169   return CLIENT_MIN_WIDTH;
170   }
171 
172 
173 // Get height
getDefaultHeight()174 FXint FXMDIClient::getDefaultHeight(){
175   return CLIENT_MIN_HEIGHT;
176   }
177 
178 
179 // Recalculate layout
layout()180 void FXMDIClient::layout(){
181   FXMDIChild* child;
182   FXint xx,yy,ww,hh;
183 
184   // Place children
185   for(child=(FXMDIChild*)getFirst(); child; child=(FXMDIChild*)child->getNext()){
186     if(child->shown()){
187       if(child->isMaximized()){
188         xx=0;
189         yy=0;
190         ww=width;
191         hh=height;
192         }
193       else if(child->isMinimized()){
194         xx=child->getX();
195         yy=child->getY();
196         ww=child->getDefaultWidth();
197         hh=child->getDefaultHeight();
198         }
199       else{
200         xx=child->getX();
201         yy=child->getY();
202         ww=child->getWidth();
203         hh=child->getHeight();
204         }
205       child->position(xx,yy,ww,hh);
206       }
207     }
208 
209   // Raise active child
210   if(active && active->shown()) active->raise();
211 
212   // No more dirty
213   flags&=~FLAG_DIRTY;
214   }
215 
216 
217 // Handle repaint
onPaint(FXObject *,FXSelector,void * ptr)218 long FXMDIClient::onPaint(FXObject*,FXSelector,void* ptr){
219   FXDCWindow dc(this,(FXEvent*)ptr);
220   if(backImage){
221     dc.setFillStyle(FILL_TILED);
222     dc.setTile(backImage);
223     dc.fillRectangle(0,0,width,height);
224     }
225   else{
226     dc.setForeground(backColor);
227     dc.fillRectangle(0,0,width,height);
228     }
229   return 1;
230   }
231 
232 
233 // Pass message to all MDI windows; the crufty loop is because
234 // it is possible for the child receiving the message to be deleted
forallWindows(FXObject * sender,FXSelector sel,void * ptr)235 long FXMDIClient::forallWindows(FXObject* sender,FXSelector sel,void* ptr){
236   FXWindow *child,*nextchild;
237   for(child=getFirst(); child; child=nextchild){
238     nextchild=child->getNext();
239     if(!child->handle(sender,sel,ptr)) return 0;
240     }
241   return 1;
242   }
243 
244 
245 // Pass message to all different documents; here the complication
246 // is that the whole group of child windows sharing the same
247 // document may be deleted; also, we want to send only ONE of the
248 // document-sharing children a message.
forallDocuments(FXObject * sender,FXSelector sel,void * ptr)249 long FXMDIClient::forallDocuments(FXObject* sender,FXSelector sel,void* ptr){
250   FXWindow *child,*nextchild,*ch;
251   for(child=getFirst(); child; child=nextchild){
252     nextchild=child->getNext();
253 x:  if(nextchild && nextchild->getTarget()){
254       for(ch=child; ch; ch=ch->getPrev()){
255         if(ch->getTarget()==nextchild->getTarget()){
256           nextchild=nextchild->getNext();
257           goto x;
258           }
259         }
260       }
261     if(!child->handle(sender,sel,ptr)) return 0;
262     }
263   return 1;
264   }
265 
266 
267 // Pass message to all MDI windows whose target is document;
268 // note that the child may be deleted as a result of the message.
forallDocWindows(FXObject * document,FXObject * sender,FXSelector sel,void * ptr)269 long FXMDIClient::forallDocWindows(FXObject* document,FXObject* sender,FXSelector sel,void* ptr){
270   FXWindow *child,*nextchild;
271   for(child=getFirst(); child; child=nextchild){
272     nextchild=child->getNext();
273     if(child->getTarget()==document){
274       if(!child->handle(sender,sel,ptr)) return 0;
275       }
276     }
277   return 1;
278   }
279 
280 
281 // Set the active child
setActiveChild(FXMDIChild * child,FXbool notify)282 FXbool FXMDIClient::setActiveChild(FXMDIChild* child,FXbool notify){
283   FXbool wasmax=false;
284   if(active!=child){
285 
286     if(active){
287 
288       // Was it maximized?
289       wasmax=active->isMaximized();
290 
291       // Deactivate old MDIChild
292       active->handle(this,FXSEL(SEL_DESELECTED,0),(void*)child);        // FIXME should call member function
293 
294       // Restore to normal size if it was maximized
295       if(wasmax && child) active->restore(notify);
296       }
297 
298     if(child){
299 
300       // Activate new MDIChild
301       child->handle(this,FXSEL(SEL_SELECTED,0),(void*)active);          // FIXME should call member function
302 
303       // Maximize because the old MDIChild was maximized
304       if(wasmax) child->maximize(notify);
305 
306       // Raise it
307       child->raise();
308       }
309 
310     active=child;
311 
312     // Need layout
313     recalc();
314 
315     // GUI update will be needed
316     getApp()->refresh();
317 
318     // Notify target
319     if(notify && target){ target->tryHandle(this,FXSEL(SEL_CHANGED,message),child); }
320 
321     return true;
322     }
323   return false;
324   }
325 
326 
327 // Get active document; this is the target of the active MDI Child
getActiveDocument() const328 FXObject* FXMDIClient::getActiveDocument() const {
329   return getActiveChild() ? getActiveChild()->getTarget() : NULL;
330   }
331 
332 
333 // Change background image
setBackImage(FXImage * img)334 void FXMDIClient::setBackImage(FXImage* img){
335   if(backImage!=img){
336     backImage=img;
337     update();
338     }
339   }
340 
341 
342 // Cascade windows
cascade(FXbool notify)343 void FXMDIClient::cascade(FXbool notify){
344   FXMDIChild* child;
345   FXint childx,childy,childw,childh;
346   childx=5;
347   childy=5;
348   childw=(2*width)/3;
349   childh=(2*height)/3;
350   for(child=(FXMDIChild*)getFirst(); child; child=(FXMDIChild*)child->getNext()){
351     if(child==active) continue;
352     if(child->shown() && !child->isMinimized()){
353       child->restore(notify);
354       child->position(childx,childy,childw,childh);
355       child->raise();
356       childx+=cascadex;
357       childy+=cascadey;
358       if(childx+child->getWidth()>width){ childx=5; childy=5; }
359       if(childy+child->getHeight()>height){ childy=5; }
360       }
361     }
362   if(active && active->shown() && !active->isMinimized()){
363     active->restore(notify);
364     active->position(childx,childy,childw,childh);
365     active->raise();
366     }
367   }
368 
369 
370 // Layout horizontally
horizontal(FXbool notify)371 void FXMDIClient::horizontal(FXbool notify){
372   FXMDIChild* child;
373   FXint n,nr,nc,hroom,vroom,r,c;
374   for(n=0,child=(FXMDIChild*)getFirst(); child; child=(FXMDIChild*)child->getNext()){
375     if(child->shown() && !child->isMinimized()) n++;
376     }
377   nr=n;
378   nc=1;
379   if(n>3){
380     nc=(int)Math::sqrt((double)n);
381     nr=(n+nc-1)/nc;
382     }
383   hroom=0;
384   vroom=0;
385   if(nc>0) hroom=width/nc;
386   if(nr>0) vroom=height/nr;
387   for(child=(FXMDIChild*)getFirst(),n=0; child; child=(FXMDIChild*)child->getNext()){
388     if(child->shown() && !child->isMinimized()){
389       r=n/nc;
390       c=n%nc;
391       child->restore(notify);
392       child->position(c*hroom,r*vroom,hroom,vroom);
393       n++;
394       }
395     }
396   if(active && active->shown()) active->raise();
397   }
398 
399 
400 // Layout vertically
vertical(FXbool notify)401 void FXMDIClient::vertical(FXbool notify){
402   FXMDIChild* child;
403   FXint n,nr,nc,hroom,vroom,r,c;
404   for(n=0,child=(FXMDIChild*)getFirst(); child; child=(FXMDIChild*)child->getNext()){
405     if(child->shown() && !child->isMinimized()) n++;
406     }
407   nc=n;
408   nr=1;
409   if(n>3){
410     nr=(int)Math::sqrt((double)n);
411     nc=(n+nr-1)/nr;
412     }
413   hroom=0;
414   vroom=0;
415   if(nc>0) hroom=width/nc;
416   if(nr>0) vroom=height/nr;
417   for(child=(FXMDIChild*)getFirst(),n=0; child; child=(FXMDIChild*)child->getNext()){
418     if(child->shown() && !child->isMinimized()){
419       r=n/nc;
420       c=n%nc;
421       child->restore(notify);
422       child->position(c*hroom,r*vroom,hroom,vroom);
423       n++;
424       }
425     }
426   if(active && active->shown()) active->raise();
427   }
428 
429 
430 // User clicks on one of the window menus
onCmdWindowSelect(FXObject *,FXSelector sel,void *)431 long FXMDIClient::onCmdWindowSelect(FXObject*,FXSelector sel,void*){
432   setActiveChild((FXMDIChild*)childAtIndex(FXSELID(sel)-ID_MDI_1),true);
433   return 1;
434   }
435 
436 
437 // Update handler for window menus
onUpdWindowSelect(FXObject * sender,FXSelector sel,void *)438 long FXMDIClient::onUpdWindowSelect(FXObject *sender,FXSelector sel,void*){
439   FXint which=FXSELID(sel)-ID_MDI_1;
440   FXMDIChild *child=(FXMDIChild*)childAtIndex(which);
441   if(child){
442     FXString string;
443     if(which<9)
444       string.format("&%d %s",which+1,child->getTitle().text());
445     else
446       string.format("1&0 %s",child->getTitle().text());
447     sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_SETSTRINGVALUE),(void*)&string);
448     sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_SHOW),NULL);
449     if(child==active)
450       sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_CHECK),NULL);
451     else
452       sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_UNCHECK),NULL);
453     }
454   else{
455     sender->handle(this,FXSEL(SEL_COMMAND,FXWindow::ID_HIDE),NULL);
456     }
457   return 1;
458   }
459 
460 
461 
462 // Show a menu of other MDI child windows
onCmdOthersWindows(FXObject *,FXSelector,void *)463 long FXMDIClient::onCmdOthersWindows(FXObject*,FXSelector,void*){
464   FXDialogBox choose(this,tr("Select Window"),DECOR_TITLE|DECOR_BORDER|DECOR_RESIZE,0,0,300,200,10,10,10,10, 10,10);
465   FXHorizontalFrame* buttons=new FXHorizontalFrame(&choose,LAYOUT_SIDE_BOTTOM|LAYOUT_FILL_X|PACK_UNIFORM_WIDTH|PACK_UNIFORM_HEIGHT,0,0,0,0,0,0,0,0);
466   new FXButton(buttons,tr("&OK"),NULL,&choose,FXDialogBox::ID_ACCEPT,BUTTON_INITIAL|BUTTON_DEFAULT|FRAME_RAISED|FRAME_THICK|LAYOUT_RIGHT,0,0,0,0,HORZ_PAD,HORZ_PAD,VERT_PAD,VERT_PAD);
467   new FXButton(buttons,tr("&Cancel"),NULL,&choose,FXDialogBox::ID_CANCEL,BUTTON_DEFAULT|FRAME_RAISED|FRAME_THICK|LAYOUT_RIGHT,0,0,0,0,HORZ_PAD,HORZ_PAD,VERT_PAD,VERT_PAD);
468   FXVerticalFrame* mdilistframe=new FXVerticalFrame(&choose,FRAME_SUNKEN|FRAME_THICK|LAYOUT_FILL_X|LAYOUT_FILL_Y,0,0,0,0,0,0,0,0,0,0);
469   FXList* mdilist=new FXList(mdilistframe,NULL,0,LIST_BROWSESELECT|LAYOUT_FILL_X|LAYOUT_FILL_Y);
470   mdilist->setNumVisible(10);
471   for(FXMDIChild *child=(FXMDIChild*)getFirst(); child; child=(FXMDIChild*)child->getNext()){
472     mdilist->appendItem(child->getTitle(),child->getIcon(),child);
473     if(child==active) mdilist->setCurrentItem(mdilist->getNumItems()-1);
474     }
475   if(choose.execute(PLACEMENT_OWNER)){
476     FXASSERT(mdilist->getCurrentItem()>=0);
477     setActiveChild((FXMDIChild*)mdilist->getItemData(mdilist->getCurrentItem()));
478     }
479   return 1;
480   }
481 
482 
483 // Update button to show menu of other MDI child windows when more than N windows
onUpdOthersWindows(FXObject * sender,FXSelector sel,void *)484 long FXMDIClient::onUpdOthersWindows(FXObject *sender,FXSelector sel,void*){
485   sender->handle(this,((FXSELID(sel)-ID_MDI_OVER_1)<numChildren())?FXSEL(SEL_COMMAND,FXWindow::ID_SHOW):FXSEL(SEL_COMMAND,FXWindow::ID_HIDE),NULL);
486   return 1;
487   }
488 
489 
490 // Show or hide depending on whether there are any windows
onUpdAnyWindows(FXObject * sender,FXSelector,void *)491 long FXMDIClient::onUpdAnyWindows(FXObject *sender,FXSelector,void*){
492   sender->handle(this,getFirst()?FXSEL(SEL_COMMAND,ID_SHOW):FXSEL(SEL_COMMAND,ID_HIDE),NULL);
493   return 1;
494   }
495 
496 
497 // Update restore; gray if no active
onUpdRestore(FXObject * sender,FXSelector sel,void * ptr)498 long FXMDIClient::onUpdRestore(FXObject* sender,FXSelector sel,void* ptr){
499   if(active) return active->handle(sender,sel,ptr);
500   sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
501   return 1;
502   }
503 
504 
505 // Update maximized; gray if no active
onUpdMaximize(FXObject * sender,FXSelector sel,void * ptr)506 long FXMDIClient::onUpdMaximize(FXObject* sender,FXSelector sel,void* ptr){
507   if(active) return active->handle(sender,sel,ptr);
508   sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
509   return 1;
510   }
511 
512 
513 // Update minimized
onUpdMinimize(FXObject * sender,FXSelector sel,void * ptr)514 long FXMDIClient::onUpdMinimize(FXObject* sender,FXSelector sel,void* ptr){
515   if(active) return active->handle(sender,sel,ptr);
516   sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
517   return 1;
518   }
519 
520 
521 // Update close active child
onUpdClose(FXObject * sender,FXSelector sel,void * ptr)522 long FXMDIClient::onUpdClose(FXObject* sender,FXSelector sel,void* ptr){
523   if(active) return active->handle(sender,sel,ptr);
524   sender->handle(this,FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
525   return 1;
526   }
527 
528 
529 // Update menu's restore button
onUpdMenuWindow(FXObject * sender,FXSelector sel,void * ptr)530 long FXMDIClient::onUpdMenuWindow(FXObject* sender,FXSelector sel,void* ptr){
531   if(active) return active->handle(sender,sel,ptr);
532   sender->handle(this,FXSEL(SEL_COMMAND,ID_HIDE),NULL);
533   return 1;
534   }
535 
536 
537 // Update menu's restore button
onUpdMenuRestore(FXObject * sender,FXSelector sel,void * ptr)538 long FXMDIClient::onUpdMenuRestore(FXObject* sender,FXSelector sel,void* ptr){
539   if(active) return active->handle(sender,sel,ptr);
540   sender->handle(this,FXSEL(SEL_COMMAND,ID_HIDE),NULL);
541   return 1;
542   }
543 
544 
545 // Update menu's minimized button
onUpdMenuMinimize(FXObject * sender,FXSelector sel,void * ptr)546 long FXMDIClient::onUpdMenuMinimize(FXObject* sender,FXSelector sel,void* ptr){
547   if(active) return active->handle(sender,sel,ptr);
548   sender->handle(this,FXSEL(SEL_COMMAND,ID_HIDE),NULL);
549   return 1;
550   }
551 
552 
553 // Update menu's close button
onUpdMenuClose(FXObject * sender,FXSelector sel,void * ptr)554 long FXMDIClient::onUpdMenuClose(FXObject* sender,FXSelector sel,void* ptr){
555   if(active) return active->handle(sender,sel,ptr);
556   sender->handle(this,FXSEL(SEL_COMMAND,ID_HIDE),NULL);
557   return 1;
558   }
559 
560 
561 // Tile horizontally (actually, prefer wider windows)
onCmdTileHorizontal(FXObject *,FXSelector,void *)562 long FXMDIClient::onCmdTileHorizontal(FXObject*,FXSelector,void*){
563   horizontal(true);
564   return 1;
565   }
566 
567 
568 // Update tile horizontally
onUpdTileHorizontal(FXObject * sender,FXSelector,void *)569 long FXMDIClient::onUpdTileHorizontal(FXObject* sender,FXSelector,void*){
570   sender->handle(this,getFirst()?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
571   return 1;
572   }
573 
574 
575 // Tile vertically (actually, prefer taller windows)
onCmdTileVertical(FXObject *,FXSelector,void *)576 long FXMDIClient::onCmdTileVertical(FXObject*,FXSelector,void*){
577   vertical(true);
578   return 1;
579   }
580 
581 
582 // Update tile vertically
onUpdTileVertical(FXObject * sender,FXSelector,void *)583 long FXMDIClient::onUpdTileVertical(FXObject* sender,FXSelector,void*){
584   sender->handle(this,getFirst()?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
585   return 1;
586   }
587 
588 
589 // Cascade windows
onCmdCascade(FXObject *,FXSelector,void *)590 long FXMDIClient::onCmdCascade(FXObject*,FXSelector,void*){
591   cascade(true);
592   return 1;
593   }
594 
595 
596 // Update cascade
onUpdCascade(FXObject * sender,FXSelector,void *)597 long FXMDIClient::onUpdCascade(FXObject* sender,FXSelector,void*){
598   sender->handle(this,getFirst()?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
599   return 1;
600   }
601 
602 
603 // Activate next child
onCmdActivateNext(FXObject *,FXSelector,void *)604 long FXMDIClient::onCmdActivateNext(FXObject*,FXSelector,void*){
605   if(active && active->getNext()) setActiveChild((FXMDIChild*)active->getNext(),true);
606   return 1;
607   }
608 
609 
610 // Activate next child
onUpdActivateNext(FXObject * sender,FXSelector,void *)611 long FXMDIClient::onUpdActivateNext(FXObject* sender,FXSelector,void*){
612   sender->handle(this,(active && active->getNext())?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
613   return 1;
614   }
615 
616 
617 // Activate previous child
onCmdActivatePrev(FXObject *,FXSelector,void *)618 long FXMDIClient::onCmdActivatePrev(FXObject*,FXSelector,void*){
619   if(active && active->getPrev()) setActiveChild((FXMDIChild*)active->getPrev(),true);
620   return 1;
621   }
622 
623 
624 // Activate previous child
onUpdActivatePrev(FXObject * sender,FXSelector,void *)625 long FXMDIClient::onUpdActivatePrev(FXObject* sender,FXSelector,void*){
626   sender->handle(this,(active && active->getPrev())?FXSEL(SEL_COMMAND,ID_ENABLE):FXSEL(SEL_COMMAND,ID_DISABLE),NULL);
627   return 1;
628   }
629 
630 
631 // Delegate all other messages to active child; we can't block any
632 // messages here as some are meant fot the FXMDIChild and some for
633 // the FXMDIChild's content or target.
onDefault(FXObject * sender,FXSelector sel,void * ptr)634 long FXMDIClient::onDefault(FXObject* sender,FXSelector sel,void* ptr){
635   return active && active->handle(sender,sel,ptr);
636   }
637 
638 
639 // Save object to stream
save(FXStream & store) const640 void FXMDIClient::save(FXStream& store) const {
641   FXComposite::save(store);
642   store << active;
643   store << cascadex;
644   store << cascadey;
645   }
646 
647 
648 // Load object from stream
load(FXStream & store)649 void FXMDIClient::load(FXStream& store){
650   FXComposite::load(store);
651   store >> active;
652   store >> cascadex;
653   store >> cascadey;
654   }
655 
656 
657 // Destruct thrashes object
~FXMDIClient()658 FXMDIClient::~FXMDIClient(){
659   active=(FXMDIChild*)-1L;
660   backImage=(FXImage*)-1L;
661   }
662 
663 }
664