TabBar.cpp

IƱaki Zabala, 06/27/2020 01:27 AM

Download (52.4 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 && ii >= 0 && !IsCancelClose(ii))
459
                bar.Add(tabs.GetCount() > mintabcount, t_("Close"), [=] {
460
                        if (CancelClose && !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 && !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 && CancelCloseAll())
555
                return true;
556
        
557
        if (CancelCloseSome) {
558
                ValueArray vv;
559
                vv.Add(tabs[id].key);
560
                if (CancelCloseSome(vv))
561
                        return true;
562
        }
563
        
564
        if (CancelClose && CancelClose(tabs[id].key))
565
                return true;
566
        return false;
567
}
568

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

    
590
TabBar::Tab::Tab()
591
{
592
        id = -1;
593
        stack = -1;
594
        visible = true;
595
        itn = 0;
596
        items.SetCount(5);
597

    
598
        pos = cross_pos = tab_pos = Point(0, 0);
599
        cross_size = size = tab_size = Size(0, 0);
600
}
601

    
602
void TabBar::Tab::Set(const Tab& t)
603
{
604
        id = t.id;
605
        
606
        img = t.img;
607
        col = t.col;
608
        key = t.key;
609
        value = t.value;
610
        group = t.group;
611
        
612
        stackid = t.stackid;
613
        stack = t.stack;
614

    
615
        visible = t.visible;
616

    
617
        pos = t.pos;
618
        size = t.size;
619
        
620
        cross_pos = t.cross_pos;
621
        cross_size = t.cross_size;
622
        
623
        tab_pos = t.tab_pos;
624
        tab_size = t.tab_size;
625
        
626
        items <<= t.items;
627
}
628

    
629
void TabBar::Tab::Serialize(Stream& s)
630
{
631
        s % id % key % value % group % stackid % stack % visible;
632
}
633

    
634
bool TabBar::Tab::HasMouse(const Point& p) const
635
{
636
        if(!visible)
637
                return false;
638
        
639
        return p.x >= tab_pos.x && p.x < tab_pos.x + tab_size.cx &&
640
               p.y >= tab_pos.y && p.y < tab_pos.y + tab_size.cy;
641
}
642

    
643
bool TabBar::Tab::HasMouseCross(const Point& p) const
644
{
645
        if(!visible)
646
                return false;
647

    
648
        return p.x >= cross_pos.x && p.x < cross_pos.x + cross_size.cx &&
649
               p.y >= cross_pos.y && p.y < cross_pos.y + cross_size.cy;
650
}
651

    
652
int TabBar::FindGroup(const String& g) const
653
{
654
        for(int i = 0; i < groups.GetCount(); i++)
655
                if(groups[i].name == g)
656
                        return i;
657
        return -1;
658
}
659

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

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

    
715
void TabBar::SortStack(int stackix)
716
{
717
        if (!stacksort) return;
718
        
719
        int head = FindStackHead(stackix);
720
        int tail = head;
721
        while (tail < tabs.GetCount() && tabs[tail].stack == stackix)
722
                ++tail;
723
        SortStack(stackix, head, tail-1);
724
}
725

    
726
void TabBar::SortStack(int stackix, int head, int tail)
727
{
728
        if (!stacksort) return;
729
        
730
        int headid = tabs[head].id;
731
        StableSort(SubRange(tabs, head, tail - head).Write(), *stacksorter);
732
        while (tabs[head].id != headid)
733
                CycleTabStack(head, stackix);
734
}
735

    
736
void TabBar::MakeGroups()
737
{
738
        for(const auto& tab : tabs)
739
                if(FindGroup(tab.group) < 0)
740
                        NewGroup(tab.group);
741
        
742
        groups[0].count = tabs.GetCount();
743
        groups[0].first = 0;
744
        groups[0].last = tabs.GetCount() - 1;
745

    
746
        if (groupsort)
747
                StableSort(tabs, *groupsorter);
748

    
749
        for(int i = 1; i < groups.GetCount(); i++)
750
        {
751
                groups[i].count = 0;
752
                groups[i].first = 10000000;
753
                groups[i].last = 0;
754
        }
755

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

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

    
797
void TabBar::DoGrouping(int n)
798
{
799
        group = n;
800
        Repos();
801
        SyncScrollBar();
802
}
803

    
804

    
805
void TabBar::DoCloseGroup(int n)
806
{
807
        int cnt = groups.GetCount();
808
        if(cnt <= 0)
809
                return;
810

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

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

    
889
        if (cnt == n)
890
                group--;
891

    
892
        if(cnt > 1) // what if CancelClose suppressed some tab closing ?
893
                groups.Remove(n);
894
        MakeGroups();
895
        Repos();
896
        SetCursor(-1);
897
}
898

    
899
void TabBar::NewGroup(const String &name)
900
{
901
        Group &g = groups.Add();
902
        g.name = name;
903
        g.count = 0;
904
        g.first = 10000000;
905
        g.last = 0;
906
        g.active = -1;
907
}
908

    
909
Image TabBar::AlignImage(int align, const Image& img)
910
{
911
        switch(align) {
912
                case AlignedFrame::LEFT: 
913
                        return RotateAntiClockwise(img);
914
                case AlignedFrame::RIGHT: 
915
                        return RotateClockwise(img);
916
                case AlignedFrame::BOTTOM:
917
                        return MirrorVert(img);
918
                default:
919
                        return img;
920
        }
921
}
922

    
923
Value TabBar::AlignValue(int align, const Value &v, const Size &sz)
924
{
925
        Size isz = sz;
926
        if(align == AlignedFrame::LEFT || align == AlignedFrame::RIGHT)
927
                Swap(isz.cx, isz.cy);
928

    
929
        ImageDraw w(isz.cx, isz.cy);
930
        w.DrawRect(isz, SColorFace());
931
        ChPaint(w, isz, v);
932
        ImageBuffer img;
933
        return AlignImage(align, (Image)w);
934
}
935

    
936
void TabBar::TabItem::Clear()
937
{
938
        text.Clear();
939
        ink = Null;
940
        img = Null;
941
        side = LEFT;
942
        clickable = false;
943
        cross = false;
944
        stacked_tab = -1;
945
}
946

    
947
TabBar::TabItem& TabBar::Tab::AddItem()
948
{
949
        if(itn < items.GetCount())
950
        {
951
                TabItem& ti = items[itn++];
952
                ti.Clear();
953
                return ti;
954
        }
955
        else
956
        {
957
                ++itn;
958
                return items.Add();
959
        }
960
}
961

    
962
void TabBar::Tab::Clear()
963
{
964
        itn = 0;
965
}
966

    
967
TabBar::TabItem& TabBar::Tab::AddImage(const Image& img, int side)
968
{
969
        TabItem& ti = AddItem();
970
        ti.img = img;
971
        ti.size = img.GetSize();
972
        ti.side = side;
973
        return ti;
974
}
975

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

    
999
TabBar::TabItem& TabBar::Tab::AddText(const WString& s, const Font& font, const Color& ink)
1000
{
1001
        TabItem& ti = AddItem();
1002

    
1003
        ti.font = font;
1004
        ti.ink = ink;
1005
        ti.text = s;
1006
        ti.size = GetTextSize(ti.text, ti.font);
1007
        return ti;
1008
}
1009

    
1010
TabBar::TabItem& TabBar::Tab::AddSpace(int space, int side)
1011
{
1012
        TabItem& ti = AddItem();
1013
        
1014
        ti.size.cx = space;
1015
        ti.size.cy = 0;
1016
        ti.side = side;
1017
        return ti;
1018
}
1019

    
1020
void TabBar::ComposeTab(Tab& tab, const Font &font, Color ink, int style)
1021
{
1022
        if(PaintIcons() && tab.HasIcon())
1023
        {
1024
                tab.AddImage(tab.img);
1025
                tab.AddSpace(DPI(TB_SPACEICON));
1026
        }
1027
        tab.AddValue(tab.value, font, ink).Clickable();
1028
}
1029

    
1030
void TabBar::ComposeStackedTab(Tab& tab, const Tab& stacked_tab, const Font& font, Color ink, int style)
1031
{
1032
        tab.AddImage(stacked_tab.img);
1033
        tab.AddText("|...", font, ink);
1034
}
1035

    
1036
int TabBar::GetTextAngle()
1037
{
1038
        return AlignedFrame::IsVert() ? (GetAlign() == LEFT ? 900 : 2700) : 0;
1039
}
1040

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

    
1063
Point TabBar::GetImagePosition(int align, const Rect& r, int cx, int cy, int space, int side, int offset) const
1064
{
1065
        Point p;
1066

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

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

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

    
1162
        int ndx = !enable ? CTRL_DISABLED :
1163
                       ac ? CTRL_PRESSED :
1164
                       hl ? CTRL_HOT : CTRL_NORMAL;
1165

    
1166
        int c = align == LEFT ? cnt - n : n;        
1167
        int lx = n > 0 ? s.extendleft : 0;
1168
        int x = t.pos.x - sc.GetPos() - lx + s.margin;
1169
        
1170
        int dy = -s.sel.top * ac;
1171
        int sel = s.sel.top;
1172

    
1173
        int df = 0;
1174
        
1175
        if (IsBR())
1176
        {
1177
                dy = -dy;
1178
                sel = s.sel.bottom;
1179
                df = Fixed(sz).cy;
1180
        }
1181

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

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

    
1188
        Rect ra(Fixed(pa), Fixed(sa));
1189
        Rect rn(Fixed(pn), Fixed(sn));
1190
        
1191
        t.tab_pos = (ac ? ra : rn).TopLeft();
1192
        t.tab_size = (ac ? ra : rn).GetSize();
1193

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

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

    
1221
        t.Clear();
1222

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

    
1237
                while (ix < tabs.GetCount() && tabs[ix].stack == t.stack) {
1238
                        Tab &q = tabs[ix];
1239
                        int ndx = !enable ? CTRL_DISABLED :
1240
                                           highlight == ix ? CTRL_HOT : CTRL_NORMAL;
1241

    
1242
                        int sn = t.itn;
1243
                        ComposeStackedTab(t, q, s.font, s.text_color[ndx], ndx);
1244
                        if(t.itn > sn)
1245
                                for(; sn < t.itn; sn++)
1246
                                        t.items[sn].stacked_tab = ix;
1247
                        
1248
                        ix++;
1249
                }
1250
        }
1251
        
1252
        PaintTabItems(t, w, rn, align);
1253
}
1254

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

    
1271
        IsVert() ? w.DrawRect(align == LEFT ? sz.cx - 1 : 0, 0, 1, sz.cy, Blend(SColorDkShadow, SColorShadow)):
1272
                w.DrawRect(0, align == TOP ? sz.cy - 1 : 0, sz.cx, 1, Blend(SColorDkShadow, SColorShadow));        
1273

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

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

    
1372
        // If not in a frame fill any spare area
1373
        if (!InFrame())
1374
                w.DrawRect(GetClientArea(), SColorFace());
1375
}
1376

    
1377
Image TabBar::GetDragSample()
1378
{
1379
        int h = drag_highlight;
1380
        if(stacking)
1381
                h = FindStackHead(tabs[h].stack);
1382
        return GetDragSample(h);
1383
}
1384

    
1385
Image TabBar::GetDragSample(int n)
1386
{
1387
        if (n < 0) return Image();
1388
        Tab &t = tabs[n];
1389

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

    
1409
void TabBar::Scroll()
1410
{
1411
        Refresh();
1412
}
1413

    
1414
int TabBar::GetWidth(int n)
1415
{
1416
        return GetStdSize(tabs[n]).cx + GetExtraWidth(n);
1417
}
1418

    
1419
int TabBar::GetExtraWidth(int n)
1420
{
1421
        return DPI(TB_MARGIN) * 2 + (DPI(TB_SPACE) + GetStyle().crosses[0].GetSize().cx) * (crosses && !IsCancelClose(n));        
1422
}
1423

    
1424
Size TabBar::GetStdSize(const Value &q)
1425
{
1426
        if (display)
1427
                return display->GetStdSize(q);
1428
        else if (q.GetType() == STRING_V || q.GetType() == WSTRING_V)
1429
                return GetTextSize(WString(q), GetStyle().font);
1430
        else
1431
                return GetTextSize("A Tab", GetStyle().font);        
1432
}
1433

    
1434
Size TabBar::GetStackedSize(const Tab &t)
1435
{
1436
        if (!IsNull(t.img))
1437
                return t.img.GetSize();
1438
        return GetTextSize("...", GetStyle().font, 3);
1439
}
1440

    
1441
Size TabBar::GetStdSize(const Tab &t)
1442
{
1443
        return (PaintIcons() && t.HasIcon()) ? (GetStdSize(t.value) + Size(TB_ICON + 2, 0)) : GetStdSize(t.value);
1444
}
1445

    
1446
TabBar& TabBar::Add(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::Insert(int ix, const Value &value, Image icon, String group, bool make_active)
1452
{
1453
        return InsertKey(tabs.GetCount(), value, value, icon, group, make_active);
1454
}
1455

    
1456
TabBar& TabBar::AddKey(const Value &key, const Value &value, Image icon, String group, bool make_active)
1457
{
1458
        return InsertKey(tabs.GetCount(), key, value, icon, group, make_active);
1459
}
1460

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

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

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

    
1538
int TabBar::GetHeight(bool scrollbar) const
1539
{
1540
        return TabBar::GetStyleHeight() + TB_SBSEPARATOR * int(scrollbar);
1541
}
1542

    
1543
int TabBar::GetStyleHeight() const
1544
{
1545
        const Style& s = GetStyle();
1546
        return s.tabheight + s.sel.top;
1547
}
1548

    
1549
void TabBar::Repos()
1550
{
1551
        if(!tabs.GetCount())
1552
                return;
1553

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

    
1568
Size TabBar::GetBarSize(Size ctrlsz) const
1569
{
1570
        return IsVert() ? Size(GetFrameSize() - scrollbar_sz * int(sc.IsShown()), ctrlsz.cy) 
1571
                        : Size(ctrlsz.cx, GetFrameSize() - scrollbar_sz * int(sc.IsShown()));
1572
}
1573

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

    
1594
int TabBar::TabPos(const String &g, bool &first, int i, int j, bool inactive)
1595
{
1596
        bool ishead = IsStackHead(i);
1597
        bool v = IsNull(g) ? true : g == tabs[i].group;
1598
        Tab& t = tabs[i];
1599

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

    
1617
                // Stacked/shortened tabs
1618
                if (stacking) {
1619
                        for(int n = i + 1; n < tabs.GetCount() && tabs[n].stack == t.stack; n++)
1620
                                cx += GetStackedSize(tabs[n]).cx;
1621
                }
1622
                        
1623
                t.size.cx = cx + GetExtraWidth(i);
1624

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

    
1644
void TabBar::ShowScrollbarFrame(bool b)
1645
{
1646
        SetFrameSize((b ? sc.GetFrameSize() : TB_SBSEPARATOR) + GetHeight(b), false);
1647
        sc.Show(b);
1648
        RefreshParentLayout();
1649
}
1650

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

    
1667
int TabBar::FindId(int id) const
1668
{
1669
        for(int i = 0; i < tabs.GetCount(); i++)
1670
                if(tabs[i].id == id)
1671
                        return i;
1672
        return -1;
1673
}
1674

    
1675
int TabBar::GetNext(int n, bool drag) const
1676
{
1677
        for(int i = n + 1; i < tabs.GetCount(); i++)
1678
                if(tabs[i].visible)
1679
                        return i;
1680
        return drag ? tabs.GetCount() : -1;
1681
}
1682

    
1683
int TabBar::GetPrev(int n, bool drag) const
1684
{
1685
        for(int i = n - 1; i >= 0; i--)
1686
                if(tabs[i].visible)
1687
                        return i;
1688
        return -1;
1689
}
1690

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

1707
TabBar& TabBar::Crosses(bool b, int side)
1708
{
1709
        crosses = b;
1710
        crosses_side = side;
1711
        Repos();
1712
        Refresh();
1713
        return *this;
1714
}
1715

1716
TabBar& TabBar::SortTabs(bool b)
1717
{
1718
        tabsort = b;
1719
        if (b)
1720
                DoTabSort(*tabsorter);
1721
        return *this;
1722
}
1723

1724
TabBar& TabBar::SortTabsOnce()
1725
{
1726
        DoTabSort(*tabsorter);
1727
        return *this;
1728
}
1729

1730
TabBar& TabBar::SortTabsOnce(TabSort &sort)
1731
{
1732
        DoTabSort(sort);
1733
        return *this;
1734
}
1735

1736
TabBar& TabBar::SortTabs(TabSort &sort)
1737
{
1738
        tabsorter = &sort;
1739
        return SortTabs(true);        
1740
}
1741

1742
TabBar& TabBar::SortTabValues(ValueOrder &sort)
1743
{
1744
        valuesorter_inst.vo = &sort;
1745
        tabsorter = &valuesorter_inst;
1746
        return SortTabs(true);        
1747
}
1748

1749
TabBar& TabBar::SortTabValuesOnce(ValueOrder &sort)
1750
{
1751
        TabValueSort q;
1752
        q.vo = &sort;
1753
        DoTabSort(q);
1754
        return *this;        
1755
}
1756

1757
TabBar& TabBar::SortTabKeys(ValueOrder &sort)
1758
{
1759
        keysorter_inst.vo = &sort;
1760
        tabsorter = &keysorter_inst;
1761
        return SortTabs(true);        
1762
}
1763

1764
TabBar& TabBar::SortTabKeysOnce(ValueOrder &sort)
1765
{
1766
        TabKeySort q;
1767
        q.vo = &sort;
1768
        DoTabSort(q);
1769
        return *this;                
1770
}
1771

1772
TabBar& TabBar::SortGroups(bool b)
1773
{
1774
        groupsort = b;
1775
        if (!b) return *this;;
1776
        
1777
        Value v = GetData();
1778
        MakeGroups();
1779
        Repos();
1780
        if (!IsNull(v))
1781
                SetData(v);
1782
        Refresh();
1783
        return *this;        
1784
}
1785

1786
TabBar& TabBar::SortGroupsOnce()
1787
{
1788
        if (!grouping) return *this;;
1789
        
1790
        Value v = GetData();
1791
        MakeGroups();
1792
        Repos();
1793
        if (!IsNull(v))
1794
                SetData(v);
1795
        Refresh();
1796
        return *this;        
1797
}
1798

1799
TabBar& TabBar::SortGroupsOnce(TabSort &sort)
1800
{
1801
        TabSort *current = groupsorter;
1802
        groupsorter = &sort;
1803
        SortGroupsOnce();
1804
        groupsorter = current;        
1805
        return *this;
1806
}
1807

1808
TabBar& TabBar::SortGroups(TabSort &sort)
1809
{
1810
        groupsorter = &sort;
1811
        return SortGroups(true);        
1812
}
1813

1814
TabBar& TabBar::SortStacks(bool b)
1815
{
1816
        stacksort = b;
1817
        if (stacking) {
1818
                DoStacking();
1819
                Refresh();
1820
        }
1821
        return *this;
1822
}
1823

1824
TabBar& TabBar::SortStacksOnce()
1825
{
1826
        if (stacking) {
1827
                DoStacking();
1828
                Refresh();
1829
        }
1830
        return *this;
1831
}
1832

1833
TabBar& TabBar::SortStacksOnce(TabSort &sort)
1834
{
1835
        TabSort *current = stacksorter;
1836
        stacksorter = &sort;
1837
        SortStacksOnce();
1838
        stacksorter = current;
1839
        return *this;
1840
}
1841

1842
TabBar& TabBar::SortStacks(TabSort &sort)
1843
{
1844
        stacksorter = &sort;
1845
        return SortStacks(true);        
1846
}
1847

1848
TabBar& TabBar::SortStacks(ValueOrder &sort)
1849
{
1850
        stacksorter_inst.vo = &sort;
1851
        stacksorter = &stacksorter_inst;
1852
        return SortStacks(true);        
1853
}
1854

1855
void TabBar::DoTabSort(TabSort &sort)
1856
{
1857
        Value v = GetData();
1858
        StableSort(tabs, sort);
1859
        Repos();
1860
        if (!IsNull(v))
1861
                SetData(v);
1862
        Refresh();
1863
}
1864

1865
void TabBar::SortTabs0()
1866
{
1867
        if (tabsort)
1868
                StableSort(tabs, *tabsorter);
1869
}
1870

1871
TabBar& TabBar::Grouping(bool b)
1872
{
1873
        grouping = b;
1874
        Repos(); 
1875
        Refresh();        
1876
        return *this;
1877
}
1878

1879
TabBar& TabBar::ContextMenu(bool b)
1880
{
1881
        contextmenu = b;
1882
        return *this;
1883
}
1884

1885
TabBar& TabBar::GroupSeparators(bool b)
1886
{
1887
        groupseps = b;
1888
        Repos();
1889
        Refresh();
1890
        return *this;
1891
}
1892

1893
TabBar& TabBar::AutoScrollHide(bool b)
1894
{
1895
        autoscrollhide = b;
1896
        sc.Hide();
1897
        SetFrameSize(GetHeight(false), false);
1898
        SyncScrollBar(GetWidth());
1899
        return *this;
1900
}
1901

1902
TabBar& TabBar::InactiveDisabled(bool b)
1903
{
1904
        inactivedisabled = b; 
1905
        Repos(); 
1906
        Refresh();        
1907
        return *this;
1908
}
1909

1910
TabBar& TabBar::AllowNullCursor(bool b)
1911
{
1912
        allownullcursor = b;
1913
        return *this;
1914
}
1915

1916
TabBar& TabBar::Icons(bool v)
1917
{
1918
        icons = v;
1919
        Repos();
1920
        Refresh();
1921
        return *this;
1922
}
1923

1924
TabBar& TabBar::Stacking(bool b)
1925
{
1926
        stacking = b;        
1927
        if (b)
1928
                DoStacking();
1929
        else
1930
                DoUnstacking();
1931
        Refresh();
1932
        return *this;
1933
}
1934

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

1960
        SyncScrollBar(true);
1961
}
1962

1963
TabBar& TabBar::SetScrollThickness(int sz)
1964
{
1965
        scrollbar_sz = max(sz + 2, 3);
1966
        FrameSet(); 
1967
        RefreshLayout();
1968
        return *this;        
1969
}
1970

1971
void TabBar::Layout()
1972
{
1973
        if (autoscrollhide && tabs.GetCount()) 
1974
                SyncScrollBar(false); 
1975
        Repos();
1976
}
1977

1978
int TabBar::FindValue(const Value &v) const
1979
{
1980
        for (int i = 0; i < tabs.GetCount(); i++)
1981
                if (tabs[i].value == v)
1982
                        return i;
1983
        return -1;
1984
}
1985

1986
int TabBar::FindKey(const Value &v) const
1987
{
1988
        for (int i = 0; i < tabs.GetCount(); i++)
1989
                if (tabs[i].key == v)
1990
                        return i;
1991
        return -1;
1992
}
1993

1994
bool TabBar::IsStackHead(int n) const
1995
{
1996
        return tabs[n].stack < 0 
1997
                || n == 0 
1998
                || (n > 0 && tabs[n - 1].stack != tabs[n].stack);
1999
}
2000

2001
bool TabBar::IsStackTail(int n) const
2002
{
2003
        return tabs[n].stack < 0
2004
                || n >= tabs.GetCount() - 1
2005
                || (n < tabs.GetCount() && tabs[n + 1].stack != tabs[n].stack);
2006
}
2007

2008
int TabBar::GetStackCount(int stackix) const
2009
{
2010
        int tc = tabs.GetCount();
2011
        int L = 0;
2012

2013
        for ( int i = 0; i < tc; ++i ) 
2014
                if ( tabs[ i ].stack == stackix )
2015
                  ++L;
2016

2017
        return L;
2018
}
2019

2020
int TabBar::FindStackHead(int stackix) const
2021
{
2022
        int i = 0;
2023
        while (tabs[i].stack != stackix)
2024
                i++;
2025
        return i;
2026
}
2027

2028
int TabBar::FindStackTail(int stackix) const
2029
{
2030
        int i = tabs.GetCount() - 1;
2031
        while (tabs[i].stack != stackix)
2032
                i--;
2033
        return i;
2034
}
2035

2036
int TabBar::SetStackHead(Tab &t)
2037
// Returns index of stack head
2038
{
2039
        ASSERT(stacking);
2040
        int id = t.id;
2041
        int stack = t.stack;
2042
        int head = FindStackHead(stack);
2043
        while (tabs[head].id != id)
2044
                CycleTabStack(head, stack);
2045
        return head;
2046
}
2047

2048
int TabBar::CycleTabStack(int n)
2049
// Returns index of stack head
2050
{
2051
        int head = FindStackHead(n);
2052
        CycleTabStack(head, n);
2053
        return head;
2054
}
2055

2056
void TabBar::CycleTabStack(int head, int n)
2057
{
2058
        // Swap tab to end of stack
2059
        int ix = head;
2060
        while (!IsStackTail(ix)) {
2061
                tabs.Swap(ix, ix + 1);
2062
                ++ix;
2063
        }
2064
}
2065

2066
Value TabBar::GetData() const
2067
{
2068
        return (HasCursor() && active < GetCount())
2069
                ? GetKey(active)
2070
                : Value();
2071
}
2072

2073
void TabBar::SetData(const Value &key)
2074
{
2075
        int n = FindKey(key); 
2076
        if (n >= 0) {
2077
                if (stacking && tabs[n].stack >= 0)
2078
                        n = SetStackHead(tabs[n]);
2079
                SetCursor(n);
2080
        }
2081
}
2082

2083
void TabBar::Set(int n, const Value &newkey, const Value &newvalue)
2084
{
2085
        Set(n, newkey, newvalue, tabs[n].img);
2086
}
2087

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

2106
void TabBar::SetValue(const Value &key, const Value &newvalue)
2107
{
2108
        Set(FindKey(key), key, newvalue);
2109
}
2110

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

2116
void TabBar::SetKey(int n, const Value &newkey)
2117
{
2118
        Set(n, newkey, tabs[n].value);        
2119
}
2120

2121
void TabBar::SetIcon(int n, Image icon)
2122
{
2123
        ASSERT(n >= 0 && n < tabs.GetCount());
2124
        tabs[n].img = icon;
2125
        Repos();
2126
        Refresh();
2127
}
2128

2129
void TabBar::LeftDown(Point p, dword keyflags)
2130
{
2131
        p = AdjustMouse(p);
2132
        SetCapture();
2133
        
2134
        if(keyflags & K_SHIFT)
2135
        {
2136
                highlight = -1;
2137
                Refresh();
2138
                Fix(p);
2139
                oldp = p;
2140
                return;
2141
        }
2142

2143
        drag_highlight = highlight;
2144
        
2145
        isctrl = keyflags & K_CTRL;
2146
        if(isctrl)
2147
                return;
2148

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

2180
void TabBar::LeftUp(Point p, dword keyflags)
2181
{
2182
        ReleaseCapture();
2183
}
2184

2185
void TabBar::LeftDouble(Point p, dword keysflags)
2186
{
2187
        WhenLeftDouble();
2188
}
2189

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

2202
void TabBar::MiddleDown(Point p, dword keyflags)
2203
{
2204
    if (highlight >= 0)
2205
    {
2206
        Value v = tabs[highlight].key;
2207
        ValueArray vv;
2208
        vv.Add(v);
2209
        int highlightBack = highlight;
2210
        if (!CancelClose(v) && ! CancelCloseSome(vv)) {
2211
            // 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
2212
            Value v = tabs[highlightBack].key;
2213
            // 2014/03/06 - FIRST the callbacks, THEN remove the tab
2214
            // otherwise keys in WhenCloseSome() are invalid
2215
            WhenClose(v);
2216
            WhenCloseSome(vv);
2217
            TabClosed(v);
2218
            Close(highlightBack);
2219
        }
2220
    }
2221
}
2222

2223
void TabBar::MiddleUp(Point p, dword keyflags)
2224
{
2225
}
2226

2227
int TabBar::GetTargetTab(Point p)
2228
{
2229
        p.x += sc.GetPos();
2230

2231
        int f = GetFirst();
2232
        int l = GetLast();
2233
        
2234
        if(tabs[f].visible && p.x < tabs[f].pos.x + tabs[f].size.cx / 2)
2235
                return f;
2236

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

2254
        return t;
2255
}
2256

2257
void TabBar::MouseWheel(Point p, int zdelta, dword keyflags)
2258
{
2259
        sc.AddPos(-zdelta / 4, true);
2260
        Scroll();
2261
        MouseMove(p, 0);
2262
}
2263

2264
Point TabBar::AdjustMouse(Point const &p) const
2265
{
2266
        int align = GetAlign();
2267
        if(align == TOP || align == LEFT)
2268
                return p;
2269

2270
        Size ctrlsz = GetSize();
2271
        Size sz = GetBarSize(ctrlsz);
2272
        return Point(p.x - ctrlsz.cx + sz.cx, p.y - ctrlsz.cy + sz.cy);
2273
}
2274

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

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

2307
void TabBar::SetHighlight(int n)
2308
{
2309
        highlight = n;
2310
        WhenHighlight();
2311
        Refresh();
2312
}
2313

2314
void TabBar::SetColor(int n, Color c)
2315
{
2316
        tabs[n].col = c;
2317
        Refresh();
2318
}
2319

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

2347
        if(highlight >= 0 || cross >= 0)
2348
        {
2349
                highlight = cross = -1;
2350
                WhenHighlight();
2351
                Refresh();
2352
        }
2353
}
2354

2355
void TabBar::MouseLeave()
2356
{
2357
        if(isdrag)
2358
                return;
2359
        highlight = cross = -1;
2360
        WhenHighlight();
2361
        Refresh();
2362
}
2363

2364
void TabBar::DragAndDrop(Point p, PasteClip& d)
2365
{
2366
        Fix(p);
2367
        int c = GetTargetTab(p);
2368
        int tab = isctrl ? drag_highlight : active;
2369

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

2372
        if (stacking) {
2373
                tab = FindStackHead(tabs[tab].stack);
2374
                if(c < tabs.GetCount())
2375
                        c = FindStackHead(tabs[c].stack);
2376
        }
2377

2378
        bool sametab = c == tab || c == GetNext(tab, true);
2379
        bool internal = AcceptInternal<TabBar>(d, "tabs");
2380

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

2437
void TabBar::CancelMode()
2438
{
2439
        isdrag = false;
2440
        target = -1;
2441
        Refresh();
2442
}
2443

2444
void TabBar::LeftDrag(Point p, dword keyflags)
2445
{
2446
        if(keyflags & K_SHIFT)
2447
                return;
2448
        if(highlight < 0)
2449
                return;
2450

2451
        Sync();
2452
        isdrag = true;
2453
        dragtab = GetDragSample();
2454
        DoDragAndDrop(InternalClip(*this, "tabs"));
2455
}
2456

2457
void TabBar::DragEnter()
2458
{
2459
}
2460

2461
void TabBar::DragLeave()
2462
{
2463
        target = -1;
2464
        Refresh();
2465
}
2466

2467
void TabBar::DragRepeat(Point p)
2468
{
2469
        if(target >= 0)
2470
        {
2471
                Point dx = GetDragScroll(this, p, 16);
2472
                Fix(dx);
2473
                if(dx.x != 0)
2474
                        sc.AddPos(dx.x);
2475
        }
2476
}
2477

2478
bool TabBar::SetCursor0(int n, bool action)
2479
{
2480
        if(tabs.GetCount() == 0)
2481
                return false;
2482

2483
        if(n < 0)
2484
        {
2485
                n = max(0, FindId(GetGroupActive()));
2486
                active = -1;
2487
                highlight = -1;
2488
                drag_highlight = -1;
2489
                if (allownullcursor)
2490
                        return true;
2491
        }
2492

2493
        bool is_all = IsGroupAll();
2494
        bool same_group = tabs[n].group == GetGroupName();
2495

2496
        if((same_group || is_all) && active == n)
2497
                return false;
2498

2499
        bool repos = false;
2500

2501
        if (!IsStackHead(n)) 
2502
        {
2503
                n = SetStackHead(tabs[n]);
2504
                repos = true;                
2505
        }
2506

2507
        active = n;
2508

2509
        if(!is_all && !same_group) 
2510
        {
2511
                SetGroup(tabs[n].group);
2512
                repos = true;
2513
        }
2514
        if (repos)
2515
                Repos();
2516

2517
        SetGroupActive(tabs[n].id);
2518

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

2537
        Refresh();
2538

2539
        if(Ctrl::HasMouse())
2540
        {
2541
                Refresh();
2542
                Sync();
2543
                MouseMove(GetMouseViewPos(), 0);
2544
        }
2545
        return true;
2546
}
2547

2548
void TabBar::SetCursor(int n)
2549
{
2550
        SetCursor0(n, true);
2551
}
2552

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

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

2600
void TabBar::Close(int n, bool action)
2601
{
2602
        if(tabs.GetCount() <= mintabcount)
2603
                return;
2604

2605
        CloseForce(n, action);
2606
}
2607

2608
void TabBar::CloseKey(const Value &key)
2609
{
2610
        int tabix = FindKey(key);
2611
        if (tabix < 0) return;
2612
        Close(tabix);
2613
}
2614

2615
TabBar::Style& TabBar::Style::DefaultCrosses()
2616
{
2617
        crosses[0] = TabBarImg::CR0();
2618
        crosses[1] = TabBarImg::CR1();
2619
        crosses[2] = TabBarImg::CR2();
2620
        return *this;
2621
}
2622

2623
TabBar::Style& TabBar::Style::Variant1Crosses()
2624
{
2625
        crosses[0] = TabBarImg::VARIANT1_CR0();
2626
        crosses[1] = TabBarImg::VARIANT1_CR1();
2627
        crosses[2] = TabBarImg::VARIANT1_CR2();
2628
        return *this;        
2629
}
2630

2631
TabBar::Style& TabBar::Style::Variant2Crosses()
2632
{
2633
        crosses[0] = TabBarImg::VARIANT2_CR0();
2634
        crosses[1] = TabBarImg::VARIANT2_CR1();
2635
        crosses[2] = TabBarImg::VARIANT2_CR2();
2636
        return *this;        
2637
}
2638

2639
TabBar::Style& TabBar::Style::GroupSeparators(Value horz, Value vert)
2640
{
2641
        group_separators[0] = horz;
2642
        group_separators[0] = vert;
2643
        return *this;
2644
}
2645

2646
TabBar::Style& TabBar::Style::DefaultGroupSeparators()
2647
{
2648
        return GroupSeparators(TabBarImg::SEP(), TabBarImg::SEPV());        
2649
}
2650

2651
Vector<Value> TabBar::GetKeys() const
2652
{
2653
        Vector<Value> keys;
2654
        keys.SetCount(tabs.GetCount());
2655
        for (int i = 0; i < tabs.GetCount(); i++)
2656
                keys[i] = tabs[i].key;
2657
        return keys;
2658
}
2659

2660
Vector<Image> TabBar::GetIcons() const
2661
{
2662
        Vector<Image> img;
2663
        img.SetCount(tabs.GetCount());
2664
        for (int i = 0; i < tabs.GetCount(); i++)
2665
                img[i] = tabs[i].img;
2666
        return img;
2667
}
2668

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

2689
TabBar& TabBar::CopySettings(const TabBar &src)
2690
{
2691
        
2692
        CopyBaseSettings(src);
2693
        
2694
        if (stacking != src.stacking)
2695
                Stacking(src.stacking);
2696
        else {
2697
                MakeGroups();
2698
                Repos();
2699
                Refresh();
2700
        }
2701
        return *this;
2702
}
2703

2704
void TabBar::Serialize(Stream& s)
2705
{
2706
        int version = 1;
2707
        s / version;
2708

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

2752
TabBar& TabBar::SetStyle(const TabBar::Style& s)        {
2753
        if(style != &s) {
2754
                style = &s;
2755
                RefreshLayout();
2756
                Refresh();
2757
        }
2758
        return *this;
2759
}
2760

2761
CH_STYLE(TabBar, Style, StyleDefault)
2762
{
2763
        Assign(TabCtrl::StyleDefault());
2764
#ifdef PLATFORM_WIN32
2765
        if(IsWinVista())
2766
                Variant2Crosses();
2767
        else
2768
                DefaultCrosses();
2769
#else
2770
        DefaultCrosses();
2771
#endif
2772
        DefaultGroupSeparators();
2773
}
2774

2775
}
2776