1 /********************************************************************************
2 *                                                                               *
3 *                         T a b   B o o k   W i d g e t                         *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 1997,2005 by Jeroen van der Zijp.   All Rights Reserved.        *
7 *********************************************************************************
8 * This library is free software; you can redistribute it and/or                 *
9 * modify it under the terms of the GNU Lesser General Public                    *
10 * License as published by the Free Software Foundation; either                  *
11 * version 2.1 of the License, or (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 GNU             *
16 * Lesser General Public License for more details.                               *
17 *                                                                               *
18 * You should have received a copy of the GNU Lesser General Public              *
19 * License along with this library; if not, write to the Free Software           *
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA.    *
21 *********************************************************************************
22 * $Id: FXTabBook.cpp,v 1.18 2005/01/16 16:06:07 fox Exp $                       *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "fxkeys.h"
28 #include "FXHash.h"
29 #include "FXThread.h"
30 #include "FXStream.h"
31 #include "FXString.h"
32 #include "FXSize.h"
33 #include "FXPoint.h"
34 #include "FXRectangle.h"
35 #include "FXRegistry.h"
36 #include "FXAccelTable.h"
37 #include "FXApp.h"
38 #include "FXDCWindow.h"
39 #include "FXFont.h"
40 #include "FXIcon.h"
41 #include "FXTabBook.h"
42 
43 
44 /*
45   Notes:
46   - Should focus go to tab items?
47   - Should callbacks come from tab items?
48   - Should redesign this stuff a little.
49   - Tab items should observe various border styles.
50   - TAB/TABTAB should go into content, arrow keys navigate between tabs.
51   - FXTabBook: pane's hints make no sense to observe
52   - We hide the panes in FXTabBook.  This way, we don't have to change
53     the position of each pane when the FXTabBook itself changes.
54     Only the active pane needs to be resized, leading to much faster
55     layouts.
56   - Fix setCurrent() to be like FXSwitcher.
57 */
58 
59 
60 #define TABBOOK_MASK       (TABBOOK_SIDEWAYS|TABBOOK_BOTTOMTABS)
61 
62 using namespace FX;
63 
64 /*******************************************************************************/
65 
66 namespace FX {
67 
68 FXDEFMAP(FXTabBook) FXTabBookMap[]={
69   FXMAPFUNC(SEL_PAINT,0,FXTabBook::onPaint),
70   FXMAPFUNC(SEL_FOCUS_NEXT,0,FXTabBook::onFocusNext),
71   FXMAPFUNC(SEL_FOCUS_PREV,0,FXTabBook::onFocusPrev),
72   FXMAPFUNC(SEL_FOCUS_UP,0,FXTabBook::onFocusUp),
73   FXMAPFUNC(SEL_FOCUS_DOWN,0,FXTabBook::onFocusDown),
74   FXMAPFUNC(SEL_FOCUS_LEFT,0,FXTabBook::onFocusLeft),
75   FXMAPFUNC(SEL_FOCUS_RIGHT,0,FXTabBook::onFocusRight),
76   FXMAPFUNC(SEL_COMMAND,FXTabBar::ID_OPEN_ITEM,FXTabBook::onCmdOpenItem),
77   };
78 
79 
80 // Object implementation
FXIMPLEMENT(FXTabBook,FXTabBar,FXTabBookMap,ARRAYNUMBER (FXTabBookMap))81 FXIMPLEMENT(FXTabBook,FXTabBar,FXTabBookMap,ARRAYNUMBER(FXTabBookMap))
82 
83 
84 // Make a tab book
85 FXTabBook::FXTabBook(FXComposite* p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb):
86   FXTabBar(p,tgt,sel,opts,x,y,w,h,pl,pr,pt,pb){
87   }
88 
89 
90 // Get width
getDefaultWidth()91 FXint FXTabBook::getDefaultWidth(){
92   register FXint w,wtabs,wmaxtab,wpnls,t,ntabs;
93   register FXWindow *tab,*pane;
94   register FXuint hints;
95 
96   // Left or right tabs
97   if(options&TABBOOK_SIDEWAYS){
98     wtabs=wpnls=0;
99     for(tab=getFirst(); tab && tab->getNext(); tab=tab->getNext()->getNext()){
100       pane=tab->getNext();
101       if(tab->shown()){
102         hints=tab->getLayoutHints();
103         if(hints&LAYOUT_FIX_WIDTH) t=tab->getWidth()-2; else t=tab->getDefaultWidth()-2;
104         if(t>wtabs) wtabs=t;
105         t=pane->getDefaultWidth();
106         if(t>wpnls) wpnls=t;
107         }
108       }
109     w=wtabs+wpnls;
110     }
111 
112   // Top or bottom tabs
113   else{
114     wtabs=wpnls=wmaxtab=ntabs=0;
115     for(tab=getFirst(); tab && tab->getNext(); tab=tab->getNext()->getNext()){
116       pane=tab->getNext();
117       if(tab->shown()){
118         hints=tab->getLayoutHints();
119         if(hints&LAYOUT_FIX_WIDTH) t=tab->getWidth(); else t=tab->getDefaultWidth();
120         if(t>wmaxtab) wmaxtab=t;
121         wtabs+=t;
122         t=pane->getDefaultWidth();
123         if(t>wpnls) wpnls=t;
124         ntabs++;
125         }
126       }
127     if(options&PACK_UNIFORM_WIDTH) wtabs=ntabs*wmaxtab;
128     wtabs+=5;
129     w=FXMAX(wtabs,wpnls);
130     }
131   return w+padleft+padright+(border<<1);
132   }
133 
134 
135 // Get height
getDefaultHeight()136 FXint FXTabBook::getDefaultHeight(){
137   register FXint h,htabs,hmaxtab,hpnls,t,ntabs;
138   register FXWindow *tab,*pane;
139   register FXuint hints;
140 
141   // Left or right tabs
142   if(options&TABBOOK_SIDEWAYS){
143     htabs=hpnls=hmaxtab=ntabs=0;
144     for(tab=getFirst(); tab && tab->getNext(); tab=tab->getNext()->getNext()){
145       pane=tab->getNext();
146       if(tab->shown()){
147         hints=tab->getLayoutHints();
148         if(hints&LAYOUT_FIX_HEIGHT) t=tab->getHeight(); else t=tab->getDefaultHeight();
149         if(t>hmaxtab) hmaxtab=t;
150         htabs+=t;
151         t=pane->getDefaultHeight();
152         if(t>hpnls) hpnls=t;
153         ntabs++;
154         }
155       }
156     if(options&PACK_UNIFORM_HEIGHT) htabs=ntabs*hmaxtab;
157     htabs+=5;
158     h=FXMAX(htabs,hpnls);
159     }
160 
161   // Top or bottom tabs
162   else{
163     htabs=hpnls=0;
164     for(tab=getFirst(); tab && tab->getNext(); tab=tab->getNext()->getNext()){
165       pane=tab->getNext();
166       if(tab->shown()){
167         hints=tab->getLayoutHints();
168         if(hints&LAYOUT_FIX_HEIGHT) t=tab->getHeight()-2; else t=tab->getDefaultHeight()-2;
169         if(t>htabs) htabs=t;
170         t=pane->getDefaultHeight();
171         if(t>hpnls) hpnls=t;
172         }
173       }
174     h=htabs+hpnls;
175     }
176   return h+padtop+padbottom+(border<<1);
177   }
178 
179 
180 // Recalculate layout
layout()181 void FXTabBook::layout(){
182   register int i,x,y,w,h,px,py,pw,ph,wmaxtab,hmaxtab,newcurrent;
183   register FXWindow *raisepane=NULL;
184   register FXWindow *raisetab=NULL;
185   register FXWindow *pane,*tab;
186   register FXuint hints;
187 
188   newcurrent=-1;
189 
190   // Measure tabs again
191   wmaxtab=hmaxtab=0;
192   for(tab=getFirst(),i=0; tab && tab->getNext(); tab=tab->getNext()->getNext(),i++){
193     pane=tab->getNext();
194     if(tab->shown()){
195       hints=tab->getLayoutHints();
196       if(hints&LAYOUT_FIX_WIDTH) w=tab->getWidth(); else w=tab->getDefaultWidth();
197       if(hints&LAYOUT_FIX_HEIGHT) h=tab->getHeight(); else h=tab->getDefaultHeight();
198       if(w>wmaxtab) wmaxtab=w;
199       if(h>hmaxtab) hmaxtab=h;
200       if(newcurrent<0 || i<=current) newcurrent=i;
201       }
202     }
203 
204   // This will change only if current now invisible
205   current=newcurrent;
206 
207   // Left or right tabs
208   if(options&TABBOOK_SIDEWAYS){
209 
210     // Place panel
211     px=(options&TABBOOK_BOTTOMTABS) ? border+padleft : border+padleft+wmaxtab-2;
212     py=border+padtop;
213     pw=width-padleft-padright-(border<<1)-wmaxtab+2;
214     ph=height-padtop-padbottom-(border<<1);
215 
216     // Place all of the children
217     for(tab=getFirst(),y=py,i=0; tab && tab->getNext(); tab=tab->getNext()->getNext(),i++){
218       pane=tab->getNext();
219       if(tab->shown()){
220         pane->position(px,py,pw,ph);
221         hints=tab->getLayoutHints();
222         if(hints&LAYOUT_FIX_WIDTH) w=tab->getWidth();
223         else if(options&PACK_UNIFORM_WIDTH) w=wmaxtab;
224         else w=tab->getDefaultWidth();
225         if(hints&LAYOUT_FIX_HEIGHT) h=tab->getHeight();
226         else if(options&PACK_UNIFORM_HEIGHT) h=hmaxtab;
227         else h=tab->getDefaultHeight();
228         if(current==i){
229           if(options&TABBOOK_BOTTOMTABS)
230             tab->position(px+pw-2,y,w,h);
231           else
232             tab->position(px-w+2,y,w,h);
233           pane->show();
234           raisetab=tab;
235           raisepane=pane;
236           y+=h-3;
237           }
238         else{
239           if(options&TABBOOK_BOTTOMTABS)
240             tab->position(px+pw-4,y+2,w,h);
241           else
242             tab->position(px-w+4,y+2,w,h);
243           tab->update(0,0,wmaxtab,h);
244           pane->hide();
245           y+=h;
246           }
247         }
248       else{
249         pane->hide();
250         }
251       }
252 
253     // Hide spurious last tab
254     if(tab) tab->resize(0,0);
255     }
256 
257   // Top or bottom tabs
258   else{
259 
260     // Place panel
261     px=border+padleft;
262     py=(options&TABBOOK_BOTTOMTABS) ? border+padtop : border+padtop+hmaxtab-2;
263     pw=width-padleft-padright-(border<<1);
264     ph=height-padtop-padbottom-(border<<1)-hmaxtab+2;
265 
266     // Place all of the children
267     for(tab=getFirst(),x=px,i=0; tab && tab->getNext(); tab=tab->getNext()->getNext(),i++){
268       pane=tab->getNext();
269       if(tab->shown()){
270         pane->position(px,py,pw,ph);
271         hints=tab->getLayoutHints();
272         if(hints&LAYOUT_FIX_WIDTH) w=tab->getWidth();
273         else if(options&PACK_UNIFORM_WIDTH) w=wmaxtab;
274         else w=tab->getDefaultWidth();
275         if(hints&LAYOUT_FIX_HEIGHT) h=tab->getHeight();
276         else if(options&PACK_UNIFORM_HEIGHT) h=hmaxtab;
277         else h=tab->getDefaultHeight();
278         if(current==i){
279           if(options&TABBOOK_BOTTOMTABS)
280             tab->position(x,py+ph-2,w,h);
281           else
282             tab->position(x,py-h+2,w,h);
283           pane->show();
284           raisepane=pane;
285           raisetab=tab;
286           x+=w-3;
287           }
288         else{
289           if(options&TABBOOK_BOTTOMTABS)
290             tab->position(x+2,py+ph-4,w,h);
291           else
292             tab->position(x+2,py-h+4,w,h);
293           pane->hide();
294           x+=w;
295           }
296         }
297       else{
298         pane->hide();
299         }
300       }
301 
302     // Hide spurious last tab
303     if(tab) tab->resize(0,0);
304     }
305 
306   // Raise tab over panel and panel over all other tabs
307   if(raisepane) raisepane->raise();
308   if(raisetab) raisetab->raise();
309 
310   flags&=~FLAG_DIRTY;
311   }
312 
313 
314 // The sender of the message is the item to open up
onCmdOpenItem(FXObject * sender,FXSelector,void *)315 long FXTabBook::onCmdOpenItem(FXObject* sender,FXSelector,void*){
316   setCurrent(indexOfChild((FXWindow*)sender)/2,TRUE);
317   return 1;
318   }
319 
320 
321 // Handle repaint
onPaint(FXObject *,FXSelector,void * ptr)322 long FXTabBook::onPaint(FXObject*,FXSelector,void* ptr){
323   FXEvent *ev=(FXEvent*)ptr;
324   FXDCWindow dc(this,ev);
325   dc.setForeground(backColor);
326   dc.fillRectangle(ev->rect.x,ev->rect.y,ev->rect.w,ev->rect.h);
327   drawFrame(dc,0,0,width,height);
328   return 1;
329   }
330 
331 
332 // Focus moved to next tab
onFocusNext(FXObject *,FXSelector,void * ptr)333 long FXTabBook::onFocusNext(FXObject*,FXSelector,void* ptr){
334   FXWindow *child=getFocus();
335   FXint which;
336   if(child){
337     child=child->getNext();
338     if(!child) return 0;
339     which=indexOfChild(child);
340     if(which&1){
341       child=child->getNext();
342       which++;
343       }
344     }
345   else{
346     child=getFirst();
347     which=0;
348     }
349   while(child && child->getNext() && !(child->shown() && child->isEnabled())){
350     child=child->getNext()->getNext();
351     which+=2;
352     }
353   if(child){
354     setCurrent(which>>1,TRUE);
355     child->handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
356     return 1;
357     }
358   return 0;
359   }
360 
361 
362 // Focus moved to previous
onFocusPrev(FXObject *,FXSelector,void * ptr)363 long FXTabBook::onFocusPrev(FXObject*,FXSelector,void* ptr){
364   FXWindow *child=getFocus();
365   FXint which;
366   if(child){
367     child=child->getPrev();
368     if(!child) return 0;
369     which=indexOfChild(child);
370     }
371   else{
372     child=getLast();
373     if(!child) return 0;
374     which=indexOfChild(child);
375     }
376   if(which&1){
377     child=child->getPrev();
378     }
379   while(child && child->getPrev() && !(child->shown() && child->isEnabled())){
380     child=child->getPrev()->getPrev();
381     which-=2;
382     }
383   if(child){
384     setCurrent(which>>1,TRUE);
385     child->handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
386     return 1;
387     }
388   return 0;
389   }
390 
391 
392 // Focus moved up
onFocusUp(FXObject *,FXSelector,void * ptr)393 long FXTabBook::onFocusUp(FXObject*,FXSelector,void* ptr){
394   if(options&TABBOOK_SIDEWAYS){
395     return handle(this,FXSEL(SEL_FOCUS_PREV,0),ptr);
396     }
397   if(getFocus()){
398     FXWindow *child=NULL;
399     if(indexOfChild(getFocus())&1){     // We're on a panel
400       if(!(options&TABBOOK_BOTTOMTABS)) child=getFocus()->getPrev();
401       }
402     else{                               // We're on a tab
403       if(options&TABBOOK_BOTTOMTABS) child=getFocus()->getNext();
404       }
405     if(child){
406       if(child->handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr)) return 1;
407       if(child->handle(this,FXSEL(SEL_FOCUS_UP,0),ptr)) return 1;
408       }
409     }
410   return 0;
411   }
412 
413 
414 // Focus moved down
onFocusDown(FXObject *,FXSelector,void * ptr)415 long FXTabBook::onFocusDown(FXObject*,FXSelector,void* ptr){
416   if(options&TABBOOK_SIDEWAYS){
417     return handle(this,FXSEL(SEL_FOCUS_NEXT,0),ptr);
418     }
419   if(getFocus()){
420     FXWindow *child=NULL;
421     if(indexOfChild(getFocus())&1){     // We're on a panel
422       if(options&TABBOOK_BOTTOMTABS) child=getFocus()->getPrev();
423       }
424     else{                               // We're on a tab
425       if(!(options&TABBOOK_BOTTOMTABS)) child=getFocus()->getNext();
426       }
427     if(child){
428       if(child->handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr)) return 1;
429       if(child->handle(this,FXSEL(SEL_FOCUS_DOWN,0),ptr)) return 1;
430       }
431     }
432   return 0;
433   }
434 
435 
436 // Focus moved left
onFocusLeft(FXObject *,FXSelector,void * ptr)437 long FXTabBook::onFocusLeft(FXObject*,FXSelector,void* ptr){
438   if(!(options&TABBOOK_SIDEWAYS)){
439     return handle(this,FXSEL(SEL_FOCUS_PREV,0),ptr);
440     }
441   if(getFocus()){
442     FXWindow *child=NULL;
443     if(indexOfChild(getFocus())&1){     // We're on a panel
444       if(!(options&TABBOOK_BOTTOMTABS)) child=getFocus()->getPrev();
445       }
446     else{                               // We're on a tab
447       if(options&TABBOOK_BOTTOMTABS) child=getFocus()->getNext();
448       }
449     if(child){
450       if(child->handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr)) return 1;
451       if(child->handle(this,FXSEL(SEL_FOCUS_LEFT,0),ptr)) return 1;
452       }
453     }
454   return 0;
455   }
456 
457 
458 // Focus moved right
onFocusRight(FXObject *,FXSelector,void * ptr)459 long FXTabBook::onFocusRight(FXObject*,FXSelector,void* ptr){
460   if(!(options&TABBOOK_SIDEWAYS)){
461     return handle(this,FXSEL(SEL_FOCUS_NEXT,0),ptr);
462     }
463   if(getFocus()){
464     FXWindow *child=NULL;
465     if(indexOfChild(getFocus())&1){     // We're on a panel
466       if(options&TABBOOK_BOTTOMTABS) child=getFocus()->getPrev();
467       }
468     else{                               // We're on a tab
469       if(!(options&TABBOOK_BOTTOMTABS)) child=getFocus()->getNext();
470       }
471     if(child){
472       if(child->handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr)) return 1;
473       if(child->handle(this,FXSEL(SEL_FOCUS_RIGHT,0),ptr)) return 1;
474       }
475     }
476   return 0;
477   }
478 
479 }
480