TabBar.cpp

IƱaki Zabala, 06/19/2020 04:34 PM

Download (52.2 KB)

 
1
#include "TabBar.h"
2

    
3
#define TFILE <TabBar/TabBar.t>
4
#include <Core/t.h>
5

    
6
#define IMAGECLASS TabBarImg
7
#define IMAGEFILE <TabBar/TabBar.iml>
8
#include <Draw/iml_source.h>
9

    
10
namespace Upp {
11

    
12
// AlignedFrame
13
void AlignedFrame::FrameLayout(Rect &r)
14
{
15
        switch(layout)
16
        {
17
                case LEFT:
18
                        LayoutFrameLeft(r, this, framesize);
19
                        break;
20
                case TOP:
21
                        LayoutFrameTop(r, this, framesize);
22
                        break;
23
                case RIGHT:
24
                        LayoutFrameRight(r, this, framesize);
25
                        break;
26
                case BOTTOM:
27
                        LayoutFrameBottom(r, this, framesize);
28
                        break;
29
        }
30
        r.top += border;
31
        r.left += border;
32
        r.right -= border;
33
        r.bottom -= border;
34
}
35

    
36
void AlignedFrame::FrameAddSize(Size& sz)
37
{
38
        sz += border * 2;
39
        IsVert() ? sz.cx += framesize : sz.cy += framesize;
40
}
41

    
42
void AlignedFrame::FramePaint(Draw& w, const Rect& r)
43
{
44
        if(border > 0)
45
        {
46
                Rect n = r;
47
                switch(layout)
48
                {
49
                        case LEFT:
50
                                n.left += framesize;
51
                                break;
52
                        case TOP:
53
                                n.top += framesize;
54
                                break;
55
                        case RIGHT:
56
                                n.right -= framesize;
57
                                break;
58
                        case BOTTOM:
59
                                n.bottom -= framesize;
60
                                break;
61
                }
62
                ViewFrame().FramePaint(w, n);
63
        }
64
        else
65
                FrameCtrl<Ctrl>::FramePaint(w, r);
66
}
67

    
68
AlignedFrame& AlignedFrame::SetFrameSize(int sz, bool refresh)
69
{
70
        framesize = sz; 
71
        if (refresh) RefreshParentLayout(); 
72
        return *this;        
73
}
74

    
75
void AlignedFrame::Fix(Size& sz)
76
{
77
        if(IsVert())
78
                Swap(sz.cx, sz.cy);
79
}
80

    
81
void AlignedFrame::Fix(Point& p)
82
{
83
        if(IsVert())
84
                Swap(p.x, p.y);
85
}
86

    
87
Size AlignedFrame::Fixed(const Size& sz)
88
{
89
        return IsVert() ? Size(sz.cy, sz.cx) : Size(sz.cx, sz.cy);
90
}
91

    
92
Point AlignedFrame::Fixed(const Point& p)
93
{
94
        return IsVert() ? Point(p.y, p.x) : Point(p.x, p.y);
95
}
96

    
97
// TabScrollBar
98
TabScrollBar::TabScrollBar()
99
{
100
        Clear();
101
}
102

    
103
void TabScrollBar::Clear()
104
{
105
        total = 0;
106
        pos = 0;
107
        ps = 0;
108
        start_pos = 0;
109
        new_pos = 0;
110
        old_pos = 0;
111
        sz.Clear();
112
        ready = false;        
113
}
114

    
115
void TabScrollBar::UpdatePos(bool update)
116
{
117
        sz = GetSize();
118
        Fix(sz);
119
        if(total <= 0 || sz.cx <= 0)
120
                cs = ics = 0;
121
        else
122
        {
123
                cs = sz.cx / ((double) total + 0.5);
124
                ics = total / ((double) sz.cx);
125
        }
126
        size = sz.cx * cs;
127
        if(update)
128
                pos = new_pos - start_pos;
129
        if(pos < 0)
130
                pos = 0;
131
        else if(pos + size > sz.cx)
132
                pos = sz.cx - size;
133

    
134
        ps = total > sz.cx ? pos * ics : 0;
135
}
136

    
137
void TabScrollBar::Paint(Draw &w)
138
{
139
        if(!ready)
140
        {
141
                UpdatePos();
142
                ready = true;
143
        }
144
        Size rsz = GetSize();
145
        #ifdef TABBAR_DEBUG
146
        w.DrawRect(rsz, Red);
147
        #else
148
        w.DrawRect(rsz, White);
149
        #endif
150
        Point p;
151
        
152
        if(total > sz.cx) {
153
                p = Point(ffloor(pos), 1);
154
                rsz = Size(fceil(size), (IsVert() ? rsz.cx : rsz.cy) - 2);
155
        }
156
        else {
157
                p = Point(0, 1);
158
                rsz = Size(sz.cx, (IsVert() ? rsz.cx : rsz.cy) - 2);
159
        }
160
        Fix(p);
161
        Fix(rsz);
162
        w.DrawRect(p.x, p.y, rsz.cx, rsz.cy, Blue);
163
}
164

    
165
void TabScrollBar::Layout()
166
{
167
        UpdatePos(false);
168
}
169

    
170
void TabScrollBar::LeftDown(Point p, dword keyflags)
171
{
172
        SetCapture();
173
        Fix(p);
174
        old_pos = new_pos = p.x;
175
        if(p.x < pos || p.x > pos + size)
176
                start_pos = size / 2;
177
        else
178
                start_pos = tabs(p.x - pos);
179
        UpdatePos();
180
        UpdateActionRefresh();        
181
}
182

    
183
void TabScrollBar::LeftUp(Point p, dword keyflags)
184
{
185
        ReleaseCapture();
186
        Fix(p);
187
        old_pos = p.x;
188
}
189

    
190
void TabScrollBar::MouseMove(Point p, dword keyflags)
191
{
192
        if(!HasCapture())
193
                return;
194

    
195
        Fix(p);
196
        new_pos = p.x;
197
        UpdatePos();
198
        UpdateActionRefresh();
199
}
200

    
201
void TabScrollBar::MouseWheel(Point p, int zdelta, dword keyflags)
202
{
203
        AddPos(-zdelta / 4, true);
204
        UpdateActionRefresh();
205
}
206

    
207
int TabScrollBar::GetPos() const
208
{
209
        return ffloor(ps);
210
}
211

    
212
void TabScrollBar::SetPos(int p, bool dontscale)
213
{
214
        pos = total > 0 ? dontscale ? p : iscale(p, sz.cx, total) : 0;
215
        UpdatePos(false);
216
        Refresh();
217
}
218

    
219
void TabScrollBar::AddPos(int p, bool dontscale)
220
{
221
        pos += total > 0 ? dontscale ? p : iscale(p, sz.cx, total) : 0;
222
        UpdatePos(false);
223
        Refresh();
224
}
225

    
226
int TabScrollBar::GetTotal() const
227
{
228
        return total;
229
}
230

    
231
void TabScrollBar::SetTotal(int t)
232
{
233
        bool upd = total < t;
234
        total = t;
235
        UpdatePos(upd);
236
        Refresh();
237
}
238

    
239
void TabScrollBar::AddTotal(int t)
240
{
241
        sz = GetSize();
242
        Fix(sz);
243
        total += t;
244
        if(total <= 0 || sz.cx <= 0)
245
                cs = ics = 0;
246
        else
247
                cs = sz.cx / ((double) total + 0.5);
248
        size = sz.cx * cs;
249
        ps = min(ps, (double)(total-sz.cx));
250
        pos = (int)(ps * cs);                
251
        old_pos = new_pos = (int)(pos - start_pos);
252
        
253
        Refresh();
254
}
255

    
256
void TabScrollBar::GoEnd()
257
{
258
        pos = total;
259
        UpdatePos(false);
260
        Refresh();
261
}
262

    
263
void TabScrollBar::GoBegin()
264
{
265
        pos = 0;
266
        UpdatePos(false);
267
        Refresh();
268
}
269

    
270
void TabScrollBar::Set(const TabScrollBar& t)
271
{
272
        total = t.total;
273
        pos = t.pos;
274
        ps = t.ps;
275
        Refresh();
276
}
277

    
278
bool TabScrollBar::IsScrollable() const
279
{
280
        // Note: sz already 'fixed'
281
        return total > sz.cx && sz.cx > 0;
282
}
283

    
284
// Group
285

    
286
void TabBar::Group::Serialize(Stream& s)
287
{
288
        s % name % active % count % first % last;
289
}
290

    
291

    
292
// TabBar
293

    
294
TabBar::TabBar()
295
{
296
        Clear();
297

    
298
        id = 0;
299
        display = NULL;
300
        crosses = true;
301
        crosses_side = RIGHT;
302
        grouping = true;
303
        isctrl = false;
304
        isdrag = false;
305
        inactivedisabled = false;
306
        autoscrollhide = true;
307
        stacking = false;
308
        groupseps = false;
309
        allownullcursor = false;
310
        icons = true;
311
        mintabcount = 1;
312
        scrollbar_sz = TB_SBHEIGHT;
313
        allowreorder = true;
314
        style = &StyleDefault();
315
        
316
        // Init sorting
317
        groupsort = false;
318
        tabsort = false;
319
        stacksort = true;
320
        contextmenu = true;
321
        keysorter_inst.vo = &Single<StdValueOrder>();
322
        valuesorter_inst.vo = &Single<StdValueOrder>();
323
        stacksorter_inst.vo = &Single<StdValueOrder>();
324
        tabsorter = &keysorter_inst;
325
        groupsorter = &Single<TabGroupSort>();
326
        stacksorter = &stacksorter_inst;
327

    
328
        SetAlign(TOP);
329
        SetFrameSize(GetHeight(false));
330
        BackPaint();
331
}
332

    
333
int TabBar::GetLR( int c, int jd )
334
{
335
        int new_tab;
336
        if ( jd == JumpDirLeft )
337
                new_tab = GetPrev( c );
338
        else
339
                new_tab = GetNext( c );
340
        return new_tab;
341
}
342

    
343
int TabBar::GetTabStackLR( int jd )
344
{
345
        int nt = -1;
346
        if ( HasCursor() ) {
347
                int c = GetCursor();
348
    
349
                if ( IsStacking() ) {
350
                        int c_stack = tabs[ c ].stack;
351
                        if ( jd == JumpDirLeft )
352
                                nt = FindStackTail( c_stack );
353
                        else
354
                                nt = c + 1;
355
                }
356
        }
357
        return nt;
358
}
359

    
360
int TabBar::GetTabLR( int jd )
361
{
362
        int lt = -1;
363
        bool js_NeedReset = true;
364

    
365
        if ( HasCursor() ) {
366
            int c = GetCursor();
367
            int tc = GetCount();
368
    
369
                if ( IsStacking() ) {
370
                        int c_stack = tabs[ c ].stack;
371

    
372
                        if ( jd == JumpDirRight && jump_stack.IsReset() ) {
373
        
374
                                int c_stack_count = GetStackCount( c_stack );
375

    
376
                        if ( c_stack_count > 1 ) {
377
                                        jump_stack.Activate( c_stack_count - 1, jd );
378
                                        js_NeedReset = false;
379
                                }
380
                        }
381
      
382
                        if ( jump_stack.IsReset() || ( jump_stack.Rest == 0 ) ) {
383
                                lt = GetLR( c, jd );
384
                                if ( ( lt >= 0 ) && ( lt < tc ) ) {
385
        
386
                                        int lt_stack = tabs[ lt ].stack;
387
                                        int lt_stack_count = GetStackCount( lt_stack );
388
                                        
389
                                        if ( lt_stack_count > 1 ) {
390
                                                lt = FindStackHead( lt_stack );
391
                                                jump_stack.Activate( lt_stack_count - 1, jd );
392
                                                js_NeedReset = false;
393
                                        }
394
                                }
395
                        } else {
396
                                if ( jump_stack.Rest > 0 ) {
397
                                        if ( jd == jump_stack.jump_direct ) {
398
                                                lt = c + 1;
399
                                                --jump_stack.Rest;
400
                                                js_NeedReset = false;
401
                                        } else  {
402
                                                if ( jump_stack.IsFull() ) {
403
                                                        lt = GetLR( c, jd );
404
                                                } else {
405
                                                        lt = FindStackTail( c_stack );
406
                                                        ++jump_stack.Rest;
407
                                                        js_NeedReset = false;
408
                                                }
409
                                        }
410
                                }
411
                        }
412
                } else /* !IsStacking() */ {
413
                        lt = GetLR( c, jd );
414
                }
415
        }
416

    
417
        if ( js_NeedReset )
418
                jump_stack.Reset();
419
        return lt;
420
}
421

    
422
void TabBar::Set(const TabBar& t)
423
{
424
        CopyBaseSettings(t);
425
        
426
        id = t.id;
427
        
428
        tabs.Clear();
429
        tabs <<= t.tabs;
430
        groups.Clear();
431
        groups <<= t.groups;
432
        separators.Clear();
433
        separators <<= t.separators;
434
        
435
        group = t.group;
436
        stackcount = t.stackcount;
437
        
438
        active = t.active;
439
        cross = -1;
440
        highlight = -1;
441
        target = -1;
442
        
443
        mouse.Clear();
444
        oldp.Clear();
445
        
446
        sc.Set(t.sc);
447
        SetAlign(t.GetAlign());
448
}
449

    
450
int TabBar::GetNextId()
451
{
452
        return id++;
453
}
454

    
455
void TabBar::ContextMenu(Bar& bar)
456
{
457
        int ii = GetHighlight(); // Need copy to freeze it, [=] copies 'this' and thus reference to highlight
458
        if (GetCursor() >= 0 && !IsCancelClose(ii) && ii >= 0)
459
                bar.Add(tabs.GetCount() > mintabcount, t_("Close"), [=] {
460
                        if (!CancelClose(tabs[ii].key)) {
461
                                WhenClose(tabs[ii].key);
462
                                TabClosed(tabs[ii].key);
463
                                tabs.Remove(ii);
464
                                MakeGroups();
465
                                Repos();
466
                                SetCursor(-1);
467
                        }
468
                });
469
        if (ii >= 0 && !IsCancelCloseAll(ii))
470
                bar.Add(t_("Close others"), [=] { CloseAll(ii); });
471
    if (ii >= 0 && ii < GetCount() - 1 && !IsCancelCloseAll(-1, ii + 1))
472
                bar.Add(t_("Close right tabs"), [=] { CloseAll(-1, ii + 1); });
473
        if (mintabcount <= 0 && !IsCancelCloseAll(-1))
474
                bar.Add(t_("Close all"), [=] { CloseAll(-1); });
475
        bar.Add(false, t_("Dock"), [=] {});
476
        if(ii >= 1)
477
                bar.Sub(t_("Move left before"), [=](Bar& bar) {
478
                        for(int i = 0; i < ii; i++)
479
                           bar.Add(tabs[i].value.ToString(), [=] {
480
                                tabs.Move(ii,i);
481
                                SetCursor0(i);
482
                                Repos();
483
                                Refresh();
484
                        });;
485
                });
486
        if(tabs.GetCount() - 2 >= ii && ii >= 0)
487
                bar.Sub(t_("Move right after"),  [=](Bar& bar)  {
488
                        for(int i = ii+1; i < tabs.GetCount(); i++)
489
                                bar.Add(tabs[i].value.ToString(),[=] {
490
                                tabs.Move(ii,i+1);
491
                                SetCursor0(i);
492
                                Repos();
493
                                Refresh();
494
                        });
495
                });
496
        if(grouping && ii >= 0) {
497
                if(group > 0)
498
                        bar.Add(t_("Close group"), THISBACK(CloseGroup));
499
                bar.Separator();
500
                int cnt = groups.GetCount();
501
                for(int i = 0; i < cnt; i++)
502
                {
503
                        String name = Format("%s (%d)", groups[i].name, groups[i].count);
504
                        Bar::Item &it = bar.Add(name, THISBACK1(GoGrouping, i));
505
                        if(i == group)
506
                                it.Image(TabBarImg::CHK);
507
                        if(i == 0 && cnt > 1)
508
                                bar.Separator();
509
                }
510
        }
511
}
512

    
513
void TabBar::CloseAll(int exception, int last_closed)
514
{
515
        ValueArray vv;
516
        for(int i = last_closed; i < tabs.GetCount(); i++)
517
                if(i != exception)
518
                        vv.Add(tabs[i].key);
519
                
520
        if(exception < 0 && last_closed == 0 ? CancelCloseAll() : CancelCloseSome(vv))
521
                return;
522
        
523
        WhenCloseSome(vv);
524
        if(exception < 0 && last_closed == 0)
525
                WhenCloseAll();
526

    
527
        for(int i = tabs.GetCount() - 1; i >= last_closed; i--)
528
                if(i != exception) {
529
                        if (!CancelClose(tabs[i].key)) {
530
                                WhenClose(tabs[i].key);
531
                                TabClosed(tabs[i].key);
532
                                tabs.Remove(i);
533
                        }
534
                }
535

    
536
        SetCursor(last_closed ? last_closed - 1 : 0);
537
        
538
        MakeGroups();
539
        Repos();
540
        Refresh();
541
}
542

    
543
void TabBar::CloseGroup()
544
{
545
        if(group <= 0)
546
                return;
547
        Value v = GetData();
548
        DoCloseGroup(group);
549
        SetData(v);
550
}
551

    
552
bool TabBar::IsCancelClose(int id)
553
{
554
        if (CancelCloseAll())
555
                return true;
556
        
557
        ValueArray vv;
558
        vv.Add(tabs[id].key);
559
        if (CancelCloseSome(vv))
560
                return true;
561
        
562
        if (CancelClose(tabs[id].key))
563
                return true;
564
        return false;
565
}
566

    
567
bool TabBar::IsCancelCloseAll(int exception, int last_closed)
568
{
569
        ValueArray vv;
570
        for(int i = last_closed; i < tabs.GetCount(); i++)
571
                if(i != exception)
572
                        vv.Add(tabs[i].key);
573
                
574
        if(exception < 0 && last_closed == 0 ? CancelCloseAll() : CancelCloseSome(vv))
575
                return true;
576
        
577
        for(int i = tabs.GetCount() - 1; i >= last_closed; i--)
578
                if(i != exception) {
579
                        if (!CancelClose(tabs[i].key))
580
                                return true;
581
                }
582
        return false;
583
}
584

    
585
TabBar::Tab::Tab()
586
{
587
        id = -1;
588
        stack = -1;
589
        visible = true;
590
        itn = 0;
591
        items.SetCount(5);
592

    
593
        pos = cross_pos = tab_pos = Point(0, 0);
594
        cross_size = size = tab_size = Size(0, 0);
595
}
596

    
597
void TabBar::Tab::Set(const Tab& t)
598
{
599
        id = t.id;
600
        
601
        img = t.img;
602
        col = t.col;
603
        key = t.key;
604
        value = t.value;
605
        group = t.group;
606
        
607
        stackid = t.stackid;
608
        stack = t.stack;
609

    
610
        visible = t.visible;
611

    
612
        pos = t.pos;
613
        size = t.size;
614
        
615
        cross_pos = t.cross_pos;
616
        cross_size = t.cross_size;
617
        
618
        tab_pos = t.tab_pos;
619
        tab_size = t.tab_size;
620
        
621
        items <<= t.items;
622
}
623

    
624
void TabBar::Tab::Serialize(Stream& s)
625
{
626
        s % id % key % value % group % stackid % stack % visible;
627
}
628

    
629
bool TabBar::Tab::HasMouse(const Point& p) const
630
{
631
        if(!visible)
632
                return false;
633
        
634
        return p.x >= tab_pos.x && p.x < tab_pos.x + tab_size.cx &&
635
               p.y >= tab_pos.y && p.y < tab_pos.y + tab_size.cy;
636
}
637

    
638
bool TabBar::Tab::HasMouseCross(const Point& p) const
639
{
640
        if(!visible)
641
                return false;
642

    
643
        return p.x >= cross_pos.x && p.x < cross_pos.x + cross_size.cx &&
644
               p.y >= cross_pos.y && p.y < cross_pos.y + cross_size.cy;
645
}
646

    
647
int TabBar::FindGroup(const String& g) const
648
{
649
        for(int i = 0; i < groups.GetCount(); i++)
650
                if(groups[i].name == g)
651
                        return i;
652
        return -1;
653
}
654

    
655
void TabBar::DoStacking()
656
{
657
        Value v = GetData();
658
                
659
        // Reset stack info
660
        for (int i = 0; i < tabs.GetCount(); i++) {
661
                Tab &t = tabs[i];
662
                t.stack = -1;
663
                t.stackid = GetStackId(t);
664
        }
665
        // Create stacks
666
        Vector< Array<Tab> > tstack;
667
        for (int i = 0; i < tabs.GetCount(); i++) {
668
                Tab &ti = tabs[i];
669
                if (ti.stack < 0) {
670
                        ti.stack = tstack.GetCount();
671
                        Array<Tab> &ttabs = tstack.Add();
672
                        ttabs.Add(ti);
673
                        for (int j = i + 1; j < tabs.GetCount(); j++)        {
674
                                Tab &tj = tabs[j];
675
                                if (tj.stack < 0 && tj.stackid == ti.stackid && (!grouping || tj.group == ti.group)) {
676
                                        tj.stack = ti.stack;
677
                                        ttabs.Add(tj);
678
                                }
679
                        }
680
                }
681
        }
682
        stackcount = tstack.GetCount();
683
        // Recombine
684
        tabs.SetCount(0);
685
        for (int i = 0; i < tstack.GetCount(); i++) {
686
                if (stacksort)
687
                        StableSort(tstack[i], *stacksorter);
688
                tabs.AppendPick(pick(tstack[i]));
689
        }
690
        highlight = -1;
691
        SetData(v);
692
        MakeGroups();
693
        Repos();
694
}
695

    
696
void TabBar::DoUnstacking()
697
{
698
        stackcount = 0;
699
        for (int i = 0; i < tabs.GetCount(); i++)
700
                tabs[i].stack = -1;
701
        highlight = -1;
702
        MakeGroups();
703
        Repos();
704
        if (HasCursor())
705
                SetCursor(-1);
706
        else
707
                Refresh();
708
}
709

    
710
void TabBar::SortStack(int stackix)
711
{
712
        if (!stacksort) return;
713
        
714
        int head = FindStackHead(stackix);
715
        int tail = head;
716
        while (tail < tabs.GetCount() && tabs[tail].stack == stackix)
717
                ++tail;
718
        SortStack(stackix, head, tail-1);
719
}
720

    
721
void TabBar::SortStack(int stackix, int head, int tail)
722
{
723
        if (!stacksort) return;
724
        
725
        int headid = tabs[head].id;
726
        StableSort(SubRange(tabs, head, tail - head).Write(), *stacksorter);
727
        while (tabs[head].id != headid)
728
                CycleTabStack(head, stackix);
729
}
730

    
731
void TabBar::MakeGroups()
732
{
733
        for(const auto& tab : tabs)
734
                if(FindGroup(tab.group) < 0)
735
                        NewGroup(tab.group);
736
        
737
        groups[0].count = tabs.GetCount();
738
        groups[0].first = 0;
739
        groups[0].last = tabs.GetCount() - 1;
740

    
741
        if (groupsort)
742
                StableSort(tabs, *groupsorter);
743

    
744
        for(int i = 1; i < groups.GetCount(); i++)
745
        {
746
                groups[i].count = 0;
747
                groups[i].first = 10000000;
748
                groups[i].last = 0;
749
        }
750

    
751
        for(int i = 0; i < tabs.GetCount(); i++)
752
        {
753
                Tab &tab = tabs[i];
754
                int n = FindGroup(tab.group);
755
                ASSERT(n >= 0);
756
                if (n > 0) {
757
                        if(groups[n].active < 0)
758
                                groups[n].active = tab.id;
759
                        groups[n].count++;
760
                        groups[n].last = i;
761
        
762
                        if(i < groups[n].first)
763
                                groups[n].first = i;
764
                        if(i > groups[n].last)
765
                                groups[n].last = i;
766
                }
767
        }
768
        
769
        int cnt = groups.GetCount() - 1;
770
        for(int i = cnt; i > 0; i--)
771
                if(groups[i].count == 0)
772
                        groups.Remove(i);
773
                
774
        if(group > groups.GetCount() - 1 && group > 0)
775
                group--;
776
}
777

    
778
void TabBar::GoGrouping(int n)
779
{
780
        Value c = GetData();
781
        group = n;
782
        String g = GetGroupName();
783
        for(int i = 0; i < tabs.GetCount(); i++)
784
                if(tabs[i].group == g)
785
                        c = GetKey(i);
786
        Repos();
787
        SyncScrollBar();
788
        SetData(c);
789
        Refresh();
790
}
791

    
792
void TabBar::DoGrouping(int n)
793
{
794
        group = n;
795
        Repos();
796
        SyncScrollBar();
797
}
798

    
799

    
800
void TabBar::DoCloseGroup(int n)
801
{
802
        int cnt = groups.GetCount();
803
        if(cnt <= 0)
804
                return;
805

    
806
        String groupName = groups[n].name;
807
        
808
        /*
809
                do WhenCloseSome()/CancelCloseSome() checking
810
                before WhenClose()/CancelClose() stuff
811
                (that code must be reviewed anyways...)
812
                In order to leave existing code as it is, following
813
                changes have effect *ONLY* if WhenCloseSome()/CancelCloseSome()
814
                callbacks are used, otherwise previous path is taken.
815
                I think we should anyways review some parts of it later
816
        */
817
        
818
        if(WhenCloseSome || CancelCloseSome)
819
        {
820
                ValueArray vv;
821
                int nTabs = 0;
822
                for(int i = 0; i < tabs.GetCount(); i++)
823
                        if(groupName == tabs[i].group) {
824
                                vv.Add(tabs[i].key);
825
                                nTabs++;
826
                        }
827
                // at first, we check for CancelCloseSome()
828
                if(vv.GetCount() && !CancelCloseSome(vv)) {
829
                        // we didn't cancel globally, now we check CancelClose()
830
                        // for each tab -- group gets removed ONLY if ALL of
831
                        // group tabs are closed
832
                        vv.Clear();
833
                        Vector<int>vi;
834
                        for(int i = tabs.GetCount() - 1; i >= 0; i--) {
835
                                if(groupName == tabs[i].group && tabs.GetCount() > 1) {
836
                                        Value v = tabs[i].key;
837
                                        if(!CancelClose(v))
838
                                        {
839
                                                nTabs--;
840
                                                WhenClose(v);
841
                                                // record keys and indexes of tabs to remove
842
                                                vv << v;
843
                                                vi << i;
844
                                        }
845
                                }
846
                        }
847
                        // and now do the true removal
848
                        WhenCloseSome(vv);
849
                        for(int i = 0; i < vv.GetCount(); i++)
850
                        {
851
                                if (!CancelClose(vv[i])) {
852
                                        WhenClose(vv[i]);
853
                                         TabClosed(vv[i]);
854
                                         tabs.Remove(vi[i]);
855
                                }
856
                        }
857
                        // remove group if all of its tabs get closed
858
                        if(!nTabs) {
859
                                if(cnt == n)
860
                                        group--;
861
                                if(cnt > 1)
862
                                        groups.Remove(n);
863
                        }
864
                        MakeGroups();
865
                        Repos();
866
                        SetCursor(-1);
867
                }
868
                return;
869
        }
870

    
871
        // previous code path, taken if WhenCancelSome()/WhenCloseSome()
872
        for(int i = tabs.GetCount() - 1; i >= 0; i--)
873
        {
874
                if(groupName == tabs[i].group && tabs.GetCount() > 1) {
875
                        Value v = tabs[i].value; // should be key ??
876
                        if (!CancelClose(v)) {
877
                                WhenClose(v);
878
                                TabClosed(v);
879
                                tabs.Remove(i);
880
                        }
881
                }
882
        }
883

    
884
        if (cnt == n)
885
                group--;
886

    
887
        if(cnt > 1) // what if CancelClose suppressed some tab closing ?
888
                groups.Remove(n);
889
        MakeGroups();
890
        Repos();
891
        SetCursor(-1);
892
}
893

    
894
void TabBar::NewGroup(const String &name)
895
{
896
        Group &g = groups.Add();
897
        g.name = name;
898
        g.count = 0;
899
        g.first = 10000000;
900
        g.last = 0;
901
        g.active = -1;
902
}
903

    
904
Image TabBar::AlignImage(int align, const Image& img)
905
{
906
        switch(align) {
907
                case AlignedFrame::LEFT: 
908
                        return RotateAntiClockwise(img);
909
                case AlignedFrame::RIGHT: 
910
                        return RotateClockwise(img);
911
                case AlignedFrame::BOTTOM:
912
                        return MirrorVert(img);
913
                default:
914
                        return img;
915
        }
916
}
917

    
918
Value TabBar::AlignValue(int align, const Value &v, const Size &sz)
919
{
920
        Size isz = sz;
921
        if(align == AlignedFrame::LEFT || align == AlignedFrame::RIGHT)
922
                Swap(isz.cx, isz.cy);
923

    
924
        ImageDraw w(isz.cx, isz.cy);
925
        w.DrawRect(isz, SColorFace());
926
        ChPaint(w, isz, v);
927
        ImageBuffer img;
928
        return AlignImage(align, (Image)w);
929
}
930

    
931
void TabBar::TabItem::Clear()
932
{
933
        text.Clear();
934
        ink = Null;
935
        img = Null;
936
        side = LEFT;
937
        clickable = false;
938
        cross = false;
939
        stacked_tab = -1;
940
}
941

    
942
TabBar::TabItem& TabBar::Tab::AddItem()
943
{
944
        if(itn < items.GetCount())
945
        {
946
                TabItem& ti = items[itn++];
947
                ti.Clear();
948
                return ti;
949
        }
950
        else
951
        {
952
                ++itn;
953
                return items.Add();
954
        }
955
}
956

    
957
void TabBar::Tab::Clear()
958
{
959
        itn = 0;
960
}
961

    
962
TabBar::TabItem& TabBar::Tab::AddImage(const Image& img, int side)
963
{
964
        TabItem& ti = AddItem();
965
        ti.img = img;
966
        ti.size = img.GetSize();
967
        ti.side = side;
968
        return ti;
969
}
970

    
971
TabBar::TabItem& TabBar::Tab::AddValue(const Value& q, const Font& font, const Color& ink)
972
{
973
        TabItem& ti = AddItem();
974
        
975
        ti.font = font;
976
        ti.ink = ink;
977
        
978
        if(IsType<AttrText>(q)) {
979
                const AttrText& t = ValueTo<AttrText>(q);
980
                ti.text = t.text;
981
                if(!IsNull(t.font))
982
                        ti.font = t.font;
983
                
984
                if(!IsNull(t.ink))
985
                        ti.ink = t.ink;
986
        }
987
        else
988
                ti.text = IsString(q) ? q : StdConvert().Format(q);
989
        
990
        ti.size = GetTextSize(ti.text, ti.font);
991
        return ti;
992
}
993

    
994
TabBar::TabItem& TabBar::Tab::AddText(const WString& s, const Font& font, const Color& ink)
995
{
996
        TabItem& ti = AddItem();
997

    
998
        ti.font = font;
999
        ti.ink = ink;
1000
        ti.text = s;
1001
        ti.size = GetTextSize(ti.text, ti.font);
1002
        return ti;
1003
}
1004

    
1005
TabBar::TabItem& TabBar::Tab::AddSpace(int space, int side)
1006
{
1007
        TabItem& ti = AddItem();
1008
        
1009
        ti.size.cx = space;
1010
        ti.size.cy = 0;
1011
        ti.side = side;
1012
        return ti;
1013
}
1014

    
1015
void TabBar::ComposeTab(Tab& tab, const Font &font, Color ink, int style)
1016
{
1017
        if(PaintIcons() && tab.HasIcon())
1018
        {
1019
                tab.AddImage(tab.img);
1020
                tab.AddSpace(DPI(TB_SPACEICON));
1021
        }
1022
        tab.AddValue(tab.value, font, ink).Clickable();
1023
}
1024

    
1025
void TabBar::ComposeStackedTab(Tab& tab, const Tab& stacked_tab, const Font& font, Color ink, int style)
1026
{
1027
        tab.AddImage(stacked_tab.img);
1028
        tab.AddText("|...", font, ink);
1029
}
1030

    
1031
int TabBar::GetTextAngle()
1032
{
1033
        return AlignedFrame::IsVert() ? (GetAlign() == LEFT ? 900 : 2700) : 0;
1034
}
1035

    
1036
Point TabBar::GetTextPosition(int align, const Rect& r, int cy, int space) const
1037
{
1038
        Point         p;
1039
        
1040
        if(align == LEFT)
1041
        {
1042
                p.y = r.bottom - space;
1043
                p.x = r.left + (r.GetWidth() - cy) / 2;
1044
        }
1045
        else if(align == RIGHT)
1046
        {
1047
                p.y = r.top + space;
1048
                p.x = r.right - (r.GetWidth() - cy) / 2;
1049
        }
1050
        else
1051
        {
1052
                p.x = r.left + space;
1053
                p.y = r.top + (r.GetHeight() - cy) / 2;
1054
        }
1055
        return p;
1056
}
1057

    
1058
Point TabBar::GetImagePosition(int align, const Rect& r, int cx, int cy, int space, int side, int offset) const
1059
{
1060
        Point p;
1061

    
1062
        if (align == LEFT)
1063
        {
1064
                p.x = r.left + (r.GetWidth() - cy) / 2 + offset;
1065
                p.y = side == LEFT ? r.bottom - space - cx : r.top + space;
1066
        }
1067
        else if (align == RIGHT)
1068
        {
1069
                p.x = r.right - (r.GetWidth() + cy) / 2 - offset;
1070
                p.y = side == LEFT ? r.top + space : r.bottom - space - cx;
1071
        }
1072
        else if (align == TOP)
1073
        {
1074
                p.x = side == LEFT ? r.left + space : r.right - cx - space;
1075
                p.y = r.top + (r.GetHeight() - cy) / 2 + offset;
1076
        }
1077
        else if (align == BOTTOM)
1078
        {
1079
                p.x = side == LEFT ? r.left + space : r.right - cx - space;
1080
                p.y = r.bottom - (r.GetHeight() + cy) / 2 - offset;
1081
        }
1082
        return p;
1083
}
1084

    
1085
void TabBar::PaintTabItems(Tab& t, Draw &w, const Rect& rn, int align)
1086
{
1087
        int pos_left = TB_MARGIN;
1088
        int pos_right = (IsVert() ? rn.GetHeight() : rn.GetWidth()) - TB_MARGIN;
1089
        
1090
        for(int i = 0; i < t.itn; i++)
1091
        {
1092
                const TabItem& ti = t.items[i];
1093
                
1094
                Point p(0, 0);
1095
                int pos = ti.side == LEFT ? pos_left : pos_right - ti.size.cx;
1096
                
1097
                if(!IsNull(ti.img))
1098
                {
1099
                        p = GetImagePosition(align, rn, ti.size.cx, ti.size.cy, pos, LEFT);
1100
                        w.DrawImage(p.x, p.y, IsVert() ? AlignImage(align, ti.img) : ti.img);
1101
                }
1102
                
1103
                if(!IsNull(ti.text))
1104
                {
1105
                        p = GetTextPosition(align, rn, ti.size.cy, pos);
1106
                        w.DrawText(p.x, p.y, GetTextAngle(), ti.text, ti.font, ti.ink);
1107
                }
1108
                
1109
                if(ti.cross)
1110
                {
1111
                        t.cross_size = ti.size;
1112
                        t.cross_pos = p;
1113
                }
1114
                
1115
                if(ti.stacked_tab >= 0 && ti.clickable)
1116
                {
1117
                        Tab& st = tabs[ti.stacked_tab];
1118
                        
1119
                        if(align == RIGHT)
1120
                        {
1121
                                st.tab_pos = Point(rn.left, rn.top + pos);
1122
                                st.tab_size = Size(rn.GetWidth(), ti.size.cx);        
1123
                        }
1124
                        else if(align == LEFT)
1125
                        {
1126
                                st.tab_pos = Point(rn.left, rn.bottom - pos - ti.size.cx);
1127
                                st.tab_size = Size(rn.GetWidth(), ti.size.cx);        
1128
                        }
1129
                        else
1130
                        {
1131
                                st.tab_pos = Point(rn.left + pos, rn.top);
1132
                                st.tab_size = Size(ti.size.cx, rn.GetHeight());
1133
                        }
1134
                        
1135
                        #ifdef TABBAR_DEBUG
1136
                        DrawFrame(w, Rect(st.tab_pos, st.tab_size), Red);
1137
                        #endif
1138
                }
1139
                                
1140
                if(ti.side == LEFT)
1141
                        pos_left += ti.size.cx;
1142
                else
1143
                        pos_right -= ti.size.cx;
1144
        }
1145
}
1146

    
1147
void TabBar::PaintTab(Draw &w, const Size &sz, int n, bool enable, bool dragsample)
1148
{
1149
        TabBar::Tab &t = tabs[n];
1150
        const Style& s = *style;
1151
        int align = GetAlign();
1152
        int cnt = dragsample ? 1 : tabs.GetCount();
1153
        
1154
        bool ac = (n == active && enable);
1155
        bool hl = (n == highlight && enable) || (stacking && highlight >= 0 && tabs[highlight].stack == t.stack);
1156

    
1157
        int ndx = !enable ? CTRL_DISABLED :
1158
                       ac ? CTRL_PRESSED :
1159
                       hl ? CTRL_HOT : CTRL_NORMAL;
1160

    
1161
        int c = align == LEFT ? cnt - n : n;        
1162
        int lx = n > 0 ? s.extendleft : 0;
1163
        int x = t.pos.x - sc.GetPos() - lx + s.margin;
1164
        
1165
        int dy = -s.sel.top * ac;
1166
        int sel = s.sel.top;
1167

    
1168
        int df = 0;
1169
        
1170
        if (IsBR())
1171
        {
1172
                dy = -dy;
1173
                sel = s.sel.bottom;
1174
                df = Fixed(sz).cy;
1175
        }
1176

    
1177
        Size  sa = Size(t.size.cx + lx + s.sel.right + s.sel.left, t.size.cy + s.sel.bottom);
1178
        Point pa = Point(x - s.sel.left, IsBR() ? df - sa.cy : 0);
1179

    
1180
        Size  sn = Size(t.size.cx + lx, t.size.cy - s.sel.top);
1181
        Point pn = Point(x, IsBR() ? df - sn.cy - s.sel.top : s.sel.top);
1182

    
1183
        Rect ra(Fixed(pa), Fixed(sa));
1184
        Rect rn(Fixed(pn), Fixed(sn));
1185
        
1186
        t.tab_pos = (ac ? ra : rn).TopLeft();
1187
        t.tab_size = (ac ? ra : rn).GetSize();
1188

    
1189
        const Value& sv = (cnt == 1 ? s.both : c == 0 ? s.first : c == cnt - 1 ? s.last : s.normal)[ndx];
1190
        
1191
        Image img = AlignValue(align, sv, t.tab_size);
1192
        
1193
        if(!IsNull(t.col))
1194
        {
1195
                img = Colorize(img, t.col);
1196
        }
1197

    
1198
        if(dragsample)
1199
        {
1200
                w.DrawImage(Rect(Point(0, 0), t.tab_size), img);
1201
                rn = Rect(Fixed(Point(s.sel.left * ac, sel * ac + dy)), Fixed(sn));
1202
        }
1203
        else
1204
        {
1205
                w.DrawImage(Rect(t.tab_pos, t.tab_size), img);
1206
                rn = Rect(Fixed(Point(pn.x, pn.y + dy)), Fixed(sn));
1207
        }
1208
        
1209
        #ifdef TABBAR_DEBUG
1210
        DrawFrame(w, rn, Green);
1211
        #endif
1212
                
1213
        if (display)
1214
                display->Paint(w, rn, t.value, s.text_color[ndx], SColorDisabled(), ndx);
1215

    
1216
        t.Clear();
1217

    
1218
        if(crosses && cnt > mintabcount && !dragsample && !IsCancelClose(n)) {
1219
                TabItem& ti = t.AddItem();
1220
                ti.img = s.crosses[cross == n ? 2 : ac || hl ? 1 : 0];
1221
                ti.side = crosses_side;
1222
                ti.cross = true;
1223
                ti.size = s.crosses[0].GetSize();
1224
                t.AddSpace(DPI(3), crosses_side);
1225
        }
1226
        
1227
        ComposeTab(t, s.font, s.text_color[ndx], ndx);
1228
        
1229
        if (stacking) {
1230
                int ix = n + 1;
1231

    
1232
                while (ix < tabs.GetCount() && tabs[ix].stack == t.stack) {
1233
                        Tab &q = tabs[ix];
1234
                        int ndx = !enable ? CTRL_DISABLED :
1235
                                           highlight == ix ? CTRL_HOT : CTRL_NORMAL;
1236

    
1237
                        int sn = t.itn;
1238
                        ComposeStackedTab(t, q, s.font, s.text_color[ndx], ndx);
1239
                        if(t.itn > sn)
1240
                                for(; sn < t.itn; sn++)
1241
                                        t.items[sn].stacked_tab = ix;
1242
                        
1243
                        ix++;
1244
                }
1245
        }
1246
        
1247
        PaintTabItems(t, w, rn, align);
1248
}
1249

    
1250
void TabBar::Paint(Draw &w)
1251
{
1252
        int align = GetAlign();
1253
        const Style &st = *style;
1254
        Size ctrlsz = GetSize();
1255
        Size sz = GetBarSize(ctrlsz);
1256
        
1257
        if (align == BOTTOM || align == RIGHT)
1258
                w.Offset(ctrlsz.cx - sz.cx, ctrlsz.cy - sz.cy);
1259
        
1260
        #ifdef TABBAR_DEBUG
1261
        w.DrawRect(sz, Yellow);
1262
        #else
1263
        w.DrawRect(sz, SColorFace());
1264
        #endif
1265

    
1266
        IsVert() ? w.DrawRect(align == LEFT ? sz.cx - 1 : 0, 0, 1, sz.cy, Blend(SColorDkShadow, SColorShadow)):
1267
                w.DrawRect(0, align == TOP ? sz.cy - 1 : 0, sz.cx, 1, Blend(SColorDkShadow, SColorShadow));        
1268

    
1269
        if (!tabs.GetCount()) {
1270
                if (align == BOTTOM || align == RIGHT)
1271
                        w.End();
1272
                return;
1273
        }
1274
        
1275
        int limt = sc.GetPos() + (IsVert() ? sz.cy : sz.cx);        
1276
        int first = 0;
1277
        int last = tabs.GetCount() - 1;
1278
        // Find first visible tab
1279
        for(int i = 0; i < tabs.GetCount(); i++) {
1280
                Tab &tab = tabs[i]; 
1281
                if (tab.pos.x + tab.size.cx > sc.GetPos()) {
1282
                        first = i;
1283
                        break;
1284
                }
1285
        }
1286
        // Find last visible tab
1287
        for(int i = first + 1; i < tabs.GetCount(); i++) { 
1288
                if (tabs[i].visible && tabs[i].pos.x > limt) {
1289
                        last = i;
1290
                        break;
1291
                }
1292
        }
1293
        // Draw active group
1294
        for (int i = first; i <= last; i++) {
1295
                if(tabs[i].visible && i != active)
1296
                        PaintTab(w, sz, i, IsEnabled());
1297
        }
1298
        // Clear tab_size for non-visible tabs to prevent mouse handling bugs
1299
        for (int i = 0; i < first; i++)
1300
                tabs[i].tab_size = Size(0, 0);        
1301
        for (int i = last + 1; i < tabs.GetCount(); i++)
1302
                tabs[i].tab_size = Size(0, 0);                
1303
        // Draw inactive groups
1304
        if (inactivedisabled)
1305
                for (int i = first; i <= last; i++) {
1306
                        if(!tabs[i].visible && i != active && (!stacking || IsStackHead(i)))
1307
                                PaintTab(w, sz, i, !IsEnabled());
1308
                }
1309
                        
1310
        // Draw selected tab
1311
        if(active >= first && active <= last)
1312
                PaintTab(w, sz, active, true);
1313
        
1314
        // Separators
1315
        if (grouping && groupseps) {
1316
                int cy = IsVert() ? sz.cx : sz.cy;
1317
                for (int i = 0; i < separators.GetCount(); i++) {
1318
                        int x = separators[i];
1319
                        if (x > sc.GetPos() && x < limt) {
1320
                                // Paint separator
1321
                                ChPaint(w, Rect(Fixed(Point(x - sc.GetPos() + GetStyle().sel.left, 0)), 
1322
                                        Fixed(Size(TB_SPACE - GetStyle().sel.left, cy-1))), 
1323
                                        st.group_separators[IsVert() ? 1 : 0]);                                                
1324
                        }
1325
                }
1326
        }
1327
        
1328
        // Draw drag highlights
1329
        if(target >= 0)
1330
        {
1331
                // Draw target marker
1332
                int drag = isctrl ? highlight : active;
1333
                if(target != drag && target != GetNext(drag, true))
1334
                {
1335
                        last = GetLast();
1336
                        first = GetFirst();
1337
                        int x = (target == last + 1 ? tabs[last].Right() : tabs[target].pos.x)
1338
                                - sc.GetPos() - (target <= first ? 1 : 2)
1339
                                + st.margin - (target > 0 ? st.extendleft : 0);
1340

    
1341
                        if (IsHorz())
1342
                                DrawVertDrop(w, x + 1, 0, sz.cy);
1343
                        else
1344
                                DrawHorzDrop(w, 0, x + 1, sz.cx);
1345
                }
1346
                // Draw transparent drag image
1347
                Point mouse = GetMousePos() - GetScreenRect().TopLeft();
1348
                Size isz = dragtab.GetSize();
1349
                int p = 0;
1350
                int sep = TB_SBSEPARATOR * sc.IsVisible();
1351
                
1352
                int top = drag == active ? st.sel.bottom : st.sel.top;
1353
                if (align == BOTTOM || align == RIGHT)
1354
                        p = int(drag == active) * -top + sep;
1355
                else
1356
                        p = int(drag != active) * top;
1357
                
1358
                if (IsHorz())
1359
                        w.DrawImage(mouse.x - isz.cx / 2, p, isz.cx, isz.cy, dragtab);
1360
                else
1361
                        w.DrawImage(p, mouse.y - isz.cy / 2, isz.cx, isz.cy, dragtab);
1362
        }
1363
        
1364
        if (align == BOTTOM || align == RIGHT)
1365
                w.End();        
1366

    
1367
        // If not in a frame fill any spare area
1368
        if (!InFrame())
1369
                w.DrawRect(GetClientArea(), SColorFace());
1370
}
1371

    
1372
Image TabBar::GetDragSample()
1373
{
1374
        int h = drag_highlight;
1375
        if(stacking)
1376
                h = FindStackHead(tabs[h].stack);
1377
        return GetDragSample(h);
1378
}
1379

    
1380
Image TabBar::GetDragSample(int n)
1381
{
1382
        if (n < 0) return Image();
1383
        Tab &t = tabs[n];
1384

    
1385
        Size tsz(t.tab_size);
1386
        ImageDraw iw(tsz);
1387
        iw.DrawRect(tsz, SColorFace()); //this need to be fixed - if inactive tab is dragged gray edges are visible
1388
        
1389
        PaintTab(iw, tsz, n, true, true);
1390
        
1391
        Image img = iw;
1392
        ImageBuffer ib(img);
1393
        Unmultiply(ib);
1394
        RGBA *s = ~ib;
1395
        RGBA *e = s + ib.GetLength();
1396
        while(s < e) {
1397
                s->a = 180;
1398
                s++;
1399
        }
1400
        Premultiply(ib);
1401
        return ib;
1402
}
1403

    
1404
void TabBar::Scroll()
1405
{
1406
        Refresh();
1407
}
1408

    
1409
int TabBar::GetWidth(int n)
1410
{
1411
        return GetStdSize(tabs[n]).cx + GetExtraWidth(n);
1412
}
1413

    
1414
int TabBar::GetExtraWidth(int n)
1415
{
1416
        return DPI(TB_MARGIN) * 2 + (DPI(TB_SPACE) + GetStyle().crosses[0].GetSize().cx) * (crosses && !IsCancelClose(n));        
1417
}
1418

    
1419
Size TabBar::GetStdSize(const Value &q)
1420
{
1421
        if (display)
1422
                return display->GetStdSize(q);
1423
        else if (q.GetType() == STRING_V || q.GetType() == WSTRING_V)
1424
                return GetTextSize(WString(q), GetStyle().font);
1425
        else
1426
                return GetTextSize("A Tab", GetStyle().font);        
1427
}
1428

    
1429
Size TabBar::GetStackedSize(const Tab &t)
1430
{
1431
        if (!IsNull(t.img))
1432
                return t.img.GetSize();
1433
        return GetTextSize("...", GetStyle().font, 3);
1434
}
1435

    
1436
Size TabBar::GetStdSize(const Tab &t)
1437
{
1438
        return (PaintIcons() && t.HasIcon()) ? (GetStdSize(t.value) + Size(TB_ICON + 2, 0)) : GetStdSize(t.value);
1439
}
1440

    
1441
TabBar& TabBar::Add(const Value &value, Image icon, String group, bool make_active)
1442
{
1443
        return InsertKey(tabs.GetCount(), value, value, icon, group, make_active);
1444
}
1445

    
1446
TabBar& TabBar::Insert(int ix, const Value &value, Image icon, String group, bool make_active)
1447
{
1448
        return InsertKey(tabs.GetCount(), value, value, icon, group, make_active);
1449
}
1450

    
1451
TabBar& TabBar::AddKey(const Value &key, const Value &value, Image icon, String group, bool make_active)
1452
{
1453
        return InsertKey(tabs.GetCount(), key, value, icon, group, make_active);
1454
}
1455

    
1456
TabBar& TabBar::InsertKey(int ix, const Value &key, const Value &value, Image icon, String group, bool make_active)
1457
{
1458
        int id = InsertKey0(ix, key, value, icon, group);
1459
        
1460
        SortTabs0();
1461
        MakeGroups();        
1462
        Repos();
1463
        active = -1;
1464
        if (make_active || (!allownullcursor && active < 0)) 
1465
                SetCursor((groupsort || stacking) ? FindId(id) : ( minmax(ix, 0, tabs.GetCount() - 1)));                
1466
        return *this;        
1467
}
1468

    
1469
int TabBar::InsertKey0(int ix, const Value &key, const Value &value, Image icon, String group)
1470
{
1471
        ASSERT(ix >= 0);
1472
        int g = 0;
1473
        if (!group.IsEmpty()) {
1474
                g = FindGroup(group);
1475
                if (g < 0) {
1476
                        NewGroup(group);
1477
                        g = groups.GetCount() - 1;
1478
                }
1479
        }
1480
        
1481
        group = groups[g].name;
1482
        Tab t;
1483
        t.value = value;
1484
        t.key = key;
1485
        t.img = icon;
1486
        t.id = GetNextId();
1487
        t.group = Nvl(TrimBoth(group), "Unnamed Group");        
1488
        if (stacking) {
1489
                t.stackid = GetStackId(t);
1490
                
1491
                // Override index
1492
                int tail = -1;
1493
                for (int i = 0; i < tabs.GetCount(); i++) {
1494
                        if (tabs[i].stackid == t.stackid && (!grouping || tabs[i].group == t.group)) {
1495
                                tail = FindStackTail(tabs[i].stack);
1496
                                break;
1497
                        }
1498
                }
1499
                if (tail >= 0) {
1500
                        ix = tail+1;
1501
                        t.stack = tabs[tail].stack;
1502
                        tail++;
1503
                }
1504
                else {
1505
                        ix = (ix < tabs.GetCount()) ? FindStackHead(tabs[ix].stack) : ix;
1506
                        t.stack = stackcount++;
1507
                }
1508
                tabs.Insert(ix, t);
1509
                if (tail >= 0)
1510
                        SortStack(t.stack, FindStackHead(t.stack), ix);        
1511
                        
1512
        }
1513
        else
1514
                tabs.Insert(ix, t);
1515
        return t.id;
1516
}
1517

    
1518
int TabBar::GetWidth() const
1519
{
1520
        if (!tabs.GetCount()) return 0;
1521
        int ix = GetLast();
1522
        const Style& s = StyleDefault();
1523
        if (IsStackHead(ix)) 
1524
                return tabs[ix].Right() + s.margin * 2;
1525
        int stack = tabs[ix].stack;
1526
        ix--;
1527
        while (ix >= 0 && tabs[ix].stack == stack)
1528
                ix--;
1529
        return tabs[ix + 1].Right() + s.margin * 2;
1530
        
1531
}
1532

    
1533
int TabBar::GetHeight(bool scrollbar) const
1534
{
1535
        return TabBar::GetStyleHeight() + TB_SBSEPARATOR * int(scrollbar);
1536
}
1537

    
1538
int TabBar::GetStyleHeight() const
1539
{
1540
        const Style& s = GetStyle();
1541
        return s.tabheight + s.sel.top;
1542
}
1543

    
1544
void TabBar::Repos()
1545
{
1546
        if(!tabs.GetCount())
1547
                return;
1548

    
1549
        String g = GetGroupName();
1550
        int j;
1551
        bool first = true;
1552
        j = 0;
1553
        separators.Clear();
1554
        for(int i = 0; i < tabs.GetCount(); i++)
1555
                j = TabPos(g, first, i, j, false);
1556
        if (inactivedisabled)
1557
                for(int i = 0; i < tabs.GetCount(); i++)
1558
                        if (!tabs[i].visible)
1559
                                j = TabPos(g, first, i, j, true);
1560
        SyncScrollBar();
1561
}
1562

    
1563
Size TabBar::GetBarSize(Size ctrlsz) const
1564
{
1565
        return IsVert() ? Size(GetFrameSize() - scrollbar_sz * int(sc.IsShown()), ctrlsz.cy) 
1566
                        : Size(ctrlsz.cx, GetFrameSize() - scrollbar_sz * int(sc.IsShown()));
1567
}
1568

    
1569
Rect TabBar::GetClientArea() const
1570
{
1571
        Rect rect = GetSize();
1572
        switch (GetAlign()) {
1573
                case TOP:
1574
                        rect.top += GetFrameSize();        
1575
                        break;
1576
                case BOTTOM:
1577
                        rect.bottom -= GetFrameSize();        
1578
                        break;
1579
                case LEFT:
1580
                        rect.left += GetFrameSize();        
1581
                        break;
1582
                case RIGHT:
1583
                        rect.right -= GetFrameSize();        
1584
                        break;
1585
        };
1586
        return rect;        
1587
}
1588

    
1589
int TabBar::TabPos(const String &g, bool &first, int i, int j, bool inactive)
1590
{
1591
        bool ishead = IsStackHead(i);
1592
        bool v = IsNull(g) ? true : g == tabs[i].group;
1593
        Tab& t = tabs[i];
1594

    
1595
        if(ishead && (v || inactive))
1596
        {
1597
                t.visible = v;
1598
                t.pos.y = 0;
1599
                t.size.cy = GetStyleHeight();
1600
                                
1601
                // Normal visible or inactive but greyed out tabs
1602
                t.pos.x = first ? 0 : tabs[j].Right();
1603
                
1604
                // Separators
1605
                if (groupseps && grouping && !first && t.group != tabs[j].group) {
1606
                        separators.Add(t.pos.x);
1607
                        t.pos.x += TB_SPACE;
1608
                }
1609
                
1610
                int cx = GetStdSize(t).cx;
1611

    
1612
                // Stacked/shortened tabs
1613
                if (stacking) {
1614
                        for(int n = i + 1; n < tabs.GetCount() && tabs[n].stack == t.stack; n++)
1615
                                cx += GetStackedSize(tabs[n]).cx;
1616
                }
1617
                        
1618
                t.size.cx = cx + GetExtraWidth(i);
1619

    
1620
                if (stacking) {
1621
                        for(int n = i + 1; n < tabs.GetCount() && tabs[n].stack == t.stack; n++) {
1622
                                Tab &q = tabs[n];
1623
                                q.visible = false;
1624
                                q.pos = t.pos;
1625
                                q.size = t.size;
1626
                        }
1627
                }
1628
                
1629
                j = i;
1630
                first = false;
1631
        }
1632
        else if (!(v || inactive)) {
1633
                t.visible = false;
1634
                t.pos.x = sc.GetTotal() + GetBarSize(GetSize()).cx;
1635
        }
1636
        return j;
1637
}
1638

    
1639
void TabBar::ShowScrollbarFrame(bool b)
1640
{
1641
        SetFrameSize((b ? sc.GetFrameSize() : TB_SBSEPARATOR) + GetHeight(b), false);
1642
        sc.Show(b);
1643
        RefreshParentLayout();
1644
}
1645

    
1646
void TabBar::SyncScrollBar(bool synctotal)
1647
{
1648
        if (synctotal)
1649
                sc.SetTotal(GetWidth());
1650
        if (autoscrollhide) {
1651
                bool v = sc.IsScrollable();
1652
                if (sc.IsShown() != v) {
1653
                        PostCallback(THISBACK1(ShowScrollbarFrame, v));
1654
                }
1655
        }
1656
        else {
1657
                SetFrameSize(sc.GetFrameSize() + GetHeight(true), false);
1658
                sc.Show();
1659
        }
1660
}
1661

    
1662
int TabBar::FindId(int id) const
1663
{
1664
        for(int i = 0; i < tabs.GetCount(); i++)
1665
                if(tabs[i].id == id)
1666
                        return i;
1667
        return -1;
1668
}
1669

    
1670
int TabBar::GetNext(int n, bool drag) const
1671
{
1672
        for(int i = n + 1; i < tabs.GetCount(); i++)
1673
                if(tabs[i].visible)
1674
                        return i;
1675
        return drag ? tabs.GetCount() : -1;
1676
}
1677

    
1678
int TabBar::GetPrev(int n, bool drag) const
1679
{
1680
        for(int i = n - 1; i >= 0; i--)
1681
                if(tabs[i].visible)
1682
                        return i;
1683
        return -1;
1684
}
1685

    
1686
void TabBar::Clear()
1687
{
1688
        highlight = -1;
1689
        drag_highlight = -1;
1690
        active = -1;
1691
        target = -1;
1692
        cross = -1;
1693
        stackcount = 0;
1694
        tabs.Clear();
1695
        groups.Clear();
1696
        NewGroup(t_("TabBarGroupAll\aAll"));
1697
        group = 0;
1698
        Refresh();
1699
        jump_stack.Reset();
1700
}
1701

1702
TabBar& TabBar::Crosses(bool b, int side)
1703
{
1704
        crosses = b;
1705
        crosses_side = side;
1706
        Repos();
1707
        Refresh();
1708
        return *this;
1709
}
1710

1711
TabBar& TabBar::SortTabs(bool b)
1712
{
1713
        tabsort = b;
1714
        if (b)
1715
                DoTabSort(*tabsorter);
1716
        return *this;
1717
}
1718

1719
TabBar& TabBar::SortTabsOnce()
1720
{
1721
        DoTabSort(*tabsorter);
1722
        return *this;
1723
}
1724

1725
TabBar& TabBar::SortTabsOnce(TabSort &sort)
1726
{
1727
        DoTabSort(sort);
1728
        return *this;
1729
}
1730

1731
TabBar& TabBar::SortTabs(TabSort &sort)
1732
{
1733
        tabsorter = &sort;
1734
        return SortTabs(true);        
1735
}
1736

1737
TabBar& TabBar::SortTabValues(ValueOrder &sort)
1738
{
1739
        valuesorter_inst.vo = &sort;
1740
        tabsorter = &valuesorter_inst;
1741
        return SortTabs(true);        
1742
}
1743

1744
TabBar& TabBar::SortTabValuesOnce(ValueOrder &sort)
1745
{
1746
        TabValueSort q;
1747
        q.vo = &sort;
1748
        DoTabSort(q);
1749
        return *this;        
1750
}
1751

1752
TabBar& TabBar::SortTabKeys(ValueOrder &sort)
1753
{
1754
        keysorter_inst.vo = &sort;
1755
        tabsorter = &keysorter_inst;
1756
        return SortTabs(true);        
1757
}
1758

1759
TabBar& TabBar::SortTabKeysOnce(ValueOrder &sort)
1760
{
1761
        TabKeySort q;
1762
        q.vo = &sort;
1763
        DoTabSort(q);
1764
        return *this;                
1765
}
1766

1767
TabBar& TabBar::SortGroups(bool b)
1768
{
1769
        groupsort = b;
1770
        if (!b) return *this;;
1771
        
1772
        Value v = GetData();
1773
        MakeGroups();
1774
        Repos();
1775
        if (!IsNull(v))
1776
                SetData(v);
1777
        Refresh();
1778
        return *this;        
1779
}
1780

1781
TabBar& TabBar::SortGroupsOnce()
1782
{
1783
        if (!grouping) return *this;;
1784
        
1785
        Value v = GetData();
1786
        MakeGroups();
1787
        Repos();
1788
        if (!IsNull(v))
1789
                SetData(v);
1790
        Refresh();
1791
        return *this;        
1792
}
1793

1794
TabBar& TabBar::SortGroupsOnce(TabSort &sort)
1795
{
1796
        TabSort *current = groupsorter;
1797
        groupsorter = &sort;
1798
        SortGroupsOnce();
1799
        groupsorter = current;        
1800
        return *this;
1801
}
1802

1803
TabBar& TabBar::SortGroups(TabSort &sort)
1804
{
1805
        groupsorter = &sort;
1806
        return SortGroups(true);        
1807
}
1808

1809
TabBar& TabBar::SortStacks(bool b)
1810
{
1811
        stacksort = b;
1812
        if (stacking) {
1813
                DoStacking();
1814
                Refresh();
1815
        }
1816
        return *this;
1817
}
1818

1819
TabBar& TabBar::SortStacksOnce()
1820
{
1821
        if (stacking) {
1822
                DoStacking();
1823
                Refresh();
1824
        }
1825
        return *this;
1826
}
1827

1828
TabBar& TabBar::SortStacksOnce(TabSort &sort)
1829
{
1830
        TabSort *current = stacksorter;
1831
        stacksorter = &sort;
1832
        SortStacksOnce();
1833
        stacksorter = current;
1834
        return *this;
1835
}
1836

1837
TabBar& TabBar::SortStacks(TabSort &sort)
1838
{
1839
        stacksorter = &sort;
1840
        return SortStacks(true);        
1841
}
1842

1843
TabBar& TabBar::SortStacks(ValueOrder &sort)
1844
{
1845
        stacksorter_inst.vo = &sort;
1846
        stacksorter = &stacksorter_inst;
1847
        return SortStacks(true);        
1848
}
1849

1850
void TabBar::DoTabSort(TabSort &sort)
1851
{
1852
        Value v = GetData();
1853
        StableSort(tabs, sort);
1854
        Repos();
1855
        if (!IsNull(v))
1856
                SetData(v);
1857
        Refresh();
1858
}
1859

1860
void TabBar::SortTabs0()
1861
{
1862
        if (tabsort)
1863
                StableSort(tabs, *tabsorter);
1864
}
1865

1866
TabBar& TabBar::Grouping(bool b)
1867
{
1868
        grouping = b;
1869
        Repos(); 
1870
        Refresh();        
1871
        return *this;
1872
}
1873

1874
TabBar& TabBar::ContextMenu(bool b)
1875
{
1876
        contextmenu = b;
1877
        return *this;
1878
}
1879

1880
TabBar& TabBar::GroupSeparators(bool b)
1881
{
1882
        groupseps = b;
1883
        Repos();
1884
        Refresh();
1885
        return *this;
1886
}
1887

1888
TabBar& TabBar::AutoScrollHide(bool b)
1889
{
1890
        autoscrollhide = b;
1891
        sc.Hide();
1892
        SetFrameSize(GetHeight(false), false);
1893
        SyncScrollBar(GetWidth());
1894
        return *this;
1895
}
1896

1897
TabBar& TabBar::InactiveDisabled(bool b)
1898
{
1899
        inactivedisabled = b; 
1900
        Repos(); 
1901
        Refresh();        
1902
        return *this;
1903
}
1904

1905
TabBar& TabBar::AllowNullCursor(bool b)
1906
{
1907
        allownullcursor = b;
1908
        return *this;
1909
}
1910

1911
TabBar& TabBar::Icons(bool v)
1912
{
1913
        icons = v;
1914
        Repos();
1915
        Refresh();
1916
        return *this;
1917
}
1918

1919
TabBar& TabBar::Stacking(bool b)
1920
{
1921
        stacking = b;        
1922
        if (b)
1923
                DoStacking();
1924
        else
1925
                DoUnstacking();
1926
        Refresh();
1927
        return *this;
1928
}
1929

1930
void TabBar::FrameSet()
1931
{
1932
        int al = GetAlign();
1933
        Ctrl::ClearFrames();
1934
        sc.Clear();
1935
        sc.SetFrameSize(scrollbar_sz).SetAlign((al >= 2) ? al - 2 : al + 2);
1936
        sc <<= THISBACK(Scroll);
1937
        sc.Hide();
1938
        
1939
        if (sc.IsChild()) sc.Remove();
1940
        switch (al) {
1941
                case LEFT:
1942
                        Ctrl::Add(sc.LeftPos(GetHeight(), scrollbar_sz).VSizePos());
1943
                        break;
1944
                case RIGHT:
1945
                        Ctrl::Add(sc.RightPos(GetHeight(), scrollbar_sz).VSizePos());
1946
                        break;
1947
                case TOP:
1948
                        Ctrl::Add(sc.TopPos(GetHeight(), scrollbar_sz).HSizePos());
1949
                        break;
1950
                case BOTTOM:
1951
                        Ctrl::Add(sc.BottomPos(GetHeight(), scrollbar_sz).HSizePos());
1952
                        break;                        
1953
        };
1954

1955
        SyncScrollBar(true);
1956
}
1957

1958
TabBar& TabBar::SetScrollThickness(int sz)
1959
{
1960
        scrollbar_sz = max(sz + 2, 3);
1961
        FrameSet(); 
1962
        RefreshLayout();
1963
        return *this;        
1964
}
1965

1966
void TabBar::Layout()
1967
{
1968
        if (autoscrollhide && tabs.GetCount()) 
1969
                SyncScrollBar(false); 
1970
        Repos();
1971
}
1972

1973
int TabBar::FindValue(const Value &v) const
1974
{
1975
        for (int i = 0; i < tabs.GetCount(); i++)
1976
                if (tabs[i].value == v)
1977
                        return i;
1978
        return -1;
1979
}
1980

1981
int TabBar::FindKey(const Value &v) const
1982
{
1983
        for (int i = 0; i < tabs.GetCount(); i++)
1984
                if (tabs[i].key == v)
1985
                        return i;
1986
        return -1;
1987
}
1988

1989
bool TabBar::IsStackHead(int n) const
1990
{
1991
        return tabs[n].stack < 0 
1992
                || n == 0 
1993
                || (n > 0 && tabs[n - 1].stack != tabs[n].stack);
1994
}
1995

1996
bool TabBar::IsStackTail(int n) const
1997
{
1998
        return tabs[n].stack < 0
1999
                || n >= tabs.GetCount() - 1
2000
                || (n < tabs.GetCount() && tabs[n + 1].stack != tabs[n].stack);
2001
}
2002

2003
int TabBar::GetStackCount(int stackix) const
2004
{
2005
        int tc = tabs.GetCount();
2006
        int L = 0;
2007

2008
        for ( int i = 0; i < tc; ++i ) 
2009
                if ( tabs[ i ].stack == stackix )
2010
                  ++L;
2011

2012
        return L;
2013
}
2014

2015
int TabBar::FindStackHead(int stackix) const
2016
{
2017
        int i = 0;
2018
        while (tabs[i].stack != stackix)
2019
                i++;
2020
        return i;
2021
}
2022

2023
int TabBar::FindStackTail(int stackix) const
2024
{
2025
        int i = tabs.GetCount() - 1;
2026
        while (tabs[i].stack != stackix)
2027
                i--;
2028
        return i;
2029
}
2030

2031
int TabBar::SetStackHead(Tab &t)
2032
// Returns index of stack head
2033
{
2034
        ASSERT(stacking);
2035
        int id = t.id;
2036
        int stack = t.stack;
2037
        int head = FindStackHead(stack);
2038
        while (tabs[head].id != id)
2039
                CycleTabStack(head, stack);
2040
        return head;
2041
}
2042

2043
int TabBar::CycleTabStack(int n)
2044
// Returns index of stack head
2045
{
2046
        int head = FindStackHead(n);
2047
        CycleTabStack(head, n);
2048
        return head;
2049
}
2050

2051
void TabBar::CycleTabStack(int head, int n)
2052
{
2053
        // Swap tab to end of stack
2054
        int ix = head;
2055
        while (!IsStackTail(ix)) {
2056
                tabs.Swap(ix, ix + 1);
2057
                ++ix;
2058
        }
2059
}
2060

2061
Value TabBar::GetData() const
2062
{
2063
        return (HasCursor() && active < GetCount())
2064
                ? GetKey(active)
2065
                : Value();
2066
}
2067

2068
void TabBar::SetData(const Value &key)
2069
{
2070
        int n = FindKey(key); 
2071
        if (n >= 0) {
2072
                if (stacking && tabs[n].stack >= 0)
2073
                        n = SetStackHead(tabs[n]);
2074
                SetCursor(n);
2075
        }
2076
}
2077

2078
void TabBar::Set(int n, const Value &newkey, const Value &newvalue)
2079
{
2080
        Set(n, newkey, newvalue, tabs[n].img);
2081
}
2082

2083
void TabBar::Set(int n, const Value &newkey, const Value &newvalue, Image icon)
2084
{
2085
        ASSERT(n >= 0 && n < tabs.GetCount());
2086
        tabs[n].key = newkey;
2087
        tabs[n].value = newvalue;
2088
        tabs[n].img = icon;
2089
        if (stacking) {
2090
                String id = tabs[n].stackid;
2091
                tabs[n].stackid = GetStackId(tabs[n]);
2092
                if (tabs[n].stackid != id) {
2093
                        tabs.Remove(n);
2094
                        InsertKey0(GetCount(), newkey, newvalue, tabs[n].img, tabs[n].group);
2095
                }
2096
        }
2097
        Repos();
2098
        Refresh();
2099
}
2100

2101
void TabBar::SetValue(const Value &key, const Value &newvalue)
2102
{
2103
        Set(FindKey(key), key, newvalue);
2104
}
2105

2106
void TabBar::SetValue(int n, const Value &newvalue)
2107
{
2108
        Set(n, tabs[n].key, newvalue);
2109
}
2110

2111
void TabBar::SetKey(int n, const Value &newkey)
2112
{
2113
        Set(n, newkey, tabs[n].value);        
2114
}
2115

2116
void TabBar::SetIcon(int n, Image icon)
2117
{
2118
        ASSERT(n >= 0 && n < tabs.GetCount());
2119
        tabs[n].img = icon;
2120
        Repos();
2121
        Refresh();
2122
}
2123

2124
void TabBar::LeftDown(Point p, dword keyflags)
2125
{
2126
        p = AdjustMouse(p);
2127
        SetCapture();
2128
        
2129
        if(keyflags & K_SHIFT)
2130
        {
2131
                highlight = -1;
2132
                Refresh();
2133
                Fix(p);
2134
                oldp = p;
2135
                return;
2136
        }
2137

2138
        drag_highlight = highlight;
2139
        
2140
        isctrl = keyflags & K_CTRL;
2141
        if(isctrl)
2142
                return;
2143

2144
        if(cross != -1) {
2145
                if (cross < tabs.GetCount()) {
2146
                        int tempCross = cross;
2147
                        Value v = tabs[cross].key;
2148
                        ValueArray vv;
2149
                        vv.Add(v);
2150
                        int ix = cross;
2151
                        if (!CancelClose(v) && !CancelCloseSome(vv)) {
2152
                                // 2014/03/06 - FIRST the callbacks, THEN remove the tab
2153
                                // otherwise keys in WhenCloseSome() are invalid
2154
                                WhenClose(v);
2155
                                WhenCloseSome(vv);
2156
                                TabClosed(v);
2157
                                Close(ix);
2158
                        }
2159
                        if (tempCross >= 0 && tempCross < tabs.GetCount())
2160
                                ProcessMouse(tempCross, p);
2161
                }
2162
        }
2163
        else if(highlight >= 0) {
2164
                if (stacking && highlight == active) {
2165
                        CycleTabStack(tabs[active].stack);
2166
                        Repos();
2167
                        CursorChanged();
2168
                        UpdateActionRefresh();
2169
                }
2170
                else
2171
                        SetCursor0(highlight, true);
2172
        }
2173
}
2174

2175
void TabBar::LeftUp(Point p, dword keyflags)
2176
{
2177
        ReleaseCapture();
2178
}
2179

2180
void TabBar::LeftDouble(Point p, dword keysflags)
2181
{
2182
        WhenLeftDouble();
2183
}
2184

2185
void TabBar::RightDown(Point p, dword keyflags)
2186
{
2187
        if (contextmenu)
2188
        {
2189
                // 2014/03/07 needed on X11 otherwise may crash
2190
                // if focus is nowhere (probable bug somewhere else...)
2191
                if(!GetActiveCtrl())
2192
                        GetParent()->SetFocus();
2193
                MenuBar::Execute(THISBACK(ContextMenu), GetMousePos());
2194
        }
2195
}
2196

2197
void TabBar::MiddleDown(Point p, dword keyflags)
2198
{
2199
    if (highlight >= 0)
2200
    {
2201
        Value v = tabs[highlight].key;
2202
        ValueArray vv;
2203
        vv.Add(v);
2204
        int highlightBack = highlight;
2205
        if (!CancelClose(v) && ! CancelCloseSome(vv)) {
2206
            // highlight can be changed by the prompt. When reading "v", it can be invalid. I use the value from before the prompt to fix it
2207
            Value v = tabs[highlightBack].key;
2208
            // 2014/03/06 - FIRST the callbacks, THEN remove the tab
2209
            // otherwise keys in WhenCloseSome() are invalid
2210
            WhenClose(v);
2211
            WhenCloseSome(vv);
2212
            TabClosed(v);
2213
            Close(highlightBack);
2214
        }
2215
    }
2216
}
2217

2218
void TabBar::MiddleUp(Point p, dword keyflags)
2219
{
2220
}
2221

2222
int TabBar::GetTargetTab(Point p)
2223
{
2224
        p.x += sc.GetPos();
2225

2226
        int f = GetFirst();
2227
        int l = GetLast();
2228
        
2229
        if(tabs[f].visible && p.x < tabs[f].pos.x + tabs[f].size.cx / 2)
2230
                return f;
2231

2232
        int t = -1;
2233
        
2234
        for(int i = l; i >= f; i--)
2235
                if(tabs[i].visible && p.x >= tabs[i].pos.x + tabs[i].size.cx / 2)
2236
                {
2237
                        t = i;
2238
                        break;
2239
                }
2240
        
2241
        if(stacking)
2242
                l = FindStackHead(tabs[l].stack);
2243
                
2244
        if(t == l)
2245
                t = tabs.GetCount();
2246
        else
2247
                 t = GetNext(t);
2248

2249
        return t;
2250
}
2251

2252
void TabBar::MouseWheel(Point p, int zdelta, dword keyflags)
2253
{
2254
        sc.AddPos(-zdelta / 4, true);
2255
        Scroll();
2256
        MouseMove(p, 0);
2257
}
2258

2259
Point TabBar::AdjustMouse(Point const &p) const
2260
{
2261
        int align = GetAlign();
2262
        if(align == TOP || align == LEFT)
2263
                return p;
2264

2265
        Size ctrlsz = GetSize();
2266
        Size sz = GetBarSize(ctrlsz);
2267
        return Point(p.x - ctrlsz.cx + sz.cx, p.y - ctrlsz.cy + sz.cy);
2268
}
2269

2270
bool TabBar::ProcessMouse(int i, const Point& p)
2271
{
2272
        if(i >= 0 && i < tabs.GetCount() && tabs[i].HasMouse(p))
2273
        {
2274
                if (stacking && ProcessStackMouse(i, p))
2275
                        return true;
2276
                bool iscross = crosses && !IsCancelClose(i) ? tabs[i].HasMouseCross(p) : false;
2277
                if(highlight != i || (iscross && cross != i || !iscross && cross == i))
2278
                {
2279
                        cross = iscross ? i : -1;
2280
                        SetHighlight(i);
2281
                }
2282
                return true;
2283
        }
2284
        return false;
2285
}
2286

2287
bool TabBar::ProcessStackMouse(int i, const Point& p)
2288
{
2289
        int j = i + 1;
2290
        while (j < tabs.GetCount() && tabs[j].stack == tabs[i].stack) {
2291
                if (Rect(tabs[j].tab_pos, tabs[j].tab_size).Contains(p)) {
2292
                        cross = -1;
2293
                        if (highlight != j)
2294
                                SetHighlight(j);
2295
                        return true;
2296
                }
2297
                j++;
2298
        }
2299
        return false;        
2300
}
2301

2302
void TabBar::SetHighlight(int n)
2303
{
2304
        highlight = n;
2305
        WhenHighlight();
2306
        Refresh();
2307
}
2308

2309
void TabBar::SetColor(int n, Color c)
2310
{
2311
        tabs[n].col = c;
2312
        Refresh();
2313
}
2314

2315
void TabBar::MouseMove(Point p, dword keyflags)
2316
{
2317
        p = AdjustMouse(p);
2318
        if(HasCapture() && (keyflags & K_SHIFT))
2319
        {
2320
                Fix(p);
2321
                sc.AddPos(p.x - oldp.x, true);
2322
                oldp = p;
2323
                Refresh();
2324
                return;
2325
        }
2326
        
2327
        if(HasCapture())
2328
                return;
2329
        
2330
        if(ProcessMouse(active, p))
2331
                return;
2332
                
2333
        for(int i = 0; i < tabs.GetCount(); i++)
2334
        {
2335
                if(i == active)
2336
                        continue;
2337
                
2338
                if(ProcessMouse(i, p))
2339
                        return;
2340
        }
2341

2342
        if(highlight >= 0 || cross >= 0)
2343
        {
2344
                highlight = cross = -1;
2345
                WhenHighlight();
2346
                Refresh();
2347
        }
2348
}
2349

2350
void TabBar::MouseLeave()
2351
{
2352
        if(isdrag)
2353
                return;
2354
        highlight = cross = -1;
2355
        WhenHighlight();
2356
        Refresh();
2357
}
2358

2359
void TabBar::DragAndDrop(Point p, PasteClip& d)
2360
{
2361
        Fix(p);
2362
        int c = GetTargetTab(p);
2363
        int tab = isctrl ? drag_highlight : active;
2364

2365
        if (&GetInternal<TabBar>(d) != this || tabsort || c < 0 || !allowreorder) return;
2366

2367
        if (stacking) {
2368
                tab = FindStackHead(tabs[tab].stack);
2369
                if(c < tabs.GetCount())
2370
                        c = FindStackHead(tabs[c].stack);
2371
        }
2372

2373
        bool sametab = c == tab || c == GetNext(tab, true);
2374
        bool internal = AcceptInternal<TabBar>(d, "tabs");
2375

2376
        if (CancelDragAndDrop(tab, c > tab ? c-1 : c)) 
2377
        {
2378
                target = -1;
2379
                isdrag = false;
2380
                d.Reject();
2381
                return;
2382
        }
2383
        
2384
        if(!sametab && internal && d.IsAccepted())
2385
        {
2386
                int id = active >= 0 ? tabs[active].id : -1;
2387
                
2388
                // Count stack
2389
                int count = 1;
2390
                if (stacking) {
2391
                        int ix = tab + 1;
2392
                        int stack = tabs[tab].stack;
2393
                        while (ix < tabs.GetCount() && tabs[ix].stack == stack)
2394
                                ix++;
2395
                        count = ix - tab;
2396
                }
2397
                // Copy tabs
2398
                Array<Tab> stacktemp;
2399
                stacktemp.SetCount(count);
2400
                //Copy(&stacktemp[0], &tabs[tab], count);
2401
                for(int i = 0; i < count; i++)
2402
                        stacktemp[i].Set(tabs[tab + i]);
2403
                // Remove
2404
                tabs.Remove(tab, count);
2405
                if (tab < c)
2406
                        c -= count;
2407
                // Re-insert
2408
                tabs.InsertPick(c, pick(stacktemp));
2409
                
2410
                active = id >= 0 ? FindId(id) : -1;
2411
                isdrag = false;
2412
                target = -1;
2413
                MakeGroups();
2414
                Repos();
2415
                Refresh();
2416
                Sync();
2417
                MouseMove(p, 0);
2418
        }
2419
        else if(isdrag)
2420
        {
2421
                if(internal)
2422
                {
2423
                        target = -1;
2424
                        isdrag = false;
2425
                }
2426
                else
2427
                        target = c;
2428
                Refresh();
2429
        }
2430
}
2431

2432
void TabBar::CancelMode()
2433
{
2434
        isdrag = false;
2435
        target = -1;
2436
        Refresh();
2437
}
2438

2439
void TabBar::LeftDrag(Point p, dword keyflags)
2440
{
2441
        if(keyflags & K_SHIFT)
2442
                return;
2443
        if(highlight < 0)
2444
                return;
2445

2446
        Sync();
2447
        isdrag = true;
2448
        dragtab = GetDragSample();
2449
        DoDragAndDrop(InternalClip(*this, "tabs"));
2450
}
2451

2452
void TabBar::DragEnter()
2453
{
2454
}
2455

2456
void TabBar::DragLeave()
2457
{
2458
        target = -1;
2459
        Refresh();
2460
}
2461

2462
void TabBar::DragRepeat(Point p)
2463
{
2464
        if(target >= 0)
2465
        {
2466
                Point dx = GetDragScroll(this, p, 16);
2467
                Fix(dx);
2468
                if(dx.x != 0)
2469
                        sc.AddPos(dx.x);
2470
        }
2471
}
2472

2473
bool TabBar::SetCursor0(int n, bool action)
2474
{
2475
        if(tabs.GetCount() == 0)
2476
                return false;
2477

2478
        if(n < 0)
2479
        {
2480
                n = max(0, FindId(GetGroupActive()));
2481
                active = -1;
2482
                highlight = -1;
2483
                drag_highlight = -1;
2484
                if (allownullcursor)
2485
                        return true;
2486
        }
2487

2488
        bool is_all = IsGroupAll();
2489
        bool same_group = tabs[n].group == GetGroupName();
2490

2491
        if((same_group || is_all) && active == n)
2492
                return false;
2493

2494
        bool repos = false;
2495

2496
        if (!IsStackHead(n)) 
2497
        {
2498
                n = SetStackHead(tabs[n]);
2499
                repos = true;                
2500
        }
2501

2502
        active = n;
2503

2504
        if(!is_all && !same_group) 
2505
        {
2506
                SetGroup(tabs[n].group);
2507
                repos = true;
2508
        }
2509
        if (repos)
2510
                Repos();
2511

2512
        SetGroupActive(tabs[n].id);
2513

2514
        int cx = tabs[n].pos.x - sc.GetPos();
2515
        if(cx < 0)
2516
                sc.AddPos(cx - 10);
2517
        else
2518
        {
2519
                Size sz = Ctrl::GetSize();
2520
                Fix(sz);
2521
                cx = tabs[n].pos.x + tabs[n].size.cx - sz.cx - sc.GetPos();
2522
                if(cx > 0)
2523
                        sc.AddPos(cx + 10);
2524
        }
2525
        
2526
        if(action)
2527
        {
2528
                CursorChanged();
2529
                UpdateAction();
2530
        }
2531

2532
        Refresh();
2533

2534
        if(Ctrl::HasMouse())
2535
        {
2536
                Refresh();
2537
                Sync();
2538
                MouseMove(GetMouseViewPos(), 0);
2539
        }
2540
        return true;
2541
}
2542

2543
void TabBar::SetCursor(int n)
2544
{
2545
        SetCursor0(n, true);
2546
}
2547

2548
void TabBar::SetTabGroup(int n, const String &group)
2549
{
2550
        ASSERT(n >= 0 && n < tabs.GetCount());
2551
        int g = FindGroup(group);
2552
        if (g <= 0) 
2553
                NewGroup(group);
2554
        else if (groups[g].active == tabs[n].id)
2555
                SetGroupActive(tabs[n].id);
2556
        tabs[n].group = group;
2557
        MakeGroups();
2558
        Repos();
2559
}
2560

2561
void TabBar::CloseForce(int n, bool action)
2562
{
2563
        if(n < 0 || n >= tabs.GetCount())
2564
                return;
2565
        if(n == active)
2566
        {
2567
                int c = FindId(tabs[n].id);
2568
                int nc = GetNext(c);
2569
                if(nc < 0)
2570
                        nc = max(0, GetPrev(c));
2571
                SetGroupActive(tabs[nc].id);
2572
        }
2573
        sc.AddTotal(-tabs[n].size.cx);
2574
        tabs.Remove(n);
2575
        MakeGroups();
2576
        Repos();
2577
        
2578
        if(n == active)
2579
                SetCursor0(-1, action);
2580
        else {
2581
                if (n < active)
2582
                        active--;
2583
                Refresh();
2584
                if (n == highlight && Ctrl::HasMouse()) {
2585
                        //TODO: That must be refactored
2586
                        highlight = -1;
2587
                        drag_highlight = -1;
2588
                        Refresh();
2589
                        Sync();
2590
                        MouseMove(GetMouseViewPos(), 0);
2591
                }        
2592
        }
2593
}
2594

2595
void TabBar::Close(int n, bool action)
2596
{
2597
        if(tabs.GetCount() <= mintabcount)
2598
                return;
2599

2600
        CloseForce(n, action);
2601
}
2602

2603
void TabBar::CloseKey(const Value &key)
2604
{
2605
        int tabix = FindKey(key);
2606
        if (tabix < 0) return;
2607
        Close(tabix);
2608
}
2609

2610
TabBar::Style& TabBar::Style::DefaultCrosses()
2611
{
2612
        crosses[0] = TabBarImg::CR0();
2613
        crosses[1] = TabBarImg::CR1();
2614
        crosses[2] = TabBarImg::CR2();
2615
        return *this;
2616
}
2617

2618
TabBar::Style& TabBar::Style::Variant1Crosses()
2619
{
2620
        crosses[0] = TabBarImg::VARIANT1_CR0();
2621
        crosses[1] = TabBarImg::VARIANT1_CR1();
2622
        crosses[2] = TabBarImg::VARIANT1_CR2();
2623
        return *this;        
2624
}
2625

2626
TabBar::Style& TabBar::Style::Variant2Crosses()
2627
{
2628
        crosses[0] = TabBarImg::VARIANT2_CR0();
2629
        crosses[1] = TabBarImg::VARIANT2_CR1();
2630
        crosses[2] = TabBarImg::VARIANT2_CR2();
2631
        return *this;        
2632
}
2633

2634
TabBar::Style& TabBar::Style::GroupSeparators(Value horz, Value vert)
2635
{
2636
        group_separators[0] = horz;
2637
        group_separators[0] = vert;
2638
        return *this;
2639
}
2640

2641
TabBar::Style& TabBar::Style::DefaultGroupSeparators()
2642
{
2643
        return GroupSeparators(TabBarImg::SEP(), TabBarImg::SEPV());        
2644
}
2645

2646
Vector<Value> TabBar::GetKeys() const
2647
{
2648
        Vector<Value> keys;
2649
        keys.SetCount(tabs.GetCount());
2650
        for (int i = 0; i < tabs.GetCount(); i++)
2651
                keys[i] = tabs[i].key;
2652
        return keys;
2653
}
2654

2655
Vector<Image> TabBar::GetIcons() const
2656
{
2657
        Vector<Image> img;
2658
        img.SetCount(tabs.GetCount());
2659
        for (int i = 0; i < tabs.GetCount(); i++)
2660
                img[i] = tabs[i].img;
2661
        return img;
2662
}
2663

2664
TabBar& TabBar::CopyBaseSettings(const TabBar& src)
2665
{
2666
        crosses = src.crosses;
2667
        crosses_side = src.crosses_side;
2668
        grouping = src.grouping;
2669
        contextmenu = src.contextmenu;
2670
        autoscrollhide = src.autoscrollhide;                
2671
        nosel = src.nosel;
2672
        nohl = src.nohl;
2673
        inactivedisabled = src.inactivedisabled;
2674
        stacking = src.stacking;
2675
        groupsort = src.groupsort;
2676
        groupseps = src.groupseps;
2677
        tabsort = src.tabsort;
2678
        allownullcursor = src.allownullcursor;
2679
        icons = src.icons;
2680
        mintabcount = src.mintabcount;
2681
        return *this;
2682
}
2683

2684
TabBar& TabBar::CopySettings(const TabBar &src)
2685
{
2686
        
2687
        CopyBaseSettings(src);
2688
        
2689
        if (stacking != src.stacking)
2690
                Stacking(src.stacking);
2691
        else {
2692
                MakeGroups();
2693
                Repos();
2694
                Refresh();
2695
        }
2696
        return *this;
2697
}
2698

2699
void TabBar::Serialize(Stream& s)
2700
{
2701
        int version = 1;
2702
        s / version;
2703

2704
        s % id;                
2705
        s % crosses;
2706
        s % crosses_side;
2707
        s % grouping;
2708
        s % autoscrollhide;
2709
        s % nosel;
2710
        s % nohl;
2711
        s % inactivedisabled;
2712
        s % stacking;
2713
        s % groupsort;
2714
        s % groupseps;
2715
        s % tabsort;
2716
        s % allownullcursor;
2717
        s % icons;
2718
        s % mintabcount;
2719
        s % active;
2720
        
2721
        cross = -1;
2722
        highlight = -1;
2723
        drag_highlight = -1;
2724
        target = -1;
2725
        
2726
        int n = groups.GetCount();
2727
        s % n;
2728
        groups.SetCount(clamp(n, 0, 10000));
2729
        
2730
        for(int i = 0; i < groups.GetCount(); i++)
2731
                s % groups[i];
2732
        
2733
        n = tabs.GetCount();
2734
        s % n;
2735
        tabs.SetCount(clamp(n, 0, 10000));
2736
        
2737
        for(int i = 0; i < tabs.GetCount(); i++)
2738
                s % tabs[i];
2739
        
2740
        int g = GetGroup();
2741
        s % g;
2742
        group = g;
2743
        
2744
        Repos();
2745
}
2746

2747
TabBar& TabBar::SetStyle(const TabBar::Style& s)        {
2748
        if(style != &s) {
2749
                style = &s;
2750
                RefreshLayout();
2751
                Refresh();
2752
        }
2753
        return *this;
2754
}
2755

2756
CH_STYLE(TabBar, Style, StyleDefault)
2757
{
2758
        Assign(TabCtrl::StyleDefault());
2759
#ifdef PLATFORM_WIN32
2760
        if(IsWinVista())
2761
                Variant2Crosses();
2762
        else
2763
                DefaultCrosses();
2764
#else
2765
        DefaultCrosses();
2766
#endif
2767
        DefaultGroupSeparators();
2768
}
2769

2770
}
2771