1 /********************************************************************************
2 * *
3 * H e a d e r O b j e c t *
4 * *
5 *********************************************************************************
6 * Copyright (C) 1997,2006 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: FXHeader.cpp,v 1.103.2.4 2006/11/21 19:01:53 fox Exp $ *
23 ********************************************************************************/
24 #include "xincs.h"
25 #include "fxver.h"
26 #include "fxdefs.h"
27 #include "FXHash.h"
28 #include "FXThread.h"
29 #include "FXStream.h"
30 #include "FXString.h"
31 #include "FXObjectList.h"
32 #include "FXSize.h"
33 #include "FXPoint.h"
34 #include "FXRectangle.h"
35 #include "FXSettings.h"
36 #include "FXRegistry.h"
37 #include "FXApp.h"
38 #include "FXDCWindow.h"
39 #include "FXFont.h"
40 #include "FXIcon.h"
41 #include "FXHeader.h"
42
43
44
45
46 /*
47 Notes:
48 - Perhaps, some way to drive via keyboard?
49 - Allow some header items to be stretchable.
50 - Add minimum, maximum size constraints to items.
51 - Should up/down arrows go sideways when vertically oriented?
52 - Perhaps perform drawing of border in item also.
53 - Like for FXScrollArea, pos is always <=0 when scrolled.
54 - Maybe handy feature to initialize size to default item width
55 if initial size = 0.
56 - Maybe allow attach from right instead of only from left; perhaps
57 also attach both (or none).
58 */
59
60
61 #define FUDGE 4
62 #define ICON_SPACING 4
63 #define HEADER_MASK (HEADER_BUTTON|HEADER_TRACKING|HEADER_VERTICAL|HEADER_RESIZE)
64
65 using namespace FX;
66
67 /*******************************************************************************/
68
69 namespace FX {
70
71
72 // Object implementation
73 FXIMPLEMENT(FXHeaderItem,FXObject,NULL,0)
74
75
76 // Draw item
draw(const FXHeader * header,FXDC & dc,FXint x,FXint y,FXint w,FXint h)77 void FXHeaderItem::draw(const FXHeader* header,FXDC& dc,FXint x,FXint y,FXint w,FXint h){
78 register FXint tx,ty,tw,th,ix,iy,iw,ih,s,ml,mr,mt,mb,beg,end,t,xx,yy,bb,aa,ax,ay;
79 register FXFont *font=header->getFont();
80
81 // Get border width and padding
82 bb=header->getBorderWidth();
83 ml=header->getPadLeft()+bb;
84 mr=header->getPadRight()+bb;
85 mt=header->getPadTop()+bb;
86 mb=header->getPadBottom()+bb;
87
88 // Shrink by margins
89 x+=ml; w-=ml+mr;
90 y+=mt; h-=mt+mb;
91
92 // Initial clip rectangle
93 dc.setClipRectangle(x,y,w,h);
94
95 // Text width and height
96 tw=th=iw=ih=beg=s=0;
97 do{
98 end=beg;
99 while(end<label.length() && label[end]!='\n') end++;
100 if((t=font->getTextWidth(&label[beg],end-beg))>tw) tw=t;
101 th+=font->getFontHeight();
102 beg=end+1;
103 }
104 while(end<label.length());
105
106 // Icon size
107 if(icon){
108 iw=icon->getWidth();
109 ih=icon->getHeight();
110 }
111
112 // Icon-text spacing
113 if(iw && tw) s=ICON_SPACING;
114
115 // Draw arrows
116 if(state&(ARROW_UP|ARROW_DOWN)){
117 aa=(font->getFontHeight()-5)|1;
118 ay=y+(h-aa)/2;
119 ax=x+w-aa-2;
120 if(state&ARROW_UP){
121 dc.setForeground(header->getHiliteColor());
122 dc.drawLine(ax+aa/2,ay,ax+aa-1,ay+aa);
123 dc.drawLine(ax,ay+aa,ax+aa,ay+aa);
124 dc.setForeground(header->getShadowColor());
125 dc.drawLine(ax+aa/2,ay,ax,ay+aa);
126 }
127 else{
128 dc.setForeground(header->getHiliteColor());
129 dc.drawLine(ax+aa/2,ay+aa,ax+aa-1,ay);
130 dc.setForeground(header->getShadowColor());
131 dc.drawLine(ax+aa/2,ay+aa,ax,ay);
132 dc.drawLine(ax,ay,ax+aa,ay);
133 }
134 w-=aa+4;
135 dc.setClipRectangle(x,y,w,h);
136 }
137
138 // Fix x coordinate
139 if(state&LEFT){
140 if(state&BEFORE){ ix=x; tx=ix+iw+s; }
141 else if(state&AFTER){ tx=x; ix=tx+tw+s; }
142 else{ ix=x; tx=x; }
143 }
144 else if(state&RIGHT){
145 if(state&BEFORE){ tx=x+w-tw; ix=tx-iw-s; }
146 else if(state&AFTER){ ix=x+w-iw; tx=ix-tw-s; }
147 else{ ix=x+w-iw; tx=x+w-tw; }
148 }
149 else{
150 if(state&BEFORE){ ix=x+(w-tw-iw-s)/2; tx=ix+iw+s; }
151 else if(state&AFTER){ tx=x+(w-tw-iw-s)/2; ix=tx+tw+s; }
152 else{ ix=x+(w-iw)/2; tx=x+(w-tw)/2; }
153 }
154
155 // Fix y coordinate
156 if(state&TOP){
157 if(state&ABOVE){ iy=y; ty=iy+ih; }
158 else if(state&BELOW){ ty=y; iy=ty+th; }
159 else{ iy=y; ty=y; }
160 }
161 else if(state&BOTTOM){
162 if(state&ABOVE){ ty=y+h-th; iy=ty-ih; }
163 else if(state&BELOW){ iy=y+h-ih; ty=iy-th; }
164 else{ iy=y+h-ih; ty=y+h-th; }
165 }
166 else{
167 if(state&ABOVE){ iy=y+(h-th-ih)/2; ty=iy+ih; }
168 else if(state&BELOW){ ty=y+(h-th-ih)/2; iy=ty+th; }
169 else{ iy=y+(h-ih)/2; ty=y+(h-th)/2; }
170 }
171
172 // Offset a bit when pressed
173 if(state&PRESSED){ tx++; ty++; ix++; iy++; }
174
175 // Paint icon
176 if(icon){
177 dc.drawIcon(icon,ix,iy);
178 }
179
180 // Text color
181 dc.setForeground(header->getTextColor());
182
183 // Draw text
184 yy=ty+font->getFontAscent();
185 beg=0;
186 do{
187 end=beg;
188 while(end<label.length() && label[end]!='\n') end++;
189 if(state&LEFT) xx=tx;
190 else if(state&RIGHT) xx=tx+tw-font->getTextWidth(&label[beg],end-beg);
191 else xx=tx+(tw-font->getTextWidth(&label[beg],end-beg))/2;
192 dc.drawText(xx,yy,&label[beg],end-beg);
193 yy+=font->getFontHeight();
194 beg=end+1;
195 }
196 while(end<label.length());
197
198 // Restore original clip path
199 dc.clearClipRectangle();
200 }
201
202
203 // Create icon
create()204 void FXHeaderItem::create(){ if(icon) icon->create(); }
205
206
207 // No op, we don't own icon
destroy()208 void FXHeaderItem::destroy(){ /*if(icon) icon->destroy();*/ }
209
210
211 // Detach from icon resource
detach()212 void FXHeaderItem::detach(){ if(icon) icon->detach(); }
213
214
215 // Change sort direction
setArrowDir(FXbool dir)216 void FXHeaderItem::setArrowDir(FXbool dir){
217 state&=~(ARROW_UP|ARROW_DOWN);
218 if(dir==TRUE) state|=ARROW_UP; else if(dir==FALSE) state|=ARROW_DOWN;
219 }
220
221
222 // Change sort direction
getArrowDir() const223 FXbool FXHeaderItem::getArrowDir() const {
224 return (state&ARROW_UP) ? TRUE : (state&ARROW_DOWN) ? FALSE : MAYBE;
225 }
226
227
228 // Change justify mode
setJustify(FXuint justify)229 void FXHeaderItem::setJustify(FXuint justify){
230 state=(state&~(RIGHT|LEFT|TOP|BOTTOM)) | (justify&(RIGHT|LEFT|TOP|BOTTOM));
231 }
232
233 // Change icon positioning
setIconPosition(FXuint mode)234 void FXHeaderItem::setIconPosition(FXuint mode){
235 state=(state&~(BEFORE|AFTER|ABOVE|BELOW)) | (mode&(BEFORE|AFTER|ABOVE|BELOW));
236 }
237
238
239 // Set button state
setPressed(FXbool pressed)240 void FXHeaderItem::setPressed(FXbool pressed){
241 if(pressed) state|=PRESSED; else state&=~PRESSED;
242 }
243
244
245 // Get width of item
getWidth(const FXHeader * header) const246 FXint FXHeaderItem::getWidth(const FXHeader* header) const {
247 register FXint ml=header->getPadLeft()+header->getBorderWidth();
248 register FXint mr=header->getPadRight()+header->getBorderWidth();
249 register FXFont *font=header->getFont();
250 register FXint beg,end,tw,iw,s,t,w;
251 tw=iw=beg=s=0;
252 if(icon) iw=icon->getWidth();
253 do{
254 end=beg;
255 while(end<label.length() && label[end]!='\n') end++;
256 if((t=font->getTextWidth(&label[beg],end-beg))>tw) tw=t;
257 beg=end+1;
258 }
259 while(end<label.length());
260 if(iw && tw) s=4;
261 if(state&(BEFORE|AFTER))
262 w=iw+tw+s;
263 else
264 w=FXMAX(iw,tw);
265 return ml+mr+w;
266 }
267
268
269 // Get height of item
getHeight(const FXHeader * header) const270 FXint FXHeaderItem::getHeight(const FXHeader* header) const {
271 register FXint mt=header->getPadTop()+header->getBorderWidth();
272 register FXint mb=header->getPadBottom()+header->getBorderWidth();
273 register FXFont *font=header->getFont();
274 register FXint beg,end,th,ih,h;
275 th=ih=beg=0;
276 if(icon) ih=icon->getHeight();
277 do{
278 end=beg;
279 while(end<label.length() && label[end]!='\n') end++;
280 th+=font->getFontHeight();
281 beg=end+1;
282 }
283 while(end<label.length());
284 if(state&(ABOVE|BELOW))
285 h=ih+th;
286 else
287 h=FXMAX(ih,th);
288 return h+mt+mb;
289 }
290
291
292 // Change item's text label
setText(const FXString & txt)293 void FXHeaderItem::setText(const FXString& txt){
294 label=txt;
295 }
296
297
298 // Change item's icon
setIcon(FXIcon * icn)299 void FXHeaderItem::setIcon(FXIcon* icn){
300 icon=icn;
301 }
302
303
304 // Save data
save(FXStream & store) const305 void FXHeaderItem::save(FXStream& store) const {
306 FXObject::save(store);
307 store << label;
308 store << icon;
309 store << size;
310 store << pos;
311 store << state;
312 }
313
314
315 // Load data
load(FXStream & store)316 void FXHeaderItem::load(FXStream& store){
317 FXObject::load(store);
318 store >> label;
319 store >> icon;
320 store >> size;
321 store >> pos;
322 store >> state;
323 }
324
325
326 /*******************************************************************************/
327
328 // Map
329 FXDEFMAP(FXHeader) FXHeaderMap[]={
330 FXMAPFUNC(SEL_PAINT,0,FXHeader::onPaint),
331 FXMAPFUNC(SEL_MOTION,0,FXHeader::onMotion),
332 FXMAPFUNC(SEL_LEFTBUTTONPRESS,0,FXHeader::onLeftBtnPress),
333 FXMAPFUNC(SEL_LEFTBUTTONRELEASE,0,FXHeader::onLeftBtnRelease),
334 FXMAPFUNC(SEL_UNGRABBED,0,FXHeader::onUngrabbed),
335 FXMAPFUNC(SEL_TIMEOUT,FXHeader::ID_TIPTIMER,FXHeader::onTipTimer),
336 FXMAPFUNC(SEL_QUERY_TIP,0,FXHeader::onQueryTip),
337 FXMAPFUNC(SEL_QUERY_HELP,0,FXHeader::onQueryHelp),
338 };
339
340
341 // Object implementation
FXIMPLEMENT(FXHeader,FXFrame,FXHeaderMap,ARRAYNUMBER (FXHeaderMap))342 FXIMPLEMENT(FXHeader,FXFrame,FXHeaderMap,ARRAYNUMBER(FXHeaderMap))
343
344
345 // Make a Header
346 FXHeader::FXHeader(){
347 flags|=FLAG_ENABLED|FLAG_SHOWN;
348 textColor=0;
349 font=NULL;
350 pos=0;
351 active=-1;
352 activepos=0;
353 activesize=0;
354 offset=0;
355 }
356
357
358 // Make a Header
FXHeader(FXComposite * p,FXObject * tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb)359 FXHeader::FXHeader(FXComposite* p,FXObject* tgt,FXSelector sel,FXuint opts,FXint x,FXint y,FXint w,FXint h,FXint pl,FXint pr,FXint pt,FXint pb):
360 FXFrame(p,opts,x,y,w,h,pl,pr,pt,pb){
361 flags|=FLAG_ENABLED|FLAG_SHOWN;
362 target=tgt;
363 message=sel;
364 textColor=getApp()->getForeColor();
365 font=getApp()->getNormalFont();
366 pos=0;
367 active=-1;
368 activepos=0;
369 activesize=0;
370 offset=0;
371 }
372
373
374 // Create window
create()375 void FXHeader::create(){
376 register FXint i;
377 FXFrame::create();
378 for(i=0; i<items.no(); i++){items[i]->create();}
379 font->create();
380 }
381
382
383 // Detach window
detach()384 void FXHeader::detach(){
385 register FXint i;
386 FXFrame::detach();
387 for(i=0; i<items.no(); i++){items[i]->detach();}
388 font->detach();
389 }
390
391
392 // Get default width
getDefaultWidth()393 FXint FXHeader::getDefaultWidth(){
394 register FXint i,t,w=0;
395 if(options&HEADER_VERTICAL){
396 for(i=0; i<items.no(); i++){
397 if((t=items[i]->getWidth(this))>w) w=t;
398 }
399 }
400 else{
401 for(i=0; i<items.no(); i++){
402 w+=items[i]->getSize();
403 }
404 }
405 return w;
406 }
407
408
409 // Get default height
getDefaultHeight()410 FXint FXHeader::getDefaultHeight(){
411 register FXint i,t,h=0;
412 if(options&HEADER_VERTICAL){
413 for(i=0; i<items.no(); i++){
414 h+=items[i]->getSize();
415 }
416 }
417 else{
418 for(i=0; i<items.no(); i++){
419 if((t=items[i]->getHeight(this))>h) h=t;
420 }
421 }
422 return h;
423 }
424
425
426
427 // Return total size
getTotalSize() const428 FXint FXHeader::getTotalSize() const {
429 return items.no() ? items[items.no()-1]->getPos()+items[items.no()-1]->getSize()-items[0]->getPos() : 0;
430 }
431
432
433 // Create custom item
createItem(const FXString & text,FXIcon * icon,FXint size,void * ptr)434 FXHeaderItem *FXHeader::createItem(const FXString& text,FXIcon* icon,FXint size,void* ptr){
435 return new FXHeaderItem(text,icon,size,ptr);
436 }
437
438
439 // Retrieve item
getItem(FXint index) const440 FXHeaderItem *FXHeader::getItem(FXint index) const {
441 if(index<0 || items.no()<=index){ fxerror("%s::getItem: index out of range.\n",getClassName()); }
442 return items[index];
443 }
444
445
446 // Replace item with another
setItem(FXint index,FXHeaderItem * item,FXbool notify)447 FXint FXHeader::setItem(FXint index,FXHeaderItem* item,FXbool notify){
448
449 // Must have item
450 if(!item){ fxerror("%s::setItem: item is NULL.\n",getClassName()); }
451
452 // Must be in range
453 if(index<0 || items.no()<=index){ fxerror("%s::setItem: index out of range.\n",getClassName()); }
454
455 // Notify item will be replaced
456 if(notify && target){target->tryHandle(this,FXSEL(SEL_REPLACED,message),(void*)(FXival)index);}
457
458 // Copy the size over
459 item->setSize(items[index]->getSize());
460 item->setPos(items[index]->getPos());
461
462 // Delete old
463 delete items[index];
464
465 // Add new
466 items[index]=item;
467
468 // Redo layout
469 recalc();
470 return index;
471 }
472
473
474 // Replace item with another
setItem(FXint index,const FXString & text,FXIcon * icon,FXint size,void * ptr,FXbool notify)475 FXint FXHeader::setItem(FXint index,const FXString& text,FXIcon *icon,FXint size,void* ptr,FXbool notify){
476 return setItem(index,createItem(text,icon,FXMAX(size,0),ptr),notify);
477 }
478
479
480 // Insert item
insertItem(FXint index,FXHeaderItem * item,FXbool notify)481 FXint FXHeader::insertItem(FXint index,FXHeaderItem* item,FXbool notify){
482 register FXint i,d;
483
484 // Must have item
485 if(!item){ fxerror("%s::insertItem: item is NULL.\n",getClassName()); }
486
487 // Must be in range
488 if(index<0 || items.no()<index){ fxerror("%s::insertItem: index out of range.\n",getClassName()); }
489
490 // New item position
491 item->setPos((0<index)?items[index-1]->getPos()+items[index-1]->getSize():0);
492
493 // Move over remaining items
494 for(i=index,d=item->getSize(); i<items.no(); i++) items[i]->setPos(items[i]->getPos()+d);
495
496 // Add item to list
497 items.insert(index,item);
498
499 // Notify item has been inserted
500 if(notify && target){target->tryHandle(this,FXSEL(SEL_INSERTED,message),(void*)(FXival)index);}
501
502 // Redo layout
503 recalc();
504
505 return index;
506 }
507
508
509 // Insert item
insertItem(FXint index,const FXString & text,FXIcon * icon,FXint size,void * ptr,FXbool notify)510 FXint FXHeader::insertItem(FXint index,const FXString& text,FXIcon *icon,FXint size,void* ptr,FXbool notify){
511 return insertItem(index,createItem(text,icon,FXMAX(size,0),ptr),notify);
512 }
513
514
515 // Append item
appendItem(FXHeaderItem * item,FXbool notify)516 FXint FXHeader::appendItem(FXHeaderItem* item,FXbool notify){
517 return insertItem(items.no(),item,notify);
518 }
519
520
521 // Append item
appendItem(const FXString & text,FXIcon * icon,FXint size,void * ptr,FXbool notify)522 FXint FXHeader::appendItem(const FXString& text,FXIcon *icon,FXint size,void* ptr,FXbool notify){
523 return insertItem(items.no(),createItem(text,icon,FXMAX(size,0),ptr),notify);
524 }
525
526
527 // Prepend item
prependItem(FXHeaderItem * item,FXbool notify)528 FXint FXHeader::prependItem(FXHeaderItem* item,FXbool notify){
529 return insertItem(0,item,notify);
530 }
531
532 // Prepend item
prependItem(const FXString & text,FXIcon * icon,FXint size,void * ptr,FXbool notify)533 FXint FXHeader::prependItem(const FXString& text,FXIcon *icon,FXint size,void* ptr,FXbool notify){
534 return insertItem(0,createItem(text,icon,FXMAX(size,0),ptr),notify);
535 }
536
537
538 // Fill list by appending items from array of strings
fillItems(const FXchar ** strings,FXIcon * icon,FXint size,void * ptr,FXbool notify)539 FXint FXHeader::fillItems(const FXchar** strings,FXIcon *icon,FXint size,void* ptr,FXbool notify){
540 register FXint n=0;
541 if(strings){
542 while(strings[n]){
543 appendItem(strings[n++],icon,size,ptr,notify);
544 }
545 }
546 return n;
547 }
548
549
550 // Fill list by appending items from newline separated strings
fillItems(const FXString & strings,FXIcon * icon,FXint size,void * ptr,FXbool notify)551 FXint FXHeader::fillItems(const FXString& strings,FXIcon *icon,FXint size,void* ptr,FXbool notify){
552 register FXint n=0;
553 FXString text;
554 while(!(text=strings.section('\n',n)).empty()){
555 appendItem(text,icon,size,ptr,notify);
556 n++;
557 }
558 return n;
559 }
560
561
562 // Extract node from list
extractItem(FXint index,FXbool notify)563 FXHeaderItem* FXHeader::extractItem(FXint index,FXbool notify){
564 register FXHeaderItem *result;
565 register FXint i,d;
566
567 // Must be in range
568 if(index<0 || items.no()<=index){ fxerror("%s::extractItem: index out of range.\n",getClassName()); }
569
570 // Notify item will be deleted
571 if(notify && target){target->tryHandle(this,FXSEL(SEL_DELETED,message),(void*)(FXival)index);}
572
573 // Adjust remaining columns
574 for(i=index+1,d=items[index]->getSize(); i<items.no(); i++) items[i]->setPos(items[i]->getPos()-d);
575
576 // Delete item
577 result=items[index];
578
579 // Remove item from list
580 items.erase(index);
581
582 // Redo layout
583 recalc();
584
585 // Return item
586 return result;
587 }
588
589
590 // Remove node from list
removeItem(FXint index,FXbool notify)591 void FXHeader::removeItem(FXint index,FXbool notify){
592 register FXint i,d;
593
594 // Must be in range
595 if(index<0 || items.no()<=index){ fxerror("%s::removeItem: index out of range.\n",getClassName()); }
596
597 // Notify item will be deleted
598 if(notify && target){target->tryHandle(this,FXSEL(SEL_DELETED,message),(void*)(FXival)index);}
599
600 // Adjust remaining columns
601 for(i=index+1,d=items[index]->getSize(); i<items.no(); i++) items[i]->setPos(items[i]->getPos()-d);
602
603 // Delete item
604 delete items[index];
605
606 // Remove item from list
607 items.erase(index);
608
609 // Redo layout
610 recalc();
611 }
612
613
614 // Remove all items
clearItems(FXbool notify)615 void FXHeader::clearItems(FXbool notify){
616
617 // Delete items
618 for(FXint index=items.no()-1; 0<=index; index--){
619 if(notify && target){target->tryHandle(this,FXSEL(SEL_DELETED,message),(void*)(FXival)index);}
620 delete items[index];
621 }
622
623 // Free array
624 items.clear();
625
626 // Redo layout
627 recalc();
628 }
629
630
631 // Change item's text
setItemText(FXint index,const FXString & text)632 void FXHeader::setItemText(FXint index,const FXString& text){
633 if(index<0 || items.no()<=index){ fxerror("%s::setItemText: index out of range.\n",getClassName()); }
634 if(items[index]->getText()!=text){
635 items[index]->setText(text);
636 recalc();
637 }
638 }
639
640
641 // Get item's text
getItemText(FXint index) const642 FXString FXHeader::getItemText(FXint index) const {
643 if(index<0 || items.no()<=index){ fxerror("%s::getItemText: index out of range.\n",getClassName()); }
644 return items[index]->getText();
645 }
646
647
648 // Change item's icon
setItemIcon(FXint index,FXIcon * icon)649 void FXHeader::setItemIcon(FXint index,FXIcon* icon){
650 if(index<0 || items.no()<=index){ fxerror("%s::setItemIcon: index out of range.\n",getClassName()); }
651 if(items[index]->getIcon()!=icon){
652 items[index]->setIcon(icon);
653 recalc();
654 }
655 }
656
657
658 // Get item's icon
getItemIcon(FXint index) const659 FXIcon* FXHeader::getItemIcon(FXint index) const {
660 if(index<0 || items.no()<=index){ fxerror("%s::getItemIcon: index out of range.\n",getClassName()); }
661 return items[index]->getIcon();
662 }
663
664
665 // Change item's size
setItemSize(FXint index,FXint size)666 void FXHeader::setItemSize(FXint index,FXint size){
667 register FXint i,d;
668 if(index<0 || items.no()<=index){ fxerror("%s::setItemSize: index out of range.\n",getClassName()); }
669 if(size<0) size=0;
670 d=size-items[index]->getSize();
671 if(d!=0){
672 items[index]->setSize(size);
673 for(i=index+1; i<items.no(); i++) items[i]->setPos(items[i]->getPos()+d);
674 recalc();
675 }
676 }
677
678
679 // Get item's size
getItemSize(FXint index) const680 FXint FXHeader::getItemSize(FXint index) const {
681 if(index<0 || items.no()<=index){ fxerror("%s::getItemSize: index out of range.\n",getClassName()); }
682 return items[index]->getSize();
683 }
684
685
686 // Get item's offset
getItemOffset(FXint index) const687 FXint FXHeader::getItemOffset(FXint index) const {
688 if(index<0 || items.no()<=index){ fxerror("%s::getItemOffset: index out of range.\n",getClassName()); }
689 return pos+items[index]->getPos();
690 }
691
692
693 // Get index of item at offset
getItemAt(FXint coord) const694 FXint FXHeader::getItemAt(FXint coord) const {
695 register FXint h=items.no()-1,l=0,m;
696 coord=coord-pos;
697 if(l<=h){
698 if(coord<items[l]->getPos()) return -1;
699 if(coord>=items[h]->getPos()+items[h]->getSize()) return items.no();
700 do{
701 m=(h+l)>>1;
702 if(coord<items[m]->getPos()) h=m-1;
703 else if(coord>=items[m]->getPos()+items[m]->getSize()) l=m+1;
704 else break;
705 }
706 while(h>=l);
707 return m;
708 }
709 return coord<0 ? -1 : 0;
710 }
711
712
713 // Set item data
setItemData(FXint index,void * ptr)714 void FXHeader::setItemData(FXint index,void* ptr){
715 if(index<0 || items.no()<=index){ fxerror("%s::setItemData: index out of range.\n",getClassName()); }
716 items[index]->setData(ptr);
717 }
718
719
720 // Get item data
getItemData(FXint index) const721 void* FXHeader::getItemData(FXint index) const {
722 if(index<0 || items.no()<=index){ fxerror("%s::getItemData: index out of range.\n",getClassName()); }
723 return items[index]->getData();
724 }
725
726
727 // Change sort direction
setArrowDir(FXint index,FXbool dir)728 void FXHeader::setArrowDir(FXint index,FXbool dir){
729 if(index<0 || items.no()<=index){ fxerror("%s::setArrowDir: index out of range.\n",getClassName()); }
730 if(items[index]->getArrowDir()!=dir){
731 items[index]->setArrowDir(dir);
732 updateItem(index);
733 }
734 }
735
736
737 // Return sort direction
getArrowDir(FXint index) const738 FXbool FXHeader::getArrowDir(FXint index) const {
739 if(index<0 || items.no()<=index){ fxerror("%s::getArrowDir: index out of range.\n",getClassName()); }
740 return items[index]->getArrowDir();
741 }
742
743
744 // Change item justification
setItemJustify(FXint index,FXuint justify)745 void FXHeader::setItemJustify(FXint index,FXuint justify){
746 if(index<0 || items.no()<=index){ fxerror("%s::setItemJustify: index out of range.\n",getClassName()); }
747 if(items[index]->getJustify()!=justify){
748 items[index]->setJustify(justify);
749 updateItem(index);
750 }
751 }
752
753
754 // Return item justification
getItemJustify(FXint index) const755 FXuint FXHeader::getItemJustify(FXint index) const {
756 if(index<0 || items.no()<=index){ fxerror("%s::getItemJustify: index out of range.\n",getClassName()); }
757 return items[index]->getJustify();
758 }
759
760
761 // Change relative position of icon and text of item
setItemIconPosition(FXint index,FXuint mode)762 void FXHeader::setItemIconPosition(FXint index,FXuint mode){
763 if(index<0 || items.no()<=index){ fxerror("%s::setItemIconPosition: index out of range.\n",getClassName()); }
764 if(items[index]->getIconPosition()!=mode){
765 items[index]->setIconPosition(mode);
766 recalc();
767 }
768 }
769
770
771 // Return relative icon and text position
getItemIconPosition(FXint index) const772 FXuint FXHeader::getItemIconPosition(FXint index) const {
773 if(index<0 || items.no()<=index){ fxerror("%s::getItemIconPosition: index out of range.\n",getClassName()); }
774 return items[index]->getIconPosition();
775 }
776
777
778 // Changed button item's pressed state
setItemPressed(FXint index,FXbool pressed)779 void FXHeader::setItemPressed(FXint index,FXbool pressed){
780 if(index<0 || items.no()<=index){ fxerror("%s::setItemPressed: index out of range.\n",getClassName()); }
781 if(pressed!=items[index]->isPressed()){
782 items[index]->setPressed(pressed);
783 updateItem(index);
784 }
785 }
786
787
788 // Return TRUE if button item is pressed in
isItemPressed(FXint index) const789 FXbool FXHeader::isItemPressed(FXint index) const {
790 if(index<0 || items.no()<=index){ fxerror("%s::isItemPressed: index out of range.\n",getClassName()); }
791 return items[index]->isPressed();
792 }
793
794
795 // Scroll to make given item visible
makeItemVisible(FXint index)796 void FXHeader::makeItemVisible(FXint index){
797 register FXint newpos,ioffset,isize,space;
798 if(xid){
799 newpos=pos;
800 if(0<=index && index<items.no()){
801 space=(options&HEADER_VERTICAL)?height:width;
802 ioffset=items[index]->getPos();
803 isize=items[index]->getSize();
804 newpos=pos;
805 if(newpos+ioffset+isize>=space) newpos=space-ioffset-isize;
806 if(newpos+ioffset<=0) newpos=-ioffset;
807 }
808 setPosition(newpos);
809 }
810 }
811
812
813 // Repaint cell at index
updateItem(FXint index) const814 void FXHeader::updateItem(FXint index) const {
815 if(index<0 || items.no()<=index){ fxerror("%s::updateItem: index out of range.\n",getClassName()); }
816 if(options&HEADER_VERTICAL)
817 update(0,pos+items[index]->getPos(),width,items[index]->getSize());
818 else
819 update(pos+items[index]->getPos(),0,items[index]->getSize(),height);
820 }
821
822
823 // Do layout
layout()824 void FXHeader::layout(){
825
826 // Force repaint
827 update();
828
829 // No more dirty
830 flags&=~FLAG_DIRTY;
831 }
832
833
834 // Handle repaint
onPaint(FXObject *,FXSelector,void * ptr)835 long FXHeader::onPaint(FXObject*,FXSelector,void* ptr){
836 FXEvent *ev=(FXEvent*)ptr;
837 FXDCWindow dc(this,ev);
838 register FXint x,y,w,h,i,ilo,ihi;
839
840 // Set font
841 dc.setFont(font);
842
843 // Paint background
844 dc.setForeground(backColor);
845 dc.fillRectangle(ev->rect.x,ev->rect.y,ev->rect.w,ev->rect.h);
846
847 // Vertical
848 if(options&HEADER_VERTICAL){
849
850 // Determine affected items
851 ilo=getItemAt(ev->rect.y);
852 ihi=getItemAt(ev->rect.y+ev->rect.h);
853
854 // Fragment below first item
855 if(ilo<0){
856 y=pos;
857 if(0<items.no()){
858 y=pos+items[0]->getPos();
859 }
860 if(0<y){
861 if(options&FRAME_THICK)
862 drawDoubleRaisedRectangle(dc,0,0,width,y);
863 else if(options&FRAME_RAISED)
864 drawRaisedRectangle(dc,0,0,width,y);
865 }
866 ilo=0;
867 }
868
869 // Fragment above last item
870 if(ihi>=items.no()){
871 y=pos;
872 if(0<items.no()){
873 y=pos+items[items.no()-1]->getPos()+items[items.no()-1]->getSize();
874 }
875 if(y<height){
876 if(options&FRAME_THICK)
877 drawDoubleRaisedRectangle(dc,0,y,width,height-y);
878 else if(options&FRAME_RAISED)
879 drawRaisedRectangle(dc,0,y,width,height-y);
880 }
881 ihi=items.no()-1;
882 }
883
884 // Draw only affected items
885 for(i=ilo; i<=ihi; i++){
886 y=pos+items[i]->getPos();
887 h=items[i]->getSize();
888 if(items[i]->isPressed()){
889 if(options&FRAME_THICK)
890 drawDoubleSunkenRectangle(dc,0,y,width,h);
891 else if(options&FRAME_RAISED)
892 drawSunkenRectangle(dc,0,y,width,h);
893 }
894 else{
895 if(options&FRAME_THICK)
896 drawDoubleRaisedRectangle(dc,0,y,width,h);
897 else if(options&FRAME_RAISED)
898 drawRaisedRectangle(dc,0,y,width,h);
899 }
900 items[i]->draw(this,dc,0,y,width,h);
901 }
902 }
903
904 // Horizontal
905 else{
906
907 // Determine affected items
908 ilo=getItemAt(ev->rect.x);
909 ihi=getItemAt(ev->rect.x+ev->rect.w);
910
911 // Fragment below first item
912 if(ilo<0){
913 x=pos;
914 if(0<items.no()){
915 x=pos+items[0]->getPos();
916 }
917 if(0<x){
918 if(options&FRAME_THICK)
919 drawDoubleRaisedRectangle(dc,0,0,x,height);
920 else if(options&FRAME_RAISED)
921 drawRaisedRectangle(dc,0,0,x,height);
922 }
923 ilo=0;
924 }
925
926 // Fragment above last item
927 if(ihi>=items.no()){
928 x=pos;
929 if(0<items.no()){
930 x=pos+items[items.no()-1]->getPos()+items[items.no()-1]->getSize();
931 }
932 if(x<width){
933 if(options&FRAME_THICK)
934 drawDoubleRaisedRectangle(dc,x,0,width-x,height);
935 else if(options&FRAME_RAISED)
936 drawRaisedRectangle(dc,x,0,width-x,height);
937 }
938 ihi=items.no()-1;
939 }
940
941 // Draw only the affected items
942 for(i=ilo; i<=ihi; i++){
943 x=pos+items[i]->getPos();
944 w=items[i]->getSize();
945 if(items[i]->isPressed()){
946 if(options&FRAME_THICK)
947 drawDoubleSunkenRectangle(dc,x,0,w,height);
948 else if(options&FRAME_RAISED)
949 drawSunkenRectangle(dc,x,0,w,height);
950 }
951 else{
952 if(options&FRAME_THICK)
953 drawDoubleRaisedRectangle(dc,x,0,w,height);
954 else if(options&FRAME_RAISED)
955 drawRaisedRectangle(dc,x,0,w,height);
956 }
957 items[i]->draw(this,dc,x,0,w,height);
958 }
959 }
960 return 1;
961 }
962
963
964 // We were asked about tip text
onQueryTip(FXObject * sender,FXSelector sel,void * ptr)965 long FXHeader::onQueryTip(FXObject* sender,FXSelector sel,void* ptr){
966 if(FXWindow::onQueryTip(sender,sel,ptr)) return 1;
967 if(flags&FLAG_TIP){
968 FXint index,cx,cy; FXuint btns;
969 getCursorPosition(cx,cy,btns);
970 index=getItemAt((options&HEADER_VERTICAL)?cy:cx);
971 if(0<=index && index<items.no()){
972 FXString string=items[index]->getText();
973 sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&string);
974 return 1;
975 }
976 }
977 return 0;
978 }
979
980
981 // We were asked about status text
onQueryHelp(FXObject * sender,FXSelector sel,void * ptr)982 long FXHeader::onQueryHelp(FXObject* sender,FXSelector sel,void* ptr){
983 if(FXWindow::onQueryHelp(sender,sel,ptr)) return 1;
984 if((flags&FLAG_HELP) && !help.empty()){
985 sender->handle(this,FXSEL(SEL_COMMAND,ID_SETSTRINGVALUE),(void*)&help);
986 return 1;
987 }
988 return 0;
989 }
990
991
992 // We timed out, i.e. the user didn't move for a while
onTipTimer(FXObject *,FXSelector,void *)993 long FXHeader::onTipTimer(FXObject*,FXSelector,void*){
994 FXTRACE((250,"%s::onTipTimer %p\n",getClassName(),this));
995 flags|=FLAG_TIP;
996 return 1;
997 }
998
999
1000 // Button being pressed
onLeftBtnPress(FXObject *,FXSelector,void * ptr)1001 long FXHeader::onLeftBtnPress(FXObject*,FXSelector,void* ptr){
1002 FXEvent* event=(FXEvent*)ptr;
1003 FXint coord;
1004 flags&=~FLAG_TIP;
1005 handle(this,FXSEL(SEL_FOCUS_SELF,0),ptr);
1006 if(isEnabled()){
1007 grab();
1008
1009 // First change callback
1010 if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONPRESS,message),ptr)) return 1;
1011
1012 // Where clicked
1013 coord=(options&HEADER_VERTICAL)?event->win_y:event->win_x;
1014 active=getItemAt(coord);
1015 if(0<=active && active<items.no()){
1016 if((options&HEADER_RESIZE) && (active<items.no()) && (pos+items[active]->getPos()+items[active]->getSize()-FUDGE<coord)){
1017 activepos=pos+items[active]->getPos();
1018 activesize=items[active]->getSize();
1019 offset=coord-activepos-activesize;
1020 setDragCursor((options&HEADER_VERTICAL)?getApp()->getDefaultCursor(DEF_VSPLIT_CURSOR):getApp()->getDefaultCursor(DEF_HSPLIT_CURSOR));
1021 flags|=FLAG_PRESSED|FLAG_TRYDRAG;
1022 }
1023 else if((options&HEADER_RESIZE) && (0<active) && (coord<pos+items[active-1]->getPos()+items[active-1]->getSize()+FUDGE)){
1024 active--;
1025 activepos=pos+items[active]->getPos();
1026 activesize=items[active]->getSize();
1027 offset=coord-activepos-activesize;
1028 setDragCursor((options&HEADER_VERTICAL)?getApp()->getDefaultCursor(DEF_VSPLIT_CURSOR):getApp()->getDefaultCursor(DEF_HSPLIT_CURSOR));
1029 flags|=FLAG_PRESSED|FLAG_TRYDRAG;
1030 }
1031 else if((options&HEADER_BUTTON) && (active<items.no())){
1032 activepos=pos+items[active]->getPos();
1033 activesize=items[active]->getSize();
1034 setItemPressed(active,TRUE);
1035 flags|=FLAG_PRESSED;
1036 }
1037 }
1038 flags&=~FLAG_UPDATE;
1039 return 1;
1040 }
1041 return 0;
1042 }
1043
1044
1045 // Button being released
onLeftBtnRelease(FXObject *,FXSelector,void * ptr)1046 long FXHeader::onLeftBtnRelease(FXObject*,FXSelector,void* ptr){
1047 FXuint flg=flags;
1048 if(isEnabled()){
1049 ungrab();
1050 flags|=FLAG_UPDATE;
1051 flags&=~(FLAG_PRESSED|FLAG_DODRAG|FLAG_TRYDRAG);
1052
1053 // First chance callback
1054 if(target && target->tryHandle(this,FXSEL(SEL_LEFTBUTTONRELEASE,message),ptr)) return 1;
1055
1056 // Set cursor back
1057 setDragCursor(getApp()->getDefaultCursor(DEF_ARROW_CURSOR));
1058
1059 // Clicked on split
1060 if(flg&FLAG_TRYDRAG){
1061 if(target) target->tryHandle(this,FXSEL(SEL_CLICKED,message),(void*)(FXival)active);
1062 return 1;
1063 }
1064
1065 // Dragged split
1066 if(flg&FLAG_DODRAG){
1067 if(!(options&HEADER_TRACKING)){
1068 drawSplit(activepos+activesize);
1069 setItemSize(active,activesize);
1070 if(target) target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)active);
1071 }
1072 return 1;
1073 }
1074
1075 // Pressed button
1076 if(flg&FLAG_PRESSED){
1077 if(items[active]->isPressed()){
1078 setItemPressed(active,FALSE);
1079 if(target) target->tryHandle(this,FXSEL(SEL_COMMAND,message),(void*)(FXival)active);
1080 }
1081 return 1;
1082 }
1083 }
1084 return 0;
1085 }
1086
1087
1088 // Button being moved
onMotion(FXObject *,FXSelector,void * ptr)1089 long FXHeader::onMotion(FXObject*,FXSelector,void* ptr){
1090 FXEvent* event=(FXEvent*)ptr;
1091 FXint oldsplit,newsplit,index;
1092 FXuint flg=flags;
1093
1094 // Kill the tip
1095 flags&=~FLAG_TIP;
1096
1097 // Kill the tip timer
1098 getApp()->removeTimeout(this,ID_TIPTIMER);
1099
1100 // Tentatively dragging split
1101 if(flags&FLAG_TRYDRAG){
1102 if(!(options&HEADER_TRACKING)) drawSplit(activepos+activesize);
1103 flags&=~FLAG_TRYDRAG;
1104 flags|=FLAG_DODRAG;
1105 return 1;
1106 }
1107
1108 // Dragging split
1109 if(flags&FLAG_DODRAG){
1110 oldsplit=activepos+activesize;
1111 if(options&HEADER_VERTICAL)
1112 activesize=event->win_y-offset-activepos;
1113 else
1114 activesize=event->win_x-offset-activepos;
1115 if(activesize<0) activesize=0;
1116 newsplit=activepos+activesize;
1117 if(newsplit!=oldsplit){
1118 if(!(options&HEADER_TRACKING)){
1119 drawSplit(oldsplit);
1120 drawSplit(newsplit);
1121 }
1122 else{
1123 setItemSize(active,activesize);
1124 if(target) target->tryHandle(this,FXSEL(SEL_CHANGED,message),(void*)(FXival)active);
1125 }
1126 }
1127 return 1;
1128 }
1129
1130 // Had the button pressed
1131 if(flags&FLAG_PRESSED){
1132 if(options&HEADER_VERTICAL){
1133 if(activepos<=event->win_y && event->win_y<activepos+activesize && 0<=event->win_x && event->win_x<width){
1134 setItemPressed(active,TRUE);
1135 }
1136 else{
1137 setItemPressed(active,FALSE);
1138 }
1139 }
1140 else{
1141 if(activepos<=event->win_x && event->win_x<activepos+activesize && 0<=event->win_y && event->win_y<height){
1142 setItemPressed(active,TRUE);
1143 }
1144 else{
1145 setItemPressed(active,FALSE);
1146 }
1147 }
1148 return 1;
1149 }
1150
1151 // When hovering over a split, show the split cursor
1152 if(options&HEADER_RESIZE){
1153 if(options&HEADER_VERTICAL){
1154 index=getItemAt(event->win_y-FUDGE);
1155 if(0<=index && index<items.no() && pos+items[index]->getPos()+items[index]->getSize()-FUDGE<event->win_y){
1156 setDefaultCursor(getApp()->getDefaultCursor(DEF_VSPLIT_CURSOR));
1157 }
1158 else{
1159 setDefaultCursor(getApp()->getDefaultCursor(DEF_ARROW_CURSOR));
1160 }
1161 }
1162 else{
1163 index=getItemAt(event->win_x-FUDGE);
1164 if(0<=index && index<items.no() && pos+items[index]->getPos()+items[index]->getSize()-FUDGE<event->win_x){
1165 setDefaultCursor(getApp()->getDefaultCursor(DEF_HSPLIT_CURSOR));
1166 }
1167 else{
1168 setDefaultCursor(getApp()->getDefaultCursor(DEF_ARROW_CURSOR));
1169 }
1170 }
1171 }
1172
1173 // Reset tip timer if nothing's going on
1174 getApp()->addTimeout(this,ID_TIPTIMER,getApp()->getMenuPause());
1175
1176 // Force GUI update only when needed
1177 return (flg&FLAG_TIP);
1178 }
1179
1180
1181 // The widget lost the grab for some reason
onUngrabbed(FXObject * sender,FXSelector sel,void * ptr)1182 long FXHeader::onUngrabbed(FXObject* sender,FXSelector sel,void* ptr){
1183 FXFrame::onUngrabbed(sender,sel,ptr);
1184 flags&=~FLAG_PRESSED;
1185 flags&=~FLAG_CHANGED;
1186 flags|=FLAG_UPDATE;
1187 return 1;
1188 }
1189
1190
1191 // Draw the split
drawSplit(FXint p)1192 void FXHeader::drawSplit(FXint p){
1193 FXDCWindow dc(getParent());
1194 FXint px,py;
1195 translateCoordinatesTo(px,py,getParent(),p,p);
1196 dc.clipChildren(FALSE);
1197 dc.setFunction(BLT_NOT_DST);
1198 if(options&HEADER_VERTICAL){
1199 dc.fillRectangle(0,py,getParent()->getWidth(),2);
1200 }
1201 else{
1202 dc.fillRectangle(px,0,2,getParent()->getHeight());
1203 }
1204 }
1205
1206
1207 // Set position, scrolling contents
setPosition(FXint p)1208 void FXHeader::setPosition(FXint p){
1209 if(pos!=p){
1210 if(options&HEADER_VERTICAL)
1211 scroll(0,0,width,height,0,p-pos);
1212 else
1213 scroll(0,0,width,height,p-pos,0);
1214 pos=p;
1215 }
1216 }
1217
1218
1219 // Change the font
setFont(FXFont * fnt)1220 void FXHeader::setFont(FXFont* fnt){
1221 if(!fnt){ fxerror("%s::setFont: NULL font specified.\n",getClassName()); }
1222 if(font!=fnt){
1223 font=fnt;
1224 recalc();
1225 update();
1226 }
1227 }
1228
1229
1230 // Set text color
setTextColor(FXColor clr)1231 void FXHeader::setTextColor(FXColor clr){
1232 if(textColor!=clr){
1233 textColor=clr;
1234 update();
1235 }
1236 }
1237
1238
1239 // Header style change
setHeaderStyle(FXuint style)1240 void FXHeader::setHeaderStyle(FXuint style){
1241 FXuint opts=(options&~HEADER_MASK) | (style&HEADER_MASK);
1242 if(options!=opts){
1243 options=opts;
1244 recalc();
1245 update();
1246 }
1247 }
1248
1249
1250 // Get header style
getHeaderStyle() const1251 FXuint FXHeader::getHeaderStyle() const {
1252 return (options&HEADER_MASK);
1253 }
1254
1255
1256 // Change help text
setHelpText(const FXString & text)1257 void FXHeader::setHelpText(const FXString& text){
1258 help=text;
1259 }
1260
1261
1262 // Save object to stream
save(FXStream & store) const1263 void FXHeader::save(FXStream& store) const {
1264 FXFrame::save(store);
1265 items.save(store);
1266 store << textColor;
1267 store << font;
1268 store << help;
1269 store << pos;
1270 }
1271
1272
1273
1274 // Load object from stream
load(FXStream & store)1275 void FXHeader::load(FXStream& store){
1276 FXFrame::load(store);
1277 items.load(store);
1278 store >> textColor;
1279 store >> font;
1280 store >> help;
1281 store >> pos;
1282 }
1283
1284
1285 // Clean up
~FXHeader()1286 FXHeader::~FXHeader(){
1287 getApp()->removeTimeout(this,ID_TIPTIMER);
1288 clearItems();
1289 font=(FXFont*)-1L;
1290 }
1291 }
1292