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