1 #include "CtrlLib.h"
2 #include "CtrlLib.h"
3
4 namespace Upp {
5
6 #define LTIMING(x) // DTIMING(x)
7
Column()8 ArrayCtrl::Column::Column() {
9 convert = NULL;
10 edit = NULL;
11 display = &StdDisplay();
12 accel = NULL;
13 margin = -1;
14 cached = false;
15 clickedit = true;
16 index = -1;
17 order = NULL;
18 }
19
Cache()20 ArrayCtrl::Column& ArrayCtrl::Column::Cache() {
21 cached = true;
22 return *this;
23 }
24
NoEdit()25 ArrayCtrl::Column& ArrayCtrl::Column::NoEdit()
26 {
27 if(edit)
28 edit->Remove();
29 edit = NULL;
30 return *this;
31 }
32
Edit(Ctrl & e)33 ArrayCtrl::Column& ArrayCtrl::Column::Edit(Ctrl& e) {
34 e.Hide();
35 e.SetFrame(NullFrame());
36 edit = &e;
37 arrayctrl->AddChild(edit);
38 return *this;
39 }
40
SetConvert(const Convert & c)41 ArrayCtrl::Column& ArrayCtrl::Column::SetConvert(const Convert& c) {
42 convert = &c;
43 ClearCache();
44 arrayctrl->Refresh();
45 arrayctrl->SyncInfo();
46 return *this;
47 }
48
ConvertBy(Function<Value (const Value &)> cv)49 ArrayCtrl::Column& ArrayCtrl::Column::ConvertBy(Function<Value(const Value&)> cv)
50 {
51 convertby = cv;
52 ClearCache();
53 arrayctrl->Refresh();
54 arrayctrl->SyncInfo();
55 return *this;
56 }
57
SetFormat(const char * fmt)58 ArrayCtrl::Column& ArrayCtrl::Column::SetFormat(const char *fmt)
59 {
60 FormatConvert::SetFormat(fmt);
61 return SetConvert(*this);
62 }
63
SetDisplay(const Display & d)64 ArrayCtrl::Column& ArrayCtrl::Column::SetDisplay(const Display& d)
65 {
66 display = &d;
67 ClearCache();
68 arrayctrl->SyncCtrls();
69 arrayctrl->Refresh();
70 arrayctrl->SyncInfo();
71 return *this;
72 }
73
Ctrls(Event<int,One<Ctrl> &> factory)74 ArrayCtrl::Column& ArrayCtrl::Column::Ctrls(Event<int, One<Ctrl>&> factory)
75 {
76 return WithLined(factory);
77 }
78
WithLined(Event<int,One<Ctrl> &> f)79 ArrayCtrl::Column& ArrayCtrl::Column::WithLined(Event<int, One<Ctrl>&> f)
80 {
81 factory = f;
82 arrayctrl->hasctrls = arrayctrl->headerctrls = true;
83 arrayctrl->SyncCtrls();
84 arrayctrl->Refresh();
85 arrayctrl->SyncInfo();
86 return *this;
87 }
88
With(Event<One<Ctrl> &> factory)89 ArrayCtrl::Column& ArrayCtrl::Column::With(Event<One<Ctrl>&> factory)
90 {
91 return WithLined([=](int, One<Ctrl>& x) { factory(x); });
92 }
93
Ctrls(Callback1<One<Ctrl> &> _factory)94 ArrayCtrl::Column& ArrayCtrl::Column::Ctrls(Callback1<One<Ctrl>&> _factory)
95 {
96 return Ctrls([=](int, One<Ctrl>& x) { _factory(x); });
97 }
98
ClearCache()99 void ArrayCtrl::Column::ClearCache() {
100 cache.Clear();
101 }
102
Sorts()103 void ArrayCtrl::Column::Sorts()
104 {
105 HeaderTab().WhenAction = callback1(arrayctrl, &ArrayCtrl::ToggleSortColumn, index);
106 }
107
Sorting(const ValueOrder & o)108 ArrayCtrl::Column& ArrayCtrl::Column::Sorting(const ValueOrder& o)
109 {
110 order = &o;
111 cmp.Clear();
112 line_order.Clear();
113 Sorts();
114 return *this;
115 }
116
SortingBy(Function<int (const Value & a,const Value & b)> c)117 ArrayCtrl::Column& ArrayCtrl::Column::SortingBy(Function<int (const Value& a, const Value& b)> c)
118 {
119 cmp = c;
120 order = NULL;
121 line_order.Clear();
122 Sorts();
123 return *this;
124 }
125
SortingLined(Gate<int,int> aorder)126 ArrayCtrl::Column& ArrayCtrl::Column::SortingLined(Gate<int, int> aorder)
127 {
128 line_order = aorder;
129 order = NULL;
130 cmp.Clear();
131 Sorts();
132 return *this;
133 }
134
Sorting()135 ArrayCtrl::Column& ArrayCtrl::Column::Sorting()
136 {
137 return SortingBy(StdValueCompare);
138 }
139
SortDefault(bool desc)140 ArrayCtrl::Column& ArrayCtrl::Column::SortDefault(bool desc)
141 {
142 if(!cmp && !order && !line_order)
143 Sorting();
144 arrayctrl->SetSortColumn(index, desc);
145 return *this;
146 }
147
InvalidateCache(int i)148 void ArrayCtrl::Column::InvalidateCache(int i) {
149 if(i < 0) return;
150 if(cache.Is< Vector<String> >() && i < cache.Get< Vector<String> >().GetCount())
151 cache.Get< Vector<String> >()[i] = String::GetVoid();
152 if(cache.Is< Vector<Value> >() && i < cache.Get< Vector<Value> >().GetCount())
153 cache.Get< Vector<Value> >()[i] = Value();
154 }
155
InsertCache(int i,int count)156 void ArrayCtrl::Column::InsertCache(int i, int count) {
157 if(i < 0) return;
158 if(cache.Is< Vector<String> >() && i < cache.Get< Vector<String> >().GetCount()) {
159 Vector<String>& v = cache.Get< Vector<String> >();
160 v.InsertN(i, count);
161 while(count--)
162 v[i++] = String::GetVoid();
163 }
164 if(cache.Is< Vector<Value> >() && i < cache.Get< Vector<Value> >().GetCount())
165 cache.Get< Vector<Value> >().Insert(i);
166 }
167
RemoveCache(int i)168 void ArrayCtrl::Column::RemoveCache(int i) {
169 if(i < 0) return;
170 if(cache.Is< Vector<String> >() && i < cache.Get< Vector<String> >().GetCount())
171 cache.Get< Vector<String> >().Remove(i);
172 if(cache.Is< Vector<Value> >() && i < cache.Get< Vector<Value> >().GetCount())
173 cache.Get< Vector<Value> >().Remove(i);
174 }
175
InsertValue(const Value & v)176 ArrayCtrl::Column& ArrayCtrl::Column::InsertValue(const Value& v) {
177 arrayctrl->IndexInfo(arrayctrl->Pos(pos[0])).InsertValue(v);
178 return *this;
179 }
180
InsertValue(ValueGen & g)181 ArrayCtrl::Column& ArrayCtrl::Column::InsertValue(ValueGen& g) {
182 arrayctrl->IndexInfo(arrayctrl->Pos(pos[0])).InsertValue(g);
183 return *this;
184 }
185
HeaderTab()186 HeaderCtrl::Column& ArrayCtrl::Column::HeaderTab() {
187 return arrayctrl->header.Tab(arrayctrl->header.FindIndex(index));
188 }
189
HeaderTab() const190 const HeaderCtrl::Column& ArrayCtrl::Column::HeaderTab() const
191 {
192 return arrayctrl->header.Tab(arrayctrl->header.FindIndex(index));
193 }
194
InvalidateCache(int ri)195 void ArrayCtrl::InvalidateCache(int ri)
196 {
197 for(int i = 0; i < column.GetCount(); i++)
198 column[i].InvalidateCache(ri);
199 }
200
LeftDown(Point,dword)201 void ArrayCtrl::CellCtrl::LeftDown(Point, dword)
202 {
203 if(ctrl->IsWantFocus())
204 ctrl->SetFocus();
205 }
206
Free()207 void ArrayCtrl::CellInfo::Free()
208 {
209 if(ptr.GetBit()) {
210 CellCtrl *cc = (CellCtrl *)ptr.GetPtr();
211 if(cc->owned)
212 delete cc->ctrl;
213 delete cc;
214 }
215 }
216
Set(Ctrl * ctrl,bool owned,bool value)217 void ArrayCtrl::CellInfo::Set(Ctrl *ctrl, bool owned, bool value)
218 {
219 Free();
220 CellCtrl *cc = new CellCtrl;
221 cc->ctrl = ctrl;
222 cc->Add(*ctrl);
223 cc->owned = owned;
224 cc->value = value;
225 ptr.Set1(cc);
226 }
227
CellInfo(CellInfo && s)228 ArrayCtrl::CellInfo::CellInfo(CellInfo&& s)
229 {
230 ptr = s.ptr;
231 const_cast<CellInfo&>(s).ptr.SetPtr(NULL);
232 const_cast<CellInfo&>(s).ptr.SetBit(0);
233 }
234
~CellInfo()235 ArrayCtrl::CellInfo::~CellInfo()
236 {
237 Free();
238 }
239
SetCtrl(int i,int j,Ctrl * newctrl,bool owned,bool value)240 Ctrl& ArrayCtrl::SetCtrl(int i, int j, Ctrl *newctrl, bool owned, bool value)
241 {
242 if(value)
243 newctrl->SetData(GetColumn(i, j));
244 hasctrls = true;
245 RefreshRow(i);
246 CellInfo& ci = cellinfo.At(i).At(j);
247 ci.Set(newctrl, owned, value);
248 Ctrl& c = ci.GetCtrl();
249 if(newctrl->GetPos().x.GetAlign() == LEFT && newctrl->GetPos().x.GetB() == 0)
250 newctrl->HSizePos().VCenterPos(STDSIZE);
251 j--;
252 for(;;) {
253 while(j >= 0) {
254 if(IsCtrl(i, j)) {
255 AddChild(&c, &GetCellCtrl(i, j));
256 SyncInfo();
257 return c;
258 }
259 j--;
260 }
261 i--;
262 if(i < 0) {
263 AddChild(&c, NULL);
264 SyncInfo();
265 return c;
266 }
267 j = cellinfo[i].GetCount();
268 }
269 SyncInfo();
270 }
271
SetCtrl(int i,int j,Ctrl & ctrl,bool value)272 void ArrayCtrl::SetCtrl(int i, int j, Ctrl& ctrl, bool value)
273 {
274 SetCtrl(i, j, &ctrl, false, value);
275 SyncLineCtrls(i);
276 }
277
GetCtrl(int i,int col)278 Ctrl *ArrayCtrl::GetCtrl(int i, int col)
279 {
280 SyncLineCtrls(i);
281 if(IsCtrl(i, col))
282 return GetCellCtrl(i, col).ctrl;
283 return NULL;
284 }
285
IsCtrl(int i,int j) const286 bool ArrayCtrl::IsCtrl(int i, int j) const
287 {
288 return i < cellinfo.GetCount() && j < cellinfo[i].GetCount() && cellinfo[i][j].IsCtrl();
289 }
290
GetCellCtrl(int i,int j)291 ArrayCtrl::CellCtrl& ArrayCtrl::GetCellCtrl(int i, int j)
292 {
293 LTIMING("GetCellCtrl");
294 return cellinfo[i][j].GetCtrl();
295 }
296
GetCellCtrl(int i,int j) const297 const ArrayCtrl::CellCtrl& ArrayCtrl::GetCellCtrl(int i, int j) const
298 {
299 return cellinfo[i][j].GetCtrl();
300 }
301
SetDisplay(int i,int j,const Display & d)302 void ArrayCtrl::SetDisplay(int i, int j, const Display& d)
303 {
304 cellinfo.At(i).At(j).Set(d);
305 RefreshRow(i);
306 }
307
SetRowDisplay(int i,const Display & d)308 void ArrayCtrl::SetRowDisplay(int i, const Display& d)
309 {
310 if(i >= 0 && i < GetCount())
311 for (int j = 0 ; j < GetColumnCount(); j++)
312 this->cellinfo.At(i).At(j).Set(d);
313 RefreshRow(i);
314 }
315
SetColumnDisplay(int j,const Display & d)316 void ArrayCtrl::SetColumnDisplay(int j, const Display& d)
317 {
318 if(j >= 0 && j < GetColumnCount())
319 for (int i = 0 ; i < GetCount(); i++) {
320 this->cellinfo.At(i).At(j).Set(d);
321 RefreshRow(i);
322 }
323 }
324
GetDisplay(int i,int j)325 const Display& ArrayCtrl::GetDisplay(int i, int j)
326 {
327 if(i < cellinfo.GetCount() && j < cellinfo[i].GetCount() && cellinfo[i][j].IsDisplay())
328 return cellinfo[i][j].GetDisplay();
329 return *column[j].display;
330 }
331
GetDisplay(int j)332 const Display& ArrayCtrl::GetDisplay(int j)
333 {
334 return *column[j].display;
335 }
336
Pos(int pos) const337 int ArrayCtrl::Pos(int pos) const {
338 if(pos >= 0) return pos;
339 pos = idx.Find(id_ndx[-pos]);
340 ASSERT(pos >= 0);
341 return pos;
342 }
343
Get0(int i,int ii) const344 Value ArrayCtrl::Get0(int i, int ii) const {
345 ASSERT(ii >= 0);
346 if(hasctrls)
347 for(int j = 0; j < column.GetCount(); j++)
348 if(IsCtrl(i, j)) {
349 const Column& m = column[j];
350 ASSERT(m.pos.GetCount() == 1);
351 if(Pos(m.pos[0]) == ii) {
352 const CellCtrl& c = GetCellCtrl(i, j);
353 if(c.value)
354 return c.ctrl->GetData();
355 }
356 }
357 if(i < array.GetCount()) {
358 const Vector<Value>& v = array[i].line;
359 if(ii < v.GetCount())
360 return v[ii];
361 }
362 return Null;
363 }
364
Get(int ii) const365 Value ArrayCtrl::Get(int ii) const {
366 ASSERT(IsCursor());
367 int i;
368 for(i = 0; i < column.GetCount(); i++) {
369 const Column& m = column[i];
370 if(m.edit)
371 for(int j = 0; j < m.pos.GetCount(); j++)
372 if(Pos(m.pos[j]) == ii) {
373 Value v = m.edit->GetData();
374 return m.pos.GetCount() > 1 && IsValueArray(v) ? ValueArray(v)[j] : v;
375 }
376 }
377 for(i = 0; i < control.GetCount(); i++) {
378 const Control& c = control[i];
379 if(Pos(c.pos) == ii)
380 return c.ctrl->GetData();
381 }
382 return Get0(cursor, ii);
383 }
384
Get(const Id & id) const385 Value ArrayCtrl::Get(const Id& id) const {
386 return Get(GetPos(id));
387 }
388
GetOriginal(int ii) const389 Value ArrayCtrl::GetOriginal(int ii) const {
390 return Get0(cursor, ii);
391 }
392
GetOriginal(const Id & id) const393 Value ArrayCtrl::GetOriginal(const Id& id) const {
394 return GetOriginal(GetPos(id));
395 }
396
GetKey() const397 Value ArrayCtrl::GetKey() const {
398 return IsCursor() ? Get(0) : Null;
399 }
400
GetOriginalKey() const401 Value ArrayCtrl::GetOriginalKey() const {
402 return IsCursor() ? GetOriginal(0) : Null;
403 }
404
IsModified(int ii) const405 bool ArrayCtrl::IsModified(int ii) const {
406 ASSERT(IsCursor());
407 int i;
408 if(ii < modify.GetCount() && modify[ii]) return true;
409 for(i = 0; i < column.GetCount(); i++) {
410 const Column& m = column[i];
411 if(m.edit)
412 for(int j = 0; j < m.pos.GetCount(); j++)
413 if(Pos(m.pos[j]) == ii && m.edit->IsModified())
414 return true;
415 }
416 for(i = 0; i < control.GetCount(); i++) {
417 const Control& c = control[i];
418 if(Pos(c.pos) == ii && c.ctrl->IsModified())
419 return true;
420 }
421 return false;
422 }
423
IsModified(const Id & id) const424 bool ArrayCtrl::IsModified(const Id& id) const {
425 return IsModified(GetPos(id));
426 }
427
ColEditSetData(int j)428 void ArrayCtrl::ColEditSetData(int j) {
429 ASSERT(IsCursor());
430 Column& m = column[j];
431 if(m.edit)
432 m.edit->SetData(GetColumn(cursor, j));
433 }
434
CtrlSetData(int j)435 void ArrayCtrl::CtrlSetData(int j) {
436 ASSERT(IsCursor());
437 Control& c = control[j];
438 c.ctrl->Enable();
439 if(IsNull(c.pos))
440 c.ctrl->SetData(cursor);
441 else
442 c.ctrl->SetData(GetOriginal(Pos(c.pos)));
443 }
444
SetCtrlValue(int i,int ii,const Value & v)445 void ArrayCtrl::SetCtrlValue(int i, int ii, const Value& v)
446 {
447 if(hasctrls)
448 for(int j = 0; j < column.GetCount(); j++)
449 if(IsCtrl(i, j)) {
450 const Column& m = column[j];
451 ASSERT(m.pos.GetCount() == 1);
452 if(Pos(m.pos[0]) == ii) {
453 const CellCtrl& c = GetCellCtrl(i, j);
454 if(c.value)
455 c.ctrl->SetData(v);
456 return;
457 }
458 }
459 }
460
Set(int ii,const Value & v)461 void ArrayCtrl::Set(int ii, const Value& v) {
462 ASSERT(IsCursor());
463 array.At(cursor).line.At(ii) = v;
464 int i;
465 for(i = 0; i < column.GetCount(); i++) {
466 Column& m = column[i];
467 if(m.edit)
468 for(int j = 0; j < m.pos.GetCount(); j++)
469 if(Pos(m.pos[j]) == ii) {
470 ColEditSetData(i);
471 break;
472 }
473 }
474 for(i = 0; i < control.GetCount(); i++) {
475 const Control& c = control[i];
476 if(Pos(c.pos) == ii)
477 CtrlSetData(i);
478 }
479 SetCtrlValue(cursor, ii, v);
480 modify.At(ii, false) = true;
481 InvalidateCache(cursor);
482 RefreshRow(cursor);
483 WhenArrayAction();
484 }
485
Set(const Id & id,const Value & v)486 void ArrayCtrl::Set(const Id& id, const Value& v) {
487 Set(GetPos(id), v);
488 }
489
Set0(int i,int ii,const Value & v)490 void ArrayCtrl::Set0(int i, int ii, const Value& v) {
491 if(i == cursor)
492 Set(ii, v);
493 else
494 SetCtrlValue(i, ii, v);
495 array.At(i).line.At(ii) = v;
496 WhenArrayAction();
497 }
498
AfterSet(int i)499 void ArrayCtrl::AfterSet(int i)
500 {
501 SetSb();
502 Refresh();
503 SyncInfo();
504 SyncLineCtrls(i);
505 InvalidateCache(cursor);
506 }
507
Set(int i,int ii,const Value & v)508 void ArrayCtrl::Set(int i, int ii, const Value& v)
509 {
510 Set0(i, ii, v);
511 AfterSet(i);
512 }
513
Set(int i,const Id & id,const Value & v)514 void ArrayCtrl::Set(int i, const Id& id, const Value& v) {
515 Set(i, GetPos(id), v);
516 }
517
Get(int i,int ii) const518 Value ArrayCtrl::Get(int i, int ii) const {
519 return i == cursor ? Get(ii) : Get0(i, ii);
520 }
521
Get(int i,const Id & id) const522 Value ArrayCtrl::Get(int i, const Id& id) const {
523 return Get(i, GetPos(id));
524 }
525
SetCount(int n)526 void ArrayCtrl::SetCount(int n) {
527 ASSERT(n >= 0);
528 if(cursor >= n)
529 CancelCursor();
530 array.SetCount(n);
531 if(cellinfo.GetCount() > array.GetCount())
532 cellinfo.SetCount(array.GetCount());
533 Refresh();
534 SyncInfo();
535 SyncCtrls();
536 SetSb();
537 PlaceEdits();
538 WhenArrayAction();
539 }
540
GetColumn(int row,int col) const541 Value ArrayCtrl::GetColumn(int row, int col) const {
542 const Column& c = column[col];
543 if(c.pos.GetCount() == 0) return row;
544 if(c.pos.GetCount() == 1) return Get0(row, Pos(c.pos[0]));
545 ValueMap vm;
546 for(int i = 0; i < c.pos.GetCount(); i++) {
547 int ii = Pos(c.pos[i]);
548 vm.Add(ii < idx.GetCount() ? idx.GetKey(ii) : Id(), Get0(row, ii));
549 }
550 return vm;
551 }
552
GetConvertedColumn(int i,int col) const553 Value ArrayCtrl::GetConvertedColumn(int i, int col) const {
554 LTIMING("GetConvertedColumn");
555 const Column& m = column[col];
556 Value v = GetColumn(i, col);
557 if(!m.convert && !m.convertby) return v;
558 if(m.cache.Is< Vector<String> >() && i < m.cache.Get< Vector<String> >().GetCount()) {
559 const String& s = m.cache.Get< Vector<String> >()[i];
560 if(!s.IsVoid()) return s;
561 }
562 if(m.cache.Is< Vector<Value> >() && i < m.cache.Get< Vector<Value> >().GetCount()) {
563 const Value& v = m.cache.Get< Vector<Value> >()[i];
564 if(!v.IsVoid()) return v;
565 }
566 if(m.convertby)
567 v = m.convertby(v);
568 if(m.convert)
569 v = m.convert->Format(v);
570 if(m.cached) {
571 if(m.cache.IsEmpty())
572 m.cache.Create< Vector<String> >();
573 if(IsString(v) && m.cache.Is< Vector<String> >())
574 m.cache.Get< Vector<String> >().At(i, String::GetVoid()) = v;
575 if(!IsString(v) && m.cache.Is< Vector<String> >())
576 m.cache.Create< Vector<Value> >();
577 if(m.cache.Is< Vector<Value> >())
578 m.cache.Get< Vector<Value> >().At(i) = v;
579 ASSERT(m.pos.GetCount() || m.cache.IsEmpty());
580 }
581 return v;
582 }
583
GetCount() const584 int ArrayCtrl::GetCount() const {
585 return max(virtualcount, array.GetCount());
586 }
587
SetVirtualCount(int c)588 void ArrayCtrl::SetVirtualCount(int c) {
589 virtualcount = c;
590 Refresh();
591 SyncInfo();
592 SetSb();
593 if(cursor >= virtualcount)
594 GoEnd();
595 }
596
SetSb()597 void ArrayCtrl::SetSb() {
598 sb.SetTotal(GetTotalCy() + IsInserting() * (GetLineCy() + horzgrid));
599 sb.SetPage(GetSize().cy);
600 MinMaxLine();
601 }
602
Layout()603 void ArrayCtrl::Layout() {
604 SyncColumnsPos();
605 SetSb();
606 HeaderScrollVisibility();
607 PlaceEdits();
608 SyncCtrls();
609 }
610
Reline(int i,int y)611 void ArrayCtrl::Reline(int i, int y)
612 {
613 if(WhenLineVisible)
614 while(i < ln.GetCount()) {
615 Ln& p = ln[i];
616 p.y = y;
617 if(IsLineVisible(i))
618 y += Nvl(p.cy, linecy) + horzgrid;
619 i++;
620 }
621 else
622 while(i < ln.GetCount()) {
623 Ln& p = ln[i];
624 p.y = y;
625 if(IsLineVisible0(i))
626 y += Nvl(p.cy, linecy) + horzgrid;
627 i++;
628 }
629 SetSb();
630 Refresh();
631 SyncCtrls();
632 }
633
GetLineY(int i) const634 int ArrayCtrl::GetLineY(int i) const
635 {
636 return i < ln.GetCount() ? ln[i].y
637 : (ln.GetCount() ? ln.Top().y + IsLineVisible(ln.GetCount() - 1) * (Nvl(ln.Top().cy, linecy) + horzgrid) : 0)
638 + (linecy + horzgrid) * (i - ln.GetCount());
639 }
640
GetScreenCellRect(int i,int col) const641 Rect ArrayCtrl::GetScreenCellRect(int i, int col) const
642 {
643 return GetCellRect(i, col).Offseted(GetScreenView().TopLeft());
644 }
645
GetScreenCellRectM(int i,int col) const646 Rect ArrayCtrl::GetScreenCellRectM(int i, int col) const
647 {
648 return GetCellRectM(i, col).Offseted(GetScreenView().TopLeft());
649 }
650
SetLineCy(int cy)651 ArrayCtrl& ArrayCtrl::SetLineCy(int cy)
652 {
653 linecy = cy;
654 Reline(0, 0);
655 sb.SetLine(cy);
656 Refresh();
657 return *this;
658 }
659
SetLineCy(int i,int cy)660 void ArrayCtrl::SetLineCy(int i, int cy)
661 {
662 int q = ln.GetCount();
663 if(i < q) {
664 ln[i].cy = cy;
665 Reline(i, ln[i].y);
666 }
667 else {
668 while(ln.GetCount() <= i)
669 ln.Add().cy = Null;
670 ln[i].cy = cy;
671 if(q > 0)
672 Reline(q - 1, ln[q - 1].y);
673 else
674 Reline(0, 0);
675 }
676 }
677
SetLineColor(int i,Color c)678 void ArrayCtrl::SetLineColor(int i, Color c)
679 {
680 array.At(i).paper = c;
681 }
682
GetTotalCy() const683 int ArrayCtrl::GetTotalCy() const
684 {
685 int i = GetCount();
686 return i ? GetLineY(i - 1) + GetLineCy(i - 1) + horzgrid: 0;
687 }
688
GetLineCy(int i) const689 int ArrayCtrl::GetLineCy(int i) const {
690 return i < ln.GetCount() ? Nvl(ln[i].cy, linecy) : linecy;
691 }
692
GetLineAt(int y) const693 int ArrayCtrl::GetLineAt(int y) const {
694 LTIMING("GetLineAt");
695 if(!GetCount()) return Null;
696 if(y < 0 || y >= GetTotalCy()) return Null;
697 int l = 0;
698 int h = GetCount();
699 while(l < h) {
700 int m = (l + h) / 2;
701 int yy = GetLineY(m);
702 if(yy == y) return m;
703 if(yy < y)
704 l = m + 1;
705 else
706 h = m;
707 }
708 return l > 0 ? l - 1 : l;
709 }
710
SyncLineCtrls(int i,Ctrl * p)711 Ctrl *ArrayCtrl::SyncLineCtrls(int i, Ctrl *p)
712 {
713 if(!hasctrls)
714 return NULL;
715 Size sz = GetSize();
716 for(int ii = i - 1; ii >= 0 && !p; ii--)
717 for(int j = column.GetCount() - 1; j >= 0; j--) {
718 if(IsCtrl(ii, j)) {
719 p = &GetCellCtrl(ii, j);
720 break;
721 }
722 if(column[j].factory) {
723 for(int q = 0; q <= i; q++)
724 p = SyncLineCtrls(q, p);
725 return p;
726 }
727 }
728
729 for(int j = 0; j < column.GetCount(); j++) {
730 bool ct = IsCtrl(i, j);
731 if(!ct && column[j].factory) {
732 LTIMING("Create");
733 One<Ctrl> newctrl;
734 column[j].factory(i, newctrl);
735 newctrl->SetData(GetColumn(i, j));
736 newctrl->WhenAction << Proxy(WhenCtrlsAction);
737 if(newctrl->GetPos().x.GetAlign() == LEFT && newctrl->GetPos().x.GetB() == 0)
738 newctrl->HSizePos().VCenterPos(STDSIZE);
739 CellInfo& ci = cellinfo.At(i).At(j);
740 ci.Set(newctrl.Detach(), true, true);
741 ct = true;
742 }
743 if(ct) {
744 LTIMING("PlaceCtrls");
745 Ctrl& c = GetCellCtrl(i, j);
746 if(!c.HasFocusDeep() || c.GetParent() != this)
747 AddChild(&c, p);
748 p = &c;
749 Rect r;
750 if(i < min_visible_line || i > max_visible_line)
751 r.bottom = r.top = -1;
752 else
753 r = GetCellRectM(i, j);
754 if(r.bottom < 0 || r.top > sz.cy) {
755 if(c.GetRect().top != -100000)
756 c.SetRect(-1000, -100000, 1, 1);
757 }
758 else {
759 c.SetRect(r);
760 ctrl_low = min(ctrl_low, i);
761 ctrl_high = max(ctrl_high, i);
762 }
763 }
764 }
765 return p;
766 }
767
SyncPageCtrls()768 void ArrayCtrl::SyncPageCtrls()
769 {
770 LTIMING("SyncPageCtrls");
771 if(!hasctrls)
772 return;
773 Ctrl *p = NULL;
774 for(int i = max(ctrl_low, 0); i <= min(ctrl_high, GetCount() - 1); i++)
775 p = SyncLineCtrls(i, p);
776 ctrl_low = GetCount() - 1;
777 ctrl_high = 0;
778 p = NULL;
779 for(int i = min_visible_line; i <= min(max_visible_line, GetCount() - 1); i++)
780 p = SyncLineCtrls(i, p);
781 }
782
SyncCtrls()783 void ArrayCtrl::SyncCtrls()
784 {
785 LTIMING("SyncCtrls");
786 if(!hasctrls)
787 return;
788 ctrl_low = GetCount() - 1;
789 ctrl_high = 0;
790 Ctrl *p = NULL;
791 for(int i = 0; i < GetCount(); i++)
792 p = SyncLineCtrls(i, p);
793 }
794
FindCellCtrl(Ctrl * ctrl)795 Point ArrayCtrl::FindCellCtrl(Ctrl *ctrl)
796 {
797 if(hasctrls)
798 for(int i = 0; i < cellinfo.GetCount(); i++) {
799 Vector<CellInfo>& ci = cellinfo[i];
800 for(int j = 0; j < ci.GetCount(); j++)
801 if(IsCtrl(i, j)) {
802 CellCtrl& c = GetCellCtrl(i, j);
803 if(&c == ctrl || c.ctrl == ctrl || c.ctrl->HasChildDeep(ctrl))
804 return Point(j, i);
805 }
806 }
807 return Null;
808 }
809
ChildGotFocus()810 void ArrayCtrl::ChildGotFocus()
811 {
812 if(cursor >= 0)
813 RefreshRow(cursor);
814 if(!acceptingrow) {
815 Point p = FindCellCtrl(GetFocusCtrl());
816 if(!IsNull(p)) {
817 if(nocursor)
818 ScrollInto(p.y);
819 else
820 if(p.y != cursor) // avoid setting cursor if it is already there - important for multiselect
821 SetCursor(p.y);
822 }
823 }
824 Ctrl::ChildGotFocus();
825 }
826
ChildLostFocus()827 void ArrayCtrl::ChildLostFocus()
828 {
829 if(cursor >= 0)
830 RefreshRow(cursor);
831 Ctrl::ChildLostFocus();
832 }
833
GetCellInfo(int i,int j,bool f0,Value & v,Color & fg,Color & bg,dword & st)834 const Display& ArrayCtrl::GetCellInfo(int i, int j, bool f0,
835 Value& v, Color& fg, Color& bg, dword& st)
836 {
837 st = 0;
838 bool hasfocus = f0 && !isdrag;
839 bool drop = i == dropline && (dropcol == DND_LINE || dropcol == j);
840 if(IsReadOnly())
841 st |= Display::READONLY;
842 if(i < array.GetCount() && array[i].select)
843 st |= Display::SELECT;
844 if(i == cursor && !nocursor)
845 st |= Display::CURSOR;
846 if(drop) {
847 hasfocus = true;
848 st |= Display::CURSOR;
849 }
850 if(hasfocus)
851 st |= Display::FOCUS;
852 bg = i & 1 ? evenpaper : oddpaper;
853 if(nobg)
854 bg = Null;
855 if(i < array.GetCount() && !IsNull(array[i].paper))
856 bg = array[i].paper;
857 fg = i & 1 ? evenink : oddink;
858 if((st & Display::SELECT) ||
859 !multiselect && (st & Display::CURSOR) && !nocursor ||
860 drop) {
861 fg = hasfocus ? SColorHighlightText : SColorText;
862 bg = hasfocus ? SColorHighlight : Blend(SColorDisabled, SColorPaper);
863 }
864 v = IsCtrl(i, j) && GetCellCtrl(i, j).value ? Null : GetConvertedColumn(i, j);
865 return GetDisplay(i, j);
866 }
867
SpanWideCell(const Display & d,const Value & q,int cm,int & cw,Rect & r,int i,int & j)868 void ArrayCtrl::SpanWideCell(const Display& d, const Value& q, int cm, int& cw, Rect& r, int i, int& j)
869 {
870 int cx = d.GetStdSize(q).cx;
871 while(cx > r.Width() - 2 * cm && j + 1 < column.GetCount()) {
872 Value v = GetConvertedColumn(i, j + 1);
873 if(!IsNull(v))
874 break;
875 j++;
876 cw += header.GetTabWidth(j);
877 r.right = r.left + cw - vertgrid + (j == column.GetCount() - 1);
878 }
879 }
880
DoPaint(Draw & w,bool sample)881 Size ArrayCtrl::DoPaint(Draw& w, bool sample) {
882 LTIMING("Paint");
883 SyncColumnsPos();
884 bool hasfocus0 = HasFocusDeep() || sample;
885 Size size = sample ? StdSampleSize() : GetSize();
886 Rect r;
887 r.bottom = 0;
888 int i = sample ? 0 : GetLineAt(sb);
889 int xs = sample ? 0 : -header.GetScroll();
890 int js;
891 for(js = 0; js < column.GetCount(); js++) {
892 int cw = header.GetTabWidth(js);
893 if ((xs + cw - vertgrid + (js == column.GetCount() - 1)) >= 0)
894 break;
895 xs += cw;
896 }
897 int sy = 0;
898 if(!IsNull(i))
899 while(i < GetCount()) {
900 if((!sample || i == cursor || i < array.GetCount() && array[i].select) && IsLineVisible(i)) {
901 r.top = sample ? sy : GetLineY(i) - sb;
902 if(r.top > size.cy)
903 break;
904 r.bottom = r.top + GetLineCy(i);
905 int x = xs;
906 dword st = 0;
907 if(r.bottom > r.top)
908 for(int j = js; j < column.GetCount(); j++) {
909 int cw = header.GetTabWidth(j);
910 int jj = header.GetTabIndex(j);
911 int cm = column[jj].margin;
912 if(cm < 0)
913 cm = header.Tab(j).GetMargin();
914 if(x > size.cx) break;
915 r.left = x;
916 r.right = x + cw - vertgrid + (jj == column.GetCount() - 1);
917 dword st;
918 Color fg, bg;
919 Value q;
920 const Display& d = GetCellInfo(i, jj, hasfocus0, q, fg, bg, st);
921 if(sample || w.IsPainting(r)) {
922 if(spanwidecells)
923 SpanWideCell(d, q, cm, cw, r, i, j);
924
925 if(cw < 2 * cm || editmode && i == cursor && column[jj].edit)
926 d.PaintBackground(w, r, q, fg, bg, st);
927 else {
928 d.PaintBackground(w, RectC(r.left, r.top, cm, r.Height()), q, fg, bg, st);
929 r.left += cm;
930 r.right -= cm;
931 d.PaintBackground(w, RectC(r.right, r.top, cm, r.Height()), q, fg, bg, st);
932 w.Clip(r);
933 d.Paint(w, r, q, fg, bg, st);
934 w.End();
935 }
936 }
937 x += cw;
938 if(vertgrid)
939 w.DrawRect(x - 1, r.top, 1, r.Height(), gridcolor);
940 }
941 if(horzgrid)
942 w.DrawRect(0, r.bottom, size.cx, 1, gridcolor);
943 r.left = 0;
944 r.right = x;
945 if(i == cursor && !nocursor && multiselect && (GetSelectCount() != 1 || !IsSel(i)) && hasfocus0 && !isdrag)
946 DrawFocus(w, r, st & Display::SELECT ? SColorPaper() : SColorText());
947 r.bottom += horzgrid;
948 r.left = x;
949 r.right = size.cx;
950 if(!nobg)
951 w.DrawRect(r, SColorPaper);
952 if(i == dropline && dropcol == DND_INSERTLINE)
953 DrawHorzDrop(w, 0, r.top - (i > 0), size.cx);
954 sy = r.bottom;
955 }
956 i++;
957 }
958 if(sample) return Size(r.left, sy);
959 int ldy = r.bottom;
960 r.left = 0;
961 r.right = size.cx;
962 r.top = r.bottom;
963 if(IsAppendLine() && !IsCursor()) {
964 r.bottom = r.top + linecy;
965 w.DrawRect(r, HasFocus() ? SColorHighlight : SColorFace);
966 r.top = r.bottom;
967 }
968 r.bottom = size.cy;
969 if(!nobg)
970 w.DrawRect(r, SColorPaper);
971 if(GetCount() == dropline && dropcol == DND_INSERTLINE)
972 DrawHorzDrop(w, 0, ldy - 1, size.cx);
973 scroller.Set(Point(header.GetScroll(), sb));
974 return Size();
975 }
976
Paint(Draw & w)977 void ArrayCtrl::Paint(Draw& w)
978 {
979 DoPaint(w, false);
980 }
981
GetDragSample()982 Image ArrayCtrl::GetDragSample()
983 {
984 ImageDraw iw(StdSampleSize());
985 Size sz = DoPaint(iw, true);
986 return Crop(iw, 0, 0, sz.cx, sz.cy);
987 }
988
MinMaxLine()989 void ArrayCtrl::MinMaxLine()
990 {
991 min_visible_line = Nvl(GetLineAt(sb), 0);
992 max_visible_line = Nvl(GetLineAt((int)sb + sb.GetPage()), GetCount());
993 }
994
Scroll()995 void ArrayCtrl::Scroll() {
996 SyncColumnsPos();
997 MinMaxLine();
998 SyncPageCtrls();
999 PlaceEdits();
1000 scroller.Scroll(*this, GetSize(), Point(header.GetScroll(), sb));
1001 info.Cancel();
1002 WhenScroll();
1003 }
1004
SyncColumnsPos()1005 void ArrayCtrl::SyncColumnsPos()
1006 {
1007 int x = 0;
1008 column_pos.Clear();
1009 column_width.Clear();
1010 for(int i = 0; i < header.GetCount(); i++) {
1011 int w = header.GetTabWidth(i);
1012 int ii = header.GetTabIndex(i);
1013 column_pos.At(ii, 0) = x;
1014 column_width.At(ii, 0) = w;
1015 x += w;
1016 }
1017 }
1018
HeaderLayout()1019 void ArrayCtrl::HeaderLayout() {
1020 SyncColumnsPos();
1021 MinMaxLine();
1022 Refresh();
1023 SyncInfo();
1024 SyncPageCtrls();
1025 PlaceEdits();
1026 WhenHeaderLayout();
1027 }
1028
HeaderScroll()1029 void ArrayCtrl::HeaderScroll()
1030 {
1031 Scroll();
1032 WhenHeaderLayout();
1033 }
1034
HeaderScrollVisibility()1035 void ArrayCtrl::HeaderScrollVisibility()
1036 {
1037 scrollbox.Height(ScrollBarSize());
1038 if(header.IsScroll())
1039 sb.SetFrame(scrollbox);
1040 else
1041 sb.SetFrame(NullFrame());
1042 }
1043
RefreshRow(int i)1044 void ArrayCtrl::RefreshRow(int i)
1045 {
1046 if(i >= 0 && i < GetCount())
1047 Refresh(0, GetLineY(i) - sb, GetSize().cx, GetLineCy(i) + horzgrid);
1048 if(IsAppendLine() || i == GetCount())
1049 Refresh(0, GetLineY(GetCount()) - sb, GetSize().cx, linecy + horzgrid);
1050 SyncInfo();
1051 }
1052
IndexInfo(int i)1053 ArrayCtrl::IdInfo& ArrayCtrl::IndexInfo(int i) {
1054 return idx[i];
1055 }
1056
IndexInfo(const Id & id)1057 ArrayCtrl::IdInfo& ArrayCtrl::IndexInfo(const Id& id) {
1058 return idx.Get(id);
1059 }
1060
AddIndex(const Id & id)1061 ArrayCtrl::IdInfo& ArrayCtrl::AddIndex(const Id& id) {
1062 return idx.Add(id);
1063 }
1064
AddIndex()1065 ArrayCtrl::IdInfo& ArrayCtrl::AddIndex() {
1066 return idx.Add(Id());
1067 }
1068
SetId(int i,const Id & id)1069 ArrayCtrl::IdInfo& ArrayCtrl::SetId(int i, const Id& id) {
1070 while(idx.GetCount() < i + 1)
1071 idx.Add(Id());
1072 idx.SetKey(i, id);
1073 return idx[i];
1074 }
1075
AddColumn(const char * text,int w)1076 ArrayCtrl::Column& ArrayCtrl::AddColumn(const char *text, int w) {
1077 AddIndex();
1078 return AddColumnAt(idx.GetCount() - 1, text, w);
1079 }
1080
AddColumn(const Id & id,const char * text,int w)1081 ArrayCtrl::Column& ArrayCtrl::AddColumn(const Id& id, const char *text, int w) {
1082 AddIndex(id);
1083 return AddColumnAt(idx.GetCount() - 1, text, w);
1084 }
1085
AddColumnAt(int pos,const char * text,int w)1086 ArrayCtrl::Column& ArrayCtrl::AddColumnAt(int pos, const char *text, int w) {
1087 header.Add(text, w);
1088 Column& m = column.Add();
1089 m.arrayctrl = this;
1090 m.index = column.GetCount() - 1;
1091 m.Add(pos);
1092 if(allsorting)
1093 m.Sorting();
1094 return m;
1095 }
1096
AddColumnAt(const Id & id,const char * text,int w)1097 ArrayCtrl::Column& ArrayCtrl::AddColumnAt(const Id& id, const char *text, int w) {
1098 header.Add(text, w);
1099 Column& m = column.Add();
1100 m.arrayctrl = this;
1101 m.index = column.GetCount() - 1;
1102 m.Add(id);
1103 if(allsorting)
1104 m.Sorting();
1105 return m;
1106 }
1107
AddRowNumColumn(const char * text,int w)1108 ArrayCtrl::Column& ArrayCtrl::AddRowNumColumn(const char *text, int w) {
1109 header.Add(text, w);
1110 Column& m = column.Add();
1111 m.arrayctrl = this;
1112 m.index = column.GetCount() - 1;
1113 if(allsorting)
1114 m.Sorting();
1115 return m;
1116 }
1117
FindColumnWithPos(int pos) const1118 int ArrayCtrl::FindColumnWithPos(int pos) const
1119 {
1120 for(int i = 0; i < column.GetCount(); i++) {
1121 const Mitor<int>& m = column[i].pos;
1122 for(int j = 0; j < m.GetCount(); j++)
1123 if(Pos(m[j]) == pos) return i;
1124 }
1125 return -1;
1126 }
1127
FindColumnWithId(const Id & id) const1128 int ArrayCtrl::FindColumnWithId(const Id& id) const
1129 {
1130 return FindColumnWithPos(GetPos(id));
1131 }
1132
AddCtrl(Ctrl & ctrl)1133 ArrayCtrl::IdInfo& ArrayCtrl::AddCtrl(Ctrl& ctrl) {
1134 IdInfo& f = AddIndex();
1135 AddCtrlAt(idx.GetCount() - 1, ctrl);
1136 return f;
1137 }
1138
AddCtrl(const Id & id,Ctrl & ctrl)1139 ArrayCtrl::IdInfo& ArrayCtrl::AddCtrl(const Id& id, Ctrl& ctrl) {
1140 IdInfo& f = AddIndex(id);
1141 AddCtrlAt(idx.GetCount() - 1, ctrl);
1142 return f;
1143 }
1144
AddCtrlAt(int ii,Ctrl & ctrl)1145 void ArrayCtrl::AddCtrlAt(int ii, Ctrl& ctrl) {
1146 Control& c = control.Add();
1147 c.pos = ii;
1148 c.ctrl = &ctrl;
1149 ctrl.Disable();
1150 ctrl <<= Null;
1151 }
1152
AddCtrlAt(const Id & id,Ctrl & ctrl)1153 void ArrayCtrl::AddCtrlAt(const Id& id, Ctrl& ctrl) {
1154 AddCtrlAt(-AsNdx(id), ctrl);
1155 }
1156
AddRowNumCtrl(Ctrl & ctrl)1157 void ArrayCtrl::AddRowNumCtrl(Ctrl& ctrl) {
1158 AddCtrlAt(Null, ctrl);
1159 }
1160
GetCellRect(int i,int col) const1161 Rect ArrayCtrl::GetCellRect(int i, int col) const
1162 {
1163 LTIMING("GetCellRect");
1164 Rect r;
1165 r.top = GetLineY(i) - sb;
1166 r.bottom = r.top + GetLineCy(i);
1167 r.left = (col < column_pos.GetCount() ? column_pos[col] : 0) - header.GetScroll();
1168 r.right = r.left + (col < column_width.GetCount() ? column_width[col] : 0) - vertgrid +
1169 (col == column.GetCount() - 1);
1170 return r;
1171 }
1172
GetCellRectM(int i,int col) const1173 Rect ArrayCtrl::GetCellRectM(int i, int col) const
1174 {
1175 LTIMING("GetCellRectM");
1176 Rect r = GetCellRect(i, col);
1177 int cm = column[col].margin;
1178 if(cm < 0)
1179 cm = header.Tab(header.FindIndex(col)).GetMargin();
1180 r.left += cm;
1181 r.right -= cm;
1182 return r;
1183 }
1184
Remove0(int i)1185 void ArrayCtrl::Remove0(int i) {
1186 array.Remove(i);
1187 for(int j = 0; j < column.GetCount(); j++)
1188 column[j].RemoveCache(i);
1189 if(i < ln.GetCount()) {
1190 int y = ln[i].y;
1191 ln.Remove(i);
1192 Reline(i, y);
1193 }
1194 if(i < cellinfo.GetCount())
1195 cellinfo.Remove(i);
1196 if(virtualcount > 0) virtualcount--;
1197 Refresh();
1198 PlaceEdits();
1199 SyncCtrls();
1200 SyncInfo();
1201 SetSb();
1202 selectiondirty = true;
1203 WhenArrayAction();
1204 }
1205
DisableCtrls()1206 void ArrayCtrl::DisableCtrls() {
1207 for(int i = 0; i < control.GetCount(); i++) {
1208 Ctrl& c = *control[i].ctrl;
1209 c <<= Null;
1210 c.Disable();
1211 }
1212 }
1213
RejectRow()1214 void ArrayCtrl::RejectRow() {
1215 bool rm_cursor = false;
1216 if(IsCursor()) {
1217 int i;
1218 for(i = 0; i < column.GetCount(); i++) {
1219 Column& m = column[i];
1220 if(m.edit) m.edit->Reject();
1221 }
1222 for(i = 0; i < control.GetCount(); i++)
1223 control[i].ctrl->Reject();
1224 if(IsInsert()) {
1225 Remove0(cursor);
1226 DisableCtrls();
1227 cursor = -1;
1228 rm_cursor = true;
1229 }
1230 else
1231 RefreshRow(cursor);
1232 }
1233 EndEdit();
1234 WhenArrayAction();
1235 if(rm_cursor) {
1236 WhenCursor();
1237 WhenSel();
1238 }
1239 SyncInfo();
1240 }
1241
Reject()1242 void ArrayCtrl::Reject() {
1243 RejectRow();
1244 }
1245
CancelCursor()1246 void ArrayCtrl::CancelCursor() {
1247 RejectRow();
1248 DisableCtrls();
1249 cursor = anchor = -1;
1250 WhenCursor();
1251 WhenSel();
1252 SyncInfo();
1253 }
1254
UpdateRow()1255 bool ArrayCtrl::UpdateRow() {
1256 ASSERT(IsCursor());
1257 bool iv = false;
1258 int i;
1259 for(i = 0; i < column.GetCount(); i++) {
1260 Column& m = column[i];
1261 if(m.edit && m.edit->IsModified()) {
1262 Value v = m.edit->GetData();
1263 if(m.pos.GetCount() == 1) {
1264 Value& vv = array.At(cursor).line.At(Pos(m.pos[0]));
1265 if(vv != v) {
1266 iv = true;
1267 vv = v;
1268 }
1269 }
1270 else
1271 if(m.pos.GetCount() > 1) {
1272 ValueArray va = v;
1273 for(int j = 0; j < m.pos.GetCount(); j++) {
1274 Value& vv = array.At(cursor).line.At(Pos(m.pos[j]));
1275 if(vv != va[j]) {
1276 iv = true;
1277 vv = va[j];
1278 }
1279 }
1280 }
1281 }
1282 }
1283 for(i = 0; i < control.GetCount(); i++) {
1284 Control& c = control[i];
1285 if(!IsNull(c.pos) && c.ctrl->IsModified()) {
1286 Value v = c.ctrl->GetData();
1287 Value& vv = array.At(cursor).line.At(Pos(c.pos));
1288 if(vv != v) {
1289 iv = true;
1290 vv = v;
1291 }
1292 }
1293 }
1294 if(iv) {
1295 RefreshRow(cursor);
1296 InvalidateCache(cursor);
1297 WhenArrayAction();
1298 }
1299 WhenUpdateRow();
1300 return true;
1301 }
1302
ClearModify()1303 void ArrayCtrl::ClearModify() {
1304 int i;
1305 modify.Set(0, false, idx.GetCount());
1306 for(i = 0; i < column.GetCount(); i++)
1307 if(column[i].edit)
1308 column[i].edit->ClearModify();
1309 for(i = 0; i < control.GetCount(); i++)
1310 control[i].ctrl->ClearModify();
1311 }
1312
AcceptRow()1313 bool ArrayCtrl::AcceptRow() {
1314 ASSERT(IsCursor());
1315 int i;
1316 for(i = 0; i < column.GetCount(); i++) {
1317 Column& m = column[i];
1318 if(m.edit && !m.edit->Accept())
1319 return false;
1320 if(IsCtrl(cursor, i)) {
1321 Ctrl *c = GetCellCtrl(cursor, i).ctrl;
1322 acceptingrow++;
1323 bool b = c->Accept();
1324 acceptingrow--;
1325 if(!b)
1326 return false;
1327 }
1328 }
1329 for(i = 0; i < control.GetCount(); i++)
1330 if(!control[i].ctrl->Accept())
1331 return false;
1332 acceptingrow++;
1333 bool ar = WhenAcceptRow() && UpdateRow();
1334 acceptingrow--;
1335 if(!ar) {
1336 SetCursorEditFocus();
1337 return false;
1338 }
1339 bool b = editmode;
1340 EndEdit();
1341 SetCtrls();
1342 ClearModify();
1343 if(b)
1344 WhenAcceptEdit();
1345 return true;
1346 }
1347
Accept()1348 bool ArrayCtrl::Accept() {
1349 return IsCursor() ? AcceptRow() : true;
1350 }
1351
KillCursor0()1352 bool ArrayCtrl::KillCursor0() {
1353 if(IsCursor()) {
1354 if(!AcceptRow()) return false;
1355 int c = cursor;
1356 DisableCtrls();
1357 WhenKillCursor();
1358 cursor = -1;
1359 RefreshRow(c);
1360 }
1361 return true;
1362 }
1363
KillCursor()1364 bool ArrayCtrl::KillCursor() {
1365 bool b = KillCursor0();
1366 if(b) {
1367 WhenCursor();
1368 WhenSel();
1369 SyncInfo();
1370 }
1371 return b;
1372 }
1373
SetCtrls()1374 void ArrayCtrl::SetCtrls() {
1375 int i;
1376 for(i = 0; i < column.GetCount(); i++)
1377 ColEditSetData(i);
1378 for(i = 0; i < control.GetCount(); i++)
1379 CtrlSetData(i);
1380 }
1381
ScrollInto(int line)1382 void ArrayCtrl::ScrollInto(int line)
1383 {
1384 if(line < 0)
1385 sb.Begin();
1386 else
1387 if(line >= GetCount())
1388 sb.End();
1389 else
1390 sb.ScrollInto(GetLineY(line), GetLineCy(line));
1391 }
1392
ScrollIntoCursor()1393 void ArrayCtrl::ScrollIntoCursor()
1394 {
1395 if(IsCursor())
1396 ScrollInto(cursor);
1397 }
1398
SetCursorEditFocus()1399 void ArrayCtrl::SetCursorEditFocus()
1400 {
1401 if(!IsEdit() && cursor >= 0 && hasctrls)
1402 for(int j = 0; j < column.GetCount(); j++)
1403 if(IsCtrl(cursor, j) && GetCellCtrl(cursor, j).ctrl->SetWantFocus())
1404 break;
1405 }
1406
SetCursor0(int i,bool dosel)1407 bool ArrayCtrl::SetCursor0(int i, bool dosel) {
1408 if(nocursor || GetCount() == 0 || !IsLineEnabled(i))
1409 return false;
1410 i = minmax(i, 0, GetCount() - 1);
1411 bool sel = false;
1412 if(i != cursor) {
1413 RefreshRow(cursor);
1414 if(cursor >= 0) {
1415 if(!KillCursor0()) return false;
1416 }
1417 cursor = i;
1418 ScrollIntoCursor();
1419 RefreshRow(cursor);
1420 SetCtrls();
1421 for(int j = 0; j < column.GetCount(); j++)
1422 if(IsCtrl(cursor, j) && GetCellCtrl(cursor, j).ctrl->HasFocus())
1423 goto nosetfocus;
1424 SetCursorEditFocus();
1425 nosetfocus:
1426 ClearModify();
1427 Action();
1428 WhenEnterRow();
1429 WhenCursor();
1430 sel = true;
1431 }
1432 if(dosel && multiselect) {
1433 ClearSelection(false);
1434 anchor = cursor;
1435 SelectOne(cursor, true, false);
1436 sel = true;
1437 }
1438 if(sel) {
1439 selectiondirty = true;
1440 WhenSelection();
1441 WhenSel();
1442 }
1443 SyncInfo();
1444 return true;
1445 }
1446
SetCursor(int i)1447 bool ArrayCtrl::SetCursor(int i)
1448 {
1449 return SetCursor0(i);
1450 }
1451
ShowAppendLine()1452 void ArrayCtrl::ShowAppendLine() {
1453 sb.ScrollInto(GetTotalCy(), GetLineCy());
1454 }
1455
GoBegin()1456 void ArrayCtrl::GoBegin() {
1457 if(nocursor)
1458 sb.Begin();
1459 else {
1460 int q = FindEnabled(0, 1);
1461 if(q >= 0)
1462 SetCursor(q);
1463 }
1464 }
1465
GoEnd()1466 void ArrayCtrl::GoEnd() {
1467 if(nocursor)
1468 sb.End();
1469 else {
1470 int q = FindEnabled(GetCount() - 1, -1);
1471 if(q >= 0)
1472 SetCursor(q);
1473 }
1474 }
1475
GetCursorSc() const1476 int ArrayCtrl::GetCursorSc() const {
1477 return IsCursor() ? GetLineY(cursor) - sb : 0;
1478 }
1479
ScCursor(int a)1480 void ArrayCtrl::ScCursor(int a) {
1481 if(IsCursor())
1482 sb = GetLineY(cursor) - max(a, 0);
1483 }
1484
SyncSelection() const1485 void ArrayCtrl::SyncSelection() const
1486 {
1487 if(!selectiondirty)
1488 return;
1489 selectcount = 0;
1490 for(int i = 0; i < array.GetCount(); i++)
1491 if(array[i].select)
1492 selectcount++;
1493 selectiondirty = false;
1494 }
1495
GetSelectCount() const1496 int ArrayCtrl::GetSelectCount() const
1497 {
1498 SyncSelection();
1499 return selectcount;
1500 }
1501
SelectOne(int i,bool sel,bool raise)1502 void ArrayCtrl::SelectOne(int i, bool sel, bool raise)
1503 {
1504 array.At(i).select = sel;
1505 selectiondirty = true;
1506 RefreshRow(i);
1507 if(raise) {
1508 WhenSelection();
1509 WhenSel();
1510 }
1511 SyncInfo();
1512 }
1513
Select(int i,int count,bool sel)1514 void ArrayCtrl::Select(int i, int count, bool sel)
1515 {
1516 while(count--) {
1517 array.At(i).select = sel;
1518 RefreshRow(i++);
1519 }
1520 selectiondirty = true;
1521 WhenSelection();
1522 WhenSel();
1523 SyncInfo();
1524 }
1525
EnableLine(int i,bool en)1526 void ArrayCtrl::EnableLine(int i, bool en)
1527 {
1528 array.At(i).enabled = en;
1529 }
1530
IsLineEnabled(int i) const1531 bool ArrayCtrl::IsLineEnabled(int i) const
1532 {
1533 bool b = i < 0 ? false : i < array.GetCount() ? array[i].enabled : true;
1534 WhenLineEnabled(i, b);
1535 return b;
1536 }
1537
ShowLine(int i,bool e)1538 void ArrayCtrl::ShowLine(int i, bool e)
1539 {
1540 int q = ln.GetCount();
1541 array.At(i).visible = e;
1542 ln.At(i);
1543 if(q > 0 && i > 0)
1544 Reline(i - 1, ln[i - 1].y);
1545 else
1546 Reline(0, 0);
1547 }
1548
IsLineVisible(int i) const1549 bool ArrayCtrl::IsLineVisible(int i) const
1550 {
1551 bool b = IsLineVisible0(i);
1552 WhenLineVisible(i, b);
1553 return b;
1554 }
1555
ClearSelection(bool raise)1556 void ArrayCtrl::ClearSelection(bool raise)
1557 {
1558 if(IsSelection()) {
1559 for(int i = 0; i < array.GetCount(); i++)
1560 if(array[i].select) {
1561 RefreshRow(i);
1562 array[i].select = false;
1563 }
1564 selectiondirty = false;
1565 selectcount = 0;
1566 if(raise) {
1567 WhenSelection();
1568 WhenSel();
1569 }
1570 SyncInfo();
1571 }
1572 }
1573
IsSel(int i) const1574 bool ArrayCtrl::IsSel(int i) const
1575 {
1576 return multiselect ? IsSelected(i) : GetCursor() == i;
1577 }
1578
GetSelKeys() const1579 Vector<int> ArrayCtrl::GetSelKeys() const
1580 {
1581 Vector<int> id;
1582 for(int i = 0; i < GetCount(); i++)
1583 if(IsSel(i))
1584 id.Add(Get(i, 0));
1585 return id;
1586 }
1587
GetScroll() const1588 int ArrayCtrl::GetScroll() const
1589 {
1590 return sb;
1591 }
1592
ScrollTo(int sc)1593 void ArrayCtrl::ScrollTo(int sc)
1594 {
1595 sb = sc;
1596 }
1597
CenterCursor()1598 void ArrayCtrl::CenterCursor() {
1599 if(IsCursor())
1600 ScCursor((GetSize().cy - GetLineCy(cursor)) / 2);
1601 }
1602
FindEnabled(int i,int dir)1603 int ArrayCtrl::FindEnabled(int i, int dir)
1604 {
1605 ASSERT(dir == -1 || dir == 1);
1606 while(i >= 0 && i < GetCount()) {
1607 if(IsLineEnabled(i))
1608 return i;
1609 i += dir;
1610 }
1611 return -1;
1612 }
1613
Page(int q)1614 void ArrayCtrl::Page(int q) {
1615 q = q * max(GetSize().cy - (linecy + horzgrid), linecy + horzgrid);
1616 int a = GetCursorSc();
1617 if(IsCursor()) {
1618 int i = FindEnabled(cursor + q / (linecy + horzgrid), sgn(q));
1619 if(i >= 0) {
1620 SetCursor(i);
1621 ScCursor(a);
1622 }
1623 }
1624 else
1625 if(q > 0)
1626 sb.NextPage();
1627 else
1628 sb.PrevPage();
1629 }
1630
DoPoint(Point p,bool dosel)1631 void ArrayCtrl::DoPoint(Point p, bool dosel) {
1632 p.y += sb;
1633 if(p.y >= GetTotalCy() && IsAppendLine()) {
1634 if(IsMultiSelect())
1635 ClearSelection();
1636 KillCursor();
1637 }
1638 clickpos.y = GetLineAt(p.y);
1639 if(!IsNull(clickpos.y))
1640 SetCursor0(clickpos.y, dosel);
1641 else
1642 if(IsCursor())
1643 AcceptRow();
1644 if(!HasFocusDeep())
1645 SetWantFocus();
1646 }
1647
GetClickColumn(int ii,Point p)1648 int ArrayCtrl::GetClickColumn(int ii, Point p)
1649 {
1650 for(int i = 0; i < column.GetCount(); i++)
1651 if(GetCellRect(ii, i).Contains(p))
1652 return i;
1653 return Null;
1654 }
1655
ClickColumn(Point p)1656 void ArrayCtrl::ClickColumn(Point p)
1657 {
1658 clickpos.x = Null;
1659 if(clickpos.y >= 0)
1660 clickpos.x = GetClickColumn(clickpos.y, p);
1661 }
1662
ClickSel(dword flags)1663 void ArrayCtrl::ClickSel(dword flags)
1664 {
1665 if(cursor >= 0 && multiselect) {
1666 if(flags & K_CTRL) {
1667 SelectOne(cursor, !IsSelected(cursor));
1668 anchor = cursor;
1669 }
1670 else {
1671 ClearSelection(false);
1672 if((flags & K_SHIFT) && anchor >= 0)
1673 Select(min(anchor, cursor), abs(anchor - cursor) + 1, true);
1674 else {
1675 anchor = cursor;
1676 SelectOne(cursor, true);
1677 }
1678 }
1679 Action();
1680 }
1681 }
1682
LeftDown(Point p,dword flags)1683 void ArrayCtrl::LeftDown(Point p, dword flags)
1684 {
1685 if(IsReadOnly()) return;
1686 int q = header.GetSplit(p.x);
1687 if(q >= 0) {
1688 header.StartSplitDrag(q);
1689 return;
1690 }
1691 q = clickpos.y = GetLineAt(p.y + sb);
1692 ClickColumn(p);
1693 selclick = false;
1694 if(q >= 0 && q < GetCount() && IsSel(q) && (flags & (K_CTRL|K_SHIFT)) == 0) {
1695 selclick = true;
1696 return;
1697 }
1698 DoClick(p, flags);
1699 }
1700
DoClick(Point p,dword flags)1701 void ArrayCtrl::DoClick(Point p, dword flags) {
1702 int c = cursor;
1703 bool b = HasFocus();
1704 DoPoint(p, !(flags & (K_CTRL|K_SHIFT)));
1705 ClickColumn(p);
1706 if(!IsNull(clickpos.x) && c == cursor && cursor >= 0 && b && column[clickpos.x].clickedit
1707 && (flags & (K_CTRL|K_SHIFT)) == 0)
1708 StartEdit(clickpos.x);
1709 else
1710 ClickSel(flags);
1711 WhenLeftClick();
1712 }
1713
LeftUp(Point p,dword flags)1714 void ArrayCtrl::LeftUp(Point p, dword flags)
1715 {
1716 if(selclick)
1717 DoClick(p, flags);
1718 selclick = false;
1719 }
1720
LeftDouble(Point p,dword flags)1721 void ArrayCtrl::LeftDouble(Point p, dword flags)
1722 {
1723 if(IsReadOnly()) return;
1724 DoPoint(p, !(flags & (K_CTRL|K_SHIFT)));
1725 ClickColumn(p);
1726 ClickSel(flags);
1727 if((IsInserting() || IsAppending()) && IsAppendLine() && !IsCursor())
1728 DoAppend();
1729 if(!multiselect || (flags & (K_CTRL|K_SHIFT)) == 0)
1730 WhenLeftDouble();
1731 }
1732
LeftDrag(Point p,dword)1733 void ArrayCtrl::LeftDrag(Point p, dword)
1734 {
1735 int ii = GetLineAt(p.y + sb);
1736 if(!IsNull(ii) && IsSel(ii))
1737 WhenDrag();
1738 }
1739
SyncInfo()1740 void ArrayCtrl::SyncInfo()
1741 {
1742 if((HasMouse() || info.HasMouse()) && !IsEdit() && popupex) {
1743 Point p = GetMouseViewPos();
1744 Point c;
1745 c.y = GetLineAt(p.y + sb);
1746 if(c.y >= 0) {
1747 for(c.x = 0; c.x < column.GetCount(); c.x++) {
1748 if(!IsCtrl(c.y, c.x)) {
1749 Rect r = GetCellRect(c.y, c.x);
1750 int cm = column[c.x].margin;
1751 if(cm < 0)
1752 cm = header.Tab(header.FindIndex(c.x)).GetMargin();
1753 dword st;
1754 Color fg, bg;
1755 Value q;
1756 const Display& d = GetCellInfo(c.y, c.x, HasFocusDeep(), q, fg, bg, st);
1757 int cw = r.Width();
1758 if(spanwidecells)
1759 SpanWideCell(d, q, cm, cw, r, c.y, c.x);
1760 if(r.Contains(p)) {
1761 info.Set(this, r, q, &d, fg, bg, st, cm);
1762 return;
1763 }
1764 }
1765 }
1766 }
1767 }
1768 info.Cancel();
1769 }
1770
MouseMove(Point p,dword)1771 void ArrayCtrl::MouseMove(Point p, dword)
1772 {
1773 int ii = Null;
1774 if(WhenMouseMove) {
1775 ii = GetLineAt(p.y + sb);
1776 if(IsNull(ii))
1777 WhenMouseMove(Null);
1778 else
1779 WhenMouseMove(Point(GetClickColumn(ii, p), ii));
1780 }
1781 if(mousemove && !IsReadOnly()) {
1782 if(IsNull(ii))
1783 ii = GetLineAt(p.y + sb);
1784 if(!IsNull(ii)) SetCursor(ii);
1785 }
1786 SyncInfo();
1787 }
1788
MouseLeave()1789 void ArrayCtrl::MouseLeave()
1790 {
1791 WhenMouseMove(Null);
1792 }
1793
CursorImage(Point p,dword)1794 Image ArrayCtrl::CursorImage(Point p, dword)
1795 {
1796 if(!IsNull(cursor_override))
1797 return cursor_override;
1798 return header.GetSplit(p.x) < 0 || header.GetMode() == HeaderCtrl::FIXED ? Image::Arrow()
1799 : CtrlImg::HorzPos();
1800 }
1801
RightDown(Point p,dword flags)1802 void ArrayCtrl::RightDown(Point p, dword flags) {
1803 if((flags & (K_CTRL|K_SHIFT)) == 0) {
1804 DoPoint(p, false);
1805 ClickColumn(p);
1806 if(cursor >= 0 && multiselect) {
1807 if(!IsSelected(cursor)) {
1808 ClearSelection();
1809 SelectOne(anchor = cursor);
1810 }
1811 Action();
1812 }
1813 }
1814 MenuBar::Execute(WhenBar);
1815 }
1816
TestKey(int i,int key)1817 bool ArrayCtrl::TestKey(int i, int key) {
1818 for(int j = 0; j < column.GetCount(); j++) {
1819 int (*af)(int) = column[j].accel;
1820 if(af) {
1821 int c = (*af)(key);
1822 if(c && (*af)(StdFormat(GetColumn(i, j))[0]) == c) {
1823 SetCursor(i);
1824 return true;
1825 }
1826 }
1827 }
1828 for(int ii = 0; ii < idx.GetCount(); ii++) {
1829 int (*af)(int) = idx[ii].accel;
1830 if(af) {
1831 int c = (*af)(key);
1832 if(c && (*af)(StdFormat(Get(i, ii))[0]) == c) {
1833 SetCursor(i);
1834 return true;
1835 }
1836 }
1837 }
1838 return false;
1839 }
1840
GetEditColumn() const1841 int ArrayCtrl::GetEditColumn() const {
1842 if(!IsEdit()) return -1;
1843 for(int i = 0; i < column.GetCount(); i++) {
1844 const Column& m = column[i];
1845 if(m.edit && m.edit->HasFocusDeep())
1846 return i;
1847 }
1848 return -1;
1849 }
1850
KeyMultiSelect(int aanchor,dword key)1851 void ArrayCtrl::KeyMultiSelect(int aanchor, dword key)
1852 {
1853 if(!multiselect)
1854 return;
1855 ClearSelection();
1856 if(key & K_SHIFT) {
1857 anchor = aanchor;
1858 if(anchor >= 0 && cursor >= 0)
1859 Select(min(anchor, cursor), abs(anchor - cursor) + 1);
1860 }
1861 else
1862 if(cursor >= 0) {
1863 anchor = cursor;
1864 SelectOne(anchor);
1865 }
1866 }
1867
Key(dword key,int)1868 bool ArrayCtrl::Key(dword key, int) {
1869 if(IsReadOnly()) return false;
1870 int aanchor = anchor;
1871 if(!editmode) {
1872 if(key > 32 && key < 256) {
1873 int cr = cursor >= 0 ? cursor + 1 : 0;
1874 for(int j = 0; j < column.GetCount(); j++)
1875 if(column[j].accel) {
1876 int i;
1877 for(i = cr; i < array.GetCount(); i++)
1878 if(TestKey(i, key)) return true;
1879 for(i = 0; i < cr; i++)
1880 if(TestKey(i, key)) return true;
1881 return true;
1882 }
1883 }
1884 switch(key) {
1885 case K_PAGEUP:
1886 case K_SHIFT_PAGEUP:
1887 Page(-1);
1888 KeyMultiSelect(aanchor, key);
1889 return true;
1890 case K_PAGEDOWN:
1891 case K_SHIFT_PAGEDOWN:
1892 if((IsInserting() || IsAppending()) && IsAppendLine() && cursor == GetCount() - 1)
1893 KillCursor();
1894 else {
1895 Page(1);
1896 KeyMultiSelect(aanchor, key);
1897 }
1898 return true;
1899 case K_HOME:
1900 case K_CTRL_HOME:
1901 case K_CTRL_PAGEUP:
1902 case K_SHIFT_HOME:
1903 case K_SHIFT|K_CTRL_HOME:
1904 case K_SHIFT|K_CTRL_PAGEUP:
1905 GoBegin();
1906 KeyMultiSelect(aanchor, key);
1907 return true;
1908 case K_END:
1909 case K_CTRL_END:
1910 case K_CTRL_PAGEDOWN:
1911 case K_SHIFT|K_END:
1912 case K_SHIFT|K_CTRL_END:
1913 case K_SHIFT|K_CTRL_PAGEDOWN:
1914 GoEnd();
1915 KeyMultiSelect(aanchor, key);
1916 return true;
1917 case K_CTRL_A:
1918 if(multiselect) {
1919 GoEnd();
1920 anchor = 0;
1921 KeyMultiSelect(0, K_SHIFT);
1922 }
1923 return true;
1924 }
1925 }
1926 switch(key) {
1927 case K_UP:
1928 case K_SHIFT_UP:
1929 if(IsCursor()) {
1930 int d = GetEditColumn();
1931 int i = FindEnabled(cursor - 1, -1);
1932 if(i >= 0 && SetCursor0(i)) {
1933 if(d >= 0)
1934 StartEdit(d);
1935 else
1936 KeyMultiSelect(aanchor, key);
1937 }
1938 }
1939 else
1940 if((IsInserting() || IsAppending()) && IsAppendLine())
1941 SetCursor(GetCount() - 1);
1942 else
1943 sb.PrevLine();
1944 return true;
1945 case K_DOWN:
1946 case K_SHIFT_DOWN:
1947 if((IsInserting() || IsAppending()) && IsAppendLine() && cursor == GetCount() - 1 && !editmode) {
1948 if(IsMultiSelect())
1949 ClearSelection();
1950 KillCursor();
1951 }
1952 else
1953 if(IsCursor()) {
1954 int d = GetEditColumn();
1955 int i = FindEnabled(cursor + 1, 1);
1956 if(i >= 0 && SetCursor0(i) && d >= 0)
1957 StartEdit(d);
1958 else
1959 KeyMultiSelect(aanchor, key);
1960 }
1961 else
1962 sb.NextLine();
1963 return true;
1964 case K_ENTER:
1965 if(!IsCursor() && (IsInserting() || IsAppending()) && IsAppendLine()) {
1966 DoAppend();
1967 return true;
1968 }
1969 if(editmode) {
1970 bool ins = IsInsert() && autoappend;
1971 if(AcceptEnter() && ins)
1972 DoAppend();
1973 return true;
1974 }
1975 if(WhenEnterKey && IsCursor()) {
1976 WhenEnterKey();
1977 return true;
1978 }
1979 break;
1980 case K_ESCAPE:
1981 if(IsEdit()) {
1982 int c = cursor;
1983 CancelCursor();
1984 SetCursor(c);
1985 return true;
1986 }
1987 break;
1988 }
1989 return MenuBar::Scan(WhenBar, key);
1990 }
1991
AcceptEnter()1992 bool ArrayCtrl::AcceptEnter() {
1993 if(editmode) {
1994 bool ins = IsInsert();
1995 if(!Accept())
1996 return false;
1997 if(GetCursor() == array.GetCount() - 1 &&
1998 (IsInserting() || IsAppending()) && IsAppendLine() && ins && !noinsertappend) {
1999 if(IsMultiSelect())
2000 ClearSelection();
2001 KillCursor();
2002 ShowAppendLine();
2003 }
2004 }
2005 return true;
2006 }
2007
Set(int i,const Vector<Value> & v)2008 void ArrayCtrl::Set(int i, const Vector<Value>& v) {
2009 array.At(i).line.Clear();
2010 for(int ii = 0; ii < v.GetCount(); ii++)
2011 Set(i, ii, v[ii]);
2012 InvalidateCache(i);
2013 SetSb();
2014 RefreshRow(i);
2015 WhenArrayAction();
2016 }
2017
Add()2018 void ArrayCtrl::Add() {
2019 if(GetIndexCount()) {
2020 Vector<Value> x;
2021 for(int ii = 0; ii < idx.GetCount(); ii++)
2022 x.Add(idx[ii].GetInsertValue());
2023 Set(array.GetCount(), x);
2024 }
2025 else {
2026 int i = array.GetCount();
2027 array.At(i);
2028 SetSb();
2029 Refresh();
2030 InvalidateCache(cursor);
2031 InvalidateCache(i);
2032 RefreshRow(i);
2033 }
2034 WhenArrayAction();
2035 }
2036
Add(const Vector<Value> & v)2037 void ArrayCtrl::Add(const Vector<Value>& v)
2038 {
2039 Set(array.GetCount(), v);
2040 }
2041
Set(int ii,const VectorMap<String,Value> & m)2042 void ArrayCtrl::Set(int ii, const VectorMap<String, Value>& m)
2043 {
2044 for(int i = 0; i < m.GetCount(); i++) {
2045 int j = GetPos(m.GetKey(i));
2046 if(j >= 0)
2047 Set(ii, j, m[i]);
2048 }
2049 }
2050
SetMap(int ii,const ValueMap & m)2051 void ArrayCtrl::SetMap(int ii, const ValueMap& m)
2052 {
2053 for(int i = 0; i < m.GetCount(); i++) {
2054 int j = GetPos((String)m.GetKey(i));
2055 if(j >= 0)
2056 Set(ii, j, m.GetValue(i));
2057 }
2058 }
2059
Add(const VectorMap<String,Value> & m)2060 void ArrayCtrl::Add(const VectorMap<String, Value>& m)
2061 {
2062 Set(array.GetCount(), m);
2063 }
2064
AddMap(const ValueMap & m)2065 void ArrayCtrl::AddMap(const ValueMap& m)
2066 {
2067 SetMap(array.GetCount(), m);
2068 }
2069
GetMap(int i) const2070 ValueMap ArrayCtrl::GetMap(int i) const
2071 {
2072 ValueMap m;
2073 for(int j = 0; j < GetIndexCount(); j++) {
2074 String id = ~GetId(j);
2075 if(id.GetCount())
2076 m(id, Get(i, j));
2077 }
2078 return m;
2079 }
2080
GetArray(int i) const2081 ValueArray ArrayCtrl::GetArray(int i) const
2082 {
2083 return ValueArray(clone(GetLine(i)));
2084 }
2085
SetArray(int i,const ValueArray & va)2086 void ArrayCtrl::SetArray(int i, const ValueArray& va)
2087 {
2088 Set(i, va.Get());
2089 }
2090
AddArray(const ValueArray & va)2091 void ArrayCtrl::AddArray(const ValueArray& va)
2092 {
2093 SetArray(array.GetCount(), va);
2094 }
2095
2096 struct ArrayCtrlSeparatorDisplay : Display {
PaintUpp::ArrayCtrlSeparatorDisplay2097 virtual void Paint(Draw& w, const Rect& r, const Value& q, Color ink, Color paper, dword style) const {
2098 int y = r.top + r.GetHeight() / 2;
2099 w.DrawRect(r, paper);
2100 w.DrawRect(r.left, y, r.GetWidth(), 1, SColorShadow());
2101 w.DrawRect(r.left, y + 1, r.GetWidth(), 1, SColorLight());
2102 }
GetStdSizeUpp::ArrayCtrlSeparatorDisplay2103 virtual Size GetStdSize(const Value& q) const {
2104 return Size(0, 2);
2105 }
2106 };
2107
AddSeparator()2108 void ArrayCtrl::AddSeparator()
2109 {
2110 int ii = GetCount();
2111 Add();
2112 SetLineCy(ii, Draw::GetStdFontCy() / 2);
2113 for(int i = 0; i < GetColumnCount(); i++)
2114 SetDisplay(ii, i, Single<ArrayCtrlSeparatorDisplay>());
2115 DisableLine(ii);
2116 }
2117
2118 //$-
2119 #define E__Addv(I) Set0(q, I - 1, p##I)
2120 #define E__AddF(I) \
2121 void ArrayCtrl::Add(__List##I(E__Value)) { \
2122 int q = array.GetCount(); \
2123 __List##I(E__Addv); \
2124 AfterSet(q); \
2125 }
__Expand(E__AddF)2126 __Expand(E__AddF)
2127 //$+
2128
2129 void ArrayCtrl::Insert(int i, int count) {
2130 if(i < array.GetCount()) {
2131 array.InsertN(i, count);
2132 for(int j = 0; j < column.GetCount(); j++)
2133 column[j].InsertCache(i, count);
2134 }
2135 else
2136 array.At(i + count - 1);
2137 if(i <= cursor) cursor++;
2138 if(i < cellinfo.GetCount())
2139 cellinfo.InsertN(i, count);
2140 if(i < ln.GetCount()) {
2141 int y = ln[i].y;
2142 ln.InsertN(i, count);
2143 Reline(i, y);
2144 }
2145 if(virtualcount >= 0) virtualcount += count;
2146 while(count--) {
2147 for(int ii = 0; ii < idx.GetCount(); ii++) {
2148 Value v = idx[ii].GetInsertValue();
2149 if(!IsNull(v))
2150 Set(i, ii, v);
2151 }
2152 i++;
2153 }
2154 Refresh();
2155 SetSb();
2156 PlaceEdits();
2157 SyncCtrls();
2158 SyncInfo();
2159 WhenArrayAction();
2160 }
2161
Insert(int i)2162 void ArrayCtrl::Insert(int i)
2163 {
2164 Insert(i, 1);
2165 }
2166
Insert(int i,const Vector<Value> & v)2167 void ArrayCtrl::Insert(int i, const Vector<Value>& v) {
2168 Insert(i);
2169 Set(i, v);
2170 }
2171
Insert(int i,const Vector<Vector<Value>> & v)2172 void ArrayCtrl::Insert(int i, const Vector< Vector<Value> >& v) {
2173 Insert(i, v.GetCount());
2174 for(int ii = 0; ii < v.GetCount(); ii++)
2175 Set(i++, v[ii]);
2176 }
2177
Remove(int i)2178 void ArrayCtrl::Remove(int i) {
2179 int c = cursor;
2180 if(i == cursor) CancelCursor();
2181 Remove0(i);
2182 if(c >= 0)
2183 SetCursor0(c - (i < c), false);
2184 anchor = -1;
2185 PlaceEdits();
2186 SyncCtrls();
2187 SetSb();
2188 Refresh();
2189 SyncInfo();
2190 }
2191
PlaceEdits()2192 void ArrayCtrl::PlaceEdits() {
2193 for(int i = 0; i < column.GetCount(); i++) {
2194 Column& m = column[i];
2195 if(m.edit) {
2196 m.edit->Show(editmode && header.IsTabVisible(i));
2197 if(m.edit->IsVisible())
2198 m.edit->SetRect(GetCellRectM(cursor, i));
2199 }
2200 }
2201 }
2202
IsEditing() const2203 bool ArrayCtrl::IsEditing() const {
2204 for(int i = 0; i < column.GetCount(); i++)
2205 if(column[i].edit) return true;
2206 return false;
2207 }
2208
StartEdit(int d)2209 bool ArrayCtrl::StartEdit(int d) {
2210 if(IsReadOnly()) return false;
2211 if(!IsEditing()) return false;
2212 editmode = true;
2213 PlaceEdits();
2214 for(int i = 0; i < column.GetCount(); i++) {
2215 Column& m = column[(i + d) % column.GetCount()];
2216 if(m.edit && (m.edit->SetWantFocus() || IterateFocusForward(m.edit, m.edit)))
2217 break;
2218 }
2219 NoWantFocus();
2220 RefreshRow(cursor);
2221 WhenStartEdit();
2222 SyncInfo();
2223 return true;
2224 }
2225
EndEdit()2226 void ArrayCtrl::EndEdit() {
2227 if(!IsEditing())
2228 return;
2229 bool f = HasFocusDeep();
2230 editmode = false;
2231 insertmode = false;
2232 PlaceEdits();
2233 WantFocus();
2234 if(f) SetWantFocus();
2235 SyncInfo();
2236 }
2237
LostFocus()2238 void ArrayCtrl::LostFocus() {
2239 if(GetSelectCount() > 1)
2240 Refresh();
2241 else
2242 if(IsCursor())
2243 RefreshRow(cursor);
2244 SyncInfo();
2245 }
2246
GotFocus()2247 void ArrayCtrl::GotFocus() {
2248 if(GetSelectCount() > 1)
2249 Refresh();
2250 else
2251 if(IsCursor())
2252 RefreshRow(cursor);
2253 else
2254 if(focussetcursor)
2255 GoBegin();
2256 SetCursorEditFocus();
2257 SyncInfo();
2258 }
2259
DoEdit()2260 void ArrayCtrl::DoEdit() {
2261 if(IsReadOnly()) return;
2262 if(!editmode && IsCursor())
2263 StartEdit();
2264 }
2265
DoInsert(int c)2266 void ArrayCtrl::DoInsert(int c) {
2267 if(IsReadOnly()) return;
2268 Insert(c);
2269 SetCursor(c);
2270 insertmode = true;
2271 DoEdit();
2272 if(!IsEdit())
2273 insertmode = false;
2274 WhenArrayAction();
2275 }
2276
DoInsertBefore()2277 void ArrayCtrl::DoInsertBefore() {
2278 DoInsert(IsCursor() ? GetCursor() : array.GetCount());
2279 }
2280
DoInsertAfter()2281 void ArrayCtrl::DoInsertAfter() {
2282 DoInsert(IsCursor() ? GetCursor() + 1 : array.GetCount());
2283 }
2284
DoAppend()2285 void ArrayCtrl::DoAppend() {
2286 DoInsert(array.GetCount());
2287 }
2288
DoSelectAll()2289 void ArrayCtrl::DoSelectAll()
2290 {
2291 Select(0, GetCount());
2292 }
2293
RowFormat(const char * s)2294 String ArrayCtrl::RowFormat(const char *s)
2295 {
2296 return Sprintf(s, ~row_name);
2297 }
2298
DoRemove()2299 bool ArrayCtrl::DoRemove()
2300 {
2301 if(IsReadOnly()) return false;
2302 if(!IsCursor() || askremove && !PromptOKCancel(RowFormat(t_("Do you really want to delete the selected %s ?"))))
2303 return false;
2304 if(multiselect) {
2305 Bits sel;
2306 for(int i = 0; i < GetCount(); i++)
2307 sel.Set(i, IsSelected(i));
2308 for(int i = GetCount() - 1; i >= 0; i--)
2309 if(sel[i])
2310 Remove(i);
2311 }
2312 else
2313 Remove(cursor);
2314 WhenArrayAction();
2315 return true;
2316 }
2317
DoRemovem()2318 void ArrayCtrl::DoRemovem()
2319 {
2320 DoRemove();
2321 }
2322
DoDuplicate()2323 void ArrayCtrl::DoDuplicate() {
2324 if(IsReadOnly()) return;
2325 if(!IsCursor()) return;
2326 int c = cursor;
2327 if(!KillCursor()) return;
2328 Vector<Value> va = ReadRow(c);
2329 c = IsAppending() ? array.GetCount() : c + 1;
2330 Insert(c, va);
2331 SetCursor(c);
2332 DoEdit();
2333 WhenArrayAction();
2334 }
2335
StdBar(Bar & menu)2336 void ArrayCtrl::StdBar(Bar& menu)
2337 {
2338 bool e = IsEditable();
2339 bool c = !IsEdit() && e;
2340 bool d = c && IsCursor();
2341 if(inserting)
2342 menu.Add(e, RowFormat(t_("Insert %s")), THISBACK(DoInsertBefore))
2343 .Help(RowFormat(t_("Insert a new %s into the table.")))
2344 .Key(K_INSERT);
2345 if(bains == 1) {
2346 menu.Add(e, RowFormat(t_("Insert %s before")), THISBACK(DoInsertBefore))
2347 .Help(RowFormat(t_("Insert a new %s into the table before current")))
2348 .Key(K_INSERT);
2349 menu.Add(e, RowFormat(t_("Insert %s after")), THISBACK(DoInsertAfter))
2350 .Help(RowFormat(t_("Insert a new %s into the table after current")))
2351 .Key(K_SHIFT_INSERT);
2352 }
2353 if(bains == 2) {
2354 menu.Add(e, RowFormat(t_("Insert %s after")), THISBACK(DoInsertAfter))
2355 .Help(RowFormat(t_("Insert a new %s into the table after current")))
2356 .Key(K_INSERT);
2357 menu.Add(e, RowFormat(t_("Insert %s before")), THISBACK(DoInsertBefore))
2358 .Help(RowFormat(t_("Insert a new %s into the table before current")))
2359 .Key(K_SHIFT_INSERT);
2360 }
2361 if(IsAppending())
2362 menu.Add(c, RowFormat(t_("Append %s")), THISBACK(DoAppend))
2363 .Help(RowFormat(t_("Append a new %s at the end of the table.")))
2364 .Key(inserting ? (int)K_SHIFT_INSERT : (int)K_INSERT);
2365 if(duplicating)
2366 menu.Add(d, RowFormat(t_("Duplicate %s")), THISBACK(DoDuplicate))
2367 .Help(RowFormat(t_("Duplicate current table %s.")))
2368 .Key(K_CTRL_INSERT);
2369 if(IsEditing())
2370 menu.Add(d, RowFormat(t_("Edit %s")), THISBACK(DoEdit))
2371 .Help(RowFormat(t_("Edit active %s.")))
2372 .Key(K_CTRL_ENTER);
2373 if(removing)
2374 menu.Add(d, RowFormat(t_("Delete %s\tDelete")), THISBACK(DoRemovem))
2375 .Help(RowFormat(t_("Delete active %s.")))
2376 .Key(K_DELETE);
2377 if(moving) {
2378 menu.Add(GetCursor() > 0, RowFormat(t_("Move %s up")), THISBACK(SwapUp))
2379 .Help(RowFormat(t_("Swap %s with previous thus moving it up.")))
2380 .Key(K_CTRL_UP);
2381 menu.Add(GetCursor() >= 0 && GetCursor() < GetCount() - 1,
2382 RowFormat(t_("Move %s down")), THISBACK(SwapDown))
2383 .Help(RowFormat(t_("Swap %s with next thus moving it down.")))
2384 .Key(K_CTRL_DOWN);
2385 }
2386 if(multiselect) {
2387 menu.Add(GetCount() > 0, RowFormat(t_("Select all")), CtrlImg::select_all(), THISBACK(DoSelectAll))
2388 .Help(t_("Select all table rows"))
2389 .Key(K_CTRL_A);
2390 }
2391 }
2392
Find(const Value & v,int ii,int i) const2393 int ArrayCtrl::Find(const Value& v, int ii, int i) const {
2394 ASSERT(ii >= 0);
2395 int n = GetCount();
2396 while(i < n) {
2397 if(IsLineEnabled(i) && Get(i, ii) == v) return i;
2398 i++;
2399 }
2400 return -1;
2401 }
2402
Find(const Value & v,const Id & id,int i) const2403 int ArrayCtrl::Find(const Value& v, const Id& id, int i) const {
2404 return Find(v, GetPos(id), i);
2405 }
2406
FindSetCursor(const Value & val,int ii,int i)2407 bool ArrayCtrl::FindSetCursor(const Value& val, int ii, int i) {
2408 i = Find(val, ii, i);
2409 if(i < 0) return false;
2410 if(!SetCursor(i)) return false;
2411 return true;
2412 }
2413
FindSetCursor(const Value & val,const Id & id,int i)2414 bool ArrayCtrl::FindSetCursor(const Value& val, const Id& id, int i) {
2415 return FindSetCursor(val, idx.Find(id), i);
2416 }
2417
ClearCache()2418 void ArrayCtrl::ClearCache() {
2419 for(int i = 0; i < column.GetCount(); i++)
2420 column[i].ClearCache();
2421 }
2422
2423 struct ArrayCtrl::SortPredicate {
2424 const ArrayCtrl::Order *order;
operator ()Upp::ArrayCtrl::SortPredicate2425 bool operator()(const Line& a, const Line& b) const {
2426 return order->operator()(a.line, b.line);
2427 }
2428 };
2429
SortA()2430 void ArrayCtrl::SortA()
2431 {
2432 if(hasctrls) {
2433 for(int i = 0; i < array.GetCount(); i++)
2434 for(int j = 0; j < column.GetCount(); j++)
2435 if(IsCtrl(i, j)) {
2436 const Column& m = column[j];
2437 ASSERT(m.pos.GetCount() == 1);
2438 array[i].line.At(m.pos[0]) = GetCellCtrl(i, j).ctrl->GetData();
2439 }
2440 }
2441 }
2442
SortB(const Vector<int> & o)2443 void ArrayCtrl::SortB(const Vector<int>& o)
2444 {
2445 Vector<Line> narray;
2446 narray.SetCount(array.GetCount());
2447 Vector<Ln> nln;
2448 Vector< Vector<CellInfo> > ncellinfo;
2449 for(int i = 0; i < o.GetCount(); i++) {
2450 int oi = o[i];
2451 narray[i] = pick(array[oi]);
2452 if(oi < cellinfo.GetCount())
2453 ncellinfo.At(i) = pick(cellinfo[oi]);
2454 if(oi < ln.GetCount())
2455 nln.At(i) = ln[oi];
2456 }
2457 array = pick(narray);
2458 cellinfo = pick(ncellinfo);
2459 ln = pick(nln);
2460 Reline(0, 0);
2461 if(hasctrls) {
2462 for(int i = 0; i < array.GetCount(); i++)
2463 for(int j = 0; j < column.GetCount(); j++)
2464 if(IsCtrl(i, j)) {
2465 const Column& m = column[j];
2466 ASSERT(m.pos.GetCount() == 1);
2467 array[i].line.At(m.pos[0]) = Null;
2468 }
2469 SyncCtrls();
2470 ChildGotFocus();
2471 }
2472 }
2473
ReArrange(const Vector<int> & order)2474 void ArrayCtrl::ReArrange(const Vector<int>& order)
2475 {
2476 KillCursor();
2477 ClearSelection();
2478 ClearCache();
2479 SortA();
2480 SortB(order);
2481 Refresh();
2482 SyncInfo();
2483 }
2484
Sort(int from,int count,Gate<int,int> order)2485 void ArrayCtrl::Sort(int from, int count, Gate<int, int> order)
2486 {
2487 KillCursor();
2488 ClearSelection();
2489 ClearCache();
2490 Vector<int> h;
2491 for(int i = 0; i < array.GetCount(); i++)
2492 h.Add(i);
2493 SortA();
2494 CoStableSort(SubRange(h, from, count).Write(), order);
2495 SortB(h);
2496 Refresh();
2497 SyncInfo();
2498 }
2499
Sort(Gate<int,int> order)2500 void ArrayCtrl::Sort(Gate<int, int> order)
2501 {
2502 if(sorting_from < array.GetCount())
2503 Sort(sorting_from, array.GetCount() - sorting_from, order);
2504 }
2505
OrderPred(int i1,int i2,const ArrayCtrl::Order * o)2506 bool ArrayCtrl::OrderPred(int i1, int i2, const ArrayCtrl::Order *o)
2507 {
2508 return (*o)(array[i1].line, array[i2].line);
2509 }
2510
Sort(int from,int count,const ArrayCtrl::Order & order)2511 void ArrayCtrl::Sort(int from, int count, const ArrayCtrl::Order& order)
2512 {
2513 Sort(from, count, THISBACK1(OrderPred, &order));
2514 }
2515
Sort(const ArrayCtrl::Order & order)2516 void ArrayCtrl::Sort(const ArrayCtrl::Order& order)
2517 {
2518 if(sorting_from < array.GetCount())
2519 Sort(sorting_from, array.GetCount() - sorting_from, order);
2520 }
2521
2522 struct sAC_ColumnSort : public ValueOrder {
2523 bool descending;
2524 const ValueOrder *order;
2525 Function<int (const Value& a, const Value& b)> cmp;
2526
operator ()Upp::sAC_ColumnSort2527 virtual bool operator()(const Value& a, const Value& b) const {
2528 return descending ? order ? (*order)(b, a) : cmp(b, a) < 0
2529 : order ? (*order)(a, b) : cmp(a, b) < 0;
2530 }
2531 };
2532
ColumnSortPred(int i1,int i2,int column,const ValueOrder * o)2533 bool ArrayCtrl::ColumnSortPred(int i1, int i2, int column, const ValueOrder *o)
2534 {
2535 return (*o)(GetConvertedColumn(i1, column), GetConvertedColumn(i2, column));
2536 }
2537
ColumnSort(int column,Gate<int,int> order)2538 void ArrayCtrl::ColumnSort(int column, Gate<int, int> order)
2539 {
2540 Value key = GetKey();
2541 CHECK(KillCursor());
2542 if(columnsortsecondary)
2543 Sort(*columnsortsecondary);
2544 Sort(order);
2545 if(columnsortfindkey)
2546 FindSetCursor(key);
2547 }
2548
ColumnSort(int column,const ValueOrder & order)2549 void ArrayCtrl::ColumnSort(int column, const ValueOrder& order)
2550 {
2551 ColumnSort(column, THISBACK2(ColumnSortPred, column, &order));
2552 }
2553
ColumnSort(int column,int (* compare)(const Value & a,const Value & b))2554 void ArrayCtrl::ColumnSort(int column, int (*compare)(const Value& a, const Value& b))
2555 {
2556 sAC_ColumnSort cs;
2557 cs.descending = false;
2558 cs.order = NULL;
2559 cs.cmp = compare;
2560 if(!cs.cmp)
2561 cs.cmp = StdValueCompare;
2562 ColumnSort(column, cs);
2563 }
2564
SetSortColumn(int ii,bool desc)2565 void ArrayCtrl::SetSortColumn(int ii, bool desc)
2566 {
2567 sortcolumn = ii;
2568 sortcolumndescending = desc;
2569 DoColumnSort();
2570 }
2571
ToggleSortColumn(int ii)2572 void ArrayCtrl::ToggleSortColumn(int ii)
2573 {
2574 if(sortcolumn == ii)
2575 sortcolumndescending = !sortcolumndescending;
2576 else {
2577 sortcolumn = ii;
2578 sortcolumndescending = false;
2579 }
2580 DoColumnSort();
2581 }
2582
DoColumnSort()2583 void ArrayCtrl::DoColumnSort()
2584 {
2585 if(sortcolumn < 0) {
2586 sortcolumndescending = false;
2587 for(int i = 0; i < column.GetCount(); i++)
2588 if(column[i].order || column[i].cmp) {
2589 sortcolumn = i;
2590 break;
2591 }
2592 }
2593 if(sortcolumn < 0)
2594 return;
2595 for(int i = 0; i < header.GetCount(); i++)
2596 header.Tab(i).SetRightImage(header.GetTabIndex(i) == sortcolumn ?
2597 sortcolumndescending ? CtrlImg::SortUp()
2598 : CtrlImg::SortDown()
2599 : Image());
2600 if(sortcolumn >= 0 && sortcolumn < column.GetCount()) {
2601 sAC_ColumnSort cs;
2602 const Column& c = column[sortcolumn];
2603 if(c.line_order)
2604 ColumnSort(sortcolumn, c.line_order);
2605 else {
2606 cs.descending = sortcolumndescending;
2607 cs.order = c.order;
2608 cs.cmp = c.cmp;
2609 if(!cs.order && !cs.cmp)
2610 cs.cmp = StdValueCompare;
2611 ColumnSort(sortcolumn, cs);
2612 }
2613 }
2614 WhenColumnSorted();
2615 }
2616
AllSorting()2617 ArrayCtrl& ArrayCtrl::AllSorting()
2618 {
2619 allsorting = true;
2620 for(int i = 0; i < column.GetCount(); i++)
2621 column[i].Sorting();
2622 return *this;
2623 }
2624
2625 struct ArrayCtrl::RowOrder : public ArrayCtrl::Order {
2626 int (*compare)(const Vector<Value>& v1, const Vector<Value>& v2);
2627
operator ()Upp::ArrayCtrl::RowOrder2628 virtual bool operator()(const Vector<Value>& row1, const Vector<Value>& row2) const {
2629 return (*compare)(row1, row2) < 0;
2630 }
2631 };
2632
Sort(int from,int count,int (* compare)(const Vector<Value> & v1,const Vector<Value> & v2))2633 void ArrayCtrl::Sort(int from, int count, int (*compare)(const Vector<Value>& v1, const Vector<Value>& v2))
2634 {
2635 RowOrder io;
2636 io.compare = compare;
2637 Sort(from, count, io);
2638 }
2639
Sort(int (* compare)(const Vector<Value> & v1,const Vector<Value> & v2))2640 void ArrayCtrl::Sort(int (*compare)(const Vector<Value>& v1, const Vector<Value>& v2)) {
2641 Sort(0, GetCount(), compare);
2642 }
2643
2644 struct ArrayCtrl::ItemOrder : public ArrayCtrl::Order {
2645 int ii;
2646 int (*compare)(const Value& v1, const Value& v2);
2647
operator ()Upp::ArrayCtrl::ItemOrder2648 virtual bool operator()(const Vector<Value>& row1, const Vector<Value>& row2) const {
2649 return (*compare)(row1[ii], row2[ii]) < 0;
2650 }
2651 };
2652
Sort(int ii,int (* compare)(const Value & v1,const Value & v2))2653 void ArrayCtrl::Sort(int ii, int (*compare)(const Value& v1, const Value& v2)) {
2654 ItemOrder io;
2655 io.ii = ii;
2656 io.compare = compare;
2657 Sort(io);
2658 }
2659
Sort(const Id & id,int (* compare)(const Value & v1,const Value & v2))2660 void ArrayCtrl::Sort(const Id& id, int (*compare)(const Value& v1, const Value& v2)) {
2661 Sort(idx.Find(id), compare);
2662 }
2663
Clear()2664 void ArrayCtrl::Clear() {
2665 EndEdit();
2666 if(cursor >= 0) {
2667 WhenKillCursor();
2668 cursor = -1;
2669 info.Cancel();
2670 }
2671 array.Clear();
2672 if(cursor >= 0) {
2673 WhenCursor();
2674 WhenSel();
2675 }
2676 cellinfo.Clear();
2677 ln.Clear();
2678 ClearCache();
2679 DisableCtrls();
2680 virtualcount = -1;
2681 anchor = -1;
2682 SetSb();
2683 Refresh();
2684 SyncInfo();
2685 }
2686
Shrink()2687 void ArrayCtrl::Shrink() {
2688 array.Shrink();
2689 cellinfo.Shrink();
2690 ln.Shrink();
2691 }
2692
SerializeSettings(Stream & s)2693 void ArrayCtrl::SerializeSettings(Stream& s)
2694 {
2695 int version = 0;
2696 s / version;
2697 header.Serialize(s);
2698 s % sortcolumn % sortcolumndescending;
2699 if(s.IsLoading())
2700 DoColumnSort();
2701 }
2702
Serialize(Stream & s)2703 void ArrayCtrl::Serialize(Stream& s)
2704 {
2705 }
2706
Reset()2707 void ArrayCtrl::Reset() {
2708 header.Reset();
2709 idx.Clear();
2710 id_ndx.Clear();
2711 id_ndx.Add(Id());
2712 column.Clear();
2713 control.Clear();
2714 nocursor = false;
2715 vertgrid = horzgrid = true;
2716 mousemove = false;
2717 inserting = false;
2718 popupex = true;
2719 appending = false;
2720 removing = false;
2721 askremove = true;
2722 duplicating = false;
2723 moving = false;
2724 appendline = false;
2725 noinsertappend = false;
2726 editmode = false;
2727 insertmode = false;
2728 multiselect = false;
2729 selectiondirty = false;
2730 hasctrls = false;
2731 headerctrls = false;
2732 nobg = false;
2733 selectcount = 0;
2734 bains = 0;
2735 row_name = t_("row");
2736 gridcolor = SColorShadow;
2737 autoappend = false;
2738 focussetcursor = true;
2739 sortcolumn = -1;
2740 allsorting = false;
2741 acceptingrow = 0;
2742 columnsortfindkey = false;
2743 spanwidecells = false;
2744 linecy = Draw::GetStdFontCy();
2745 Clear();
2746 sb.SetLine(linecy);
2747 columnsortsecondary = NULL;
2748 sorting_from = 0;
2749 min_visible_line = 0;
2750 max_visible_line = INT_MAX;
2751 ctrl_low = ctrl_high = 0;
2752 }
2753
CancelMode()2754 void ArrayCtrl::CancelMode()
2755 {
2756 isdrag = false;
2757 selclick = false;
2758 dropline = dropcol = -1;
2759 info.Cancel();
2760 }
2761
MouseWheel(Point p,int zdelta,dword keyflags)2762 void ArrayCtrl::MouseWheel(Point p, int zdelta, dword keyflags) {
2763 sb.Wheel(zdelta);
2764 }
2765
ReadRow(int i) const2766 Vector<Value> ArrayCtrl::ReadRow(int i) const {
2767 Vector<Value> v;
2768 int n = max(idx.GetCount(), i < array.GetCount() ? array[i].line.GetCount() : 0);
2769 for(int j = 0; j < n; j++)
2770 v.Add(Get(i, j));
2771 return v;
2772 }
2773
Move(int dir)2774 void ArrayCtrl::Move(int dir) {
2775 int c = GetCursor();
2776 int d = c + dir;
2777 if(c < 0 || c >= GetCount() || d < 0 || d >= GetCount()) return;
2778 Vector<Value> row(ReadRow(c), 0);
2779 Set(c, ReadRow(d));
2780 Set(d, row);
2781 SetCursor(d);
2782 WhenArrayAction();
2783 }
2784
SwapUp()2785 void ArrayCtrl::SwapUp() {
2786 Move(-1);
2787 }
2788
SwapDown()2789 void ArrayCtrl::SwapDown() {
2790 Move(1);
2791 }
2792
ColumnWidths(const char * s)2793 ArrayCtrl& ArrayCtrl::ColumnWidths(const char *s)
2794 {
2795 Vector<String> w = Split(s, ' ');
2796 for(int i = 0; i < min(w.GetCount(), header.GetCount()); i++) {
2797 int q = header.FindIndex(i);
2798 if(q >= 0)
2799 header.SetTabRatio(q, atoi(w[i]));
2800 }
2801 return *this;
2802 }
2803
GetColumnWidths()2804 String ArrayCtrl::GetColumnWidths()
2805 {
2806 String text;
2807 for(int i = 0; i < header.GetCount(); i++)
2808 text << Format(i ? " %d" : "%d",
2809 header.GetMode() == HeaderCtrl::SCROLL ? (int)header[i].GetRatio()
2810 : header.GetTabWidth(i));
2811 return text;
2812 }
2813
OddRowColor(Color paper,Color ink)2814 ArrayCtrl& ArrayCtrl::OddRowColor(Color paper, Color ink)
2815 {
2816 oddpaper = paper;
2817 oddink = ink;
2818 Refresh();
2819 SyncInfo();
2820 return *this;
2821 }
2822
EvenRowColor(Color paper,Color ink)2823 ArrayCtrl& ArrayCtrl::EvenRowColor(Color paper, Color ink)
2824 {
2825 evenpaper = paper;
2826 evenink = ink;
2827 Refresh();
2828 SyncInfo();
2829 return *this;
2830 }
2831
RefreshSel()2832 void ArrayCtrl::RefreshSel()
2833 {
2834 Size sz = GetSize();
2835 int i = GetLineAt(sb);
2836 if(i >= 0) {
2837 int y = GetLineY(i) - sb;
2838 while(y < sz.cy && i < array.GetCount()) {
2839 if(IsSelected(i))
2840 RefreshRow(i);
2841 if(IsLineVisible(i))
2842 y += GetLineCy(i++);
2843 }
2844 }
2845 }
2846
DnD(int line,int col)2847 void ArrayCtrl::DnD(int line, int col)
2848 {
2849 if(dropline != line || dropcol != col) {
2850 RefreshRow(dropline - 1);
2851 RefreshRow(dropline);
2852 dropline = line;
2853 dropcol = col;
2854 RefreshRow(dropline - 1);
2855 RefreshRow(dropline);
2856 }
2857 }
2858
DnDInsert(int line,int py,PasteClip & d,int q)2859 bool ArrayCtrl::DnDInsert(int line, int py, PasteClip& d, int q)
2860 {
2861 int y = GetLineY(line);
2862 int cy = GetLineCy();
2863 if(py < y + cy / q) {
2864 WhenDropInsert(line, d);
2865 if(d.IsAccepted()) {
2866 DnD(line, DND_INSERTLINE);
2867 return true;
2868 }
2869 }
2870 if(py >= y + (q - 1) * cy / q && line < GetCount()) {
2871 WhenDropInsert(line + 1, d);
2872 if(d.IsAccepted()) {
2873 DnD(line + 1, DND_INSERTLINE);
2874 return true;
2875 }
2876 }
2877 return false;
2878 }
2879
DragAndDrop(Point p,PasteClip & d)2880 void ArrayCtrl::DragAndDrop(Point p, PasteClip& d)
2881 {
2882 if(IsReadOnly())
2883 return;
2884 if(!isdrag) {
2885 isdrag = true;
2886 RefreshSel();
2887 }
2888 int py = p.y + sb;
2889 int line = GetLineAt(py);
2890 int col = -1;
2891 if(line >= 0) {
2892 for(int i = 0; i < column.GetCount(); i++)
2893 if(GetCellRect(line, i).Contains(p)) {
2894 col = i;
2895 break;
2896 }
2897 if(col >= 0) {
2898 WhenDropCell(line, col, d);
2899 if(d.IsAccepted()) {
2900 DnD(line, col);
2901 return;
2902 }
2903 }
2904 if(DnDInsert(line, py, d, 4)) return;
2905 if(line >= 0) {
2906 WhenDropLine(line, d);
2907 if(d.IsAccepted()) {
2908 DnD(line, DND_LINE);
2909 return;
2910 }
2911 }
2912 if(DnDInsert(line, py, d, 2)) return;
2913 }
2914 int ci = GetCount();
2915 if(py < GetLineY(ci) + GetLineCy(ci) / 4 || !WhenDrop) {
2916 WhenDropInsert(ci, d);
2917 if(d.IsAccepted()) {
2918 DnD(GetCount(), DND_INSERTLINE);
2919 return;
2920 }
2921 }
2922 WhenDrop(d);
2923 DnD(-1, -1);
2924 }
2925
DragRepeat(Point p)2926 void ArrayCtrl::DragRepeat(Point p)
2927 {
2928 sb = sb + GetDragScroll(this, p, 16).y;
2929 }
2930
DragLeave()2931 void ArrayCtrl::DragLeave()
2932 {
2933 isdrag = false;
2934 RefreshSel();
2935 DnD(-1, -1);
2936 }
2937
RemoveSelection()2938 void ArrayCtrl::RemoveSelection()
2939 {
2940 int ci = cursor;
2941 KillCursor();
2942 for(int i = GetCount() - 1; i >= 0; i--)
2943 if(IsSel(i) || i == ci)
2944 Remove(i); // Optimize!
2945 }
2946
InsertDrop(int line,const Vector<Vector<Value>> & data,PasteClip & d,bool self)2947 void ArrayCtrl::InsertDrop(int line, const Vector< Vector<Value> >& data, PasteClip& d, bool self)
2948 {
2949 if(data.GetCount() == 0)
2950 return;
2951 if(d.GetAction() == DND_MOVE && self) {
2952 if(IsMultiSelect())
2953 for(int i = GetCount() - 1; i >= 0; i--) {
2954 if(IsSel(i)) {
2955 Remove(i); // Optimize!
2956 if(i < line)
2957 line--;
2958 }
2959 }
2960 else
2961 if(IsCursor()) {
2962 if(GetCursor() < line)
2963 line--;
2964 Remove(GetCursor());
2965 }
2966 KillCursor();
2967 d.SetAction(DND_COPY);
2968 }
2969 Insert(line, data);
2970 ClearSelection();
2971 SetCursor(line + data.GetCount() - 1);
2972 if(IsMultiSelect())
2973 Select(line, data.GetCount());
2974 }
2975
InsertDrop(int line,const Vector<Value> & data,PasteClip & d,bool self)2976 void ArrayCtrl::InsertDrop(int line, const Vector<Value>& data, PasteClip& d, bool self)
2977 {
2978 Vector< Vector<Value> > x;
2979 x.Add() <<= data;
2980 InsertDrop(line, x, d, self);
2981 }
2982
InsertDrop(int line,const Value & data,PasteClip & d,bool self)2983 void ArrayCtrl::InsertDrop(int line, const Value& data, PasteClip& d, bool self)
2984 {
2985 Vector<Value> x;
2986 x.Add(data);
2987 InsertDrop(line, x, d, self);
2988 }
2989
InsertDrop(int line,const ArrayCtrl & src,PasteClip & d)2990 void ArrayCtrl::InsertDrop(int line, const ArrayCtrl& src, PasteClip& d)
2991 {
2992 Vector< Vector<Value> > data;
2993 for(int i = 0; i < src.GetCount(); i++)
2994 if(src.IsSel(i))
2995 data.Add(src.GetLine(i));
2996 InsertDrop(line, data, d, &src == this);
2997 }
2998
InsertDrop(int line,PasteClip & d)2999 void ArrayCtrl::InsertDrop(int line, PasteClip& d)
3000 {
3001 InsertDrop(line, GetInternal<ArrayCtrl>(d), d);
3002 }
3003
AsText(String (* format)(const Value &),bool sel,const char * tab,const char * row,const char * hdrtab,const char * hdrrow) const3004 String ArrayCtrl::AsText(String (*format)(const Value&), bool sel,
3005 const char *tab, const char *row,
3006 const char *hdrtab, const char *hdrrow) const
3007 {
3008 String txt;
3009 if(hdrtab) {
3010 for(int i = 0; i < GetColumnCount(); i++) {
3011 if(i)
3012 txt << hdrtab;
3013 txt << (*format)(HeaderTab(i).GetText());
3014 }
3015 if(hdrrow)
3016 txt << hdrrow;
3017 }
3018 bool next = false;
3019 for(int r = 0; r < GetCount(); r++)
3020 if(!sel || IsSel(r)) {
3021 if(next)
3022 txt << row;
3023 for(int i = 0; i < GetColumnCount(); i++) {
3024 if(i)
3025 txt << tab;
3026 txt << (*format)(GetConvertedColumn(r, i));
3027 }
3028 next = true;
3029 }
3030 return txt;
3031 }
3032
SetClipboard(bool sel,bool hdr) const3033 void ArrayCtrl::SetClipboard(bool sel, bool hdr) const
3034 {
3035 ClearClipboard();
3036 AppendClipboardText(AsText(AsString, sel, "\t", "\r\n", hdr ? "\t" : NULL, "\r\n"));
3037 }
3038
sQtfFormat(const Value & v)3039 static String sQtfFormat(const Value& v)
3040 {
3041 return "\1" + AsString(v) + "\1";
3042 }
3043
AsQtf(bool sel,bool hdr)3044 String ArrayCtrl::AsQtf(bool sel, bool hdr)
3045 {
3046 String qtf;
3047 qtf << "{{";
3048 for(int i = 0; i < GetColumnCount(); i++) {
3049 if(i)
3050 qtf << ":";
3051 qtf << header.GetTabWidth(i);
3052 }
3053 if(hdr)
3054 qtf << "@L [*";
3055 return qtf << ' ' << AsText(sQtfFormat, sel, ":: ", ":: ", hdr ? ":: " : NULL, "::@W ]") << "}}";
3056 }
3057
sCsvFormat(const Value & v)3058 static String sCsvFormat(const Value& v)
3059 {
3060 return IsNumber(v) ? AsString(v) : CsvString(AsString(v));
3061 }
3062
AsCsv(bool sel,int sep,bool hdr)3063 String ArrayCtrl::AsCsv(bool sel, int sep, bool hdr)
3064 {
3065 char h[2] = { (char)sep, 0 };
3066 return AsText(sCsvFormat, sel, h, "\r\n", hdr ? h : NULL, "\r\n");
3067 }
3068
ArrayCtrl()3069 ArrayCtrl::ArrayCtrl() {
3070 cursor = -1;
3071 Reset();
3072 AddFrame(sb);
3073 AddFrame(header);
3074 header.WhenLayout = THISBACK(HeaderLayout);
3075 header.WhenScroll = THISBACK(HeaderScroll);
3076 sb.WhenScroll = THISBACK(Scroll);
3077 header.Moving();
3078 WhenAcceptRow = [] { return true; };
3079 WhenBar = THISBACK(StdBar);
3080 SetFrame(ViewFrame());
3081 oddpaper = evenpaper = SColorPaper;
3082 oddink = evenink = SColorText;
3083 }
3084
~ArrayCtrl()3085 ArrayCtrl::~ArrayCtrl() {}
3086
ArrayOption()3087 ArrayOption::ArrayOption()
3088 {
3089 array = NULL;
3090 f = 0;
3091 t = 1;
3092 hswitch = vswitch = false;
3093 threestate = false;
3094 frame = true;
3095 }
3096
~ArrayOption()3097 ArrayOption::~ArrayOption()
3098 {
3099 }
3100
Click()3101 void ArrayOption::Click()
3102 {
3103 if(!array)
3104 return;
3105 int c;
3106 for(c = index.GetCount(); --c >= 0;)
3107 if(array->FindColumnWithPos(index[c]) == array->GetClickColumn())
3108 break;
3109 int cr = array->GetCursor();
3110 if(c >= 0 && cr >= 0) {
3111 Value v = array->Get(index[c]);
3112 if(!IsNull(v) && v != t && v != f) {
3113 BeepExclamation();
3114 return;
3115 }
3116 if(hswitch)
3117 for(int i = 0; i < index.GetCount(); i++)
3118 if(i != c)
3119 array->Set(index[i], f);
3120 array->Set(index[c], threestate && v == t
3121 ? Value() : v != t && !(threestate && IsNull(v)) ? t : f);
3122 if(vswitch) {
3123 for(int r = 0; r < array->GetCount(); r++)
3124 if(r != cr) {
3125 if(hswitch)
3126 for(int i = 0; i < index.GetCount(); i++)
3127 if(i != c)
3128 array->Set(r, index[i], f);
3129 array->Set(r, index[c], f);
3130 }
3131 }
3132 WhenAction();
3133 array->Action();
3134 array->WhenArrayAction();
3135 }
3136 }
3137
Disconnect()3138 void ArrayOption::Disconnect()
3139 {
3140 array = NULL;
3141 index.Clear();
3142 }
3143
Connect(ArrayCtrl & a,int i)3144 void ArrayOption::Connect(ArrayCtrl& a, int i)
3145 {
3146 array = &a;
3147 if(index.IsEmpty()) {
3148 a.WhenLeftClick << THISBACK(Click);
3149 a.WhenLeftDouble << THISBACK(Click);
3150 }
3151 index.Add(i);
3152 }
3153
AddColumn(ArrayCtrl & ac,const char * text,int w)3154 ArrayCtrl::Column& ArrayOption::AddColumn(ArrayCtrl& ac, const char *text, int w)
3155 {
3156 return AddColumn(ac, Id(), text, w).NoClickEdit();
3157 }
3158
AddColumn(ArrayCtrl & ac,const Id & id,const char * text,int w)3159 ArrayCtrl::Column& ArrayOption::AddColumn(ArrayCtrl& ac, const Id& id, const char *text, int w)
3160 {
3161 Connect(ac, ac.GetIndexCount());
3162 return ac.AddColumn(id, text, w).SetDisplay(*this).InsertValue(f).NoClickEdit();
3163 }
3164
Paint(Draw & w,const Rect & r,const Value & q,Color ink,Color paper,dword style) const3165 void ArrayOption::Paint(Draw& w, const Rect& r, const Value& q,
3166 Color ink, Color paper, dword style) const
3167 {
3168 bool gray = (array && !array->IsEnabled());
3169 w.DrawRect(r, paper);
3170
3171 int st = (gray ? CTRL_DISABLED : CTRL_NORMAL);
3172 int g = threestate && (q != t && q != f) ? CtrlsImg::I_O2 : q == t ? CtrlsImg::I_O1
3173 : CtrlsImg::I_O0;
3174
3175 Image img = CtrlsImg::Get(g + st);
3176
3177 Size crsz = min(img.GetSize(), r.Size());
3178 Rect cr = r.CenterRect(crsz);
3179
3180 Point p = cr.CenterPos(img.GetSize());
3181 w.DrawImage(p.x, p.y, img);
3182 }
3183
3184 }
3185