1 /********************************************************************************
2 *                                                                               *
3 *                 S h u t t e r   C o n t a i n e r   W i d g e t               *
4 *                                                                               *
5 *********************************************************************************
6 * Copyright (C) 1998,2021 by Charles W. Warren.   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 "FXStringDictionary.h"
34 #include "FXSettings.h"
35 #include "FXRegistry.h"
36 #include "FXAccelTable.h"
37 #include "FXEvent.h"
38 #include "FXWindow.h"
39 #include "FXApp.h"
40 #include "FXIcon.h"
41 #include "FXFrame.h"
42 #include "FXPacker.h"
43 #include "FXVerticalFrame.h"
44 #include "FXLabel.h"
45 #include "FXButton.h"
46 #include "FXScrollBar.h"
47 #include "FXScrollWindow.h"
48 #include "FXShutter.h"
49 
50 /*
51   Notes:
52   - Horizontal scrollbar is turned off; this means default width is computed
53     from contents.
54   - Scroll window in collapsed items is not hidden, but made zero-height.  This
55     means content width is still computed properly and thus the FXShutter will
56     get a default width which depends on ALL items, not just the current one.
57   - On a collapsing item, the scrollbar stays the way it was before the collapse
58     started, while on an expanding item, the scrollbar stays off until the item is
59     fully expanded.
60 */
61 
62 using namespace FX;
63 
64 /*******************************************************************************/
65 
66 namespace FX {
67 
68 // Map
69 FXDEFMAP(FXShutterItem) FXShutterItemMap[]={
70   FXMAPFUNC(SEL_FOCUS_UP,0,FXShutterItem::onFocusUp),
71   FXMAPFUNC(SEL_FOCUS_DOWN,0,FXShutterItem::onFocusDown),
72   FXMAPFUNC(SEL_COMMAND,FXShutterItem::ID_SHUTTERITEM_BUTTON,FXShutterItem::onCmdButton),
73   };
74 
75 
76 // Object implementation
FXIMPLEMENT(FXShutterItem,FXVerticalFrame,FXShutterItemMap,ARRAYNUMBER (FXShutterItemMap))77 FXIMPLEMENT(FXShutterItem,FXVerticalFrame,FXShutterItemMap,ARRAYNUMBER(FXShutterItemMap))
78 
79 
80 // Serialization
81 FXShutterItem::FXShutterItem(){
82   button=(FXButton*)-1L;
83   scrollWindow=(FXScrollWindow*)-1L;
84   content=(FXVerticalFrame*)-1L;
85   }
86 
87 
88 // Construct shutter item
FXShutterItem(FXShutter * p,const FXString & text,FXIcon * icon,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb,FXint hs,FXint vs)89 FXShutterItem::FXShutterItem(FXShutter* p,const FXString& text,FXIcon* icon,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb,FXint hs,FXint vs):FXVerticalFrame(p,(opts&~(PACK_UNIFORM_HEIGHT|PACK_UNIFORM_WIDTH)),x,y,w,h,0,0,0,0,0,0){
90   button=new FXButton(this,text,icon,this,FXShutterItem::ID_SHUTTERITEM_BUTTON,FRAME_RAISED|FRAME_THICK|LAYOUT_FILL_X,0,0,0,0,0,0,0,0);
91   scrollWindow=new FXScrollWindow(this,HSCROLLING_OFF|LAYOUT_FILL_X|LAYOUT_FIX_HEIGHT,0,0,0,0);
92   content=new FXVerticalFrame(scrollWindow,LAYOUT_FILL_X|LAYOUT_FILL_Y|(opts&(PACK_UNIFORM_HEIGHT|PACK_UNIFORM_WIDTH)),0,0,0,0,pl,pr,pt,pb,hs,vs);
93   content->setBackColor(getApp()->getShadowColor());
94   }
95 
96 
97 // Button Pressed
onCmdButton(FXObject *,FXSelector,void * ptr)98 long FXShutterItem::onCmdButton(FXObject*,FXSelector,void* ptr){
99   getParent()->handle(this,FXSEL(SEL_COMMAND,FXShutter::ID_OPEN_SHUTTERITEM),ptr);
100   return 1;
101   }
102 
103 
104 // Focus moved up
onFocusUp(FXObject * sender,FXSelector sel,void * ptr)105 long FXShutterItem::onFocusUp(FXObject* sender,FXSelector sel,void* ptr){
106   return FXVerticalFrame::onFocusPrev(sender,sel,ptr);
107   }
108 
109 
110 // Focus moved down
onFocusDown(FXObject * sender,FXSelector sel,void * ptr)111 long FXShutterItem::onFocusDown(FXObject* sender,FXSelector sel,void* ptr){
112   return FXVerticalFrame::onFocusNext(sender,sel,ptr);
113   }
114 
115 
116 // Change help text
setHelpText(const FXString & text)117 void FXShutterItem::setHelpText(const FXString& text){
118   button->setHelpText(text);
119   }
120 
121 
122 // Get help text
getHelpText() const123 FXString FXShutterItem::getHelpText() const {
124   return button->getHelpText();
125   }
126 
127 
128 // Change tip text
setTipText(const FXString & text)129 void FXShutterItem::setTipText(const FXString& text){
130   button->setTipText(text);
131   }
132 
133 
134 // Get tip text
getTipText() const135 FXString FXShutterItem::getTipText() const {
136   return button->getTipText();
137   }
138 
139 
140 // Save object to stream
save(FXStream & store) const141 void FXShutterItem::save(FXStream& store) const {
142   FXVerticalFrame::save(store);
143   store << button;
144   store << scrollWindow;
145   store << content;
146   }
147 
148 
149 // Load object from stream
load(FXStream & store)150 void FXShutterItem::load(FXStream& store){
151   FXVerticalFrame::load(store);
152   store >> button;
153   store >> scrollWindow;
154   store >> content;
155   }
156 
157 
158 // Thrash it
~FXShutterItem()159 FXShutterItem::~FXShutterItem(){
160   button=(FXButton*)-1L;
161   scrollWindow=(FXScrollWindow*)-1L;
162   content=(FXVerticalFrame*)-1L;
163   }
164 
165 
166 /*******************************************************************************/
167 
168 // Map
169 FXDEFMAP(FXShutter) FXShutterMap[]={
170   FXMAPFUNC(SEL_FOCUS_UP,0,FXShutter::onFocusUp),
171   FXMAPFUNC(SEL_FOCUS_DOWN,0,FXShutter::onFocusDown),
172   FXMAPFUNCS(SEL_UPDATE,FXShutter::ID_OPEN_FIRST,FXShutter::ID_OPEN_LAST,FXShutter::onUpdOpen),
173   FXMAPFUNC(SEL_TIMEOUT,FXShutter::ID_SHUTTER_TIMEOUT,FXShutter::onTimeout),
174   FXMAPFUNC(SEL_COMMAND,FXShutter::ID_OPEN_SHUTTERITEM,FXShutter::onOpenItem),
175   FXMAPFUNC(SEL_COMMAND,FXShutter::ID_SETVALUE,FXShutter::onCmdSetValue),
176   FXMAPFUNC(SEL_COMMAND,FXShutter::ID_SETINTVALUE,FXShutter::onCmdSetIntValue),
177   FXMAPFUNC(SEL_COMMAND,FXShutter::ID_GETINTVALUE,FXShutter::onCmdGetIntValue),
178   FXMAPFUNCS(SEL_COMMAND,FXShutter::ID_OPEN_FIRST,FXShutter::ID_OPEN_LAST,FXShutter::onCmdOpen),
179   };
180 
181 
182 // Object implementation
FXIMPLEMENT(FXShutter,FXVerticalFrame,FXShutterMap,ARRAYNUMBER (FXShutterMap))183 FXIMPLEMENT(FXShutter,FXVerticalFrame,FXShutterMap,ARRAYNUMBER(FXShutterMap))
184 
185 
186 // Serialization
187 FXShutter::FXShutter(){
188   current=-1;
189   closing=-1;
190   closingHeight=0;
191   heightIncrement=1;
192   }
193 
194 
195 // Make shutter
FXShutter(FXComposite * p,FXObject * tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb,FXint hs,FXint vs)196 FXShutter::FXShutter(FXComposite* p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb,FXint hs,FXint vs):FXVerticalFrame(p,opts,x,y,w,h,pl,pr,pt,pb,hs,vs){
197   target=tgt;
198   message=sel;
199   current=-1;
200   closing=-1;
201   closingHeight=0;
202   heightIncrement=1;
203   }
204 
205 
206 // Focus moved up
onFocusUp(FXObject * sender,FXSelector sel,void * ptr)207 long FXShutter::onFocusUp(FXObject* sender,FXSelector sel,void* ptr){
208   return FXVerticalFrame::onFocusPrev(sender,sel,ptr);
209   }
210 
211 
212 // Focus moved down
onFocusDown(FXObject * sender,FXSelector sel,void * ptr)213 long FXShutter::onFocusDown(FXObject* sender,FXSelector sel,void* ptr){
214   return FXVerticalFrame::onFocusNext(sender,sel,ptr);
215   }
216 
217 
218 // Update value from a message
onCmdSetValue(FXObject *,FXSelector,void * ptr)219 long FXShutter::onCmdSetValue(FXObject*,FXSelector,void* ptr){
220   setCurrent((FXint)(FXival)ptr);
221   return 1;
222   }
223 
224 
225 // Update value from a message
onCmdSetIntValue(FXObject *,FXSelector,void * ptr)226 long FXShutter::onCmdSetIntValue(FXObject*,FXSelector,void* ptr){
227   setCurrent(*((FXint*)ptr));
228   return 1;
229   }
230 
231 
232 // Obtain value from text field
onCmdGetIntValue(FXObject *,FXSelector,void * ptr)233 long FXShutter::onCmdGetIntValue(FXObject*,FXSelector,void* ptr){
234   *((FXint*)ptr)=current;
235   return 1;
236   }
237 
238 
239 // Open item
onCmdOpen(FXObject *,FXSelector sel,void *)240 long FXShutter::onCmdOpen(FXObject*,FXSelector sel,void*){
241   setCurrent(FXSELID(sel)-ID_OPEN_FIRST,true);
242   return 1;
243   }
244 
245 
246 // Update the nth button
onUpdOpen(FXObject * sender,FXSelector sel,void * ptr)247 long FXShutter::onUpdOpen(FXObject* sender,FXSelector sel,void* ptr){
248   sender->handle(this,((FXSELID(sel)-ID_OPEN_FIRST)==current) ? FXSEL(SEL_COMMAND,ID_CHECK) : FXSEL(SEL_COMMAND,ID_UNCHECK),ptr);
249   return 1;
250   }
251 
252 
253 // The sender of the message is the item to open up
onOpenItem(FXObject * sender,FXSelector,void *)254 long FXShutter::onOpenItem(FXObject* sender,FXSelector,void*){
255   FXint which=indexOfChild((FXWindow*)sender);
256   if(current==which) which--;
257   if(0<=which){
258     FXTime speed=getApp()->getAnimSpeed();
259     if(0<speed){
260       FXShutterItem *closingItem=(FXShutterItem*)childAtIndex(current);
261       if(closingItem){
262         closingHeight=closingItem->getHeight();
263         closing=current;
264         heightIncrement=0;
265         getApp()->addTimeout(this,ID_SHUTTER_TIMEOUT,speed);
266         }
267       }
268     }
269   setCurrent(which,true);
270   return 1;
271   }
272 
273 
274 // Shutter Item Animation
onTimeout(FXObject *,FXSelector,void *)275 long FXShutter::onTimeout(FXObject*,FXSelector,void*){
276   if(0<=closing){
277     heightIncrement+=5;
278     closingHeight-=heightIncrement;
279     if(closingHeight<0) closingHeight=0;
280     recalc();
281     if(0<closingHeight){
282       getApp()->addTimeout(this,ID_SHUTTER_TIMEOUT,getApp()->getAnimSpeed());
283       return 1;
284       }
285     closing=-1;
286     }
287   return 1;
288   }
289 
290 
291 // Layout
layout()292 void FXShutter::layout(){
293   FXShutterItem* child;
294   FXint i;
295 
296   // One of the children may have disappeared
297   if(current<0) current=0;
298   if(current>=numChildren()) current=numChildren()-1;
299 
300   // Force only one of the children to be open
301   for(child=(FXShutterItem*)getFirst(),i=0; child; child=(FXShutterItem*)child->getNext(),i++){
302     if(i==current){           // Expanded or expanded
303       child->setLayoutHints(LAYOUT_FILL_X|LAYOUT_FILL_Y);
304       child->getScrollWindow()->setLayoutHints(LAYOUT_FILL_X|LAYOUT_FILL_Y);
305       }
306     else if(i==closing){      // Collapsing
307       child->setLayoutHints(LAYOUT_FILL_X);
308       child->getScrollWindow()->setLayoutHints(LAYOUT_FILL_X|LAYOUT_FIX_HEIGHT);
309       child->getScrollWindow()->setHeight(closingHeight);
310       }
311     else{                     // Collapsed
312       child->setLayoutHints(LAYOUT_FILL_X);
313       child->getScrollWindow()->setLayoutHints(LAYOUT_FILL_X|LAYOUT_FIX_HEIGHT);
314       child->getScrollWindow()->setHeight(0);
315       }
316     }
317 
318   // Then layout normally
319   FXVerticalFrame::layout();
320   }
321 
322 
323 // Set current subwindow
setCurrent(FXint panel,FXbool notify)324 void FXShutter::setCurrent(FXint panel,FXbool notify){
325   if(panel!=current && 0<=panel && panel<numChildren()){
326     current=panel;
327     recalc();
328     if(notify && target){ target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)(FXival)current); }
329     }
330   }
331 
332 
333 // Save object to stream
save(FXStream & store) const334 void FXShutter::save(FXStream& store) const {
335   FXVerticalFrame::save(store);
336   store << current;
337   }
338 
339 
340 // Load object from stream
load(FXStream & store)341 void FXShutter::load(FXStream& store){
342   FXVerticalFrame::load(store);
343   store >> current;
344   }
345 
346 
347 // Clean up
~FXShutter()348 FXShutter::~FXShutter() {
349   getApp()->removeTimeout(this,ID_SHUTTER_TIMEOUT);
350   }
351 
352 }
353 
354