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