1 /*
2   FXiTe - The Free eXtensIble Text Editor
3   Copyright (c) 2009-2013 Jeffrey Pohlmeyer <yetanothergeek@gmail.com>
4 
5   This program is free software; you can redistribute it and/or modify it
6   under the terms of the GNU General Public License version 3 as
7   published by the Free Software Foundation.
8 
9   This software is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   GNU General Public License for more details.
13 
14   You should have received a copy of the GNU General Public License
15   along with this program; if not, write to the Free Software
16   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18 
19 
20 #include <unistd.h>
21 #include <fx.h>
22 
23 #include "compat.h"
24 #include "appwin_pub.h"
25 
26 #include "intl.h"
27 #include "doctabs.h"
28 
29 
30 FXDEFMAP(DocTabs) DocTabsMap[]={
31   FXMAPFUNC(SEL_COMMAND,DocTabs::ID_TAB_POPUP_MENU,DocTabs::onTabPopupMenu),
32   FXMAPFUNC(SEL_COMMAND,DocTabs::ID_POPUP_CLICK,DocTabs::onPopupClick),
33   FXMAPFUNC(SEL_COMMAND,DocTabs::ID_OPEN_ITEM,DocTabs::onCmdOpenItem),
34 #ifndef WIN32
35   FXMAPFUNC(SEL_DND_ENTER, 0, DocTabs::onDnd),
36   FXMAPFUNC(SEL_DND_LEAVE, 0, DocTabs::onDnd),
37   FXMAPFUNC(SEL_DND_DROP, 0, DocTabs::onDnd),
38   FXMAPFUNC(SEL_DND_MOTION, 0, DocTabs::onDnd),
39 #endif
40 };
41 
42 FXIMPLEMENT(DocTabs,ShadyTabs,DocTabsMap,ARRAYNUMBER(DocTabsMap));
43 
44 
45 
46 #ifdef WIN32
47 # include <windows.h>
create()48 void DocTabs::create()
49 {
50   FXTabBook::create();
51   ::DragAcceptFiles((HWND)id(),true);
52 }
53 #else
onDnd(FXObject * sender,FXSelector sel,void * p)54 long DocTabs::onDnd(FXObject* sender,FXSelector sel, void*p)
55 {
56   switch (FXSELTYPE(sel)) {
57     case SEL_DND_ENTER: {
58       if (getApp()->getCursorWindow()==this) { return 0; }
59       setDragRectangle(0,0,width,height,false);
60       if (offeredDNDType(FROM_DRAGNDROP,urilistType)) {
61         acceptDrop();
62         dnd_accept=true;
63       } else {
64         dnd_accept=false;
65       }
66       break;
67     }
68     case SEL_DND_LEAVE: {
69         dnd_accept=false;
70         break;
71     }
72     case SEL_DND_DROP: {
73       dnd_accept=false;
74       FXuchar*dnd_data=NULL;
75       FXuint size=0;
76       if (getDNDData(FROM_DRAGNDROP,urilistType,dnd_data,size)) {
77         if (dnd_data)  {
78           dnd_accept=false;
79           FXchar *p1, *p2;
80           for (p1=(FXchar*)dnd_data; p1&&*p1; p1=p2) {
81             p2=strchr(p1,'\r');
82             if (p2) {
83               FXString uri;
84               uri.assign(p1,p2-p1);
85               if (compare(uri, "file://",7)==0) {
86                 uri.erase(0,7);
87                 TopWinPub::OpenFile(uri.text(),NULL,false,true);
88               }
89               p2+=2;
90             }
91           }
92         }
93       }
94       break;
95     }
96     case SEL_DND_MOTION:  {
97       if (dnd_accept) { acceptDrop(); }
98       break;
99     }
100   }
101   return 1;
102 }
103 #endif
104 
105 
106 
onPopupClick(FXObject * sender,FXSelector sel,void * p)107 long DocTabs::onPopupClick( FXObject* sender, FXSelector sel, void* p )
108 {
109   DocTab*tab=(DocTab*)(((FXMenuCommand*)sender)->getParent()->getUserData());
110   DocTab*curr=ActiveTab();
111   if ( tab && ActivateTab(tab) ) {
112     TopWinPub::CloseFile(false,true);
113   }
114   if (curr && (tab!=curr)) { ActivateTab(curr); }
115   return 1;
116 }
117 
118 
119 
onTabPopupMenu(FXObject * sender,FXSelector sel,void * p)120 long DocTabs::onTabPopupMenu( FXObject* sender, FXSelector sel, void* p )
121 {
122   FXEvent*ev=(FXEvent*)p;
123   if (!ev->moved) {
124     if (!tab_popup->shown()) { tab_popup->create(); }
125     tab_popup->setUserData(sender);
126     tab_popup->popup(NULL,ev->root_x-2,ev->root_y-2);
127     getApp()->runModalWhileShown(tab_popup);
128   }
129   return 1;
130 }
131 
132 
133 
DocTabs(FXComposite * p,FXObject * trg,FXSelector sel,FXuint opts)134 DocTabs::DocTabs(FXComposite*p,FXObject*trg,FXSelector sel,FXuint opts):
135   ShadyTabs(p,trg,sel,opts,0,0,0,0,0,0,0,0)
136 {
137   tab_width_max=0;
138   tab_popup=new FXMenuPane(this);
139   new FXMenuCommand(tab_popup,_("&Close"),NULL,this,ID_POPUP_CLICK);
140   dnd_accept=false;
141   dropEnable();
142 }
143 
144 
145 
~DocTabs()146 DocTabs::~DocTabs()
147 {
148   delete tab_popup;
149 }
150 
151 
152 
UpdateTabWidths(FXint index,DocTab * tab,FXWindow * page,void * user_data)153 bool DocTabs::UpdateTabWidths(FXint index, DocTab*tab, FXWindow*page, void*user_data)
154 {
155   tab->setText(tab->getText());
156   return true;
157 }
158 
159 
160 
MaxTabWidth(FXint w)161 void DocTabs::MaxTabWidth(FXint w)
162 {
163   if (w!=tab_width_max) {
164     tab_width_max=w;
165     ForEachTab(UpdateTabWidths,NULL,false);
166   }
167 }
168 
169 
170 
171 class MySplitter: public FXSplitter {
172   FXDECLARE(MySplitter);
MySplitter()173   MySplitter(){}
174 protected:
175   FXWindow *last_focused_child;
176   virtual void changeFocus(FXWindow*child);
177 public:
178   long onFocusIn(FXObject*o,FXSelector sel,void* p);
179   long onFixFocus(FXObject*o,FXSelector sel,void* p);
MySplitter(FXComposite * p,FXuint opts)180   MySplitter(FXComposite *p, FXuint opts):FXSplitter(p,opts) { last_focused_child=NULL; }
~MySplitter()181   ~MySplitter() { getApp()->removeChore(this,ID_FIX_FOCUS); }
LastFocusedChild()182   FXWindow *LastFocusedChild() { return last_focused_child; }
183   enum {
184     ID_FIX_FOCUS=FXSplitter::ID_LAST,
185     ID_LAST
186   };
187 };
188 
189 
190 
191 FXDEFMAP(MySplitter) MySplitterMap[] = {
192   FXMAPFUNC(SEL_FOCUSIN,0,MySplitter::onFocusIn),
193   FXMAPFUNC(SEL_CHORE,MySplitter::ID_FIX_FOCUS,MySplitter::onFixFocus)
194 };
195 
FXIMPLEMENT(MySplitter,FXSplitter,MySplitterMap,ARRAYNUMBER (MySplitterMap))196 FXIMPLEMENT(MySplitter,FXSplitter,MySplitterMap,ARRAYNUMBER(MySplitterMap))
197 
198 
199 
200 void MySplitter::changeFocus(FXWindow*child)
201 {
202   if (child) { last_focused_child=child; }
203   FXSplitter::changeFocus(child);
204 }
205 
206 
207 
onFixFocus(FXObject * o,FXSelector sel,void * p)208 long MySplitter::onFixFocus(FXObject*o,FXSelector sel,void* p)
209 {
210   if (p) {
211     ((FXWindow*)p)->setFocus();
212   }
213   return 1;
214 }
215 
216 
217 
onFocusIn(FXObject * o,FXSelector sel,void * p)218 long MySplitter::onFocusIn(FXObject*o,FXSelector sel,void* p)
219 {
220   long rv=FXSplitter::onFocusIn(o,sel,p);
221   if ((numChildren()>1) && last_focused_child) {
222     getApp()->addChore(this,ID_FIX_FOCUS,(void*)last_focused_child);
223   }
224   return rv;
225 }
226 
ActiveView()227 FXWindow*DocTabs::ActiveView()
228 {
229   FXWindow*w=ActivePage();
230   return w?((MySplitter*)w)->LastFocusedChild():NULL;
231   return NULL;
232 }
233 
NewTab(FXString text)234 DocTab*DocTabs::NewTab(FXString text)
235 {
236   DocTab*tab=new DocTab(this,text);
237   switch (getTabStyle()) {
238     case TABBOOK_TOPTABS:    { tab->setTabOrientation(TAB_TOP);    break; }
239     case TABBOOK_BOTTOMTABS: { tab->setTabOrientation(TAB_BOTTOM); break; }
240     case TABBOOK_LEFTTABS:   { tab->setTabOrientation(TAB_LEFT);   break; }
241     case TABBOOK_RIGHTTABS:  { tab->setTabOrientation(TAB_RIGHT);  break; }
242   }
243   MySplitter*frame=new MySplitter(this,FRAME_RAISED|LAYOUT_FILL|SPLITTER_VERTICAL);
244   if (shown()) {
245     tab->create();
246     frame->create();
247     tab->setText(text);
248   }
249   return tab;
250 }
251 
252 
253 
MoveTab(FXint how)254 void DocTabs::MoveTab(FXint how){
255   DocTab*tab=ActiveTab();
256   if (!tab) { return; }
257   FXWindow*page=tab->getNext();
258   if (!page) { return; }
259   FXint iCurr=getCurrent();
260   switch (how) {
261     case MOVETOLAST: {
262       tab->reparent(this, NULL);
263       page->reparent(this, NULL);
264       setCurrent((numChildren()/2)-1,true);
265       break;
266     }
267     case MOVETOFIRST: {
268       page->reparent(this, getFirst());
269       tab->reparent(this, page);
270       setCurrent(0,true);
271       break;
272     }
273     case MOVEUP: {
274       FXWindow*prv=tab->getPrev();
275       if (!prv) {return;}
276       prv=prv->getPrev();
277       page->reparent(this, prv);
278       tab->reparent(this, page);
279       setCurrent(iCurr-1,true);
280       break;
281     }
282     case MOVEDOWN: {
283       FXWindow*nxt=page->getNext(); /* is tab */
284       if (!nxt) {return;}
285       nxt=nxt->getNext();/* is page */
286       nxt=nxt->getNext();/* is tab, or NULL */
287       page->reparent(this, nxt);
288       tab->reparent(this, page);
289       setCurrent(iCurr+1,true);
290       break;
291     }
292   }
293 }
294 
295 
296 
ActivateTab(DocTab * tab)297 bool DocTabs::ActivateTab(DocTab*tab)
298 {
299 
300   FXint i=indexOfChild(tab);
301   if (i>=0) {
302     setCurrent(i/2,true);
303     return true;
304   } else {
305     return false;
306   }
307 
308 }
309 
310 
311 
ActivateTab(FXint n)312 bool DocTabs::ActivateTab(FXint n)
313 {
314   FXWindow*w=childAtIndex((n*2)+1);
315   if (w) {
316     setCurrent(n,true);
317     return true;
318   } else {
319     return false;
320   }
321 }
322 
323 
324 
ActiveTab()325 DocTab*DocTabs::ActiveTab() {
326   return (DocTab*)childAtIndex(getCurrent()*2);
327 }
328 
329 
330 
ActivePage()331 FXWindow*DocTabs::ActivePage() {
332   FXWindow*page=childAtIndex((getCurrent()*2)+1);
333   if (page) {
334     return page;
335   } else {
336     layout();
337     return childAtIndex((getCurrent()*2)+1);
338   }
339 }
340 
341 
342 
343 /* ForEachTab walks through the open tabs, and passes the tab index, the tab item object,
344    and the page item object to the callback, along with the user data. The callback should
345    return true to continue iterating, or false to break out of the loop. By default, the
346    "wait" cursor is displayed during execution, unless you call it with hourglass=false.
347 */
ForEachTab(TabCallback cb,void * user_data,bool hourglass)348 void DocTabs::ForEachTab(TabCallback cb, void *user_data, bool hourglass)
349 {
350   FXWindow*tab,*page;
351   FXint index=0;
352   update();
353   if ( hourglass ) {
354     getApp()->beginWaitCursor();
355   }
356   for (tab=getFirst(); tab && (page=tab->getNext()); tab=page->getNext(), index++) {
357     if (!cb(index,(DocTab*)tab,page,user_data)) { break; }
358   }
359   if ( hourglass ) { getApp()->endWaitCursor(); }
360 }
361 
362 
363 
PageAt(FXint n)364 FXWindow*DocTabs::PageAt(FXint n)
365 {
366   return childAtIndex((n*2)+1);
367 }
368 
369 
OrientTabsCB(FXint index,DocTab * tab,FXWindow * page,void * user_data)370 bool OrientTabsCB(FXint index, DocTab*tab, FXWindow*page, void*user_data)
371 {
372   FXint ornt=*((FXint*)user_data);
373   tab->setTabOrientation(ornt);
374   tab->setDefaultDragCursor(
375   tab->getApp()->getDefaultCursor(((ornt==TAB_TOP)||(ornt==TAB_BOTTOM)) ? DEF_HSPLIT_CURSOR : DEF_VSPLIT_CURSOR));
376   tab->setDragCursor(tab->getApp()->getDefaultCursor(DEF_ARROW_CURSOR));
377   return true;
378 }
379 
380 
381 
setTabStyle(FXuint style)382 void DocTabs::setTabStyle(FXuint style)
383 {
384   FXTabBook::setTabStyle(style);
385   FXint TabOrientation=TAB_TOP;
386   switch(style){
387     case TABBOOK_TOPTABS:
388       TabOrientation=TAB_TOP;
389       break;
390     case TABBOOK_BOTTOMTABS:
391       TabOrientation=TAB_BOTTOM;
392       break;
393     case TABBOOK_LEFTTABS:
394       TabOrientation=TAB_LEFT;
395       break;
396     case TABBOOK_RIGHTTABS:
397       TabOrientation=TAB_RIGHT;
398       break;
399   }
400   ForEachTab(OrientTabsCB, &TabOrientation, false);
401   if (tabs_compact=='A') setTabsCompact('A');
402   setCurrent(getCurrent(),false);
403 }
404 
405 
406 
407 // T, B, L, R: Top, Bottom, Left, Right
setTabStyleByChar(FXuchar c)408 void DocTabs::setTabStyleByChar(FXuchar c)
409 {
410   switch (c) {
411     case 'B': { setTabStyle(TABBOOK_BOTTOMTABS); break; }
412     case 'L': { setTabStyle(TABBOOK_LEFTTABS);   break; }
413     case 'R': { setTabStyle(TABBOOK_RIGHTTABS);  break; }
414     default:  { setTabStyle(TABBOOK_TOPTABS);    break; }
415   }
416 }
417 
418 
419 
420 // Set tab header widths:
421 // 'U' -- Uniform   (All tabs the same width, based on the widest label)
422 // 'P' -- Packed    (Tabs sized individually according to their label width)
423 // 'A' -- Automatic ("Packed" when orientation is top or bottom, "Uniform" when it's left or right)
setTabsCompact(FXuchar compact)424 void DocTabs::setTabsCompact(FXuchar compact)
425 {
426   FXuint packing_hints = getPackingHints();
427   FXuint ts=getTabStyle();
428   tabs_compact=compact;
429   if (compact=='A') {
430     compact=(ts==TABBOOK_TOPTABS||ts==TABBOOK_BOTTOMTABS)?'P':'U';
431   }
432   if ( compact=='P' ) {
433     packing_hints &= ~PACK_UNIFORM_WIDTH;
434   } else {
435     packing_hints |= PACK_UNIFORM_WIDTH;
436   }
437   setPackingHints(packing_hints);
438 }
439 
440 
441 // If forward is TRUE, activate next tab, else activate previous tab.
442 // Behavior is circular - that is, last->next==first; first->prev==last;.
FocusNextTab(bool forward)443 void DocTabs::FocusNextTab(bool forward)
444 {
445   int ntabs=numChildren()/2;
446   int itab=getCurrent();
447   if (forward) {
448     if ((itab+1)==ntabs) { itab=0; } else { itab++; }
449   } else {
450     if (itab==0) { itab=ntabs-1; } else { itab--; }
451   }
452   setCurrent(itab,true);
453 }
454 
455 
UpdateTabIconsCB(FXint index,DocTab * tab,FXWindow * page,void * user_data)456 static bool UpdateTabIconsCB(FXint index, DocTab*tab, FXWindow*page, void*user_data)
457 {
458   tab->SetIcon(DOCTAB_SAME);
459   return true;
460 }
461 
462 
setCurrent(FXint panel,FXbool notify)463 void DocTabs::setCurrent(FXint panel,FXbool notify)
464 {
465   ShadyTabs::setCurrent(panel,notify);
466   ForEachTab(UpdateTabIconsCB,NULL,false);
467 }
468 
469 
show()470 void DocTabs::show()
471 {
472   setCurrent(getCurrent(),false);
473 }
474 
475 
476 #define TAB_DND_NAME "FxteDnDTab"
477 static FXDragType FxteDnDTabType=0;
478 
479 
480 
481 FXDEFMAP(DocTab) DocTabMap[]={
482   FXMAPFUNC(SEL_DND_ENTER, 0, DocTab::onDnd),
483   FXMAPFUNC(SEL_DND_LEAVE, 0, DocTab::onDnd),
484   FXMAPFUNC(SEL_DND_DROP, 0, DocTab::onDnd),
485   FXMAPFUNC(SEL_DND_MOTION, 0, DocTab::onDnd),
486   FXMAPFUNC(SEL_DND_REQUEST, 0, DocTab::onDnd),
487   FXMAPFUNC(SEL_BEGINDRAG, 0, DocTab::onDnd),
488   FXMAPFUNC(SEL_ENDDRAG, 0, DocTab::onDnd),
489   FXMAPFUNC(SEL_LEFTBUTTONPRESS, 0, DocTab::onDnd),
490   FXMAPFUNC(SEL_LEFTBUTTONRELEASE, 0, DocTab::onDnd),
491   FXMAPFUNC(SEL_DRAGGED, 0, DocTab::onDnd),
492   FXMAPFUNC(SEL_MOTION, 0, DocTab::onDnd),
493   FXMAPFUNC(SEL_RIGHTBUTTONRELEASE, 0, DocTab::onDnd)
494 };
495 
496 FXIMPLEMENT(DocTab,FXTabItem,DocTabMap,ARRAYNUMBER(DocTabMap));
497 
498 
499 
500 
501 
onDnd(FXObject * sender,FXSelector sel,void * p)502 long DocTab::onDnd(FXObject* sender,FXSelector sel, void*p)
503 {
504   FXEvent* ev=(FXEvent*)p;
505   switch(FXSELTYPE(sel)) {
506     case SEL_DND_ENTER:  {
507       setDragRectangle(0,0,width,height,false);
508       if (offeredDNDType(FROM_DRAGNDROP,FxteDnDTabType)) {
509         acceptDrop();
510         dnd_accept=true;
511       } else {
512           dnd_accept=false;
513          ((DocTabs*)getParent())->handle(sender,sel,p);
514       }
515       break;
516     }
517     case SEL_DND_LEAVE:  {
518       dnd_accept=false;
519       break;
520     }
521     case SEL_DND_DROP:   {
522       dnd_accept=false;
523       FXuchar*dnd_data=NULL;
524       FXuint size=0;
525       if (getDNDData(FROM_DRAGNDROP,FxteDnDTabType,dnd_data,size)) {
526         if (dnd_data)  {
527           dnd_accept=false;
528           DocTab*src;
529           FXint pid;
530           sscanf((const char*)dnd_data, "%p,%d", &src, &pid);
531           FXFREE(&dnd_data);
532           if ((pid==fxgetpid())&&(src!=this)) {
533             FXWindow *trg;
534             if (defaultDragCursor==getApp()->getDefaultCursor(DEF_HSPLIT_CURSOR)) {
535               trg=((src->getX()+ev->win_x)-getX())>(getWidth()/2)?this->getNext()->getNext():this;
536             } else {
537               trg=((src->getY()+ev->win_y)-getY())>(getHeight()/2)?this->getNext()->getNext():this;
538             }
539             FXWindow*doc=src->getNext();
540             doc->reparent(getParent(), trg);
541             src->reparent(getParent(), doc);
542             ((DocTabs*)getParent())->ActivateTab(src);
543           }
544         }
545       } else {
546         ((DocTabs*)getParent())->handle(sender,sel,p);
547       }
548       break;
549     }
550     case SEL_DND_MOTION: {
551       if (dnd_accept) { acceptDrop(); }
552       break;
553     }
554 
555 
556     case SEL_LEFTBUTTONPRESS: {
557       grab();
558       flags|=FLAG_TRYDRAG;
559       break;
560     }
561 
562 
563     case SEL_MOTION: {
564       if ((flags&FLAG_DODRAG)) {
565         handle(this,FXSEL(SEL_DRAGGED,0),p);
566         return 1;
567       }
568       if ((flags&FLAG_TRYDRAG) && (ev->moved) ) {
569         flags&=~FLAG_TRYDRAG;
570         if (handle(this,FXSEL(SEL_BEGINDRAG,0),p)) {
571           flags|=FLAG_DODRAG;
572         }
573         return 1;
574       }
575       return 0;
576     }
577     case SEL_LEFTBUTTONRELEASE: {
578       if (flags&FLAG_DODRAG) {
579         handle(this,FXSEL(SEL_ENDDRAG,0),p);
580       } else {
581         if (((DocTabs*)getParent())->ActiveTab()!=this) { ((DocTabs*)getParent())->ActivateTab(this); }
582       }
583       flags&=~(FLAG_DODRAG|FLAG_TRYDRAG);
584       ungrab();
585       break;
586     }
587     case SEL_BEGINDRAG:{
588       beginDrag(&FxteDnDTabType,1);
589       break;
590     }
591     case SEL_DRAGGED: {
592       handleDrag(ev->root_x,ev->root_y,DRAG_MOVE);
593       if (didAccept()!=DRAG_REJECT) {
594         setDragCursor(defaultDragCursor);
595       } else {
596         setDragCursor(getApp()->getDefaultCursor(DEF_DNDSTOP_CURSOR));
597       }
598       break;
599     }
600     case SEL_ENDDRAG:{
601       endDrag();
602       setDragCursor(getApp()->getDefaultCursor(DEF_ARROW_CURSOR));
603       break;
604     }
605     case SEL_DND_REQUEST: {
606       FXString fmt="";
607       fmt.format("%p,%d", this, fxgetpid());
608       FXint len=fmt.length()+1;
609       char*dnd_data=NULL;
610       FXMALLOC(&dnd_data,char,len);
611       strncpy(dnd_data,fmt.text(), len);
612       dnd_data[len-1]='\0';
613       setDNDData(FROM_DRAGNDROP,FxteDnDTabType,(FXuchar*)dnd_data,len);
614     }
615     case SEL_RIGHTBUTTONRELEASE: {
616       return ((DocTabs*)getParent())->onTabPopupMenu(this, sel,p);
617     }
618     default: return 0;
619   }
620   return 1;
621 }
622 
623 
624 
DocTab(FXTabBar * bar,const FXString & text)625 DocTab::DocTab(FXTabBar*bar, const FXString&text):FXTabItem(bar,FXString::null,NULL,ICON_AFTER_TEXT|JUSTIFY_HZ_APART)
626 {
627   FXApp*a=bar->getApp();
628   if (!FxteDnDTabType) {
629     FXString DnDName;
630     DnDName.format("%s_%d", TAB_DND_NAME, fxgetpid());
631     FxteDnDTabType=a->registerDragType(DnDName);
632   }
633   setDefaultDragCursor(a->getDefaultCursor(
634   ((bar->getTabStyle()==TABBOOK_TOPTABS)||(bar->getTabStyle()==TABBOOK_BOTTOMTABS))?
635     DEF_HSPLIT_CURSOR:DEF_VSPLIT_CURSOR
636   ));
637   setDragCursor(getApp()->getDefaultCursor(DEF_ARROW_CURSOR));
638   dropEnable();
639   setText(text);
640   SetIcon(DOCTAB_CLEAN);
641 }
642 
643 
644 #define ICO_SIZE 8
645 
646 
647 static const char clean_mask[] =
648   "________"
649   "________"
650   "________"
651   "________"
652   "________"
653   "________"
654   "________"
655   "________"
656 ;
657 
658 
659 static const char dirty_mask[] =
660   "X__X__X_"
661   "_X___X__"
662   "__XXX___"
663   "XXXXXXX_"
664   "__XXX___"
665   "_X___X__"
666   "X__X__X_"
667   "________"
668 ;
669 
670 
671 static const char locked_mask[] =
672   "___##___"
673   "__#__#__"
674   "_#____#_"
675   "_#____#_"
676   "_######_"
677   "_######_"
678   "_##__##_"
679   "_######_"
680 ;
681 
682 
683 
SetIcon(DocTabState state)684 void DocTab::SetIcon(DocTabState state)
685 {
686   const char *mask=NULL;
687   if (state==DOCTAB_SAME) { state=_state; } else { _state=state; }
688   if (!xid) { return; }
689   switch (state) {
690     case DOCTAB_CLEAN:  { mask=clean_mask;   break; }
691     case DOCTAB_DIRTY:  { mask=dirty_mask;   break; }
692     case DOCTAB_LOCKED: { mask=locked_mask;  break; }
693     default:            { mask=clean_mask;   break; }
694   }
695   if (getIcon()) {
696     delete getIcon();
697     setIcon(NULL);
698   }
699   FXuint horiz=getTabOrientation()==TAB_TOP||getTabOrientation()==TAB_BOTTOM;
700   FXuchar packed=((DocTabs*)getParent())->getTabsCompact();
701   if (packed=='A') { packed=horiz?'P':'U'; }
702   if ((state==DOCTAB_CLEAN) && (packed=='P')) { return; }
703   FXColor ico_buf[ICO_SIZE*ICO_SIZE];
704   FXColor bg=state==DOCTAB_CLEAN?getBackColor():getApp()->getBackColor();
705   FXColor fg=getTextColor();
706   for (FXint i=0; i<ICO_SIZE*ICO_SIZE; i++) { ico_buf[i]=mask[i]=='_'?bg:fg; }
707   FXIcon *ico=new FXIcon(getApp(),ico_buf,0,IMAGE_OPAQUE,ICO_SIZE,ICO_SIZE);
708   ico->create();
709   setIcon(ico);
710 }
711 
712