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