1 /********************************************************************************
2 *                                                                               *
3 *                     T o o l   B a r   T a b   O b j e c t                     *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 1999,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 "fxkeys.h"
26 #include "FXArray.h"
27 #include "FXHash.h"
28 #include "FXMutex.h"
29 #include "FXStream.h"
30 #include "FXString.h"
31 #include "FXSize.h"
32 #include "FXPoint.h"
33 #include "FXRectangle.h"
34 #include "FXStringDictionary.h"
35 #include "FXSettings.h"
36 #include "FXRegistry.h"
37 #include "FXFont.h"
38 #include "FXEvent.h"
39 #include "FXWindow.h"
40 #include "FXDCWindow.h"
41 #include "FXApp.h"
42 #include "FXToolBarTab.h"
43 
44 
45 /*
46   To do:
47 */
48 
49 
50 // Size
51 #define TOOLBARTAB_WIDTH    9       // Width for horizontal toolbar tab
52 #define TOOLBARTAB_HEIGHT   24      // Height for horizontal toolbar tab
53 
54 // Tool Bar Tab styles
55 #define TOOLBARTAB_MASK        TOOLBARTAB_VERTICAL
56 
57 using namespace FX;
58 
59 /*******************************************************************************/
60 
61 namespace FX {
62 
63 // Map
64 FXDEFMAP(FXToolBarTab) FXToolBarTabMap[]={
65   FXMAPFUNC(SEL_UPDATE,0,FXToolBarTab::onUpdate),
66   FXMAPFUNC(SEL_PAINT,0,FXToolBarTab::onPaint),
67   FXMAPFUNC(SEL_ENTER,0,FXToolBarTab::onEnter),
68   FXMAPFUNC(SEL_LEAVE,0,FXToolBarTab::onLeave),
69   FXMAPFUNC(SEL_UNGRABBED,0,FXToolBarTab::onUngrabbed),
70   FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,FXToolBarTab::onLeftBtnPress),
71   FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,FXToolBarTab::onLeftBtnRelease),
72   FXMAPFUNC(SEL_KEYPRESS,0,FXToolBarTab::onKeyPress),
73   FXMAPFUNC(SEL_KEYRELEASE,0,FXToolBarTab::onKeyRelease),
74   FXMAPFUNC(SEL_QUERY_TIP,0,FXToolBarTab::onQueryTip),
75   FXMAPFUNC(SEL_UPDATE,FXToolBarTab::ID_COLLAPSE,FXToolBarTab::onUpdCollapse),
76   FXMAPFUNC(SEL_UPDATE,FXToolBarTab::ID_UNCOLLAPSE,FXToolBarTab::onUpdUncollapse),
77   FXMAPFUNC(SEL_COMMAND,FXToolBarTab::ID_COLLAPSE,FXToolBarTab::onCmdCollapse),
78   FXMAPFUNC(SEL_COMMAND,FXToolBarTab::ID_UNCOLLAPSE,FXToolBarTab::onCmdUncollapse),
79   FXMAPFUNC(SEL_COMMAND,FXToolBarTab::ID_SETTIPSTRING,FXToolBarTab::onCmdSetTip),
80   FXMAPFUNC(SEL_COMMAND,FXToolBarTab::ID_GETTIPSTRING,FXToolBarTab::onCmdGetTip),
81   };
82 
83 
84 // Object implementation
FXIMPLEMENT(FXToolBarTab,FXFrame,FXToolBarTabMap,ARRAYNUMBER (FXToolBarTabMap))85 FXIMPLEMENT(FXToolBarTab,FXFrame,FXToolBarTabMap,ARRAYNUMBER(FXToolBarTabMap))
86 
87 
88 // Deserialization
89 FXToolBarTab::FXToolBarTab(){
90   flags|=FLAG_ENABLED;
91   activeColor=FXRGB(150,156,224);
92   collapsed=false;
93   down=false;
94   }
95 
96 
97 // Construct and init
FXToolBarTab(FXComposite * p,FXObject * tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h)98 FXToolBarTab::FXToolBarTab(FXComposite* p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h):FXFrame(p,opts,x,y,w,h){
99   flags|=FLAG_ENABLED;
100   activeColor=FXRGB(150,156,224);
101   target=tgt;
102   message=sel;
103   collapsed=false;
104   down=false;
105   }
106 
107 
108 // If window can have focus
canFocus() const109 FXbool FXToolBarTab::canFocus() const { return true; }
110 
111 
112 // Enable the window
enable()113 void FXToolBarTab::enable(){
114   if(!(flags&FLAG_ENABLED)){
115     FXFrame::enable();
116     update();
117     }
118   }
119 
120 
121 // Disable the window
disable()122 void FXToolBarTab::disable(){
123   if(flags&FLAG_ENABLED){
124     FXFrame::disable();
125     update();
126     }
127   }
128 
129 
130 // Get default width
getDefaultWidth()131 FXint FXToolBarTab::getDefaultWidth(){
132   FXWindow *sibling=getNext() ? getNext() : getPrev();
133   FXint w;
134   if(options&TOOLBARTAB_VERTICAL){          // Vertical
135     if(collapsed){
136       w=TOOLBARTAB_WIDTH;
137       }
138     else{
139       w=TOOLBARTAB_HEIGHT;
140       if(sibling) w=sibling->getDefaultWidth();
141       }
142     }
143   else{                                     // Horizontal
144     if(collapsed){
145       w=TOOLBARTAB_HEIGHT;
146       if(sibling) w=sibling->getDefaultHeight();
147       }
148     else{
149       w=TOOLBARTAB_WIDTH;
150       }
151     }
152   return w;
153   }
154 
155 
156 // Get default height
getDefaultHeight()157 FXint FXToolBarTab::getDefaultHeight(){
158   FXWindow *sibling=getNext() ? getNext() : getPrev();
159   FXint h;
160   if(options&TOOLBARTAB_VERTICAL){          // Vertical
161     if(collapsed){
162       h=TOOLBARTAB_HEIGHT;
163       if(sibling) h=sibling->getDefaultWidth();
164       }
165     else{
166       h=TOOLBARTAB_WIDTH;
167       }
168     }
169   else{                                     // Horizontal
170     if(collapsed){
171       h=TOOLBARTAB_WIDTH;
172       }
173     else{
174       h=TOOLBARTAB_HEIGHT;
175       if(sibling) h=sibling->getDefaultHeight();
176       }
177     }
178   return h;
179   }
180 
181 
182 // Collapse or uncollapse
collapse(FXbool fold,FXbool notify)183 void FXToolBarTab::collapse(FXbool fold,FXbool notify){
184   FXWindow *sibling;
185   if(fold!=collapsed){
186     sibling=getNext() ? getNext() : getPrev();
187     if(sibling){
188       if(fold){
189         sibling->hide();
190         }
191       else{
192         sibling->show();
193         }
194       }
195     collapsed=fold;
196     recalc();
197     update();
198     if(notify && target) target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)(FXuval)fold);
199     }
200   }
201 
202 
203 // Update
onUpdate(FXObject * sender,FXSelector sel,void * ptr)204 long FXToolBarTab::onUpdate(FXObject* sender,FXSelector sel,void* ptr){
205   FXWindow *sibling=getNext() ? getNext() : getPrev();
206   FXFrame::onUpdate(sender,sel,ptr);
207   if(sibling){
208     if(sibling->shown() && collapsed){
209       collapsed=false;
210       update();
211       recalc();
212       }
213     else if(!sibling->shown() && !collapsed){
214       collapsed=true;
215       update();
216       recalc();
217       }
218     }
219   return 1;
220   }
221 
222 
223 // Entered button
onEnter(FXObject * sender,FXSelector sel,void * ptr)224 long FXToolBarTab::onEnter(FXObject* sender,FXSelector sel,void* ptr){
225   FXFrame::onEnter(sender,sel,ptr);
226   if(isEnabled()){
227     if(flags&FLAG_PRESSED) down=true;
228     update();
229     }
230   return 1;
231   }
232 
233 
234 // Leave button
onLeave(FXObject * sender,FXSelector sel,void * ptr)235 long FXToolBarTab::onLeave(FXObject* sender,FXSelector sel,void* ptr){
236   FXFrame::onLeave(sender,sel,ptr);
237   if(isEnabled()){
238     if(flags&FLAG_PRESSED) down=false;
239     update();
240     }
241   return 1;
242   }
243 
244 
245 // Pressed mouse button
onLeftBtnPress(FXObject * sender,FXSelector sel,void * ptr)246 long FXToolBarTab::onLeftBtnPress(FXObject* sender,FXSelector sel,void* ptr){
247   if(!FXFrame::onLeftBtnPress(sender,sel,ptr)){
248     if(isEnabled() && !(flags&FLAG_PRESSED)){
249       flags|=FLAG_PRESSED;
250       flags&=~FLAG_UPDATE;
251       down=true;
252       update();
253       return 1;
254       }
255     }
256   return 0;
257   }
258 
259 
260 // Released mouse button
onLeftBtnRelease(FXObject * sender,FXSelector sel,void * ptr)261 long FXToolBarTab::onLeftBtnRelease(FXObject* sender,FXSelector sel,void* ptr){
262   FXbool click=down;
263   if(!FXFrame::onLeftBtnRelease(sender,sel,ptr)){
264     if(isEnabled() && (flags&FLAG_PRESSED)){
265       flags|=FLAG_UPDATE;
266       flags&=~FLAG_PRESSED;
267       down=false;
268       update();
269       if(click) collapse(!collapsed,true);
270       return 1;
271       }
272     }
273   return 0;
274   }
275 
276 
277 // The widget lost the grab for some reason
onUngrabbed(FXObject * sender,FXSelector sel,void * ptr)278 long FXToolBarTab::onUngrabbed(FXObject* sender,FXSelector sel,void* ptr){
279   FXFrame::onUngrabbed(sender,sel,ptr);
280   flags&=~FLAG_PRESSED;
281   flags|=FLAG_UPDATE;
282   down=false;
283   update();
284   return 1;
285   }
286 
287 
288 // Key Press
onKeyPress(FXObject *,FXSelector,void * ptr)289 long FXToolBarTab::onKeyPress(FXObject*,FXSelector,void* ptr){
290   FXEvent* event=(FXEvent*)ptr;
291   flags&=~FLAG_TIP;
292   if(isEnabled() && !(flags&FLAG_PRESSED)){
293     if(target && target->tryHandle(this,FXSEL(SEL_KEYPRESS,message),ptr)) return 1;
294     if(event->code==KEY_space || event->code==KEY_KP_Space){
295       down=true;
296       update();
297       flags|=FLAG_PRESSED;
298       flags&=~FLAG_UPDATE;
299       return 1;
300       }
301     }
302   return 0;
303   }
304 
305 
306 // Key Release
onKeyRelease(FXObject *,FXSelector,void * ptr)307 long FXToolBarTab::onKeyRelease(FXObject*,FXSelector,void* ptr){
308   FXEvent* event=(FXEvent*)ptr;
309   if(isEnabled() && (flags&FLAG_PRESSED)){
310     if(target && target->tryHandle(this,FXSEL(SEL_KEYRELEASE,message),ptr)) return 1;
311     if(event->code==KEY_space || event->code==KEY_KP_Space){
312       down=false;
313       update();
314       flags|=FLAG_UPDATE;
315       flags&=~FLAG_PRESSED;
316       collapse(!collapsed,true);
317       return 1;
318       }
319     }
320   return 0;
321   }
322 
323 
324 // Collapse
onCmdCollapse(FXObject *,FXSelector,void *)325 long FXToolBarTab::onCmdCollapse(FXObject*,FXSelector,void*){
326   collapse(true,true);
327   return 1;
328   }
329 
330 
331 // Update collapse
onUpdCollapse(FXObject * sender,FXSelector,void *)332 long FXToolBarTab::onUpdCollapse(FXObject* sender,FXSelector,void*){
333   if(collapsed)
334     sender->handle(this,FXSEL(SEL_COMMAND,ID_CHECK),NULL);
335   else
336     sender->handle(this,FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
337   return 1;
338   }
339 
340 
341 // Uncollapse
onCmdUncollapse(FXObject *,FXSelector,void *)342 long FXToolBarTab::onCmdUncollapse(FXObject*,FXSelector,void*){
343   collapse(false,true);
344   return 1;
345   }
346 
347 
348 // Update uncollapse
onUpdUncollapse(FXObject * sender,FXSelector,void *)349 long FXToolBarTab::onUpdUncollapse(FXObject* sender,FXSelector,void*){
350   if(!collapsed)
351     sender->handle(this,FXSEL(SEL_COMMAND,ID_CHECK),NULL);
352   else
353     sender->handle(this,FXSEL(SEL_COMMAND,ID_UNCHECK),NULL);
354   return 1;
355   }
356 
357 
358 // We were asked about tip text
onQueryTip(FXObject * sender,FXSelector sel,void * ptr)359 long FXToolBarTab::onQueryTip(FXObject* sender,FXSelector sel,void* ptr){
360   if(FXFrame::onQueryTip(sender,sel,ptr)) return 1;
361   if((flags&FLAG_TIP) && !tip.empty()){
362     sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&tip);
363     return 1;
364     }
365   return 0;
366   }
367 
368 
369 // Set tip using a message
onCmdSetTip(FXObject *,FXSelector,void * ptr)370 long FXToolBarTab::onCmdSetTip(FXObject*,FXSelector,void* ptr){
371   setTipText(*((FXString*)ptr));
372   return 1;
373   }
374 
375 
376 // Get tip using a message
onCmdGetTip(FXObject *,FXSelector,void * ptr)377 long FXToolBarTab::onCmdGetTip(FXObject*,FXSelector,void* ptr){
378   *((FXString*)ptr)=getTipText();
379   return 1;
380   }
381 
382 
383 // Draw horizontal speckles
drawHSpeckles(FXDCWindow & dc,FXint x,FXint w)384 void FXToolBarTab::drawHSpeckles(FXDCWindow& dc,FXint x,FXint w){
385   FXint i;
386   dc.setForeground(hiliteColor);
387   for(i=0; i<w-5; i+=4){dc.drawPoint(x+i,2);dc.drawPoint(x+i+1,5);}
388   dc.setForeground(shadowColor);
389   for(i=0; i<w-5; i+=4){dc.drawPoint(x+i+1,3);dc.drawPoint(x+i+2,6);}
390   }
391 
392 
393 // Draw vertical speckles
drawVSpeckles(FXDCWindow & dc,FXint y,FXint h)394 void FXToolBarTab::drawVSpeckles(FXDCWindow& dc,FXint y,FXint h){
395   FXint i;
396   dc.setForeground(hiliteColor);
397   for(i=0; i<h-5; i+=3){dc.drawPoint(2,y+i+1);dc.drawPoint(5,y+i);}
398   dc.setForeground(shadowColor);
399   for(i=0; i<h-5; i+=3){dc.drawPoint(6,y+i+1);dc.drawPoint(3,y+i+2);}
400   }
401 
402 
403 // Draw up arrow
drawUpArrow(FXDCWindow & dc)404 void FXToolBarTab::drawUpArrow(FXDCWindow& dc){
405   dc.setForeground(borderColor);
406   dc.drawLine(2,height-5,6,height-5);
407   dc.drawPoint(3,height-6);
408   dc.drawPoint(4,height-7);
409   dc.drawPoint(5,height-6);
410   dc.drawPoint(4,height-6);
411   }
412 
413 
414 // Draw down arrow
drawDownArrow(FXDCWindow & dc)415 void FXToolBarTab::drawDownArrow(FXDCWindow& dc){
416   dc.setForeground(borderColor);
417   dc.drawLine(2,4,6,4);
418   dc.drawPoint(3,5);
419   dc.drawPoint(4,6);
420   dc.drawPoint(5,5);
421   dc.drawPoint(4,5);
422   }
423 
424 
425 // Draw left arrow
drawLeftArrow(FXDCWindow & dc)426 void FXToolBarTab::drawLeftArrow(FXDCWindow& dc){
427   dc.setForeground(borderColor);
428   dc.drawLine(width-5,2,width-5,6);
429   dc.drawPoint(width-6,3);
430   dc.drawPoint(width-7,4);
431   dc.drawPoint(width-6,5);
432   dc.drawPoint(width-6,4);
433   }
434 
435 
436 // Draw right arrow
drawRightArrow(FXDCWindow & dc)437 void FXToolBarTab::drawRightArrow(FXDCWindow& dc){
438   dc.setForeground(borderColor);
439   dc.drawLine(4,2,4,6);
440   dc.drawPoint(5,3);
441   dc.drawPoint(6,4);
442   dc.drawPoint(5,5);
443   dc.drawPoint(5,4);
444   }
445 
446 
447 // Handle repaint
onPaint(FXObject *,FXSelector,void * ptr)448 long FXToolBarTab::onPaint(FXObject*,FXSelector,void* ptr){
449   FXEvent *ev=(FXEvent*)ptr;
450   FXDCWindow dc(this,ev);
451 
452   // Got a border at all?
453   if(options&(FRAME_RAISED|FRAME_SUNKEN)){
454 
455     // Draw sunken if enabled and either checked or pressed
456     if(isEnabled() && down){
457       if(down) dc.setForeground(hiliteColor); else dc.setForeground(backColor);
458       dc.fillRectangle(border,border,width-border*2,height-border*2);
459       if(options&FRAME_THICK) drawDoubleSunkenRectangle(dc,0,0,width,height);
460       else drawSunkenRectangle(dc,0,0,width,height);
461       }
462 
463     // Draw in up state if disabled or up
464     else{
465       if(underCursor())
466         dc.setForeground(activeColor);
467       else
468         dc.setForeground(backColor);
469       dc.fillRectangle(border,border,width-border*2,height-border*2);
470       if(options&FRAME_THICK) drawDoubleRaisedRectangle(dc,0,0,width,height);
471       else drawRaisedRectangle(dc,0,0,width,height);
472       }
473     }
474 
475   // No borders
476   else{
477     if(isEnabled() && down){
478       dc.setForeground(hiliteColor);
479       dc.fillRectangle(0,0,width,height);
480       }
481     else{
482       if(underCursor())
483         dc.setForeground(activeColor);
484       else
485         dc.setForeground(backColor);
486       dc.fillRectangle(0,0,width,height);
487       }
488     }
489 
490   // Draw spickles
491   if(options&TOOLBARTAB_VERTICAL){          // Vertical
492     if(collapsed){
493       if(options&LAYOUT_BOTTOM){
494         drawVSpeckles(dc,3,height-10);
495         drawUpArrow(dc);
496         }
497       else{
498         drawVSpeckles(dc,10,height-10);
499         drawDownArrow(dc);
500         }
501       }
502     else{
503       if(options&LAYOUT_RIGHT){
504         drawHSpeckles(dc,3,width-10);
505         drawLeftArrow(dc);
506         }
507       else{
508         drawHSpeckles(dc,10,width-10);
509         drawRightArrow(dc);
510         }
511       }
512     }
513   else{                                     // Horizontal
514     if(collapsed){
515       if(options&LAYOUT_RIGHT){
516         drawHSpeckles(dc,3,width-10);
517         drawLeftArrow(dc);
518         }
519       else{
520         drawHSpeckles(dc,10,width-10);
521         drawRightArrow(dc);
522         }
523       }
524     else{
525       if(options&LAYOUT_BOTTOM){
526         drawVSpeckles(dc,3,height-10);
527         drawUpArrow(dc);
528         }
529       else{
530         drawVSpeckles(dc,10,height-10);
531         drawDownArrow(dc);
532         }
533       }
534     }
535   return 1;
536   }
537 
538 
539 // Change tab style
setTabStyle(FXuint style)540 void FXToolBarTab::setTabStyle(FXuint style){
541   FXuint opts=(options&~TOOLBARTAB_MASK) | (style&TOOLBARTAB_MASK);
542   if(options!=opts){
543     options=opts;
544     update();
545     }
546   }
547 
548 
549 // Get tab style
getTabStyle() const550 FXuint FXToolBarTab::getTabStyle() const {
551   return (options&TOOLBARTAB_MASK);
552   }
553 
554 
555 // Set text color
setActiveColor(FXColor clr)556 void FXToolBarTab::setActiveColor(FXColor clr){
557   if(clr!=activeColor){
558     activeColor=clr;
559     update();
560     }
561   }
562 
563 
564 // Save object to stream
save(FXStream & store) const565 void FXToolBarTab::save(FXStream& store) const {
566   FXFrame::save(store);
567   store << activeColor;
568   store << tip;
569   store << collapsed;
570   }
571 
572 
573 
574 // Load object from stream
load(FXStream & store)575 void FXToolBarTab::load(FXStream& store){
576   FXFrame::load(store);
577   store >> activeColor;
578   store >> tip;
579   store >> collapsed;
580   }
581 
582 }
583