1 #include <GridCtrl/GridCtrl.h>
2 
3 namespace Upp {
4 
5 #ifdef COMPILER_MSC
6 #pragma warning(disable: 4355)
7 #endif
8 
9 #define TFILE <GridCtrl/GridCtrl.t>
10 #include <Core/t.h>
11 
12 bool GridCtrl::index_as_column = false;
13 bool GridCtrl::reverse_sort_icon = false;
14 
GridCtrl()15 GridCtrl::GridCtrl() : holder(*this)
16 {
17 	sortCol = -1;
18 	hcol = -1;
19 	hrow = -1;
20 
21 	fixed_click = false;
22 	fixed_top_click = false;
23 	fixed_left_click = false;
24 	size_changed = true;
25 
26 	resize_panel_open = false;
27 
28 	synced = false;
29 	sc_fr = -1;
30 	sc_lr = -1;
31 
32 	resizeCol = false;
33 	resizeRow = false;
34 
35 	ready = false;
36 	doscroll = true;
37 	firstRow = lastRow = -1;
38 	firstCol = lastCol = -1;
39 
40 	firstVisCol = -1;
41 	lastVisCol  = -1;
42 	firstVisRow = -1;
43 	lastVisRow  = -1;
44 
45 	colidx = -1;
46 	rowidx = -1;
47 	rowfnd = -1;
48 
49 	GD_COL_WIDTH  = 50;
50 	GD_ROW_HEIGHT = Draw::GetStdFontCy() + 5;
51 	GD_HDR_HEIGHT = GD_ROW_HEIGHT + 2;
52 	GD_IND_WIDTH  = 9;
53 
54 	display = new GridDisplay();
55 	display->SetTextAlign(GD::VCENTER);
56 	orgdisp = display;
57 
58 	sbx.Horz();
59 	sby.Vert();
60 	sbx.WhenScroll = THISBACK(Scroll);
61 	sby.WhenScroll = THISBACK(Scroll);
62 	sbx.SetLine(5);
63 	sby.SetLine(GridCtrl::GD_ROW_HEIGHT);
64 
65 	fixed_cols = 1;
66 	fixed_rows = 1;
67 
68 	total_cols = 0;
69 	total_rows = 0;
70 
71 	minRowSelected = -1;
72 	maxRowSelected = -1;
73 
74 	bains = 0;
75 	coluid = 0;
76 	rowuid = 0;
77 
78 	close.SetLabel(t_("Close"));
79 	close <<= THISBACK(CloseGrid);
80 
81 	oldpos.Clear();
82 
83 	indicator              = false;
84 	resizing_cols          = true;
85 	resizing_rows          = true;
86 	resizing_fixed_cols    = true;
87 	resizing_fixed_rows    = false;
88 	resize_paint_mode      = 2;
89 	resize_col_mode        = 1;
90 	resize_row_mode        = 0;
91 	multi_select           = false;
92 	select_row             = true;
93 	moving_cols            = false;
94 	moving_rows            = false;
95 	dragging               = false;
96 	horz_grid              = true;
97 	vert_grid              = true;
98 	draw_last_horz_line    = true;
99 	draw_last_vert_line    = true;
100 	sorting                = false;
101 	live_cursor            = false;
102 	row_changing           = true;
103 	edit_mode              = GE_ROW;
104 	one_click_edit         = false;
105 	goto_first_edit        = true;
106 	coloring_mode          = 0;
107 	isedit                 = false;
108 	genr_ctrls             = 0;
109 	edit_ctrls             = false;
110 	sorting                = true;
111 	sorting_multicol       = true;
112 	header                 = true;
113 
114 	cancel_update_cell     = false;
115 	cancel_update          = false;
116 	cancel_insert          = false;
117 	cancel_remove          = false;
118 	cancel_accept          = false;
119 	cancel_duplicate       = false;
120 	cancel_cursor          = false;
121 	cancel_move            = false;
122 
123 	inserting              = false;
124 	appending              = false;
125 	duplicating            = false;
126 	removing               = false;
127 	accepting              = false;
128 	canceling              = false;
129 	moving                 = false;
130 	navigating             = false;
131 	searching              = false;
132 	editing                = false;
133 	edits_in_new_row       = true;
134 	closing                = false;
135 	hiding                 = false;
136 	clipboard              = false;
137 	extra_paste            = true;
138 	fixed_paste            = false;
139 	copy_allowed           = true;
140 	cut_allowed            = true;
141 	paste_allowed          = true;
142 	copy_column_names      = false;
143 	draw_focus             = false;
144 	ask_remove             = false;
145 
146 	search_hide            = true;
147 	search_highlight       = true;
148 	search_highlight_first = false;
149 	search_immediate       = true;
150 	search_case            = false;
151 	search_move_cursor     = true;
152 	search_display         = true;
153 
154 	row_order              = false;
155 	row_data               = false;
156 
157 	reject_null_row        = true;
158 	tab_changes_row        = true;
159 	tab_adds_row           = false;
160 	enter_like_tab         = false;
161 	keep_last_row          = false;
162 	remove_hides           = false;
163 	full_col_resizing      = true;
164 	full_row_resizing      = false;
165 	chameleon              = false;
166 	summary_row            = false;
167 	update_summary         = true;
168 	popups                 = true;
169 	focus_lost_accepting   = false;
170 
171 	mouse_move             = false;
172 	row_modified           = 0;
173 
174 	valid_cursor           = false;
175 
176 	curpos.x   = curpos.y  = -1;
177 	oldcur.x   = oldcur.y  = -1;
178 	curid.x    = curid.y   = -1;
179 	ctrlid.x   = ctrlid.y  = -1;
180 	ctrlpos.x  = ctrlpos.y = -1;
181 	osz.cx     = osz.cy    = -1;
182 	livecur.x  = livecur.y = -1;
183 	leftpnt.x  = leftpnt.y = -1;
184 	shiftpos.x = shiftpos.y = -1;
185 
186 	fixed_width  = 0;
187 	fixed_height = 0;
188 	total_width  = 0;
189 	total_height = 0;
190 	summary_height = 0;
191 
192 	ItemRect &ir = vitems.Add();
193 	ir.parent = this;
194 	ir.edits = &edits;
195 	items.Add();
196 
197 	/* add indicator, total_cols = 1 */
198 	AddColumn("", 0);
199 
200 	shiftmode = false;
201 	recalc_cols = false;
202 	recalc_rows = false;
203 	selected_rows = 0;
204 	selected_items = 0;
205 
206 	WhenMenuBar = THISBACK(StdMenuBar);
207 	WhenToolBar = THISBACK(StdToolBar);
208 
209 	find <<= THISBACK(DoFind);
210 
211 	StdAppend = THISBACK(DoAppend);
212 	StdRemove = THISBACK(DoRemove);
213 	StdInsert = THISBACK(DoInsertBefore);
214 	StdDuplicate = THISBACK(DoDuplicate);
215 	StdEdit = THISBACK(DoEdit);
216 
217 	newrow_inserted = false;
218 	newrow_appended = false;
219 	row_removed = false;
220 	just_clicked = false;
221 
222 	call_whenchangecol = true;
223 	call_whenchangerow = true;
224 	call_whenremoverow = true;
225 	call_whenupdaterow = true;
226 	call_wheninsertrow = true;
227 
228 	sel_begin = false;
229 	sel_end = false;
230 
231 	moving_header = false;
232 	moving_body = false;
233 	moving_allowed = false;
234 
235 	join_group = 0;
236 
237 	curSplitCol = oldSplitCol = -1;
238 	curSplitRow = oldSplitRow = -1;
239 
240 	moveCol = moveRow = -1;
241 	find_offset = 0;
242 
243 	scrollLeftRight = false;
244 
245 	fg_focus  = SColorHighlightText;
246 	bg_focus  = SColorHighlight;
247 	fg_select = Black;
248 	bg_select = Color(217, 198, 251);
249 	fg_live   = SColorText;
250 	bg_live   = IsDarkColorFace() ? Blend(SColorHighlight, Black, 132) : Blend(SColorHighlight, White, 132);
251 	fg_found  = Color(0, 0, 0);
252 	bg_found  = Blend(SColorHighlight, Color(189,231,237), 200);
253 	fg_even   = SColorText;
254 	fg_odd    = SColorText;
255 	bg_even   = SColorPaper;
256 	bg_odd    = SColorPaper;
257 	fg_grid   = SColorShadow;
258 
259 	focused_ctrl = NULL;
260 	focused_ctrl_id = -1;
261 	focused_col = -1;
262 
263 	find.NullText(t_("Search"));
264 	find.WhenBar = THISBACK(FindOptsBar);
265 
266 	/* frames added at the very end, otherwise there will be strange crash in optimal mode... */
267 	sbx.AutoHide();
268 	sby.AutoHide();
269 	SetFrame(ViewFrame());
270 	AddFrame(sbx);
271 	AddFrame(sby);
272 	Ctrl::Add(holder);
273 
274 	resize_panel_open = false;
275 	resize_panel.WhenClose = Proxy(WhenClose);
276 
277 	resizing = false;
278 	selecting = false;
279 	is_clipboard = false;
280 	enabled = true;
281 	sync_flag = 0;
282 	paint_flag = 0;
283 }
284 
~GridCtrl()285 GridCtrl::~GridCtrl()
286 {
287 	delete orgdisp;
288 }
289 
StdToolBar(Bar & bar)290 void GridCtrl::StdToolBar(Bar &bar)
291 {
292 	bool e = IsEnabled();
293 	bool c = e && IsCursor();
294 	bool d = c && IsRowEditable();
295 
296 	if(appending)
297 		bar.Add(e, t_("Append"), GridImg::Append(), StdAppend);
298 
299 	if(inserting)
300 		bar.Add(c, t_("Insert "), GridImg::Insert(), StdInsert);
301 
302 	if(duplicating)
303 		bar.Add(d && !isedit, t_("Duplicate"), GridImg::Duplicate(), StdDuplicate);
304 
305 	if(removing)
306 		bar.Add(d && (keep_last_row ? GetCount() > 1 : true), t_("Delete "), GridImg::Delete(), StdRemove);
307 
308 	if(editing)
309 	{
310 		bar.Add(!isedit && d, t_("Edit"), GridImg::Modify(), StdEdit);
311 		if(accepting)
312 			bar.Add(isedit, t_("Accept"), GridImg::Commit(), THISBACK(DoEndEdit));
313 		if(canceling)
314 			bar.Add(isedit, t_("Cancel"), GridImg::Cancel(), THISBACK(DoCancelEdit));
315 	}
316 
317 	if(searching)
318 	{
319 		if(inserting || appending || removing)
320 			bar.Separator();
321 		FindBar(bar, 150);
322 	}
323 
324 	if(moving)
325 	{
326 		if(searching)
327 			bar.Separator();
328 
329 		bar.Add(c, t_("Move up"), GridImg::MoveUp(), THISBACK(DoSwapUp));
330 		bar.Add(c, t_("Move down"), GridImg::MoveDn(), THISBACK(DoSwapDown));
331 	}
332 
333 	if(navigating)
334 	{
335 		if(!closing)
336 			bar.GapRight();
337 		else
338 			bar.Separator();
339 
340 		NavigatingBar(bar);
341 	}
342 
343 	if(closing)
344 	{
345 		bar.GapRight();
346 		bar.Add(close, 76, 24);
347 	}
348 }
349 
FindBar(Bar & bar,int cx)350 void GridCtrl::FindBar(Bar &bar, int cx)
351 {
352 	bar.Add(find, cx);
353 }
354 
InfoBar(Bar & bar,int cx)355 void GridCtrl::InfoBar(Bar &bar, int cx)
356 {
357 	bar.Add(info, cx);
358 }
359 
SetToolBarInfo(String inf)360 void GridCtrl::SetToolBarInfo(String inf)
361 {
362 	info.SetLabel(inf);
363 }
364 
NavigatingBar(Bar & bar)365 void GridCtrl::NavigatingBar(Bar &bar)
366 {
367 	bar.Add(RowFormat(t_("First %s")), GridImg::FirstRec(), THISBACK(DoGoBegin));
368 	bar.Add(RowFormat(t_("Previous %s")), GridImg::PrevRec(), THISBACK(DoGoPrev));
369 	bar.Add(RowFormat(t_("Next %s")), GridImg::NextRec(), THISBACK(DoGoNext));
370 	bar.Add(RowFormat(t_("Last %s")), GridImg::LastRec(), THISBACK(DoGoEnd));
371 }
372 
SetToolBar(bool b,int align,int frame)373 GridCtrl& GridCtrl::SetToolBar(bool b, int align, int frame)
374 {
375 	RemoveFrame(bar);
376 
377 	if(!b)
378 		return *this;
379 
380 	InsertFrame(frame, bar.Align(align));
381 	bar.SetStyle(ToolBar::StyleDefault());
382 
383 	if(frame == 1)
384 		switch(align)
385 		{
386 			case BarCtrl::BAR_TOP:
387 				RemoveFrame(TopSeparatorFrame());
388 				InsertFrame(2, TopSeparatorFrame());
389 				break;
390 			case BarCtrl::BAR_BOTTOM:
391 				RemoveFrame(BottomSeparatorFrame());
392 				InsertFrame(2, BottomSeparatorFrame());
393 				break;
394 			case BarCtrl::BAR_LEFT:
395 				RemoveFrame(LeftSeparatorFrame());
396 				InsertFrame(2, LeftSeparatorFrame());
397 				break;
398 			case BarCtrl::BAR_RIGHT:
399 				RemoveFrame(RightSeparatorFrame());
400 				InsertFrame(2, RightSeparatorFrame());
401 				break;
402 		}
403 	WhenToolBar(bar);
404 	return *this;
405 }
406 
ResizePanel(bool b)407 GridCtrl& GridCtrl::ResizePanel(bool b)
408 {
409 	resize_panel_open = b;
410 	RemoveFrame(resize_panel);
411 	RemoveFrame(BottomSeparatorFrame());
412 	if(!b)
413 		return *this;
414 	InsertFrame(1, resize_panel);
415 	InsertFrame(2, BottomSeparatorFrame());
416 	return *this;
417 }
418 
FindOptsBar(Bar & bar)419 void GridCtrl::FindOptsBar(Bar &bar)
420 {
421 	bar.Add(t_("Immediate search"), THISBACK1(SetFindOpts, 0)).Check(search_immediate);
422 	bar.Add(t_("Hide rows"), THISBACK1(SetFindOpts, 1)).Check(search_hide);
423 	bar.Add(t_("Highlight found cells"), THISBACK1(SetFindOpts, 2)).Check(search_highlight);
424 	bar.Add(t_("Case sensitive"), THISBACK1(SetFindOpts, 3)).Check(search_case);
425 }
426 
SetFindOpts(int n)427 void GridCtrl::SetFindOpts(int n)
428 {
429 	switch(n)
430 	{
431 		case 0:
432 			search_immediate = !search_immediate;
433 			if(!search_immediate)
434 			{
435 				find <<= THISBACK(Nothing);
436 				find.WhenEnter = THISBACK(DoFind);
437 			}
438 			else
439 			{
440 				find <<= THISBACK(DoFind);
441 				find.WhenEnter = THISBACK(Nothing);
442 			}
443 			break;
444 		case 1:
445 			search_hide = !search_hide;
446 			if(!String(~find).IsEmpty())
447 			{
448 				if(!search_hide)
449 					ShowRows();
450 				else
451 					DoFind();
452 			}
453 			break;
454 		case 2:
455 			search_highlight = !search_highlight;
456 			if(!search_highlight)
457 			{
458 				ClearFound(false);
459 				Refresh();
460 			}
461 			else
462 				DoFind();
463 			break;
464 		case 3:
465 			search_case = !search_case;
466 			DoFind();
467 			break;
468 	}
469 }
470 
RowFormat(const char * s)471 String GridCtrl::RowFormat(const char *s)
472 {
473 	String row = t_("row");
474 	return Sprintf(s, ~row);
475 }
476 
StdMenuBar(Bar & bar)477 void GridCtrl::StdMenuBar(Bar &bar)
478 {
479 	bool c = IsCursor();
480 	bool e = c && IsRowEditable();
481 	bool isitem = false;
482 
483 	if(inserting)
484 	{
485 		if(bains == 0)
486 		{
487 			bar.Add(c, t_("Insert "), StdInsert)
488 			   .Image(GridImg::Insert())
489 			   .Help(RowFormat(t_("Insert a new %s into the table.")))
490 			   .Key(K_INSERT);
491 		}
492 		else if(bains == 1)
493 		{
494 			bar.Add(c, t_("Insert before"), THISBACK(DoInsertBefore))
495 			   .Image(GridImg::InsertBefore())
496 			   .Help(RowFormat(t_("Insert a new %s into the table before current")))
497 			   .Key(K_INSERT);
498 			bar.Add(c, t_("Insert after"), THISBACK(DoInsertAfter))
499 			   .Image(GridImg::InsertAfter())
500 			   .Help(RowFormat(t_("Insert a new %s into the table after current")))
501 			   .Key(K_ALT_INSERT);
502 		}
503 		else if(bains == 2)
504 		{
505 			bar.Add(c, t_("Insert after"), THISBACK(DoInsertAfter))
506 			   .Image(GridImg::InsertAfter())
507 			   .Help(RowFormat(t_("Insert a new %s into the table after current")))
508 			   .Key(K_INSERT);
509 			bar.Add(c, t_("Insert before"), THISBACK(DoInsertBefore))
510 			   .Image(GridImg::InsertBefore())
511 			   .Help(RowFormat(t_("Insert a new %s into the table before current")))
512 			   .Key(K_ALT_INSERT);
513 		}
514 		isitem = true;
515 	}
516 
517 	if(appending)
518 	{
519 		bar.Add(t_("Append"), StdAppend)
520 		   .Image(GridImg::Append())
521 		   .Help(RowFormat(t_("Append a new %s at the end of the table.")))
522 		   .Key(inserting ? (dword) K_CTRL_INSERT : (dword) K_INSERT);
523 
524 		isitem = true;
525 	}
526 
527 	if(duplicating)
528 	{
529 		bar.Add(c, t_("Duplicate"), THISBACK(DoDuplicate))
530 		   .Image(GridImg::Duplicate())
531 		   .Help(RowFormat(t_("Duplicate current table %s.")))
532 		   .Key(K_CTRL_D);
533 
534 		isitem = true;
535 	}
536 
537 	if(editing)
538 	{
539 		bar.Add(!isedit && e, t_("Edit"), StdEdit)
540 		   .Image(GridImg::Modify())
541 		   .Help(RowFormat(t_("Edit active %s.")))
542 		   .Key(K_ENTER);
543 
544 		isitem = true;
545 	}
546 
547 	if(removing)
548 	{
549 		RemovingMenu(bar);
550 		isitem = true;
551 	}
552 
553 	if(moving)
554 	{
555 		MovingMenu(bar);
556 		isitem = true;
557 	}
558 
559 	if(multi_select || !select_row)
560 	{
561 		SelectMenu(bar);
562 		isitem = true;
563 	}
564 
565 	if(clipboard)
566 	{
567 		if(isitem)
568 			bar.Separator();
569 		ClipboardMenu(bar);
570 		isitem = true;
571 	}
572 
573 	if(hiding)
574 	{
575 		if(isitem)
576 			bar.Separator();
577 		ColumnsMenu(bar);
578 	}
579 }
580 
RemovingMenu(Bar & bar)581 void GridCtrl::RemovingMenu(Bar &bar)
582 {
583 	bool c = IsCursor() && IsRowEditable();
584 	bar.Add(c && (keep_last_row ? GetCount() > 1 : true), t_("Delete "), StdRemove)
585 	   .Image(GridImg::Delete())
586 	   .Help(RowFormat(t_("Delete active %s.")))
587 	   .Key(K_DELETE);
588 }
589 
MovingMenu(Bar & bar)590 void GridCtrl::MovingMenu(Bar &bar)
591 {
592 	bool c = IsCursor();
593 	bar.Add(c && curpos.y > fixed_rows, t_("Move up"), THISBACK(DoSwapUp))
594 	   .Image(GridImg::MoveUp())
595 	   .Key(K_CTRL_UP);
596 	bar.Add(c && curpos.y >= fixed_rows && curpos.y < total_rows - 1, t_("Move down"), THISBACK(DoSwapDown))
597 	   .Image(GridImg::MoveDn())
598 	   .Key(K_CTRL_DOWN);
599 }
600 
SelectMenu(Bar & bar)601 void GridCtrl::SelectMenu(Bar &bar)
602 {
603 	bar.Add(total_rows > fixed_rows, RowFormat(t_("Select all")), THISBACK(DoSelectAll))
604 	   .Image(GridImg::SelectAll())
605 	   .Help(t_("Select all table rows"))
606 	   .Key(K_CTRL_A);
607 }
608 
ColumnsMenu(Bar & bar)609 void GridCtrl::ColumnsMenu(Bar &bar)
610 {
611 	bar.Add(t_("Columns"), THISBACK(ColumnList));
612 }
613 
ColumnList(Bar & bar)614 void GridCtrl::ColumnList(Bar &bar)
615 {
616 	int cnt = 0;
617 	for(int i = fixed_cols; i < total_cols; i++)
618 		if(!hitems[i].index && !hitems[i].hidden)
619 			cnt++;
620 
621 	for(int i = fixed_cols; i < total_cols; i++)
622 	{
623 		if(!hitems[i].index)
624 			bar.Add((String) items[0][hitems[i].id].val, THISBACK1(MenuHideColumn, i))
625 			   .Check(!hitems[i].hidden)
626 			   .Enable(cnt > 1 || (cnt == 1 && hitems[i].hidden));
627 	}
628 }
629 
ClipboardMenu(Bar & bar)630 void GridCtrl::ClipboardMenu(Bar &bar)
631 {
632 	bool c = IsCursor();
633 	bool s = c || IsSelection();
634 	bar.Add(t_("Copy"), THISBACK(DoCopy)).Image(CtrlImg::copy()).Key(K_CTRL_C).Enable(s && copy_allowed);
635 	bar.Add(t_("Cut"), THISBACK(Nothing)).Image(CtrlImg::cut()).Key(K_CTRL_X).Enable(s && cut_allowed);
636 	bar.Add(t_("Paste"), THISBACK(DoPaste)).Image(CtrlImg::paste()).Key(K_CTRL_V).Enable(c && paste_allowed && IsClipboardAvailable());
637 	if(extra_paste)
638 		bar.Add(t_("Paste as"), THISBACK(PasteAsMenu));
639 }
640 
PasteAsMenu(Bar & bar)641 void GridCtrl::PasteAsMenu(Bar &bar)
642 {
643 	bool c = IsCursor();
644 	bool s = IsClipboardAvailable() && !fixed_paste;
645 	bar.Add(t_("appended"), THISBACK(DoPasteAppendedRows)).Key(K_CTRL_E).Enable(s && paste_allowed);
646 	bar.Add(t_("inserted"), THISBACK(DoPasteInsertedRows)).Key(K_CTRL_I).Enable(c && paste_allowed && s);
647 }
648 
IsClipboardAvailable()649 bool GridCtrl::IsClipboardAvailable()
650 {
651 	return IsClipboardFormatAvailable<GridClipboard>() ||
652 	       IsClipboardAvailableText();
653 }
654 
GetClipboard()655 GridClipboard GridCtrl::GetClipboard()
656 {
657 	GridClipboard gc = ReadClipboardFormat<GridClipboard>();
658 	return gc;
659 }
660 
SetClipboard(bool all,bool silent)661 void GridCtrl::SetClipboard(bool all, bool silent)
662 {
663 	if(!clipboard)
664 		return;
665 
666 	GridClipboard gc;
667 
668 	Point minpos(total_cols, total_rows);
669 	Point maxpos(fixed_cols, fixed_rows);
670 
671 	String body;
672 	Vector<int> sc;
673 	sc.Set(0, -1, total_cols);
674 
675 	int prev_row = -1;
676 
677 	for(int i = fixed_rows; i < total_rows; i++)
678 	{
679 		bool row_selected = select_row && IsSelected(i, false);
680 
681 		for(int j = fixed_cols; j < total_cols; j++)
682 		{
683 			if(all || row_selected || IsSelected(i, j, false))
684 			{
685 				if(prev_row < 0)
686 					prev_row = i;
687 
688 				GridClipboard::ClipboardData &d = gc.data.Add();
689 				d.col = j;
690 				d.row = i;
691 				d.v = Get0(i, j);
692 
693 				if(i < minpos.y) minpos.y = i;
694 				else if(i > maxpos.y) maxpos.y = i;
695 				if(j < minpos.x) minpos.x = j;
696 				else if(j > maxpos.x) maxpos.x = j;
697 
698 				if(i != prev_row)
699 				{
700 					body += "\r\n";
701 					prev_row = i;
702 				}
703 				body += GetStdConvertedColumn(j, d.v).ToString() + '\t';
704 
705 				sc[j] = 1;
706 			}
707 		}
708 
709 		int cnt = body.GetCount();
710 		if(cnt > 0 && body[cnt - 1] == '\t')
711 			body.Remove(cnt - 1);
712 	}
713 
714 	String header;
715 
716 	if(copy_column_names)
717 	{
718 		for(int i = 0; i < sc.GetCount(); i++)
719 			if(sc[i] >= 0)
720 				header += hitems[i].GetName() + '\t';
721 
722 		int cnt = header.GetCount();
723 		if(cnt > 0 && header[cnt - 1] == '\t')
724 			header.Remove(cnt - 1);
725 		header += "\r\n";
726 	}
727 
728 	gc.minpos = minpos;
729 	gc.maxpos = maxpos;
730 
731 	bool row_selected = select_row && IsSelected(curpos.y, false);
732 	gc.shiftmode = row_selected ? true : shiftmode;
733 
734 	WriteClipboardFormat(gc);
735 	AppendClipboardText(header + body);
736 
737 	if(!silent)
738 	{
739 		Color c0 = bg_select;
740 		Color c1 = White;
741 		Color c2 = bg_focus;
742 
743 		for(int i = 0; i < 256; i += 64)
744 		{
745 			bg_select = Blend(c0, c1, i);
746 			bg_focus = Blend(c2, c1, i);
747 			Refresh(); Sync();
748 			Sleep(1);
749 		}
750 
751 		for(int i = 0; i < 256; i += 32)
752 		{
753 			bg_select = Blend(c1, c0, i);
754 			bg_focus = Blend(c1, c2, i);
755 			Refresh(); Sync();
756 			Sleep(1);
757 		}
758 	}
759 }
760 
PasteCallbacks(bool new_row)761 void GridCtrl::PasteCallbacks(bool new_row)
762 {
763 	if(new_row)
764 	{
765 		LOG("WhenInsertRow() - paste");
766 		#ifdef LOG_CALLBACKS
767 		LGR(2, "WhenInsertRow() - paste");
768 		#endif
769 		WhenInsertRow();
770 	}
771 	else
772 	{
773 		#ifdef LOG_CALLBACKS
774 		LGR(2, "WhenUpdateRow() - paste");
775 		#endif
776 		WhenUpdateRow();
777 	}
778 }
779 
Paste(int mode)780 void GridCtrl::Paste(int mode)
781 {
782 	if(!clipboard)
783 		return;
784 
785 	GridClipboard gc = GetClipboard();
786 
787 	bool is_gc = !gc.data.IsEmpty();
788 	bool is_tc = IsClipboardAvailableText();
789 
790 	if(!is_gc && !is_tc)
791 		return;
792 
793 	if(is_gc && select_row && !gc.shiftmode)
794 		return;
795 
796 	Point cp(curpos);
797 
798 	if(cp.x < 0 || select_row)
799 		cp.x = fixed_cols;
800 	if(cp.y < 0)
801 		cp.y = fixed_rows;
802 
803 	Vector<String> lines;
804 
805 	if(is_tc && !is_gc)
806 		lines = Upp::Split(FromUnicode(ReadClipboardUnicodeText()), '\n');
807 
808 	if(mode == 1)
809 	{
810 		int dy = is_gc ? gc.maxpos.y - gc.minpos.y + 1
811 		               : lines.GetCount();
812 		Insert0(curpos.y, dy);
813 		curpos.y += dy;
814 	}
815 	else if(mode == 2)
816 		cp.y = total_rows;
817 
818 	int lc = -1, lr = -1;
819 
820 	int tr = total_rows;
821 
822 	if(!is_gc)
823 	{
824 		if(is_tc)
825 		{
826 			int pr = 0;
827 			bool new_row = false;
828 
829 			for(int i = 0; i < lines.GetCount(); i++)
830 			{
831 				String line = TrimRight(lines[i]);
832 				Vector<String> cells = Upp::Split(line, '\t', false);
833 				for(int j = 0; j < cells.GetCount(); j++)
834 				{
835 					int r = i;
836 					int c = j;
837 
838 					if(r > pr)
839 					{
840 						PasteCallbacks(new_row);
841 						pr = r;
842 					}
843 
844 					if(cp.x + c < total_cols)
845 					{
846 						lc = cp.x + c;
847 						lr = cp.y + r;
848 
849 						rowidx = lr;
850 
851 						new_row = lr >= tr || mode > 0;
852 
853 						if(fixed_paste && new_row)
854 							break;
855 
856 						Value v(cells[j]);
857 						WhenPasteCell(lr - fixed_rows, lc - fixed_cols, v);
858 						Set0(lr, lc, v, true);
859 					}
860 
861 					if(i == lines.GetCount() - 1 && j == cells.GetCount() - 1)
862 						PasteCallbacks(new_row);
863 				}
864 			}
865 		}
866 	}
867 	else if(!select_row && gc.shiftmode)
868 	{
869 		lc = cp.x;
870 		lr = cp.y;
871 
872 		for(int i = 0; i < gc.data.GetCount(); i++)
873 		{
874 			Set0(lr, lc, gc.data[i].v, true);
875 
876 			bool data_end = i == gc.data.GetCount() - 1;
877 			bool new_row = ++lc > total_cols - 1;
878 			if(new_row || data_end)
879 			{
880 				bool nr = lr + 1 >= tr;
881 				if(new_row && nr && fixed_paste)
882 					break;
883 
884 				PasteCallbacks(new_row && (nr || mode > 0));
885 
886 				if(!data_end)
887 				{
888 					lc = fixed_cols;
889 					++lr;
890 					rowidx = lr;
891 				}
892 			}
893 		}
894 	}
895 	else
896 	{
897 		int pr = gc.data[0].row - gc.minpos.y;
898 		bool new_row = false;
899 
900 		for(int i = 0; i < gc.data.GetCount(); i++)
901 		{
902 			int r = gc.data[i].row - gc.minpos.y;
903 			int c = gc.data[i].col - gc.minpos.x;
904 
905 			if(r > pr)
906 			{
907 				PasteCallbacks(new_row);
908 				pr = r;
909 			}
910 
911 			if(cp.x + c < total_cols)
912 			{
913 				lc = cp.x + c;
914 				lr = cp.y + r;
915 
916 				rowidx = lr;
917 
918 				new_row = lr >= tr || mode > 0;
919 
920 				if(fixed_paste && new_row)
921 					break;
922 				Set0(lr, lc, gc.data[i].v, true);
923 			}
924 
925 			if(i == gc.data.GetCount() - 1)
926 				PasteCallbacks(new_row);
927 		}
928 	}
929 
930 	if(lc >= 0 && lr >= 0)
931 	{
932 		SetCursor0(lc, lr);
933 		sby.Set(vitems[curpos.y].nBottom() - GetSize().cy);
934 	}
935 	WhenPaste();
936 	ClearSelection();
937 }
938 
DoCopy()939 void GridCtrl::DoCopy()
940 {
941 	SetClipboard();
942 }
943 
DoPaste()944 void GridCtrl::DoPaste()
945 {
946 	Paste(0);
947 }
948 
DoPasteInsertedRows()949 void GridCtrl::DoPasteInsertedRows()
950 {
951 	Paste(1);
952 }
953 
DoPasteAppendedRows()954 void GridCtrl::DoPasteAppendedRows()
955 {
956 	Paste(2);
957 }
958 
SetOrder()959 void GridCtrl::SetOrder()
960 {
961 	row_order = true;
962 	WhenChangeOrder();
963 }
964 
Nothing()965 void GridCtrl::Nothing()
966 {
967 }
968 
DrawLine(bool iniLine,bool delLine)969 void GridCtrl::DrawLine(bool iniLine, bool delLine)
970 {
971 	if((resizeCol || resizeRow) && resize_paint_mode < 2)
972 	{
973 		int sx = resize_paint_mode == 1 ? fixed_width : 0;
974 		int sy = resize_paint_mode == 1 ? fixed_height : 0;
975 		ViewDraw w(this);
976 		Size sz = GetSize();
977 
978 		Point curPnt;
979 		static Point oldPnt = curPnt;
980 
981 		if(resizeCol)
982 		{
983 			curPnt.x = hitems[splitCol].nRight(sbx) - 1;
984 			if(delLine) w.DrawRect(oldPnt.x, sy, 1, sz.cy, InvertColor());
985 			if(iniLine) w.DrawRect(curPnt.x, sy, 1, sz.cy, InvertColor());
986 	    }
987 		if(resizeRow)
988 		{
989 			curPnt.y = vitems[splitRow].nBottom(sby) - 1;
990 			if(delLine) w.DrawRect(sx, oldPnt.y, sz.cx, 1, InvertColor());
991 			if(iniLine) w.DrawRect(sx, curPnt.y, sz.cx, 1, InvertColor());
992 		}
993 
994 		oldPnt = curPnt;
995 	}
996 }
997 
GetItemValue(const Item & it,int id,const ItemRect & hi,const ItemRect & vi)998 Value GridCtrl::GetItemValue(const Item& it, int id, const ItemRect& hi, const ItemRect& vi)
999 {
1000 	Value val = hi.IsConvertion() && vi.IsConvertion()
1001 		? GetConvertedColumn(id, it.val)
1002 		: it.val;
1003 
1004 	return val;
1005 }
1006 
GetAttrTextVal(const Value & val)1007 Value GridCtrl::GetAttrTextVal(const Value& val)
1008 {
1009 	if(IsType<AttrText>(val))
1010 	{
1011 		const AttrText& t = ValueTo<AttrText>(val);
1012 		return t.text;
1013 	}
1014 	return val;
1015 }
1016 
GetItemAttrs(const Item & it,const Value & value,int r,int c,const ItemRect & vi,const ItemRect & hi,dword & style,GridDisplay * & gd,Color & fg,Color & bg,Font & fnt)1017 void GridCtrl::GetItemAttrs(const Item& it, const Value& value, int r, int c, const ItemRect& vi, const ItemRect& hi, dword& style, GridDisplay*& gd, Color& fg, Color& bg, Font& fnt)
1018 {
1019 	if(!IsNull(vi.fg))
1020 		fg = vi.fg;
1021 	else if(!IsNull(hi.fg))
1022 		fg = hi.fg;
1023 
1024 	if(!IsNull(vi.bg))
1025 		bg = vi.bg;
1026 	else if(!IsNull(hi.bg))
1027 		bg = hi.bg;
1028 
1029 	fnt = StdFont();
1030 
1031 	if(r < fixed_rows && !IsNull(hi.hfnt))
1032 		fnt = hi.hfnt;
1033 	else if(c < fixed_cols && !IsNull(vi.hfnt))
1034 		fnt = vi.hfnt;
1035 	else if(!IsNull(vi.fnt))
1036 		fnt = vi.fnt;
1037 	else if(!IsNull(hi.fnt))
1038 		fnt = hi.fnt;
1039 
1040 	GridDisplay * hd = hi.display;
1041 	GridDisplay * vd = vi.display;
1042 	gd = display;
1043 	if(!hi.ignore_display && !vi.ignore_display)
1044 		gd = vd ? vd : (hd ? hd : (it.display ? it.display : display));
1045 
1046 	gd->SetLeftImage(Null);
1047 
1048 	const Value& val = IsNull(value) ? it.val : value;
1049 
1050 	if(IsType<AttrText>(val))
1051 	{
1052 		const AttrText& t = ValueTo<AttrText>(val);
1053 
1054 		if(!IsNull(t.paper)) bg  = t.paper;
1055 		if(!IsNull(t.ink))   fg  = t.ink;
1056 		if(!IsNull(t.font))  fnt = t.font;
1057 		dword s = 0;
1058 		if(!IsNull(t.align))
1059 		{
1060 			if(t.align == ALIGN_LEFT)
1061 				s = GD::LEFT;
1062 			else if(t.align == ALIGN_RIGHT)
1063 				s = GD::RIGHT;
1064 			else if(t.align == ALIGN_CENTER)
1065 				s = GD::HCENTER;
1066 			style &= ~GD::HALIGN;
1067 			style |= s;
1068 		}
1069 		if(!IsNull(t.img))
1070 			gd->SetLeftImage(t.img);
1071 	}
1072 
1073 }
1074 
GetItemSize(int & r,int & c,int & x,int & y,int & cx,int & cy,bool & skip,bool relx,bool rely)1075 GridCtrl::Item& GridCtrl::GetItemSize(int &r, int &c, int &x, int &y, int &cx, int &cy, bool &skip, bool relx, bool rely)
1076 {
1077 	int idx = hitems[c].id;
1078 	int idy = vitems[r].id;
1079 
1080 	int dx = 0;
1081 	int dy = 0;
1082 
1083 	Item *it = &items[idy][idx];
1084 
1085 	if(it->isjoined)
1086 	{
1087 		int group = it->group;
1088 		it = &items[it->idy][it->idx];
1089 		skip = it->paint_flag == paint_flag;
1090 		it->paint_flag = paint_flag;
1091 		if(skip)
1092 			return *it;
1093 
1094 		while(c >= 0 && items[idy][hitems[c].id].group == group) --c;
1095 		++c;
1096 		while(r >= 0 && items[vitems[r].id][idx].group == group) --r;
1097 		++r;
1098 
1099 		dx = it->cx;
1100 		dy = it->cy;
1101 	}
1102 	else
1103 		skip = false;
1104 
1105 	x  = hitems[c].nLeft();
1106 	y  = vitems[r].nTop();
1107 	cx = hitems[c + dx].nRight() - x;
1108 	cy = vitems[r + dy].nBottom() - y;
1109 
1110 	if(!draw_last_vert_line && c == total_cols - 1 && r >= fixed_rows) cx += 1;
1111 	if(!draw_last_horz_line && r == total_rows - 1 && c >= fixed_cols) cy += 1;
1112 
1113 	if(relx) x -= sbx;
1114 	if(rely) y -= sby;
1115 
1116 	return *it;
1117 }
1118 
Paint(Draw & w)1119 void GridCtrl::Paint(Draw &w)
1120 {
1121 	static int paintcnt = 0;
1122 
1123 	Font stdfont(StdFont());
1124 
1125 	Size sz = GetSize();
1126 	Rect rc = Rect(sz);  //w.GetClip() - it always returns view rect now. bug??
1127 
1128 	if(!ready)
1129 	{
1130 		w.DrawRect(rc, SColorPaper);
1131 		return;
1132 	}
1133 
1134 	int i, j, cx, cy, x, y;
1135 	bool skip;
1136 	Rect r;
1137 
1138 	LG(0, "---- Paint(%d)", ++paintcnt);
1139 
1140 	if(total_cols <= 1 || total_rows == 0)
1141 	{
1142 		LG(0, "---- Paint(%d) Empty.", paintcnt);
1143 		w.DrawRect(sz, SColorPaper);
1144 		return;
1145 	}
1146 
1147 	if(UpdateCols() || UpdateRows())
1148 		UpdateSizes();
1149 
1150 	if(firstCol < 0) firstCol = GetFirstVisCol(fixed_width);
1151 	if(firstRow < 0) firstRow = GetFirstVisRow(fixed_height);
1152 
1153 	LG(0, "firstCol %d", firstCol);
1154 	LG(0, "firstRow %d", firstRow);
1155 
1156 	int en = IsShowEnabled() ? 0 : GD::READONLY;
1157 
1158 	//---------------------------------------------------------------------------------------
1159 
1160 	if(fixed_width > 0 && fixed_height > 0)
1161 	{
1162 		w.Clip(0, 0, fixed_width, fixed_height);
1163 		dword style = chameleon ? GD::CHAMELEON : 0;
1164 
1165 		display->PaintFixed(w, 1, 1, 0, 0, fixed_width, fixed_height,
1166 							Value(""),
1167 							style, stdfont, false, false,
1168 							0, -1, 0,
1169 							true);
1170 		w.End();
1171 	}
1172 
1173 	r.Set(fixed_width, 0, sz.cx, summary_row ? sz.cy : fixed_height);
1174 
1175 	if(w.IsPainting(r) && total_cols > 1)
1176 	{
1177 		LG(0, "Top header");
1178 		w.Clip(r);
1179 
1180 		x = hitems[total_cols - 1].nRight(sbx);
1181 		int rx = x;
1182 
1183 		int firstcol = indicator ? 0 : (fixed_cols > 1 ? 1 : firstVisCol);
1184 		if(firstCol < 0) firstCol = 0;
1185 		int jfc = chameleon ? 0 : firstCol;
1186 
1187 		for(i = 0; i < fixed_rows; i++)
1188 		{
1189 			for(j = jfc; j < total_cols; j++)
1190 			{
1191 				ItemRect& hi = hitems[j];
1192 
1193 				if(hi.hidden)
1194 					continue;
1195 
1196 				int jj = j;
1197 				Item &it = GetItemSize(i, j, x, y, cx, cy, skip, true, false);
1198 				if(skip)
1199 					continue;
1200 
1201 				if(x >= rc.right)
1202 					break;
1203 
1204 				if(w.IsPainting(x, y, cx, cy))
1205 				{
1206 					GridDisplay * gd = it.display ? it.display : display;
1207 
1208 					dword style = hi.style | hi.halign;
1209 					if(i > 0) style &= ~GD::HIGHLIGHT;
1210 					if(chameleon)
1211 						style |= GD::CHAMELEON;
1212 
1213 					Font fnt(stdfont);
1214 					gd->SetLeftImage(hi.img);
1215 					gd->ReverseSortIcon(reverse_sort_icon);
1216 					gd->PaintFixed(w, jj == firstcol, i == 0, x, y, cx, cy,
1217 								i == 0 ? it.val : GetConvertedColumn(hi.id, it.val),
1218 								style | en, IsNull(hi.hfnt) ? fnt : hi.hfnt, false, false,
1219 								i == 0 ? hi.sortmode : 0,
1220 								hi.sortcol,
1221 								sortOrder.GetCount(),
1222 								true);
1223 
1224 					it.rcx = gd->real_size.cx;
1225 					it.rcy = gd->real_size.cy;
1226 				}
1227 
1228 				if(summary_row && i == 0)
1229 				{
1230 					cy = GD_HDR_HEIGHT;
1231 					y = sz.cy - cy;
1232 
1233 					if(w.IsPainting(x, y, cx, cy))
1234 					{
1235 						Item &it = summary[hi.id];
1236 						GridDisplay * gd = it.display ? it.display : display;
1237 
1238 						dword style = en | hi.halign;
1239 						if(chameleon)
1240 							style |= GD::CHAMELEON;
1241 
1242 						String s;
1243 						if(hi.sop != SOP_NONE && !hi.sopfrm.IsEmpty() && !IsNull(it.val))
1244 							s = Format(hi.sopfrm, it.val);
1245 						else
1246 							s = IsString(it.val) ? it.val : StdConvert().Format(it.val);
1247 						gd->SetLeftImage(Null);
1248 						//gd->PaintFixed(w, jj == firstcol, i == 0, x, y, cx, cy, s,
1249 						//			   style | en, stdfont, false, false, 0, -1, 0, true);
1250 						Color fg = Black;
1251 						Color bg = Blend(Blue, White, 240);
1252 						w.DrawRect(x, y, cx, 1, Gray);
1253 						Font fnt(stdfont);
1254 
1255 						if(style & GD::READONLY)
1256 						{
1257 							bg = Blend(bg, SGray(), 40);
1258 							fg = Blend(fg, SGray(), 200);
1259 						}
1260 
1261 						gd->Paint(w, x, y + 1, cx, cy - 1, s, style, fg, bg, fnt.Bold(), false, 0, 0);
1262 
1263 						it.rcx = gd->real_size.cx;
1264 						it.rcy = gd->real_size.cy;
1265 					}
1266 				}
1267 			}
1268 		}
1269 		if(rx < sz.cx || chameleon)
1270 		{
1271 			int cx = sz.cx - rx + 1;
1272 			dword style = 0;
1273 			if(chameleon)
1274 			{
1275 				cx = max(10, cx);
1276 				style = GD::CHAMELEON;
1277 			}
1278 			display->PaintFixed(w, 0, 1, rx, 0, cx, fixed_height,
1279 								Value(""),
1280 								style, stdfont, false, false,
1281 								0, -1, 0,
1282 								true);
1283 			if(summary_row)
1284 			{
1285 				Color fg = Black;
1286 				Color bg = Blend(Blue, White, 240);
1287 				w.DrawRect(rx, y, cx, 1, Gray);
1288 
1289 				if(style & GD::READONLY)
1290 				{
1291 					bg = Blend(bg, SGray(), 40);
1292 					fg = Blend(fg, SGray(), 200);
1293 				}
1294 				display->Paint(w, rx, y + 1, cx, GD_HDR_HEIGHT - 1, Value(""), style, fg, bg, stdfont, false, 0, 0);
1295 			}
1296 		}
1297 
1298 		w.End();
1299 	}
1300 	//---------------------------------------------------------------------------------------
1301 
1302 	bool can_paint = firstCol >= 0 && firstRow >= 0;
1303 	r.Set(0, fixed_height, fixed_width, sz.cy);
1304 
1305 	if(can_paint && w.IsPainting(r))
1306 	{
1307 		LG(0, "Left header");
1308 		w.Clip(r);
1309 		y = vitems[total_rows - 1].nBottom(sby);
1310 
1311 		if(y < sz.cy)
1312 			w.DrawRect(Rect(0, y, fixed_width, sz.cy - summary_height), SColorPaper);
1313 
1314 		for(i = 0; i < fixed_cols; i++)
1315 		{
1316 			bool firstx = (i == !indicator);
1317 			bool indicator = (i == 0);
1318 			int id = hitems[i].id;
1319 
1320 			for(j = firstRow; j < total_rows; j++)
1321 			{
1322 				ItemRect& vi = vitems[j];
1323 
1324 				if(vi.hidden)
1325 					continue;
1326 
1327 				Item &it = GetItemSize(j, i, x, y, cx, cy, skip, false, true);
1328 
1329 				if(skip)
1330 					continue;
1331 
1332 				if(y >= rc.bottom)
1333 					break;
1334 
1335 				if(w.IsPainting(x, y, cx, cy))
1336 				{
1337 					GridDisplay * gd = it.display ? it.display : display;
1338 
1339 					dword style = vi.style;
1340 					if(i > 0) style &= ~GD::HIGHLIGHT;
1341 					if(chameleon)
1342 						style |= GD::CHAMELEON;
1343 
1344 					Font fnt(stdfont);
1345 					gd->PaintFixed(w, firstx, j == 0, x, y, cx, cy,
1346 									GetConvertedColumn(id, it.val),
1347 									style | en, IsNull(vi.hfnt) ? fnt : vi.hfnt,
1348 									indicator, false, 0, -1, 0, false);
1349 
1350 					it.rcx = gd->real_size.cx;
1351 					it.rcy = gd->real_size.cy;
1352 				}
1353 			}
1354 
1355 			if(summary_row)
1356 			{
1357 				j = 0;
1358 				cy = GD_HDR_HEIGHT;
1359 				y = sz.cy - cy;
1360 				if(w.IsPainting(x, y, cx, cy))
1361 				{
1362 					Item& it = summary[hitems[i].id];
1363 					GridDisplay * gd = it.display ? it.display : display;
1364 
1365 					dword style = vitems[j].style;
1366 					if(chameleon)
1367 						style |= GD::CHAMELEON;
1368 
1369 //					gd->PaintFixed(w, firstx, true, x, y, cx, cy,
1370 //									GetConvertedColumn(id, it.val),
1371 //									style | en, stdfont,
1372 //									false, false, 0, -1, 0, false);
1373 //
1374 					Color fg = Black;
1375 					Color bg = Blend(Blue, White, 240);
1376 					w.DrawRect(x, y, cx, 1, Gray);
1377 					Font fnt(stdfont);
1378 					gd->Paint(w, x, y + 1, cx, cy - 1, GetConvertedColumn(id, it.val), style | en, fg, bg, fnt.Bold(), false, 0, 0);
1379 
1380 				}
1381 			}
1382 		}
1383 
1384 		w.End();
1385 	}
1386 
1387 	//---------------------------------------------------------------------------------------
1388 
1389 	r.Set(fixed_width, fixed_height, sz.cx, sz.cy - summary_height);
1390 
1391 	if(can_paint && w.IsPainting(r))
1392 	{
1393 		LG(0, "Body");
1394 		w.Clip(r);
1395 
1396 		x = hitems[total_cols - 1].nRight(sbx);
1397 		y = vitems[total_rows - 1].nBottom(sby);
1398 
1399 		if(x < sz.cx) w.DrawRect(Rect(max(x, rc.left), max(fixed_height, rc.top), sz.cx, sz.cy), SColorPaper);
1400 		if(y < sz.cy) w.DrawRect(Rect(max(fixed_width, rc.left), max(y, rc.top), sz.cx, sz.cy), SColorPaper);
1401 
1402 		bool hasfocus = HasFocus() || holder.HasFocusDeep();
1403 
1404 		for(i = max(firstRow, fixed_rows); i < total_rows; i++)
1405 		{
1406 			ItemRect& vi = vitems[i];
1407 			if(vi.hidden) continue;
1408 
1409 			bool even = coloring_mode == 2 ? (i - vi.n - fixed_rows) & 1 : false;
1410 
1411 			for(j = max(firstCol, fixed_cols); j < total_cols; j++)
1412 			{
1413 				const ItemRect& hi = hitems[j];
1414 
1415 				if(hi.hidden)
1416 					continue;
1417 
1418 				Item &it = GetItemSize(i, j, x, y, cx, cy, skip);
1419 				if(skip)
1420 					continue;
1421 
1422 				if(y >= rc.bottom)
1423 					goto end_paint;
1424 
1425 				if(x >= rc.right)
1426 					break;
1427 
1428 				if(!w.IsPainting(0, y, sz.cx, cy))
1429 					break;
1430 
1431 				if(w.IsPainting(x, y, cx, cy))
1432 				{
1433 					bool iscur = draw_focus ? (i == curpos.y && j == curpos.x) : false;
1434 
1435 					if(coloring_mode == 1)
1436 						even = (j - hi.n - fixed_cols) & 1;
1437 
1438 					int id = hi.id;
1439 
1440 					dword style = en | (select_row ? vi.style : it.style) | hi.balign;
1441 					dword istyle = it.style;
1442 
1443 					if(hitems[j].wrap)
1444 						style |= GD::WRAP;
1445 					if(ctrlpos.y == i && edits[id].ctrl && edits[id].ctrl->IsShown())
1446 						style |= GD::NOTEXT;
1447 					if(it.ctrl)
1448 						style |= GD::NOTEXT;
1449 
1450 					if(coloring_mode > 0)
1451 						style |= (even ? GD::EVEN : GD::ODD);
1452 					if(hasfocus)
1453 						style |= GD::FOCUS;
1454 
1455 					Color cfg;
1456 					Color cbg;
1457 
1458 					Font fnt = StdFont();
1459 					GridDisplay* gd;
1460 					Value val = GetItemValue(it, id, hi, vi);
1461 					GetItemAttrs(it, val, i, j, vi, hi, style, gd, cfg, cbg, fnt);
1462 
1463 					Color fg = SColorText;
1464 					Color bg = SColorPaper;
1465 
1466 					bool custom = true;
1467 
1468 					if(style & GD::CURSOR)
1469 					{
1470 						if(style & GD::FOCUS)
1471 						{
1472 							fg = iscur ? Blend(bg_focus, Black, 230) : fg_focus;
1473 							bg = iscur ? Blend(bg_focus, White, 230) : bg_focus;
1474 						}
1475 						else
1476 						{
1477 							bg = Blend(SColorDisabled, bg);
1478 						}
1479 						custom = false;
1480 					}
1481 					else if(style & GD::LIVE)
1482 					{
1483 						fg = fg_live;
1484 						bg = bg_live;
1485 						custom = false;
1486 					}
1487 					else if(istyle & GD::FOUND)
1488 					{
1489 						fg = fg_found;
1490 						bg = bg_found;
1491 						custom = false;
1492 					}
1493 					else if(style & GD::SELECT)
1494 					{
1495 						fg = fg_select;
1496 						bg = bg_select;
1497 						custom = false;
1498 					}
1499 					else if(style & GD::EVEN)
1500 					{
1501 						fg = fg_even;
1502 						bg = bg_even;
1503 					}
1504 					else if(style & GD::ODD)
1505 					{
1506 						fg = fg_odd;
1507 						bg = bg_odd;
1508 					}
1509 
1510 					if(custom)
1511 					{
1512 						if(!IsNull(cfg)) fg = cfg;
1513 						if(!IsNull(cbg)) bg = cbg;
1514 					}
1515 
1516 					if(style & GD::READONLY)
1517 					{
1518 						bg = Blend(bg, SGray(), 40);
1519 						fg = Blend(fg, SGray(), 200);
1520 					}
1521 
1522 					gd->SetBgImage(vi.img);
1523 					gd->col = j - fixed_rows;
1524 					gd->row = i - fixed_cols;
1525 					gd->parent = this;
1526 
1527 					val = GetAttrTextVal(val);
1528 
1529 					gd->Paint(w, x, y, cx, cy,
1530 					          val, style,
1531 					          fg, bg, fnt, it.style & GD::FOUND, it.fs, it.fe);
1532 
1533 					it.rcx = gd->real_size.cx;
1534 					it.rcy = gd->real_size.cy;
1535 
1536 					if(vert_grid)
1537 					{
1538 						bool skip = false;
1539 						if(!draw_last_vert_line && j == total_cols - 1)
1540 							skip = true;
1541 
1542 						if(!skip)
1543 							w.DrawRect(x + cx - 1, y, 1, cy, fg_grid);
1544 					}
1545 					if(horz_grid)
1546 					{
1547 						bool skip = false;
1548 						if(!draw_last_horz_line && i == total_rows - 1)
1549 							skip = true;
1550 
1551 						if(!skip)
1552 							w.DrawRect(x, y + cy - 1, cx, 1, fg_grid);
1553 					}
1554 
1555 					if(iscur && draw_focus)
1556 						Upp::DrawFocus(w, x, y, cx, cy);
1557 				}
1558 				//if(curpos.y == i)
1559 				//	DrawFocus(w, hitems[fixed_cols].nLeft(), y, hitems[total_cols - 1].nRight() - 1, cy - 1);
1560 			}
1561 		}
1562 
1563 	end_paint:
1564 
1565 		w.End();
1566 
1567 		lastCol = j - 1;
1568 		lastRow = i - 1;
1569 	}
1570 
1571 
1572 	if(moving_header && fixed_top_click && curSplitCol >= 0)
1573 	{
1574 		r.Set(fixed_width, 0, sz.cx, fixed_height);
1575 		w.Clip(r);
1576 		bool lastcol = curSplitCol == lastVisCol;
1577 
1578 		int cp = curSplitCol;
1579 		if(!lastcol)
1580 			while(++cp < total_cols && hitems[cp].hidden);
1581 
1582 		int cx = hitems[cp].nWidth() / 2;
1583 		int x = lastcol ? hitems[curSplitCol].nLeft(sbx) + cx
1584 		                : hitems[curSplitCol].nRight(sbx) - 1;
1585 		DrawFatFrame(w, x, 0, cx, vitems[fixed_rows - 1].nBottom(), moving_allowed ? LtBlue : Red, 2);
1586 		w.End();
1587 	}
1588 
1589 	if(moving_header && fixed_left_click)
1590 	{
1591 		int dy = curSplitRow == lastVisRow ? -2 : -1;
1592 		int y = curSplitRow >= 0 ? vitems[curSplitRow].nBottom(sby) + dy : 0;
1593 		if(y >= fixed_height - 1)
1594 			DrawVertDragLine(w, y, hitems[fixed_cols - 1].nRight(), 0, moving_allowed ? LtBlue : Red);
1595 	}
1596 
1597 	if(moving_body)
1598 	{
1599 		int dy = curSplitRow == lastVisRow ? -2 : -1;
1600 		int y = curSplitRow >= 0 ? vitems[curSplitRow].nBottom(sby) + dy : 0;
1601 		if(y >= fixed_height - 1)
1602 			DrawVertDragLine(w, y, sz.cx, fixed_width - 1, LtBlue);
1603 	}
1604 
1605 	if(search_display && !search_string.IsEmpty())
1606 	{
1607 		Size tsz = GetTextSize(search_string, StdFont());
1608 		w.DrawRect(Rect(0, sz.cy - tsz.cy - 4, tsz.cx + 4, sz.cy), SRed);
1609 		w.DrawText(2, sz.cy - tsz.cy - 2, search_string, StdFont(), SYellow);
1610 	}
1611 
1612 	if(!can_paint)
1613 		w.DrawRect(Rect(0, total_cols > 1 ? fixed_height : 0, sz.cx, sz.cy), SColorPaper);
1614 	if(++paint_flag > 100)
1615 		paint_flag = 0;
1616 
1617 	LG(0, "---- Paint(%d).", paintcnt);
1618 }
1619 
DrawHorzDragLine(Draw & w,int pos,int cx,int size,Color c)1620 void GridCtrl::DrawHorzDragLine(Draw &w, int pos, int cx, int size, Color c)
1621 {
1622 	//w.DrawRect(pos, 0, cx / 2, size - 1, Color(100, 100, 100, 100);
1623 	DrawFrame(w, pos + 1, 1, cx / 2 - 2, size - 2, c);
1624 	DrawFrame(w, pos, 0, cx / 2, size, c);
1625 //	DrawFatFrame(w, x, 0,ďż˝ int cxďż˝ int cyďż˝ Color colorďż˝ int n)
1626 }
1627 
DrawVertDragLine(Draw & w,int pos,int size,int dx,Color c)1628 void GridCtrl::DrawVertDragLine(Draw &w, int pos, int size, int dx, Color c)
1629 {
1630 	w.DrawRect(1 + dx, pos, size - dx - 1, 2, c);
1631 }
1632 
GetStdConvertedValue(const Value & v) const1633 Value GridCtrl::GetStdConvertedValue(const Value& v) const
1634 {
1635 	if(IsString(v))
1636 	{
1637 		return v;
1638 	}
1639 	else if(IsType<AttrText>(v))
1640 	{
1641 		const AttrText& t = ValueTo<AttrText>(v);
1642 		return t.text;
1643 	}
1644 	else
1645 		return StdConvert().Format(v);
1646 }
1647 
GetConvertedColumn(int col,const Value & v) const1648 Value GridCtrl::GetConvertedColumn(int col, const Value &v) const
1649 {
1650 	Convert *conv = edits[col].convert;
1651 	return conv ? conv->Format(v) : v;
1652 }
1653 
GetStdConvertedColumn(int col,const Value & v) const1654 Value GridCtrl::GetStdConvertedColumn(int col, const Value &v) const
1655 {
1656 	Value val = GetConvertedColumn(col, v);
1657 	return GetStdConvertedValue(val);
1658 }
1659 
GetString(Id id) const1660 String GridCtrl::GetString(Id id) const
1661 {
1662 	int c = aliases.Get(id);
1663 	return GetStdConvertedColumn(c, Get0(rowidx, c));
1664 }
1665 
InsertColumn(int col,const char * name,int size,bool idx)1666 GridCtrl::ItemRect& GridCtrl::InsertColumn(int col, const char *name, int size, bool idx)
1667 {
1668 	int id;
1669 
1670 	if(size < 0)
1671 		size = GD_COL_WIDTH;
1672 
1673 	if(col < total_cols)
1674 	{
1675 		id = hitems[col].id;
1676 		for(int i = 0; i < total_cols; i++)
1677 		{
1678 			if(hitems[i].id >= id)
1679 				hitems[i].id += 1;
1680 		}
1681 	}
1682 	else
1683 		id = total_cols;
1684 
1685 	ItemRect& ir = hitems.Insert(col);
1686 	ir.parent = this;
1687 	ir.items = &items;
1688 	ir.edits = &edits;
1689 	ir.prop = size;
1690 	ir.id = id;
1691 	ir.uid = coluid++;
1692 	ir.index = idx;
1693 
1694 	if(index_as_column && idx)
1695 	{
1696 		size = 70;
1697 		ir.prop = size;
1698 		ir.Fixed(size);
1699 	}
1700 
1701 	ir.Size(size);
1702 	aliases.Add(ToLower(name), id);
1703 	rowbkp.Insert(id);
1704 	edits.Insert(id);
1705 	summary.Insert(id);
1706 
1707 	for(int i = 0; i < total_rows; i++)
1708 		items[i].Insert(id);
1709 
1710 	items[0][id].val = name;
1711 
1712 	colidx = col;
1713 	total_cols++;
1714 
1715 	UpdateJoins(-1, col, 1);
1716 
1717 	firstCol = -1;
1718 
1719 	if(ready)
1720 	{
1721 		RecalcCols();
1722 		UpdateSizes();
1723 		UpdateSb();
1724 		SyncSummary();
1725 		SyncCtrls();
1726 		Refresh(); //RefreshFromCol??
1727 	}
1728 	else
1729 		recalc_cols = true;
1730 
1731 	SetModify();
1732 
1733 	return  hitems[col];
1734 }
1735 
AddColumn(const char * name,int size,bool idx)1736 GridCtrl::ItemRect& GridCtrl::AddColumn(const char *name, int size, bool idx)
1737 {
1738 	ItemRect::aliases = &aliases;
1739 
1740 	if(size < 0)
1741 		size = GD_COL_WIDTH;
1742 
1743 	if(total_rows > 1)
1744 		for(int i = 1; i < total_rows; ++i)
1745 			items[i].Add();
1746 	else
1747 		total_rows = 1;
1748 
1749 	Item &it = items[0].Add();
1750 	it.val = name;
1751 
1752 	ItemRect &ib = hitems.Add();
1753 
1754 	ib.parent = this;
1755 	ib.items  = &items;
1756 	ib.edits  = &edits;
1757 	ib.prop   = size;
1758 	ib.id     = total_cols;
1759 	ib.uid    = coluid++;
1760 	ib.index  = idx;
1761 
1762 	if(index_as_column && idx)
1763 	{
1764 		size = 70;
1765 		ib.prop = size;
1766 		ib.Fixed(size);
1767 	}
1768 
1769 	ib.Size(size);
1770 	if(!ib.hidden)
1771 	{
1772 		lastVisCol = total_cols;
1773 		if(firstVisCol < 0)
1774 			firstVisCol = lastVisCol;
1775 	}
1776 
1777 	if(header && vitems[0].nsize == 0)
1778 	{
1779 		vitems[0].size = vitems[0].nsize = GD_HDR_HEIGHT;
1780 		vitems[0].hidden = false;
1781 	}
1782 	if(!header)
1783 	{
1784 		vitems[0].size = vitems[0].nsize = 0;
1785 		vitems[0].hidden = true;
1786 	}
1787 
1788 	edits.Add();
1789 	summary.Add();
1790 	rowbkp.Add();
1791 	aliases.Add(ToLower(name), ib.id);
1792 	total_cols++;
1793 
1794 	if(ready && IsOpen())
1795 	{
1796 		recalc_cols = true;
1797 		RefreshLayout();
1798 	}
1799 
1800 	return ib;
1801 }
1802 
AddColumn(const Id id,const char * name,int size,bool idx)1803 GridCtrl::ItemRect& GridCtrl::AddColumn(const Id id, const char *name, int size, bool idx)
1804 {
1805 	return AddColumn(name ? name : (const char *) ~id, size, idx).Alias(id);
1806 }
1807 
AddColumn(const String & name,int size,bool idx)1808 GridCtrl::ItemRect& GridCtrl::AddColumn(const String& name, int size, bool idx)
1809 {
1810 	return AddColumn((const char *) name, size, idx);
1811 }
1812 
RemoveColumn(int n,int count)1813 void GridCtrl::RemoveColumn(int n, int count)
1814 {
1815 	n += fixed_cols;
1816 	if(count < 0)
1817 		count = total_cols - n;
1818 	if(n < fixed_cols || n + count > total_cols)
1819 		return;
1820 	for(int i = 0; i < total_rows; i++)
1821 		items[i].Remove(n, count);
1822 
1823 	Vector<int> r;
1824 	for(int i = 0; i < count; i++)
1825 	{
1826 		if(edits[hitems[n + i].id].factory)
1827 			--genr_ctrls;
1828 		r.Add(hitems[n + i].id);
1829 	}
1830 
1831 	int id = hitems[n].id;
1832 
1833 	Upp::Sort(r);
1834 
1835 	hitems.Remove(n, count);
1836 
1837 	rowbkp.Remove(r);
1838 	summary.Remove(r);
1839 	edits.Remove(r);
1840 	total_cols -= count;
1841 	recalc_cols = true;
1842 
1843 	for(int i = 0; i < total_cols; i++)
1844 		if(hitems[i].id >= id)
1845 			hitems[i].id -= count;
1846 
1847 	valid_cursor = SetCursor0(min(curpos.x, total_cols - 1), curpos.y).IsValid();
1848 	Repaint(true);
1849 }
1850 
AddRow(int n,int size)1851 GridCtrl::ItemRect& GridCtrl::AddRow(int n, int size)
1852 {
1853 	Append0(n, size);
1854 	return GetRow();
1855 }
1856 
AddSeparator(Color c)1857 GridCtrl& GridCtrl::AddSeparator(Color c)
1858 {
1859 	Append0(1, 3);
1860 	ItemRect& ir = GetRow();
1861 	ir.Bg(c);
1862 	ir.Editable(false);
1863 	ir.Clickable(false);
1864 	ir.Skip(true);
1865 	return *this;
1866 }
1867 
SetRowCount(int n,int size)1868 void GridCtrl::SetRowCount(int n, int size)
1869 {
1870 	Clear();
1871 	Append0(n, size);
1872 }
1873 
SetColCount(int n,int size)1874 void GridCtrl::SetColCount(int n, int size)
1875 {
1876 	Reset();
1877 	for(int i = 0; i < n; i++)
1878 		AddColumn("", size, false);
1879 }
1880 
MouseMove(Point p,dword keyflags)1881 void GridCtrl::MouseMove(Point p, dword keyflags)
1882 {
1883 	mouse_move = true;
1884 
1885 	if(resizing)
1886 	{
1887 		int si, lp, mp, off;
1888 		RectItems *its;
1889 		static int sub = 0;
1890 
1891 		if(resizeCol)
1892 		{
1893 			mp = p.x;
1894 			lp = leftpnt.x;
1895 			si = splitCol;
1896 			its = &hitems;
1897 			bool top = fixed_top_click || (!fixed_left_click && full_col_resizing);
1898 			off = top ? sbx : 0;
1899 		}
1900 		else
1901 		{
1902 			mp = p.y;
1903 			lp = leftpnt.y;
1904 			si = splitRow;
1905 			its = &vitems;
1906 			bool left = fixed_left_click || (!fixed_top_click && full_row_resizing);
1907 			off = left ? sby : 0;
1908 		}
1909 
1910 		int right = (*its)[si].nRight(off);
1911 
1912 		if(just_clicked)
1913 		{
1914 			sub = right - lp;
1915 			just_clicked = false;
1916 		}
1917 
1918 		if(SetDiffItemSize(resizeCol, *its, si, mp - right + sub))
1919 		{
1920 			Split(GS_MOVE);
1921 		}
1922 
1923 		return;
1924 	}
1925 	else if(fixed_click)
1926 	{
1927 		if((fixed_top_click && !moving_cols) ||
1928 		   (fixed_left_click && !moving_rows) ||
1929 		   moveCol < 0 || moveRow < 0)
1930 		   return;
1931 
1932 		if(!moving_header)
1933 		{
1934 			int diffx = p.x - leftpnt.x;
1935 			int diffy = p.y - leftpnt.y;
1936 			if(abs(diffx) < 5 && abs(diffy) < 5)
1937 				return;
1938 
1939 			p -= Point(diffx, diffy);
1940 
1941 			moving_header = true;
1942 			int idx = hitems[moveCol].id;
1943 			int idy = vitems[moveRow].id;
1944 			pophdr.val = idy > 0 ? GetConvertedColumn(moveCol, items[idy][idx].val) : items[idy][idx].val;
1945 
1946 			if(fixed_top_click)
1947 			{
1948 				pophdr.sortmode = hitems[moveCol].sortmode;
1949 				pophdr.sortcol = hitems[moveCol].sortcol;
1950 				pophdr.sortcnt = sortOrder.GetCount();
1951 
1952 				UpdateHighlighting(GS_POPUP, p);
1953 			}
1954 			else
1955 			{
1956 				pophdr.sortmode = 0;
1957 				pophdr.sortcol = -1;
1958 				pophdr.sortcnt = 0;
1959 			}
1960 
1961 			dx = hitems[moveCol].nLeft(fixed_top_click ? sbx : 0) - p.x;
1962 			dy = vitems[moveRow].nTop(fixed_left_click ? sby : 0) - p.y;
1963 		}
1964 
1965 
1966 		Point pt = p + GetScreenRect().TopLeft() + GetBarOffset();
1967 
1968 		pophdr.display = display;
1969 		pophdr.chameleon = chameleon;
1970 		pophdr.PopUp(this, pt.x + dx, pt.y + dy, hitems[moveCol].nWidth(), vitems[moveRow].nHeight());
1971 
1972 		if(fixed_top_click && curSplitCol != oldMoveCol)
1973 		{
1974 			moving_allowed = CanMoveCol(moveCol, curSplitCol);
1975 			RefreshTop();
1976 			//Refresh(oldMoveCol >= 0 ? hitems[oldMoveCol].nRight(sbx) : 0, 0, hitems[curSplitCol].nRight(sbx), fixed_height);
1977 			oldMoveCol = curSplitCol;
1978 		}
1979 
1980 		if(fixed_left_click && curSplitRow != oldMoveRow)
1981 		{
1982 			Refresh(0, 0, fixed_width, fixed_height);
1983 			RefreshLeft();
1984 			oldMoveRow = curSplitRow;
1985 		}
1986 		return;
1987 	}
1988 
1989 	if(leftpnt != p && p.y < fixed_height)
1990 	{
1991 		UpdateHighlighting(GS_MOVE, p);
1992 	}
1993 
1994 	if(live_cursor && popup.IsOpen())
1995 	{
1996 		LG(2, "MouseMove:LiveCursor");
1997 		if(IsMouseBody(p))
1998 			SetCursor0(p, CU_MOUSE | CU_HIGHLIGHT);
1999 		else
2000 			SetCursor0(-1, -1, CU_HIGHLIGHT);
2001 	}
2002 
2003 	if(HasCapture())
2004 	{
2005 		if(!moving_body)
2006 		{
2007 			if(keyflags & K_SHIFT)
2008 			{
2009 				if(SetCursor0(p, CU_MOUSE))
2010 				{
2011 					DoShiftSelect();
2012 					selecting = true;
2013 				}
2014 				return;
2015 			}
2016 
2017 			bool select = true;
2018 			if(select_row && !multi_select)
2019 				select = false;
2020 
2021 			if(select && (keyflags & K_CTRL))
2022 			{
2023 				if(SetCursor0(p, CU_MOUSE))
2024 				{
2025 					DoCtrlSelect();
2026 					selecting = true;
2027 				}
2028 				return;
2029 			}
2030 		}
2031 
2032 		if(moveCol < 0 || moveRow < 0)
2033 			return;
2034 
2035 		if(!dragging)
2036 			return;
2037 
2038 		if(!moving_body)
2039 		{
2040 			popup.Close();
2041 
2042 			if(!top_click && valid_cursor &&
2043 			   p.x < total_width &&
2044 			   p.y < total_height &&
2045 			   (abs(p.y - leftpnt.y) > 5 ||
2046 			    abs(p.x - leftpnt.x) > 5))
2047 				moving_body = true;
2048 
2049 			oldMoveRow = -1;
2050 		}
2051 		else
2052 		{
2053 			Point pt = p + GetScreenRect().TopLeft();
2054 
2055 			int row = curSplitRow - fixed_rows + 2;
2056 			if(select_row)
2057 			{
2058 				int count = max(1, selected_rows);
2059 
2060 				if(vitems[curpos.y].IsSelect())
2061 					popup.val = Format(t_("Moving selection (%d %s) before row %d"), count, count == 1 ? t_("row") : t_("rows"), row);
2062 				else
2063 					popup.val = Format(t_("Moving row %d before row %d"), curpos.y - fixed_rows + 1, row);
2064 			}
2065 			else
2066 			{
2067 				int count = max(1, selected_items);
2068 				popup.val = Format(t_("Moving %d %s before row %d"), count, count == 1 ? t_("cell") : t_("cells"), row);
2069 			}
2070 
2071 			int px = pt.x + 15;
2072 			int py = pt.y + GD_ROW_HEIGHT;
2073 
2074 			popup.gd = display;
2075 			popup.gd->row = 0;
2076 			popup.gd->col = 0;
2077 			popup.fg = SColorText;
2078 			popup.bg = SColorPaper;
2079 			popup.fnt = StdFont();
2080 			popup.style = 0;
2081 			popup.PopUp(this, px, py, GetTextSize((String) popup.val, StdFont()).cx + 10, GD_ROW_HEIGHT);
2082 			SetFocus();
2083 
2084 			if(curSplitRow != oldMoveRow || scrollLeftRight)
2085 			{
2086 				int dy = sby;
2087 				if(oldMoveRow >= 0)
2088 					Refresh(Rect(0, vitems[oldMoveRow].nBottom(dy) - 5, GetSize().cx, vitems[oldMoveRow].nBottom(dy) + 5));
2089 				else
2090 					Refresh(Rect(0, 0, GetSize().cx, 5));
2091 				if(curSplitRow >= 0)
2092 					Refresh(Rect(0, vitems[curSplitRow].nBottom(dy) - 5, GetSize().cx, vitems[curSplitRow].nBottom(dy) + 5));
2093 				else
2094 					Refresh(Rect(0, 0, GetSize().cx, 5));
2095 
2096 				oldMoveRow = curSplitRow;
2097 				popup.Refresh();
2098 
2099 				scrollLeftRight = false;
2100 			}
2101 		}
2102 	}
2103 	else
2104 		SyncPopup();
2105 }
2106 
SyncPopup()2107 void GridCtrl::SyncPopup()
2108 {
2109 	if(!IsPopUp() && popups)
2110 	{
2111 		Point p = GetMouseViewPos();
2112 
2113 		bool fc = p.x < fixed_width;
2114 		bool fr = p.y < fixed_height;
2115 
2116 		int c = GetMouseCol(p, !fc, fc);
2117 		int r = GetMouseRow(p, !fr, fr);
2118 
2119 		bool new_cell = false;
2120 
2121 		if(r != oldSplitRow)
2122 		{
2123 			oldSplitRow = r;
2124 			new_cell = true;
2125 		}
2126 		if(c != oldSplitCol)
2127 		{
2128 			oldSplitCol = c;
2129 			new_cell = true;
2130 		}
2131 
2132 		bool valid_pos = c >= 0 && r >= 0;
2133 
2134 		Ctrl* ctrl = valid_pos ? GetCtrl(r, c, true, false, false, false) : NULL;
2135 
2136 		bool close = popup.IsOpen();
2137 
2138 		if(valid_pos && !ctrl)
2139 		{
2140 			Item& it = GetItem(r, c);
2141 			ItemRect& hi = hitems[c];
2142 			ItemRect& vi = vitems[r];
2143 
2144 			if(it.rcx > 0 || it.rcy > 0)
2145 			{
2146 				close = false;
2147 
2148 				Value val = GetStdConvertedValue(r == 0 ? it.val : GetItemValue(it, hi.id, hi, vi));
2149 
2150 				if(new_cell)
2151 				{
2152 					popup.fg = SColorText;
2153 					popup.bg = SColorPaper;
2154 					popup.fnt = StdFont();
2155 					popup.style = 0;
2156 					popup.val = val;
2157 
2158 					Point p0 = GetMousePos();
2159 					int x = hi.npos + p0.x - p.x - 1 - sbx.Get() * int(!fc);
2160 					int y = vi.npos + p0.y - p.y - 1 - sby.Get() * int(!fr);
2161 
2162 					GetItemAttrs(it, Null, r, c, vi, hi, popup.style, popup.gd, popup.fg, popup.bg, popup.fnt);
2163 					popup.gd->row = r < fixed_rows ? -1 : r - fixed_rows;
2164 					popup.gd->col = c < fixed_cols ? -1 : c - fixed_cols;
2165 					Rect scr = GetWorkArea();
2166 					int margin = popup.gd->lm + popup.gd->rm;
2167 					int cx = min(600, min((int) (scr.right * 0.4), max(it.rcx + margin + 2, hi.nsize + 1)));
2168 					int lines = popup.gd->GetLinesCount(cx - margin - 2, WString(val), popup.fnt, true);
2169 					int cy = max(lines * Draw::GetStdFontCy() + popup.gd->tm + popup.gd->bm + 2, vi.nsize + 1);
2170 					if(fr && r == 0)
2171 					{
2172 						y++;
2173 						cy--;
2174 					}
2175 					popup.PopUp(this, x, y, cx, cy);
2176 					if(!close)
2177 						popup.Refresh();
2178 					UpdateHighlighting(GS_BORDER, Point(0, 0));
2179 				}
2180 				else if(val != popup.val)
2181 				{
2182 					popup.val = val;
2183 					popup.Refresh();
2184 				}
2185 			}
2186 		}
2187 
2188 		if(close)
2189 		{
2190 			popup.Close();
2191 			oldSplitCol = -1;
2192 			oldSplitRow = -1;
2193 			UpdateHighlighting(GS_BORDER, Point(0, 0));
2194 		}
2195 	}
2196 }
2197 
LeftDown(Point p,dword keyflags)2198 void GridCtrl::LeftDown(Point p, dword keyflags)
2199 {
2200 	//popup.Close();
2201 	SetCapture();
2202 	leftpnt = p;
2203 	just_clicked = true;
2204 	selecting = false;
2205 
2206 	fixed_top_click  = p.x >= fixed_width && p.y < fixed_height;
2207 	fixed_left_click = p.x < fixed_width && p.y >= fixed_height;
2208 	fixed_click      = fixed_top_click || fixed_left_click;
2209 	top_click        = p.y < fixed_height;
2210 
2211 	resizing = curResizeCol || curResizeRow;
2212 
2213 	if(resizing)
2214 	{
2215 		popup.Close();
2216 
2217 		splitCol  = curSplitCol;
2218 		splitRow  = curSplitRow;
2219 		resizeCol = curResizeCol;
2220 		resizeRow = curResizeRow;
2221 
2222 		Split(GS_DOWN);
2223 		return;
2224 	}
2225 	else if(fixed_click)
2226 	{
2227 		moveCol = oldMoveCol = GetMouseCol(p, fixed_top_click, 1);
2228 		moveRow = oldMoveRow = GetMouseRow(p, fixed_left_click, 1);
2229 		return;
2230 	}
2231 
2232 	SetFocus();
2233 
2234 	if(IsEmpty() || IsReadOnly())
2235 		return;
2236 
2237 	bool is_shift = keyflags & K_SHIFT;
2238 	bool is_ctrl = keyflags & K_CTRL;
2239 
2240 	CurState cs = SetCursor0(p, CU_MOUSE | CU_HIDECTRLS);
2241 	bool state_change = cs.IsValid() && !cs.IsNew() && (is_ctrl || is_shift);
2242 
2243 	if(!cs.IsAccepted() && !state_change)
2244 		return;
2245 
2246 	if(cs || state_change)
2247 	{
2248 		moveCol = curpos.x;
2249 		moveRow = curpos.y;
2250 
2251 		if(keyflags & K_CTRL)
2252 		{
2253 			bool select = true;
2254 			if(!select_row)
2255 				ClearSelection();
2256 			else if(!multi_select)
2257 				select = false;
2258 
2259 			if(select)
2260 			{
2261 				if(IsValidCursor(oldcur) && selected_items == 0 && cs.IsNew())
2262 				{
2263 					shiftpos = oldcur;
2264 
2265 					if(select_row)
2266 					{
2267 						SelectRange(oldcur, oldcur, true, true);
2268 						SelectRange(curpos, curpos, true, true);
2269 					}
2270 					else
2271 					{
2272 						SelectRange(oldcur, curpos, true, false);
2273 					}
2274 				}
2275 				else
2276 				{
2277 					shiftpos = curpos;
2278 					bool rowsel = vitems[curpos.y].IsSelect();
2279 					SelectRange(curpos, curpos, !rowsel, select_row);
2280 				}
2281 				selecting = true;
2282 			}
2283 		}
2284 		else if(keyflags & K_SHIFT)
2285 		{
2286 			DoShiftSelect();
2287 			selecting = true;
2288 		}
2289 	}
2290 
2291 	#ifdef LOG_CALLBACKS
2292 	//LGR(2, "WhenLeftClick()");
2293 	#endif
2294 
2295 	WhenLeftClick();
2296 
2297 	if(editing && one_click_edit && cs.IsValid() ) //&& IsRowEditable() ?
2298 		StartEdit();
2299 	else
2300 		RebuildToolBar();
2301 }
2302 
LeftUp(Point p,dword keyflags)2303 void GridCtrl::LeftUp(Point p, dword keyflags)
2304 {
2305 	LG(0, "LeftUp");
2306 
2307 	ReleaseCapture();
2308 	fixed_click = false;
2309 
2310 	UpdateHighlighting(resizing ? GS_MOVE : GS_UP, p);
2311 
2312 	if(moving_header)
2313 	{
2314 		LG(0, "moving_header");
2315 		pophdr.Close();
2316 
2317 		moving_header = false;
2318 		if(fixed_top_click)
2319 			MoveCol(moveCol, curSplitCol);
2320 		else
2321 			MoveRow(moveRow, curSplitRow);
2322 
2323 		if(focused_ctrl)
2324 			focused_ctrl->SetFocus();
2325 
2326 		fixed_top_click = false;
2327 		fixed_left_click = false;
2328 
2329 		return;
2330 	}
2331 
2332 	if(resizing)
2333 	{
2334 		Split(GS_UP);
2335 		resizeCol = resizeRow = resizing = false;
2336 		return;
2337 	}
2338 
2339 	if(fixed_top_click && sorting && Distance(leftpnt, p) < 3)
2340 	{
2341 		int i = GetMouseRow(leftpnt, false, true);
2342 		int j = GetMouseCol(leftpnt, true, false);
2343 
2344 		if(j >= fixed_cols && i == 0 && hitems[i].sortable)
2345 		{
2346 			int newSortCol = hitems[j].id;
2347 
2348 			if(sorting_multicol && (keyflags & K_CTRL))
2349 			{
2350 				int colidx = InMultisort(newSortCol);
2351 
2352 				if(colidx < 0)
2353 				    sortOrder.Add(newSortCol);
2354 
2355 				int cnt = sortOrder.GetCount();
2356 
2357 				hitems[j].ChangeSortMode(newSortCol == sortOrder[cnt - 1]);
2358 
2359 				if(colidx >= 0)
2360 				{
2361 					if(hitems[j].sortmode == 0)
2362 					{
2363 						sortOrder.Remove(colidx);
2364 						cnt--;
2365 					}
2366 
2367 					if(WhenSort)
2368 						WhenSort();
2369 					else
2370 					{
2371 						if(hitems[j].sortmode > 0 && colidx == cnt - 1)
2372 							GSort();
2373 						else
2374 							Multisort();
2375 					}
2376 				}
2377 				else
2378 				{
2379 					hitems[j].sortcol = cnt;
2380 					if(WhenSort)
2381 						WhenSort();
2382 					else
2383 						GSort();
2384 				}
2385 			}
2386 			else
2387 			{
2388 				if(sortCol >= 0 && sortCol != newSortCol)
2389 				{
2390 					int idx = GetIdCol(sortCol, true);
2391 					hitems[idx].sortmode = 0;
2392 				}
2393 
2394 				ClearMultisort(1);
2395 				hitems[j].ChangeSortMode();
2396 				hitems[j].sortcol = 1;
2397 
2398 				if(hitems[j].sortmode == 0)
2399 					sortCol = -1;
2400 				else
2401 					sortCol = newSortCol;
2402 
2403 				sortOrder.Add(newSortCol);
2404 
2405 				if(WhenSort)
2406 					WhenSort();
2407 				else
2408 					GSort(newSortCol, hitems[j].sortmode, fixed_rows);
2409 			}
2410 
2411 			UpdateCursor();
2412 			Repaint(false, true);
2413 
2414 			if(WhenSorted)
2415 				WhenSorted();
2416 		}
2417     }
2418 
2419 	if(moving_body)
2420 	{
2421 		popup.Close();
2422 		moving_body = false;
2423 		MoveRows(curSplitRow + 1, !vitems[curpos.y].IsSelect());
2424 		return;
2425 	}
2426 
2427 	if(selected_rows > 0 && !selecting)
2428 		ClearSelection();
2429 }
2430 
LeftDouble(Point p,dword keyflags)2431 void GridCtrl::LeftDouble(Point p, dword keyflags)
2432 {
2433 	LG(0, "LeftDouble");
2434 
2435 	if(full_col_resizing && curSplitCol >= 0)
2436 		return;
2437 
2438 	if(full_row_resizing && curSplitRow >= 0)
2439 		return;
2440 
2441 	if(IsEmpty() || !IsMouseBody(p) || IsReadOnly())
2442 		return;
2443 
2444 	if(keyflags & K_SHIFT || keyflags & K_CTRL)
2445 		return;
2446 
2447 	if(!valid_cursor)
2448 		return;
2449 
2450 	if(editing)
2451 		StartEdit();
2452 
2453 	if(!IsCtrl(curpos))
2454 	{
2455 		popup.Close();
2456 		#ifdef LOG_CALLBACKS
2457 		LGR(2, "WhenLeftDouble()");
2458 		#endif
2459 		WhenLeftDouble();
2460 	}
2461 }
2462 
LeftRepeat(Point p,dword keyflags)2463 void GridCtrl::LeftRepeat(Point p, dword keyflags)
2464 {
2465 	if(!moving_header && !resizeCol && !resizeRow)
2466 		MouseAccel(p, resize_col_mode == 0, resize_row_mode == 0, keyflags);
2467 }
2468 
RightDown(Point p,dword keyflags)2469 void GridCtrl::RightDown(Point p, dword keyflags)
2470 {
2471 	if(IsReadOnly())
2472 		return;
2473 
2474 	if(total_rows > fixed_rows)
2475 	{
2476 		if(!EndEdit())
2477 			return;
2478 
2479 		SetCursor0(p, CU_MOUSE);
2480 	}
2481 
2482 	RebuildToolBar();
2483 	SetFocus(); //jak nie bedzie menu to fokous zostanie na danym wierszu
2484 	MenuBar::Execute(WhenMenuBar);
2485 }
2486 
Init()2487 void GridCtrl::Init()
2488 {
2489 	bar.Set(WhenToolBar);
2490 	UpdateCols(true);
2491 	/* recalc_rows bo przed otworzeniem grida moglo zostac wywolane setrowheight */
2492 	UpdateRows(resize_row_mode > 0 || recalc_rows);
2493 
2494 	UpdateSizes();
2495 	UpdateSb();
2496 	UpdateHolder(true);
2497 	SyncSummary();
2498 	SyncCtrls();
2499 }
2500 
State(int reason)2501 void GridCtrl::State(int reason)
2502 {
2503 	if(reason == OPEN)
2504 	{
2505 		Init();
2506 		ready = true;
2507 		//ready po init - updatesb wola layout() a ten syncctrl
2508 		//(ktory w sumie wola sie 3 razy zanim grid sie wyswietli - niepotrzebnie)
2509 	}
2510 	else if(reason == CLOSE)
2511 	{
2512 		if(live_cursor)
2513 			SetCursor0(-1, -1, CU_HIGHLIGHT);
2514 	}
2515 	else if(reason == ENABLE)
2516 	{
2517 		bool e = IsEnabled();
2518 		if(e != enabled)
2519 		{
2520 			RebuildToolBar();
2521 			enabled = e;
2522 		}
2523 	}
2524 }
2525 
Layout()2526 void GridCtrl::Layout()
2527 {
2528 	if(!ready)
2529 		return;
2530 
2531 	UpdateCols();
2532 	UpdateRows();
2533 	UpdateSizes();
2534 	UpdateSb();
2535 	UpdateHolder();
2536 	UpdateCtrls(UC_CHECK_VIS | UC_SHOW);
2537 	SyncCtrls();
2538 }
2539 
ChildAction(Ctrl * child,int event)2540 void GridCtrl::ChildAction(Ctrl *child, int event)
2541 {
2542 	if(child != &holder && child->IsShown())
2543 		popup.Close();
2544 
2545 	if(child != focused_ctrl)
2546 	{
2547 		if(event == LEFTDOWN || event == RIGHTDOWN || event == MOUSEWHEEL)
2548 		{
2549 			//LG(2, "got event :%x child: %x", event, child);
2550 			Point cp = GetCtrlPos(child);
2551 			if(cp.x < 0 || cp.y < 0)
2552 				return;
2553 
2554 			SetCursor0(cp);
2555 			UpdateCtrls(UC_SHOW);
2556 			WhenCtrlAction();
2557 		}
2558 	}
2559 
2560 	if(event == MOUSEMOVE)
2561 	{
2562 		if(live_cursor)
2563 		{
2564 			LG(2, "Child:LiveCursor");
2565 			Point p = GetMouseViewPos();
2566 			if(IsMouseBody(p))
2567 				SetCursor0(p, CU_MOUSE | CU_HIGHLIGHT);
2568 			else
2569 				SetCursor0(-1, -1, CU_HIGHLIGHT);
2570 		}
2571 	}
2572 
2573 }
2574 
ChildMouseEvent(Ctrl * child,int event,Point p,int zdelta,dword keyflags)2575 void GridCtrl::ChildMouseEvent(Ctrl *child, int event, Point p, int zdelta, dword keyflags)
2576 {
2577 	ChildAction(child, event);
2578 	Ctrl::ChildMouseEvent(child, event, p, zdelta, keyflags);
2579 }
2580 
ChildFrameMouseEvent(Ctrl * child,int event,Point p,int zdelta,dword keyflags)2581 void GridCtrl::ChildFrameMouseEvent(Ctrl *child, int event, Point p, int zdelta, dword keyflags)
2582 {
2583 	ChildAction(child, event);
2584 	Ctrl::ChildFrameMouseEvent(child, event, p, zdelta, keyflags);
2585 }
2586 
DragAndDrop(Point p,PasteClip & d)2587 void GridCtrl::DragAndDrop(Point p, PasteClip& d)
2588 {
2589 	/*
2590 	moving_body = true;
2591 	if(curSplitRow != oldMoveRow || scrollLeftRight)
2592 	{
2593 		int dy = sby;
2594 		if(oldMoveRow >= 0)
2595 			Refresh(Rect(0, vitems[oldMoveRow].nBottom(dy) - 5, GetSize().cx, vitems[oldMoveRow].nBottom(dy) + 5));
2596 		else
2597 			Refresh(Rect(0, 0, GetSize().cx, 5));
2598 		if(curSplitRow >= 0)
2599 			Refresh(Rect(0, vitems[curSplitRow].nBottom(dy) - 5, GetSize().cx, vitems[curSplitRow].nBottom(dy) + 5));
2600 		else
2601 			Refresh(Rect(0, 0, GetSize().cx, 5));
2602 
2603 		oldMoveRow = curSplitRow;
2604 		popup.Refresh();
2605 
2606 		scrollLeftRight = false;
2607 	}
2608 	*/
2609 }
2610 
GetItemRect(int r,int c,bool hgrid,bool vgrid,bool hrel,bool vrel)2611 Rect GridCtrl::GetItemRect(int r, int c, bool hgrid, bool vgrid, bool hrel, bool vrel)
2612 {
2613 	int dx = sbx + (hrel ? fixed_width : 0);
2614 	int dy = sby + (vrel ? fixed_height : 0);
2615 
2616 	int idx = hitems[c].id;
2617 	int idy = vitems[r].id;
2618 
2619 	Item &it = items[idy][idx];
2620 
2621 	int left, top, right, bottom;
2622 
2623 	if(it.isjoined)
2624 	{
2625 		int group = it.group;
2626 
2627 		while(r > fixed_rows && items[vitems[r].id][idx].group == group) --r;
2628 		++r;
2629 
2630 		top = vitems[r].nTop(dy);
2631 		bottom = vitems[r + it.cy].nBottom(dy);
2632 
2633 		while(c > fixed_cols && items[idy][hitems[c].id].group == group) --c;
2634 		++c;
2635 
2636 		left = hitems[c].nLeft(dx);
2637 		right = hitems[c + it.cx].nRight(dx);
2638 	}
2639 	else
2640 	{
2641 		left = hitems[c].nLeft(dx);
2642 		top = vitems[r].nTop(dy);
2643 		right = hitems[c].nRight(dx);
2644 		bottom = vitems[r].nBottom(dy);
2645 	}
2646 
2647 	return Rect(left, top, right - (int) vgrid, bottom - (int) hgrid);
2648 }
2649 
AlignRect(Rect & r,int i)2650 Rect& GridCtrl::AlignRect(Rect &r, int i)
2651 {
2652 	Rect c(r);
2653 	int align = hitems[i].calign;
2654 	int sx = hitems[i].sx;
2655 	int sy = hitems[i].sy;
2656 
2657 	if(sx > 0)
2658 	{
2659 		if(align & GD::HCENTER)
2660 		{
2661 			int d = (r.Width() - sx - 1) / 2;
2662 			r.left += d;
2663 			r.right -= d;
2664 		}
2665 		else if(align & GD::LEFT)
2666 		{
2667 			r.left += hitems[i].sl;
2668 			r.right = r.left + sx;
2669 		}
2670 		else if(align & GD::RIGHT)
2671 		{
2672 			r.right -= hitems[i].sr;
2673 			r.left = r.right - sx;
2674 		}
2675 		else if(align & GD::HPOS)
2676 		{
2677 			r.left += hitems[i].sl;
2678 			r.right -= hitems[i].sr;
2679 		}
2680 	}
2681 
2682 	if(sy > 0)
2683 	{
2684 		if(align & GD::VCENTER)
2685 		{
2686 			int d = (r.Height() - sy - 1) / 2;
2687 			r.top += d;
2688 			r.bottom -= d;
2689 		}
2690 		else if(align & GD::TOP)
2691 		{
2692 			r.top += hitems[i].st;
2693 			r.bottom = r.top + sy;
2694 		}
2695 		else if(align & GD::RIGHT)
2696 		{
2697 			r.bottom -= hitems[i].sb;
2698 			r.top = r.bottom - sy;
2699 		}
2700 		else if(align & GD::VPOS)
2701 		{
2702 			r.top += hitems[i].st;
2703 			r.bottom -= hitems[i].sb;
2704 		}
2705 	}
2706 
2707 	if(r.left   < c.left)   r.left   = c.left;
2708 	if(r.right  > c.right)  r.right  = c.right;
2709 	if(r.top    < c.top)    r.top    = c.top;
2710 	if(r.bottom > c.bottom) r.bottom = c.bottom;
2711 
2712 	return r;
2713 }
2714 
2715 
Scroll()2716 void GridCtrl::Scroll()
2717 {
2718 	Point newpos(sbx, sby);
2719 	Size delta = oldpos - newpos;
2720 	oldpos = newpos;
2721 
2722 	if(delta.cx != 0) firstCol = -1;
2723 	if(delta.cy != 0) firstRow = -1;
2724 
2725 	if(!doscroll)
2726 		return;
2727 
2728 	LG(0, "Scroll (%d, %d)", delta.cx, delta.cy);
2729 
2730 	SyncCtrls();
2731 	UpdateCtrls(UC_CHECK_VIS | UC_SHOW | UC_SCROLL);
2732 
2733 	if(resizeCol || resizeRow)
2734 		return;
2735 
2736 	if(!IsFullRefresh())
2737 	{
2738 		Size sz = GetSize();
2739 		holder.ScrollView(delta);
2740 		if(delta.cx != 0)
2741 		{
2742 			ScrollView(Rect(fixed_width, 0, sz.cx, fixed_height), delta.cx, 0);
2743 			ScrollView(Rect(fixed_width, sz.cy - summary_height, sz.cx, sz.cy), delta.cx, 0);
2744 			scrollLeftRight = true;
2745 		}
2746 		if(delta.cy != 0)
2747 		{
2748 			ScrollView(Rect(0, fixed_height, fixed_width, sz.cy - summary_height), 0, delta.cy);
2749 		}
2750 	}
2751 
2752 	if(live_cursor)
2753 		SetCursor0(GetMousePos() - GetScreenRect().TopLeft(), CU_MOUSE | CU_HIGHLIGHT);
2754 
2755 	SyncPopup();
2756 }
2757 
SetFixedRows(int n)2758 void GridCtrl::SetFixedRows(int n)
2759 {
2760 	if(n >= 0 && n <= total_rows)
2761 	{
2762 		LG(0, "SetFixedRows");
2763 		fixed_rows = n;
2764 		firstRow = -1;
2765 		UpdateSizes();
2766 		UpdateHolder();
2767 		UpdateVisColRow(false);
2768 		if(ready)
2769 			SyncCtrls();
2770 		Refresh();
2771 	}
2772 }
2773 
SetFixedCols(int n)2774 void GridCtrl::SetFixedCols(int n)
2775 {
2776 	if(n >= 0 && n < total_cols)
2777 	{
2778 		LG(0, "SetFixedCols");
2779 		fixed_cols = n + 1; /* +1 - indicator! */
2780 		firstCol = -1;
2781 		UpdateSizes();
2782 		UpdateHolder();
2783 		UpdateVisColRow(true);
2784 		if(ready)
2785 			SyncCtrls();
2786 		Refresh();
2787 	}
2788 }
2789 
Set0(int r,int c,const Value & val_,bool paste)2790 void GridCtrl::Set0(int r, int c, const Value &val_, bool paste)
2791 {
2792 	Value val = val_;
2793 	if(c > total_cols - 1)
2794 		return;
2795 	if(r > total_rows - 1)
2796 		AddRow(r - total_rows + 1);
2797 
2798 	vitems[r].operation = GridOperation::UPDATE;
2799 
2800 	int ri = vitems[r].id;
2801 	Item &it = items[ri][c];
2802 
2803 	if(it.isjoined) {
2804 		ri = it.idy;
2805 		c  = it.idx;
2806 	}
2807 
2808 	Ctrl *ctrl = items[ri][c].ctrl;
2809 	bool  setctrl = true;
2810 	if(!ctrl) {
2811 		ctrl = edits[c].ctrl;
2812 		setctrl = ctrlid.y == ri;
2813 	}
2814 
2815 	if(ctrl) {
2816 		if(paste && IsString(val)) {
2817 			Convert *cv = dynamic_cast<Convert *>(ctrl);
2818 			if(cv)
2819 				val = cv->Scan(val);
2820 		}
2821 		if(setctrl)
2822 			ctrl->SetData(val);
2823 	}
2824 
2825 	items[ri][c].val = val;
2826 	RefreshItem(r, c, false);
2827 
2828 	if(paste)
2829 		WhenUpdateCell();
2830 
2831 	SyncSummary();
2832 }
2833 
Set(int r,int c,const Value & val)2834 void GridCtrl::Set(int r, int c, const Value &val)
2835 {
2836 	Set0(r + fixed_rows, c + fixed_cols, val);
2837 }
2838 
Set(int r,Id id,const Value & val)2839 void GridCtrl::Set(int r, Id id, const Value &val)
2840 {
2841 	Set0(r + fixed_rows, aliases.Get(id), val);
2842 }
2843 
Set(int r,const char * s,const Value & val)2844 void GridCtrl::Set(int r, const char *s, const Value &val)
2845 {
2846 	Set0(r + fixed_rows, aliases.Get(s), val);
2847 }
2848 
Set(int c,const Value & val)2849 void GridCtrl::Set(int c, const Value &val)
2850 {
2851 	Set0(rowidx, c + fixed_cols, val);
2852 }
2853 
Set(Id id,const Value & val)2854 void GridCtrl::Set(Id id, const Value &val)
2855 {
2856 	Set0(rowidx, aliases.Get(id), val);
2857 }
2858 
Set(int r,const Vector<Value> & v,int data_offset,int column_offset)2859 void GridCtrl::Set(int r, const Vector<Value> &v, int data_offset /* = 0*/, int column_offset /* = 0*/)
2860 {
2861 	r += fixed_rows;
2862 	vitems[r].operation = GridOperation::UPDATE;
2863 	int cnt = min(v.GetCount(), total_cols - fixed_cols);
2864 	int r0 = vitems[r].id;
2865 	int c = fixed_cols + column_offset;
2866 	for(int i = data_offset; i < cnt; i++)
2867 		items[r0][c++].val = v[i];
2868 
2869 	RefreshRow(r, false, 0);
2870 }
2871 
Set(const Vector<Value> & v,int data_offset,int column_offset)2872 void GridCtrl::Set(const Vector<Value> &v, int data_offset /* = 0*/, int column_offset /* = 0*/)
2873 {
2874 	int r = rowidx - fixed_rows;
2875 	Set(r, v, data_offset, column_offset);
2876 }
2877 
SetAny(int r,int c,const Value & val)2878 void GridCtrl::SetAny(int r, int c, const Value &val)
2879 {
2880 	Set0(r, c, val);
2881 }
2882 
SetRaw(int r,int c,const Value & val)2883 void GridCtrl::SetRaw(int r, int c, const Value &val)
2884 {
2885 	items[r][c].val = val;
2886 }
2887 
SetIndicator(int r,const Value & val)2888 void GridCtrl::SetIndicator(int r, const Value &val)
2889 {
2890 	Set0(r, 0, val);
2891 }
2892 
SetCtrl(int r,int c,Ctrl & ctrl)2893 void GridCtrl::SetCtrl(int r, int c, Ctrl& ctrl)
2894 {
2895 	r += fixed_rows;
2896 	c += fixed_cols;
2897 	GetItem(r, c).SetCtrl(ctrl, false);
2898 	++genr_ctrls;
2899 	if(ready)
2900 		SyncCtrls(r, c);
2901 }
2902 
SetCtrl(int r,int c,Ctrl * ctrl)2903 void GridCtrl::SetCtrl(int r, int c, Ctrl* ctrl)
2904 {
2905 	r += fixed_rows;
2906 	c += fixed_cols;
2907 	GetItem(r, c).SetCtrl(*ctrl, true);
2908 	++genr_ctrls;
2909 	if(ready)
2910 		SyncCtrls(r, c);
2911 }
2912 
SetCtrl(int c,Ctrl & ctrl)2913 void GridCtrl::SetCtrl(int c, Ctrl& ctrl)
2914 {
2915 	c += fixed_cols;
2916 	GetItem(rowidx, c).SetCtrl(ctrl, false);
2917 	++genr_ctrls;
2918 	if(ready)
2919 		SyncCtrls(rowidx, c);
2920 }
2921 
SetCtrl(int c,Ctrl * ctrl)2922 void GridCtrl::SetCtrl(int c, Ctrl* ctrl)
2923 {
2924 	c += fixed_cols;
2925 	GetItem(rowidx, c).SetCtrl(*ctrl, true);
2926 	++genr_ctrls;
2927 	if(ready)
2928 		SyncCtrls(rowidx, c);
2929 }
2930 
ClearCtrl(int r,int c)2931 void GridCtrl::ClearCtrl(int r, int c)
2932 {
2933 	GridCtrl::Item& item = GetItem(r + fixed_rows, c + fixed_cols);
2934 	if(item.ctrl)
2935 	{
2936 		item.ClearCtrl();
2937 		--genr_ctrls;
2938 	}
2939 }
2940 
SetCtrlValue(int r,int c,const Value & val)2941 void GridCtrl::SetCtrlValue(int r, int c, const Value &val)
2942 {
2943 	c += fixed_cols;
2944 	int ri = vitems[r].id;
2945 	Ctrl * ctrl = items[ri][c].ctrl;
2946 	if(ctrl)
2947 		ctrl->SetData(val);
2948 	else
2949 	{
2950 		ctrl = edits[c].ctrl;
2951 		if(ctrl && ctrlid.y == ri)
2952 			ctrl->SetData(val);
2953 	}
2954 }
2955 
SetCtrlValue(int c,const Value & val)2956 void GridCtrl::SetCtrlValue(int c, const Value &val)
2957 {
2958 	SetCtrlValue(rowidx, c, val);
2959 }
2960 
SetLast(int c,const Value & val)2961 void GridCtrl::SetLast(int c, const Value &val)
2962 {
2963 	c += fixed_cols;
2964 	items[vitems[rowidx].id][c].val = val;
2965 	RefreshItem(rowidx, c, false);
2966 }
2967 
SetFixed(int r,int c,const Value & val)2968 void GridCtrl::SetFixed(int r, int c, const Value &val)
2969 {
2970 	items[r][c + 1].val = val;
2971 	Refresh();
2972 }
2973 
GetFixed(int r,int c) const2974 Value GridCtrl::GetFixed(int r, int c) const
2975 {
2976 	return items[vitems[r].id][c + fixed_cols].val;
2977 }
2978 
GetFixed(int c) const2979 Value GridCtrl::GetFixed(int c) const
2980 {
2981 	return items[0][c + fixed_cols].val;
2982 }
2983 
Get0(int r,int c) const2984 Value GridCtrl::Get0(int r, int c) const
2985 {
2986 	r = vitems[r].id;
2987 	const Item &it = items[r][c];
2988 	if(it.isjoined)
2989 	{
2990 		r = it.idy;
2991 		c = it.idx;
2992 	}
2993 
2994 	Ctrl * ctrl = items[r][c].ctrl;
2995 
2996 	if(!ctrl && ctrlid.y == r)
2997 		ctrl = edits[c].ctrl;
2998 
2999 	return ctrl ? ctrl->GetData() : items[r][c].val;
3000 }
3001 
Get(int r,int c) const3002 Value GridCtrl::Get(int r, int c) const
3003 {
3004 	return Get0(r + fixed_rows, c + fixed_cols);
3005 }
3006 
Get(int c) const3007 Value GridCtrl::Get(int c) const
3008 {
3009 	return Get0(rowidx, c + fixed_cols);
3010 }
3011 
Get(Id id) const3012 Value GridCtrl::Get(Id id) const
3013 {
3014 	return Get0(rowidx, aliases.Get(id));
3015 }
3016 
Get(int r,Id id) const3017 Value GridCtrl::Get(int r, Id id) const
3018 {
3019 	return Get0(r + fixed_rows, aliases.Get(id));
3020 }
3021 
Get() const3022 Value GridCtrl::Get() const
3023 {
3024 	return Get0(curpos.y, curpos.x);
3025 }
3026 
Get(const char * alias) const3027 Value GridCtrl::Get(const char * alias) const
3028 {
3029 	return Get0(rowidx, aliases.Get(alias));
3030 }
3031 
Get(int r,const char * alias) const3032 Value GridCtrl::Get(int r, const char * alias) const
3033 {
3034 	return Get0(r + fixed_rows, aliases.Get(alias));
3035 }
3036 
GetRaw(int r,int c) const3037 Value GridCtrl::GetRaw(int r, int c) const
3038 {
3039 	return items[r][c].val;
3040 }
3041 
GetFirst(int c) const3042 Value GridCtrl::GetFirst(int c) const
3043 {
3044 	return Get0(fixed_rows, c + fixed_cols);
3045 }
3046 
GetLast(int c) const3047 Value GridCtrl::GetLast(int c) const
3048 {
3049 	return Get0(total_rows - 1, c + fixed_cols);
3050 }
3051 
GetPrev(Id id) const3052 Value GridCtrl::GetPrev(Id id) const
3053 {
3054 	return rowbkp[aliases.Get(id)];
3055 }
3056 
GetPrev(int c) const3057 Value GridCtrl::GetPrev(int c) const
3058 {
3059 	return rowbkp[c + fixed_cols];
3060 }
3061 
GetNew(int c) const3062 Value GridCtrl::GetNew(int c) const
3063 {
3064 	return Get0(rowidx, c + fixed_cols);
3065 }
3066 
operator ()(int r,int c)3067 Value& GridCtrl::operator() (int r, int c)
3068 {
3069 	return items[vitems[r + fixed_rows].id][c + fixed_cols].val;
3070 }
3071 
operator ()(int c)3072 Value& GridCtrl::operator() (int c)
3073 {
3074 	return items[vitems[rowidx].id][c + fixed_cols].val;
3075 }
3076 
operator ()(Id id)3077 Value& GridCtrl::operator() (Id id)
3078 {
3079 	return items[vitems[rowidx].id][aliases.Get(id)].val;
3080 }
3081 
operator ()(int r,Id id)3082 Value& GridCtrl::operator() (int r, Id id)
3083 {
3084 	return items[vitems[r + fixed_rows].id][aliases.Get(id)].val;
3085 }
3086 
operator ()(const char * alias)3087 Value& GridCtrl::operator() (const char * alias)
3088 {
3089 	return items[vitems[rowidx].id][aliases.Get(alias)].val;
3090 }
3091 
operator ()(int r,const char * alias)3092 Value& GridCtrl::operator() (int r, const char * alias)
3093 {
3094 	return items[vitems[r + fixed_rows].id][aliases.Get(alias)].val;
3095 }
3096 
SetSummary(int c,const Value & val)3097 void GridCtrl::SetSummary(int c, const Value& val)
3098 {
3099 	summary[c].val = val;
3100 	RefreshSummary();
3101 }
3102 
SetSummary(Id id,const Value & val)3103 void GridCtrl::SetSummary(Id id, const Value& val)
3104 {
3105 	summary[aliases.Get(id)].val = val;
3106 	RefreshSummary();
3107 }
3108 
GetSummary(int c)3109 Value GridCtrl::GetSummary(int c)
3110 {
3111 	return summary[c + fixed_cols].val;
3112 }
3113 
GetSummary(Id id)3114 Value GridCtrl::GetSummary(Id id)
3115 {
3116 	return summary[aliases.Get(id)].val;
3117 }
3118 
IsModified(int r,int c)3119 bool GridCtrl::IsModified(int r, int c)
3120 {
3121 	return items[vitems[r + fixed_rows].id][c + fixed_cols].modified;
3122 }
3123 
IsModified(int c)3124 bool GridCtrl::IsModified(int c)
3125 {
3126 	return items[vitems[rowidx].id][c + fixed_cols].modified;
3127 }
3128 
IsModified(int r,Id id)3129 bool GridCtrl::IsModified(int r, Id id)
3130 {
3131 	return items[vitems[r + fixed_rows].id][aliases.Get(id)].modified;
3132 }
3133 
IsModified(Id id)3134 bool GridCtrl::IsModified(Id id)
3135 {
3136 	return items[vitems[rowidx].id][aliases.Get(id)].modified;
3137 }
3138 
ReadCol(int n,int start_row,int end_row) const3139 Vector<Value> GridCtrl::ReadCol(int n, int start_row, int end_row) const
3140 {
3141 	Vector<Value> v;
3142 	int idx = hitems[n < 0 ? colidx : n + fixed_cols].id;
3143 
3144 	if(start_row < 0)
3145 		start_row = fixed_rows;
3146 	else
3147 		start_row += fixed_rows;
3148 
3149 	if(end_row < 0)
3150 		end_row = total_rows - 1;
3151 	else
3152 		end_row += fixed_rows;
3153 
3154 	for(int i = start_row; i <= end_row; i++)
3155 		v.Add(items[i][idx].val);
3156 
3157 	return v;
3158 }
3159 
ReadRow(int n,int start_col,int end_col) const3160 Vector<Value> GridCtrl::ReadRow(int n, int start_col, int end_col) const
3161 {
3162 	Vector<Value> v;
3163 	int idy = vitems[n < 0 ? rowidx : n + fixed_rows].id;
3164 
3165 	if(start_col < 0)
3166 		start_col = fixed_cols;
3167 	else
3168 		start_col += fixed_cols;
3169 
3170 	if(end_col < 0)
3171 		end_col = total_cols - 1;
3172 	else
3173 		end_col += fixed_cols;
3174 
3175 	for(int i = start_col; i <= end_col; i++)
3176 		v.Add(items[idy][i].val);
3177 
3178 	return v;
3179 }
3180 
GetValues()3181 Vector< Vector<Value> > GridCtrl::GetValues()
3182 {
3183 	Vector< Vector<Value> > v;
3184 
3185 	int rows_cnt = total_rows - fixed_rows;
3186 	int cols_cnt = total_cols - fixed_cols;
3187 
3188 	v.SetCount(rows_cnt);
3189 
3190 	for(int i = 0; i < rows_cnt; i++)
3191 	{
3192 		v[i].SetCount(cols_cnt);
3193 
3194 		for(int j = 0; j < cols_cnt; j++)
3195 		{
3196 			const Value &val = items[i + fixed_rows][j + fixed_cols].val;
3197 			if(IsType<AttrText>(val))
3198 			{
3199 				const AttrText& t = ValueTo<AttrText>(val);
3200 				v[i][j] = t.text;
3201 			}
3202 			else
3203 				v[i][j] = val;
3204 		}
3205 	}
3206 
3207 	return v;
3208 }
3209 
SetValues(const Vector<Vector<Value>> & v)3210 void GridCtrl::SetValues(const Vector< Vector<Value> >& v)
3211 {
3212 	int rows_cnt = v.GetCount();
3213 
3214 	if(rows_cnt <= 0)
3215 		return;
3216 
3217 	int cols_cnt = v[0].GetCount();
3218 
3219 	int tc = total_cols - fixed_cols;
3220 	if(cols_cnt > tc)
3221 		cols_cnt = tc;
3222 
3223 	SetRowCount(rows_cnt);
3224 
3225 	for(int i = 0; i < rows_cnt; i++)
3226 		for(int j = 0; j < cols_cnt; j++)
3227 		{
3228 			int r = i + fixed_rows;
3229 			int c = j + fixed_cols;
3230 			Ctrl * ctrl = items[r][c].ctrl;
3231 			if(ctrl)
3232 				ctrl->SetData(v[i][j]);
3233 			items[r][c].val = v[i][j];
3234 		}
3235 
3236 	SyncCtrls();
3237 	SyncSummary();
3238 	Refresh();
3239 }
3240 
Add(const Vector<Value> & v,int offset,int count,bool hidden)3241 GridCtrl& GridCtrl::Add(const Vector<Value> &v, int offset, int count, bool hidden)
3242 {
3243 	Append0(1, hidden ? 0 : GD_ROW_HEIGHT);
3244 
3245 	int cnt = min(count < 0 ? v.GetCount() : count,
3246 	              total_cols - fixed_cols);
3247 
3248 	int r0 = total_rows - 1;
3249 	int r = vitems[r0].id;
3250 	for(int i = offset; i < cnt; i++)
3251 		items[r][i + fixed_cols].val = v[i];
3252 
3253 	RefreshRow(r0, 0, 0);
3254 
3255 	return *this;
3256 }
3257 
IsColumn(const Id & id)3258 bool GridCtrl::IsColumn(const Id& id)
3259 {
3260 	return valid_cursor ? hitems[curpos.x].id == aliases.Get(id) : false;
3261 }
3262 
GetColumn(int n)3263 GridCtrl::ItemRect& GridCtrl::GetColumn(int n)
3264 {
3265 	return hitems[GetIdCol(n + fixed_cols)];
3266 }
3267 
GetColumn()3268 GridCtrl::ItemRect& GridCtrl::GetColumn()
3269 {
3270 	return hitems[curpos.x];
3271 }
3272 
GetRow(int n)3273 GridCtrl::ItemRect& GridCtrl::GetRow(int n)
3274 {
3275 	return vitems[n + fixed_rows];
3276 }
3277 
GetRow()3278 GridCtrl::ItemRect& GridCtrl::GetRow()
3279 {
3280 	return vitems[rowidx];
3281 }
3282 
GetCurrentRow() const3283 int GridCtrl::GetCurrentRow() const
3284 {
3285 	return rowidx - fixed_rows;
3286 }
3287 
IsCurrentRow() const3288 bool GridCtrl::IsCurrentRow() const
3289 {
3290 	return rowidx == curpos.y;
3291 }
3292 
RestoreCurrentRow()3293 void GridCtrl::RestoreCurrentRow()
3294 {
3295 	rowidx = curpos.y;
3296 }
3297 
GetCell(int n,int m)3298 GridCtrl::Item& GridCtrl::GetCell(int n, int m)
3299 {
3300 	return items[vitems[n + fixed_rows].id][hitems[m + fixed_cols].id];
3301 }
3302 
GetCell(int n,Id id)3303 GridCtrl::Item& GridCtrl::GetCell(int n, Id id)
3304 {
3305 	return items[vitems[n + fixed_rows].id][hitems[aliases.Get(id)].id];
3306 }
3307 
GetMouseCol(Point & p,bool relative,bool fixed,bool full)3308 int GridCtrl::GetMouseCol(Point &p, bool relative, bool fixed, bool full)
3309 {
3310 	if(!full && p.x < fixed_width)
3311 		return -1;
3312 
3313 	int dx = 0;
3314 
3315 	if(relative)
3316 		dx += sbx;
3317 
3318 	int first_col = fixed ? 0 : max(firstVisCol, fixed_cols);
3319 	int last_col = max(lastVisCol, fixed_cols - 1);
3320 
3321 	if(!fixed && last_col >= total_cols)
3322 		return -1;
3323 
3324 	for(int i = first_col; i <= last_col; i++)
3325 	{
3326 		if(p.x >= hitems[i].nLeft(dx) &&
3327 		   p.x  < hitems[i].nRight(dx))
3328 			return i;
3329 	}
3330 	return -1;
3331 }
3332 
GetMouseRow(Point & p,bool relative,bool fixed,bool full)3333 int GridCtrl::GetMouseRow(Point &p, bool relative, bool fixed, bool full)
3334 {
3335 	if(!full && p.y < fixed_height)
3336 		return -1;
3337 
3338 	int dy = 0;
3339 
3340 	if(relative)
3341 		dy += sby;
3342 
3343 	int first_row = fixed ? 0 : max(firstVisRow, fixed_rows);
3344 	int last_row = max(lastVisRow, fixed_rows - 1);
3345 
3346 	if(!fixed && last_row >= total_rows)
3347 		return -1;
3348 
3349 	for(int i = first_row; i <= last_row; i++)
3350 	{
3351 		if(p.y >= vitems[i].nTop(dy) &&
3352 		   p.y  < vitems[i].nBottom(dy))
3353 			return i;
3354 	}
3355 	return -1;
3356 }
3357 
MouseAccel(const Point & p,bool horz,bool vert,dword keyflags)3358 void GridCtrl::MouseAccel(const Point &p, bool horz, bool vert, dword keyflags)
3359 {
3360 	Size sz = GetSize();
3361 	int speedx = 0, speedy = 0;
3362 	const int bound = 5;
3363 
3364 	if(horz)
3365 	{
3366 		if(p.x > sz.cx - bound)
3367 			speedx = p.x - (sz.cx - bound);
3368 		else if(p.x < fixed_width + bound)
3369 			speedx = -(bound - p.x + fixed_width);
3370 	}
3371 
3372 	if(vert)
3373 	{
3374 		if(p.y > sz.cy - bound)
3375 			speedy = p.y - (sz.cy - bound);
3376 		else if(p.y < fixed_height + bound)
3377 			speedy = -(bound - p.y + fixed_height);
3378 	}
3379 
3380 	if(speedx) sbx.Set(sbx + speedx);
3381 	if(speedy) sby.Set(sby + speedy);
3382 
3383 	if(speedx || speedy)
3384 	{
3385 		LG(0, "speedx %d, speedy %d", speedx, speedy);
3386 		MouseMove(p, keyflags);
3387 	}
3388 
3389 }
3390 
HorzPosImage()3391 Image GridCtrl::HorzPosImage()
3392 {
3393 	#ifdef PLATFORM_X11
3394 		return Image::SizeHorz();
3395 	#else
3396 		return GridImg::HorzPos();
3397 	#endif
3398 }
3399 
VertPosImage()3400 Image GridCtrl::VertPosImage()
3401 {
3402 	#ifdef PLATFORM_X11
3403 		return Image::SizeVert();
3404 	#else
3405 		return GridImg::VertPos();
3406 	#endif
3407 }
3408 
CursorImage(Point p,dword keyflags)3409 Image GridCtrl::CursorImage(Point p, dword keyflags)
3410 {
3411 	if(!moving_header && !moving_body && HasCapture())
3412 	{
3413 		if(resizing_cols && curSplitCol >= 0)
3414 			return HorzPosImage();
3415 		if(resizing_rows && curSplitRow >= 0)
3416 			return VertPosImage();
3417 		else
3418 			return Image::Arrow();
3419 	}
3420 
3421 	if(moving_header)
3422 	{
3423 		curSplitCol = GetSplitCol(p, -1);
3424 		curSplitRow = GetSplitRow(p, -1);
3425 
3426 		if(resize_col_mode == 0 || resize_row_mode == 0)
3427 			MouseAccel(p, fixed_top_click, fixed_left_click, keyflags);
3428 
3429 		return Image::Arrow();
3430 	}
3431 	else if(moving_body)
3432 	{
3433 		curSplitRow = GetSplitRow(Point(0, p.y), -1);
3434 		return Image::Arrow();
3435 	}
3436 	else if(mouse_move)
3437 	{
3438 		curSplitCol = GetSplitCol(p);
3439 		curSplitRow = GetSplitRow(p);
3440 		mouse_move = false;
3441 	}
3442 
3443 	curResizeCol = curResizeRow = false;
3444 
3445 	if(resizing_cols && curSplitCol >= 0 || resizeCol)
3446 	{
3447 		if(curSplitCol >= 0 && hitems[curSplitCol].join > 0)
3448 		{
3449 			int idy = GetMouseRow(p, true, p.y < fixed_height, true);
3450 			if(idy >= 0)
3451 			{
3452 				Item &it = items[vitems[idy].id][hitems[curSplitCol].id];
3453 				if(it.isjoined && it.idx + it.cx != curSplitCol)
3454 					return Image::Arrow();
3455 			}
3456 		}
3457 		curResizeCol = true;
3458 		return HorzPosImage();
3459 	}
3460 	else if(resizing_rows && curSplitRow >= 0 || resizeRow)
3461 	{
3462 		if(curSplitRow >= 0 && vitems[curSplitRow].join > 0)
3463 		{
3464 			int idx = GetMouseCol(p, true, p.x < fixed_width, true);
3465 			if(idx >= 0)
3466 			{
3467 				Item &it = items[vitems[curSplitRow].id][hitems[idx].id];
3468 				if(it.isjoined && it.idy + it.cy != curSplitRow)
3469 					return Image::Arrow();
3470 			}
3471 		}
3472 		curResizeRow = true;
3473 		return VertPosImage();
3474 	}
3475 	return Image::Arrow();
3476 }
3477 
3478 
UpdateHolder(bool force)3479 void GridCtrl::UpdateHolder(bool force)
3480 {
3481 	if(size_changed || force)
3482 	{
3483 		holder.SetOffset(Point(fixed_width, fixed_height));
3484 		holder.HSizePos(fixed_width, 0).VSizePos(fixed_height, summary_height);
3485 		size_changed = false;
3486 	}
3487 }
3488 
SetCursor0(int x,int y,int opt,int dirx,int diry)3489 GridCtrl::CurState GridCtrl::SetCursor0(int x, int y, int opt, int dirx, int diry)
3490 {
3491 	return SetCursor0(Point(x, y), opt, dirx, diry);
3492 }
3493 
SetCursor0(Point p,int opt,int dirx,int diry)3494 GridCtrl::CurState GridCtrl::SetCursor0(Point p, int opt, int dirx, int diry)
3495 {
3496 	CurState cs;
3497 	if(!row_changing)
3498 	{
3499 		cs.valid = false;
3500 		return cs;
3501 	}
3502 
3503 	bool mouse = opt & CU_MOUSE;
3504 	bool highlight = opt & CU_HIGHLIGHT;
3505 	bool ctrlmode = opt & CU_CTRLMODE;
3506 	bool hidectrls = opt & CU_HIDECTRLS;
3507 
3508 	Point tmpcur;
3509 
3510 	bool mouse_valid = true;
3511 
3512 	if(mouse)
3513 	{
3514 		tmpcur.x = GetMouseCol(p, true, false);
3515 		tmpcur.y = GetMouseRow(p, true, false);
3516 		if(tmpcur.x < 0 || tmpcur.y < 0)
3517 			mouse_valid = false;
3518 	}
3519 	else
3520 		tmpcur = p;
3521 
3522 	if(!highlight && dirx == 0 && diry == 0 && !IsValidCursor(tmpcur))
3523 	{
3524 		cs.valid = false;
3525 		return cs;
3526 	}
3527 
3528 	Point oldcur = highlight ? livecur : curpos;
3529 
3530 	bool oldvalid = IsValidCursorAll(oldcur);
3531 	bool newvalid = false;
3532 	Item *nit = NULL;
3533 
3534 	if(!highlight && mouse_valid)
3535 	{
3536 		bool quit = false;
3537 
3538 		int fc = max(fixed_cols, firstVisCol);
3539 		int lc = lastVisCol;
3540 		int fr = max(fixed_rows, firstVisRow);
3541 		int lr = lastVisRow;
3542 
3543 		Item *oit = oldvalid ? &GetItem(oldcur) : NULL;
3544 
3545 		while(true)
3546 		{
3547 			bool cur = IsValidCursor(tmpcur, fc, lc, fr, lr);
3548 
3549 			bool hidden = true;
3550 			bool clickable = true;
3551 			bool group = true;
3552 
3553 			if(cur)
3554 			{
3555 				ItemRect& h = hitems[tmpcur.x];
3556 				ItemRect& v = vitems[tmpcur.y];
3557 				bool hx   = dirx != 0 ? h.hidden : false;
3558 				bool hy   = diry != 0 ? v.hidden : false;
3559 				hidden    = hx || hy;
3560 				clickable = h.clickable && v.clickable;
3561 				if(oit && oit->group >= 0 && !select_row)
3562 				{
3563 					nit = &GetItem(tmpcur);
3564 					group = nit->group != oit->group;
3565 				}
3566 			}
3567 
3568 			newvalid = cur && !hidden && clickable && group;
3569 
3570 			if(newvalid)
3571 			{
3572 				int idx = hitems[tmpcur.x].id;
3573 				int idy = vitems[tmpcur.y].id;
3574 
3575 				Item &it = items[idy][idx];
3576 
3577 				newvalid = it.clickable;
3578 
3579 				if(newvalid && ctrlmode)
3580 				{
3581 					Ctrl * ctrl = it.ctrl;
3582 					if(!ctrl && isedit)
3583 						ctrl = edits[idx].ctrl;
3584 
3585 					if(ctrl && it.editable && ctrl->IsEnabled())
3586 						break;
3587 				}
3588 			}
3589 
3590 			if(newvalid && !ctrlmode)
3591 				break;
3592 
3593 			if(quit || (dirx == 0 && diry == 0))
3594 				return cs;
3595 
3596 			if(dirx != 0)
3597 			{
3598 				tmpcur.x += dirx;
3599 
3600 				if(tmpcur.x > lc)
3601 				{
3602 					if(tab_changes_row && diry == 0)
3603 					{
3604 						tmpcur.y += 1;
3605 						if(tmpcur.y > lr)
3606 						{
3607 							tmpcur.y = lr;
3608 							tmpcur.x = lc;
3609 							quit = true;
3610 						}
3611 						else
3612 							tmpcur.x = fc;
3613 					}
3614 					else
3615 						quit = true;
3616 				}
3617 				else if(tmpcur.x < fc)
3618 				{
3619 					if(tab_changes_row && diry == 0)
3620 					{
3621 						tmpcur.y -= 1;
3622 						if(tmpcur.y < fr)
3623 						{
3624 							tmpcur.y = fr;
3625 							tmpcur.x = fc;
3626 							quit = true;
3627 						}
3628 						else
3629 							tmpcur.x = lc;
3630 					}
3631 					else
3632 						quit = true;
3633 				}
3634 				continue;
3635 			}
3636 
3637 			if(diry != 0)
3638 			{
3639 				tmpcur.y += diry;
3640 
3641 				if(tmpcur.y < fr)
3642 				{
3643 					tmpcur.y = fr;
3644 					quit = true;
3645 				}
3646 				else if(tmpcur.y > lr)
3647 				{
3648 					tmpcur.y = lr;
3649 					quit = true;
3650 				}
3651 			}
3652 		}
3653 	}
3654 	else
3655 	{
3656 		newvalid = IsValidCursor(tmpcur);
3657 		if(newvalid && highlight)
3658 		{
3659 			if(!vitems[tmpcur.y].clickable || !hitems[tmpcur.x].clickable)
3660 				newvalid = false;
3661 		}
3662 	}
3663 
3664 	bool isnewcol = oldcur.x != tmpcur.x;
3665 	bool isnewrow = oldcur.y != tmpcur.y;
3666 
3667 	if(isnewcol || isnewrow)
3668 		this->oldcur = oldcur;
3669 
3670 	cs.valid = newvalid;
3671 
3672 	if(!highlight)
3673 	{
3674 		if(!GetCtrlsData(!isnewrow))
3675 		{
3676 			cs.accepted = false;
3677 			return cs;
3678 		}
3679 		else
3680 		{
3681 			cs.accepted = true;
3682 			if(hidectrls && (edit_mode == GE_CELL || (edit_mode == GE_ROW && (isnewrow || !newvalid))))
3683 			{
3684 				UpdateCtrls(UC_HIDE | UC_CTRLS | UC_OLDCUR);
3685 				if(!one_click_edit || !newvalid)
3686 					WhenEndEdit();
3687 			}
3688 		}
3689 
3690 		oldvalid = IsValidCursorAll(oldcur);
3691 	}
3692 
3693 	if(tmpcur == oldcur)
3694 		return cs;
3695 
3696 	if(highlight)
3697 	{
3698 		livecur = tmpcur;
3699 
3700 		if(oldvalid)
3701 		{
3702 			SetItemCursor(oldcur, false, true);
3703 			RefreshRow(oldcur.y, 0);
3704 		}
3705 		if(newvalid)
3706 		{
3707 			SetItemCursor(tmpcur, true, true);
3708 			RefreshRow(tmpcur.y, 0);
3709 		}
3710 		return cs;
3711 	}
3712 
3713 	if(!newvalid)
3714 		return cs;
3715 
3716 	if(isnewrow)
3717 		WhenBeforeChangeRow();
3718 
3719 	if(isnewcol)
3720 		WhenBeforeChangeCol();
3721 
3722 	cs.newx = isnewcol;
3723 	cs.newy = isnewrow;
3724 
3725 	Point t_curpos = curpos;
3726 	Point t_curid = curid;
3727 	int t_colidx = colidx;
3728 	int t_rowidx = rowidx;
3729 	bool t_valid_cursor = valid_cursor;
3730 
3731 	valid_cursor = true;
3732 	curpos = tmpcur;
3733 	colidx = curpos.x;
3734 	rowidx = curpos.y;
3735 
3736 	curid.x = hitems[curpos.x].id;
3737 	curid.y = vitems[curpos.y].id;
3738 
3739 	WhenCursor();
3740 
3741 	if(cancel_cursor)
3742 	{
3743 		cancel_cursor = false;
3744 		curpos = t_curpos;
3745 		curid = t_curid;
3746 		colidx = t_colidx;
3747 		rowidx = t_rowidx;
3748 		valid_cursor = t_valid_cursor;
3749 		cs.Clear();
3750 		return cs;
3751 	}
3752 
3753 	if(oldvalid)
3754 	{
3755 		SetItemCursor(oldcur, false, highlight);
3756 		RefreshRow(oldcur.y, 0);
3757 	}
3758 
3759 	SetItemCursor(tmpcur, true, false);
3760 	if(isnewrow || (!select_row && isnewcol))
3761 		RefreshRow(tmpcur.y, 0);
3762 
3763 	if(call_whenchangerow && isnewrow)
3764 	{
3765 		#ifdef LOG_CALLBACKS
3766 		LGR(2, "WhenChangeRow()");
3767 		LGR(2, Format("[row: %d]", rowidx));
3768 		#endif
3769 		WhenChangeRow();
3770 	}
3771 
3772 	if(call_whenchangecol && isnewcol)
3773 	{
3774 		#ifdef LOG_CALLBACKS
3775 		LGR(2, "WhenChangeCol()");
3776 		LGR(2, Format("[col: %d]",colidx));
3777 		#endif
3778 		WhenChangeCol();
3779 	}
3780 
3781 	if(isnewrow)
3782 		SetCtrlsData();
3783 
3784 	LG(0, "cur(%d, %d)", curpos.x, curpos.y);
3785 
3786 	return cs;
3787 }
3788 
GetWidth(int n)3789 int GridCtrl::GetWidth(int n)
3790 {
3791 	if(n < 0) n = total_cols;
3792 	if(n == 0) return 0;
3793 	return hitems[n - 1].nRight();
3794 }
3795 
GetHeight(int n)3796 int GridCtrl::GetHeight(int n)
3797 {
3798 	if(n < 0) n = total_rows;
3799 	if(n == 0) return 0;
3800 	return vitems[n - 1].nBottom();
3801 }
3802 
GetFixedWidth()3803 int GridCtrl::GetFixedWidth()
3804 {
3805 	return GetWidth(fixed_cols);
3806 }
3807 
GetFixedHeight()3808 int GridCtrl::GetFixedHeight()
3809 {
3810 	return GetHeight(fixed_rows);
3811 }
3812 
GetFirst0(Vector<ItemRect> & its,int total,int sb,int p)3813 int GridCtrl::GetFirst0(Vector<ItemRect> &its, int total, int sb, int p)
3814 {
3815 	int l = 0;
3816 	int r = total - 1;
3817 
3818 	while(l <= r)
3819 	{
3820 		int i = (l + r) / 2;
3821 
3822 		int p0 = its[i].nLeft(sb);
3823 		int p1 = its[i].nRight(sb);
3824 
3825 		if(p0 <= p && p1 >= p)
3826 		{
3827 			if(!its[i].hidden)
3828 			{
3829 				return i;
3830 			}
3831 			else
3832 			{
3833 				for(int j = i + 1; j < total; j++)
3834 					if(!its[j].hidden)
3835 						return j;
3836 				for(int j = i - 1; j > 0; j--)
3837 					if(!its[j].hidden)
3838 						return j;
3839 
3840 				return -1;
3841 			}
3842 		}
3843 
3844 		if(p1 < p)
3845 			l = i + 1;
3846 		else
3847 			r = i - 1;
3848 	}
3849 	return -1;
3850 }
3851 
GetFirstVisCol(int p)3852 int GridCtrl::GetFirstVisCol(int p)
3853 {
3854 	return total_cols <= 2 ? fixed_cols : GetFirst0(hitems, total_cols, sbx, p);
3855 }
3856 
GetFirstVisRow(int p)3857 int GridCtrl::GetFirstVisRow(int p)
3858 {
3859 	return total_rows <= 1 ? fixed_rows : GetFirst0(vitems, total_rows, sby, p);
3860 }
3861 
SetColWidth(int n,int width,bool recalc)3862 GridCtrl& GridCtrl::SetColWidth(int n, int width, bool recalc /* = true */)
3863 {
3864 	if(resize_col_mode > 0 && n >= fixed_cols)
3865 		return *this;
3866 
3867 	hitems[n].Width(width);
3868 	Repaint(true, false);
3869 
3870 	return *this;
3871 }
3872 
SetRowHeight(int n,int height,bool recalc)3873 GridCtrl& GridCtrl::SetRowHeight(int n, int height, bool recalc)
3874 {
3875 	LG(0, "SetRowHeight %d %d", n, height);
3876 
3877 	if(resize_row_mode > 0 && n >= fixed_rows)
3878 		return *this;
3879 
3880 	vitems[n].Height(height);
3881 	Repaint(false, true);
3882 
3883 	return *this;
3884 }
3885 
SetDiffItemSize(bool horizontal,RectItems & its,int n,int diff,bool newsize)3886 bool GridCtrl::SetDiffItemSize(bool horizontal, RectItems &its, int n, int diff, bool newsize)
3887 {
3888 	if(diff == 0)
3889 		return false;
3890 
3891 	if(diff < 0 && its[n].IsMin())
3892 		return false;
3893 
3894 	if(diff > 0 && its[n].IsMax())
3895 		return false;
3896 
3897 	int resize_mode = horizontal ? resize_col_mode : resize_row_mode;
3898 
3899 	if(resize_mode > 0 && diff > 0)
3900 	{
3901 		bool ismin = true;
3902 		for(int i = n + 1; i < (horizontal ? total_cols : total_rows); i++)
3903 			if(!its[i].IsMin())
3904 			{
3905 				ismin = false;
3906 				break;
3907 			}
3908 		if(ismin)
3909 			return false;
3910 	}
3911 
3912 	double size = its[n].size + diff;
3913 
3914 	if(size <= its[n].min)
3915 	{
3916 		size = its[n].min;
3917 		its[n].ismin = true;
3918 	}
3919 	else if(size >= its[n].max)
3920 	{
3921 		size = its[n].max;
3922 		its[n].ismax = true;
3923 	}
3924 	else
3925 	{
3926 		its[n].ismin = false;
3927 		its[n].ismax = false;
3928 	}
3929 
3930 	double ddiff = size - its[n].size;
3931 
3932 	if(ddiff != 0)
3933 	{
3934 		Recalc(horizontal, its, n, size, ddiff);
3935 		return true;
3936 	}
3937 	return false;
3938 }
3939 
Recalc(bool horizontal,RectItems & its,int n,double size,double diff)3940 void GridCtrl::Recalc(bool horizontal, RectItems &its, int n, double size, double diff)
3941 {
3942 	its[n].size = size;
3943 
3944 	Size sz = GetSize();
3945 	int cnt = horizontal ? total_cols : total_rows;
3946 	int maxsize = horizontal ? sz.cx : sz.cy;
3947 	int tcnt = cnt;
3948 
3949 	int resize_mode = horizontal ? resize_col_mode : resize_row_mode;
3950 
3951 	if(resize_mode == 0)
3952 	{
3953 		for(int i = n + 1; i < cnt; i++)
3954 			its[i].pos += diff;
3955 	}
3956 	else if(resize_mode == 1)
3957 	{
3958 		double imaxsize = 1.0 / (double) maxsize;
3959 		double ms = maxsize;
3960 
3961 		loop:
3962 			double sumprop = 0;
3963 
3964 			for(int i = cnt - 1; i >= n + 1; --i)
3965 			{
3966 				if(its[i].hidden) continue;
3967 
3968 				bool prop = (diff > 0 && its[i].IsMin() || diff < 0 && its[i].IsMax());
3969 				if(!prop)
3970 					sumprop += its[i].prop;
3971 			}
3972 
3973 			double cps = sumprop != 0 ? -diff / sumprop : 0;
3974 
3975 			for(int i = cnt - 1; i >= n + 1; --i)
3976 			{
3977 				if(its[i].hidden)
3978 				{
3979 					its[i].pos = ms;
3980 					continue;
3981 				}
3982 
3983 				if(!(diff > 0 && its[i].IsMin() || diff < 0 && its[i].IsMax()))
3984 				{
3985 					double size = its[i].size + its[i].prop * cps;
3986 
3987 					bool minsize = (diff > 0 && size < its[i].min);
3988 					bool maxsize = (diff < 0 && size > its[i].max);
3989 
3990 					its[i].ismin = minsize;
3991 					its[i].ismax = maxsize;
3992 
3993 					if(minsize || maxsize)
3994 					{
3995 						diff += size - its[i].size;
3996 						double ns = minsize ? its[i].min : its[i].max;
3997 						its[i].size = ns;
3998 						its[i].prop = ns * imaxsize;
3999 						cnt = i + 1;
4000 						goto loop;
4001 					}
4002 					its[i].size = size;
4003 					its[i].prop = size * imaxsize;
4004 				}
4005 				ms -= its[i].size;
4006 				its[i].pos = ms;
4007 			}
4008 
4009 			its[n].size -= its[n].pos + its[n].size - ms;
4010 			its[n].prop = its[n].size * imaxsize;
4011 	}
4012 
4013 	CalcIntPos(its, n, maxsize, tcnt - 1, resize_mode, false);
4014 }
4015 
CalcIntPos(RectItems & its,int n,int maxsize,int cnt,int resize_mode,bool renumber)4016 void GridCtrl::CalcIntPos(RectItems &its, int n, int maxsize, int cnt, int resize_mode, bool renumber)
4017 {
4018 	its[0].npos = 0;
4019 
4020 	int last_vis = 1;
4021 	int hidden = 0;
4022 
4023 	for(int i = (renumber ? 1 : max(1, n)); i <= cnt ; i++)
4024 	{
4025 		its[i].npos = Round(its[i].Left());
4026 		its[i - 1].nsize = its[i].npos - its[i - 1].npos;
4027 
4028 		if(renumber)
4029 			its[i].n = hidden;
4030 
4031 		if(its[i].hidden)
4032 			hidden++;
4033 		else
4034 			last_vis = i;
4035 	}
4036 
4037 	last_vis = cnt;
4038 	if(resize_mode > 0)
4039 	{
4040 		int size = maxsize - its[last_vis].npos;
4041 		its[last_vis].nsize = size ;//>= its[cnt].min && size <= its[cnt].max ? size : Round(its[cnt].size);
4042 	}
4043 	else
4044 		its[last_vis].nsize = Round(its[last_vis].size);
4045 }
4046 
UpdateSizes()4047 bool GridCtrl::UpdateSizes()
4048 {
4049 	total_width  = total_cols ? hitems[total_cols - 1].nRight() : 0;
4050 	total_height = total_rows ? vitems[total_rows - 1].nRight() : 0;
4051 
4052 	int prev_summary_height = summary_height;
4053 
4054 	summary_height = summary_row ? GD_HDR_HEIGHT : 0;
4055 
4056 	size_changed = prev_summary_height != summary_height;
4057 
4058 	int new_fixed_width  = fixed_cols ? hitems[fixed_cols - 1].nRight() : 0;
4059 	int new_fixed_height = fixed_rows ? vitems[fixed_rows - 1].nRight() : 0;
4060 
4061 	Size sz = GetSize();
4062 
4063 	if(resize_col_mode > 0 && new_fixed_width >= sz.cx)
4064 		new_fixed_width = GridCtrl::GD_IND_WIDTH;
4065 
4066 	if(resize_row_mode > 0 && new_fixed_height >= sz.cy)
4067 		new_fixed_height = GridCtrl::GD_HDR_HEIGHT;
4068 
4069 	if(fixed_cols == 1 && !indicator)
4070 		new_fixed_width = 0;
4071 
4072 	if(new_fixed_width != fixed_width)
4073 	{
4074 		fixed_width = new_fixed_width;
4075 		size_changed = true;
4076 	}
4077 
4078 	if(new_fixed_height != fixed_height)
4079 	{
4080 		fixed_height = new_fixed_height;
4081 		size_changed = true;
4082 	}
4083 
4084 	return size_changed;
4085 }
4086 
UpdateCols(bool force)4087 bool GridCtrl::UpdateCols(bool force)
4088 {
4089 	Size sz = GetSize();
4090 	bool change = false;;
4091 
4092 	if((osz.cx != sz.cx && resize_col_mode > 0) || force || recalc_cols)
4093 	{
4094 		RecalcCols(-1);
4095 		recalc_cols = false;
4096 		change = true;
4097 	}
4098 
4099 	osz.cx = sz.cx;
4100 	return change;
4101 }
4102 
UpdateRows(bool force)4103 bool GridCtrl::UpdateRows(bool force)
4104 {
4105 	Size sz = GetSize();
4106 	bool change = false;;
4107 
4108 	if((osz.cy != sz.cy && resize_row_mode > 0) || force || recalc_rows)
4109 	{
4110 		RecalcRows(-1);
4111 		recalc_rows = false;
4112 		change = true;
4113 	}
4114 	osz.cy = sz.cy;
4115 	return change;
4116 }
4117 
Recalc(bool horizontal,RectItems & its,int resize_mode)4118 bool GridCtrl::Recalc(bool horizontal, RectItems &its, int resize_mode)
4119 {
4120 	Size sz = GetSize();
4121 
4122 	if(resize_mode < 0)
4123 		resize_mode = horizontal ? resize_col_mode : resize_row_mode;
4124 
4125 	int fixed = horizontal ? fixed_cols : fixed_rows;
4126 	int cnt = horizontal ? total_cols : total_rows;
4127 	int tcnt = cnt;
4128 
4129 	its[0].pos = 0;
4130 
4131 	if(!horizontal && !header)
4132 		its[0].size = 0;
4133 
4134 	if(resize_mode == 0)
4135 	{
4136 		for(int i = 1; i < cnt; i++)
4137 			its[i].pos = its[i - 1].pos + its[i - 1].size;
4138 	}
4139 	else if(resize_mode == 1)
4140 	{
4141 		int cs = horizontal ? sz.cx - fixed_width
4142 		                    : sz.cy - fixed_height;
4143 
4144 		//int cs = horizontal ? sz.cx : sz.cy;
4145 
4146 		if(cs <= 0)
4147 			return false;
4148 
4149 		double imaxsize = 1.0 / cs;
4150 
4151 		for(int i = fixed; i < cnt; i++)
4152 		{
4153 			its[i].ismin = false;
4154 			its[i].ismax = false;
4155 		}
4156 
4157 		double sumprop = 0;
4158 		for(int i = fixed; i < cnt; i++)
4159 			if(!its[i].hidden)
4160 				sumprop += its[i].prop;
4161 
4162 		double ics = sumprop <= 0 ? 0 : cs / sumprop;
4163 		sumprop = 0;
4164 
4165 		for(int i = fixed; i < cnt; i++)
4166 		{
4167 			if(its[i].hidden)
4168 				continue;
4169 
4170 			its[i].size = its[i].prop * ics;
4171 
4172 			if(its[i].size < its[i].min)
4173 			{
4174 				cs -= its[i].min;
4175 				its[i].size = its[i].min;
4176 				its[i].ismin = true;
4177 				its[i].prop = its[i].min * imaxsize;
4178 			}
4179 			else if(its[i].size > its[i].max)
4180 			{
4181 				cs -= its[i].max;
4182 				its[i].size = its[i].max;
4183 				its[i].ismax = true;
4184 				its[i].prop = its[i].max * imaxsize;
4185 			}
4186 			else
4187 				sumprop += its[i].prop;
4188 		}
4189 
4190 		ics = sumprop <= 0 ? 0 : cs / sumprop;
4191 
4192 		for(int i = fixed; i < cnt; i++)
4193 		{
4194 			its[i].pos = i == 0 ? 0 : its[i - 1].Right();
4195 			if(its[i].hidden)
4196 			{
4197 				its[i].size = 0;
4198 				//its[i].prop = 0;
4199 				continue;
4200 			}
4201 
4202 			if(!its[i].ismin && !its[i].ismax)
4203 				its[i].size = its[i].prop * ics;
4204 			its[i].prop = its[i].size * imaxsize;
4205 		}
4206 	}
4207 
4208 	CalcIntPos(its, 0, horizontal ? sz.cx : sz.cy, tcnt - 1, resize_mode, true);
4209 
4210 	UpdateVisColRow(horizontal);
4211 	oldpos.x = sbx;
4212 	oldpos.y = sby;
4213 
4214 	return true;
4215 }
4216 
RecalcCols(int mode)4217 bool GridCtrl::RecalcCols(int mode)
4218 {
4219 	return Recalc(true, hitems, mode);
4220 }
4221 
RecalcRows(int mode)4222 bool GridCtrl::RecalcRows(int mode)
4223 {
4224 	return Recalc(false, vitems, mode);
4225 }
4226 
GetSplitCol(const Point & p,int splitSize,bool full)4227 int GridCtrl::GetSplitCol(const Point &p, int splitSize, bool full)
4228 {
4229 	if(total_cols < 2)
4230 		return -1;
4231 
4232 	int diff = 0;
4233 	if(p.x > fixed_width || moving_body || moving_header)
4234 	{
4235 		if(!full && !full_col_resizing && p.y >= fixed_height)
4236 			return -1;
4237 		diff = sbx;
4238 	}
4239 	else if(p.y < fixed_height - splitSize)
4240 		return -1;
4241 
4242 	int tc = splitSize >= 0 ? (resize_col_mode == 0 ? 0 : 1) : 0;
4243 
4244 	int fc = lastVisCol - tc;
4245 	int lc = resizing_fixed_cols ? 1 : firstVisCol;
4246 
4247 	if(splitSize >= 0)
4248 	{
4249 		for(int i = fc; i >= lc; i--)
4250 		{
4251 			if(hitems[i].hidden) continue;
4252 			int x = hitems[i].nRight(diff);
4253 			if(p.x >= x - splitSize &&
4254 			   p.x <= x + splitSize)
4255 				return i;
4256 		}
4257 	}
4258 	else
4259 	{
4260 		int c = fc;
4261 		for(int i = fc; i >= lc; i--)
4262 		{
4263 			if(!hitems[i].hidden) c = i;
4264 			int x = hitems[c].nLeft(diff) + hitems[c].nWidth() / 2;
4265 			if(p.x >= x)
4266 				return c < fixed_cols ? firstVisCol - 1 : c;
4267 			else if(i == lc)
4268 				return c - 1;
4269 		}
4270 	}
4271 
4272 	return -1;
4273 }
4274 
GetSplitRow(const Point & p,int splitSize,bool full)4275 int GridCtrl::GetSplitRow(const Point &p, int splitSize, bool full)
4276 {
4277 	if(total_rows < 2)
4278 		return -1;
4279 
4280 	int diff = 0;
4281 	if(p.y > fixed_height || moving_body || moving_header)
4282 	{
4283 		if(!full && !moving_header && !moving_body && !full_row_resizing && p.x >= fixed_width)
4284 			return -1;
4285 		diff = sby;
4286 	}
4287 	else if(p.x < fixed_width)
4288 		return -1;
4289 
4290 	int tr = splitSize >= 0 ? (resize_row_mode == 0 ? 0 : 1) : 0;
4291 
4292 	int fr = lastVisRow - tr;
4293 	int lr = p.y < fixed_height && resizing_fixed_rows ? 0 : firstVisRow;
4294 
4295 	if(splitSize >= 0)
4296 	{
4297 		for(int i = fr; i >= lr; i--)
4298 		{
4299 			if(vitems[i].hidden)
4300 				continue;
4301 			int y = vitems[i].nBottom(diff);
4302 			if(p.y >= y - splitSize &&
4303 			   p.y <= y + splitSize)
4304 				return i;
4305 		}
4306 	}
4307 	else
4308 	{
4309 		int c = fr;
4310 		for(int i = fr; i >= lr; i--)
4311 		{
4312 			if(!vitems[i].hidden) c = i;
4313 			int y = vitems[c].nTop(diff) + vitems[c].nHeight() / 2;
4314 			if(p.y >= y)
4315 				return c < fixed_rows ? firstVisRow - 1 : c;
4316 			else if(i == lr)
4317 				return c - 1;
4318 		}
4319 	}
4320 
4321 	return -1;
4322 }
4323 
IsValidCursor(const Point & p,int fc,int lc,int fr,int lr) const4324 bool GridCtrl::IsValidCursor(const Point &p, int fc, int lc, int fr, int lr) const
4325 {
4326 	return p.x >= fc && p.x <= lc &&
4327 		   p.y >= fr && p.y <= lr;
4328 }
4329 
IsValidCursorVis(const Point & p) const4330 bool GridCtrl::IsValidCursorVis(const Point &p) const
4331 {
4332 	return p.x >= firstVisCol && p.x <= lastVisCol &&
4333 	       p.y >= firstVisRow && p.y <= lastVisRow;
4334 }
4335 
IsValidCursorAll(const Point & p) const4336 bool GridCtrl::IsValidCursorAll(const Point &p) const
4337 {
4338 	return p.x >= fixed_cols && p.x < total_cols &&
4339 	       p.y >= fixed_rows && p.y < total_rows;
4340 }
4341 
IsValidCursor(const Point & p) const4342 bool GridCtrl::IsValidCursor(const Point &p) const
4343 {
4344 	return ready ? IsValidCursorVis(p) : IsValidCursorAll(p);
4345 }
4346 
IsValidCursor(int c) const4347 bool GridCtrl::IsValidCursor(int c) const
4348 {
4349 	c += fixed_rows;
4350 	return c >= fixed_rows && c < total_rows;
4351 }
4352 
IsRowEditable(int r)4353 bool GridCtrl::IsRowEditable(int r)
4354 {
4355 	if(r < 0)
4356 		r = curpos.y;
4357 	else
4358 		r += fixed_rows;
4359 
4360 	return vitems[r].editable && hitems[curpos.x].editable;
4361 }
4362 
IsRowClickable(int r)4363 bool GridCtrl::IsRowClickable(int r /* = -1*/)
4364 {
4365 	if(r < 0)
4366 		r = curpos.y;
4367 	else
4368 		r += fixed_rows;
4369 
4370 	return vitems[r].clickable &&  hitems[curpos.x].clickable;
4371 }
4372 
SetItemCursor(Point p,bool b,bool highlight)4373 void GridCtrl::SetItemCursor(Point p, bool b, bool highlight)
4374 {
4375 	if(highlight)
4376 	{
4377 		hitems[p.x].Live(b);
4378 		vitems[p.y].Live(b);
4379 		GetItem(p).Live(b);
4380 	}
4381 	else
4382 	{
4383 		hitems[p.x].Cursor(b);
4384 		vitems[p.y].Cursor(b);
4385 		GetItem(p).Cursor(b);
4386 	}
4387 }
4388 
Indicator(bool b,int size)4389 GridCtrl& GridCtrl::Indicator(bool b, int size)
4390 {
4391 	if(size < 0)
4392 		size = GD_IND_WIDTH;
4393 	indicator = b;
4394 	fixed_width += size * (b ? 1 : -1);
4395 	SetColWidth(0, b ? size : 0);
4396 	return *this;
4397 }
4398 
RefreshRow(int n,bool relative,bool fixed)4399 void GridCtrl::RefreshRow(int n, bool relative, bool fixed)
4400 {
4401 	if(!ready)
4402 		return;
4403 	if(n < 0) { n = rowidx; relative = false; }
4404 	if(relative) n += fixed_rows;
4405 	if(vitems[n].hidden) return;
4406 	int dy = fixed ? 0 : sby;
4407 	int join = vitems[n].join;
4408 	if(join > 0)
4409 	{
4410 		int s = n;
4411 		while(s >= 0 && vitems[s].join > 0) s--;
4412 		s++;
4413 		int e = n;
4414 		while(e < total_rows && vitems[e].join > 0) e++;
4415 		e--;
4416 		Refresh(Rect(0, vitems[s].nTop(dy), GetSize().cx, vitems[e].nBottom(dy)));
4417 	}
4418 	else
4419 		Refresh(Rect(0, vitems[n].nTop(dy), GetSize().cx, vitems[n].nBottom(dy)));
4420 }
4421 
RefreshCol(int n,bool relative,bool fixed)4422 void GridCtrl::RefreshCol(int n, bool relative, bool fixed)
4423 {
4424 	if(!ready)
4425 		return;
4426 	if(n < 0) { n = curpos.x; relative = false; }
4427 	if(relative) n += fixed_cols;
4428 	if(hitems[n].hidden) return;
4429 	int dx = fixed ? 0 : sbx;
4430 	Refresh(Rect(hitems[n].nLeft(dx), 0, hitems[n].nRight(dx), GetSize().cy));
4431 }
4432 
RefreshRows(int from,int to,bool relative,bool fixed)4433 void GridCtrl::RefreshRows(int from, int to, bool relative, bool fixed)
4434 {
4435 	if(!ready)
4436 		return;
4437 	if(relative)
4438 	{
4439 		from += fixed_rows;
4440 		to += fixed_rows;
4441 	}
4442 	Refresh(Rect(0, vitems[from].nTop(sby), GetSize().cx, vitems[to].nBottom(sby)));
4443 }
4444 
RefreshFrom(int from)4445 void GridCtrl::RefreshFrom(int from)
4446 {
4447 	Size sz = GetSize();
4448 	int y = 0;
4449 	if(resize_row_mode == 0)
4450 		if(from > 2 && from <= total_rows)
4451 			y = vitems[from - 1].nBottom(sby);
4452 	Refresh(Rect(0, y, sz.cx, sz.cy));
4453 }
4454 
RefreshItem(int r,int c,bool relative)4455 void GridCtrl::RefreshItem(int r, int c, bool relative)
4456 {
4457 	if(!ready)
4458 		return;
4459 	if(relative)
4460 	{
4461 		c += fixed_cols;
4462 		r += fixed_rows;
4463 	}
4464 	Refresh(GetItemRect(r, c));
4465 }
4466 
RefreshNewRow()4467 void GridCtrl::RefreshNewRow()
4468 {
4469 	RefreshRow(rowidx, 0);
4470 }
4471 
RefreshTop()4472 void GridCtrl::RefreshTop()
4473 {
4474 	Refresh(0, 0, GetSize().cx, fixed_height);
4475 }
4476 
RefreshLeft()4477 void GridCtrl::RefreshLeft()
4478 {
4479 	Refresh(0, fixed_height, fixed_width, GetSize().cy - fixed_height);
4480 }
4481 
RefreshSummary()4482 void GridCtrl::RefreshSummary()
4483 {
4484 	Size sz = GetSize();
4485 	Refresh(0, sz.cy - GD_HDR_HEIGHT, sz.cx, GD_HDR_HEIGHT);
4486 }
4487 
IsMouseBody(Point & p)4488 bool GridCtrl::IsMouseBody(Point &p)
4489 {
4490 	return p.x >= fixed_width && p.x < total_width && p.y >= fixed_height && p.y < total_height;
4491 }
4492 
GetIdCol(int id,bool checkall) const4493 int GridCtrl::GetIdCol(int id, bool checkall) const
4494 {
4495 	for(int i = checkall ? 1 : fixed_cols; i < total_cols; i++)
4496 	{
4497 		if(id == hitems[i].id)
4498 			return i;
4499 	}
4500 	return -1;
4501 }
4502 
GetIdRow(int id,bool checkall) const4503 int GridCtrl::GetIdRow(int id, bool checkall) const
4504 {
4505 	for(int i = checkall ? 0 : fixed_rows; i < total_rows; i++)
4506 	{
4507 		if(id == vitems[i].id)
4508 			return i;
4509 	}
4510 	return -1;
4511 }
4512 
GetNextRow(int n)4513 int GridCtrl::GetNextRow(int n)
4514 {
4515 	n += fixed_rows;
4516 	for(int i = n + 1; i < total_rows; i++)
4517 		if(!vitems[i].hidden)
4518 			return i - fixed_rows;
4519 	return -1;
4520 }
4521 
GetPrevRow(int n)4522 int GridCtrl::GetPrevRow(int n)
4523 {
4524 	n += fixed_rows;
4525 	for(int i = n - 1; i >= fixed_rows; i--)
4526 		if(!vitems[i].hidden)
4527 			return i - fixed_rows;
4528 	return -1;
4529 }
4530 
UpdateCursor()4531 void GridCtrl::UpdateCursor()
4532 {
4533 	curpos.x = GetIdCol(curid.x);
4534 	curpos.y = GetIdRow(curid.y);
4535 	rowidx = curpos.y;
4536 	shiftpos = curpos;
4537 	ctrlid.y = curpos.y < 0 ? -1 : curid.y;
4538 	ctrlpos.y = curpos.y;
4539 	rowfnd = curpos.y;
4540 }
4541 
Find(const Value & v,int col,int start_from,int opt) const4542 int GridCtrl::Find(const Value &v, int col, int start_from, int opt) const
4543 {
4544 	for(int i = fixed_rows + start_from; i < total_rows; i++)
4545 	{
4546 		if(opt & GF::SKIP_CURRENT_ROW && i == rowidx)
4547 			continue;
4548 		if(opt & GF::SKIP_HIDDEN && vitems[i].hidden)
4549 			continue;
4550 		if(!vitems[i].skip && items[vitems[i].id][col + fixed_cols].val == v)
4551 			return i - fixed_rows;
4552 	}
4553 	return -1;
4554 }
4555 
Find(const Value & v,Id id,int opt) const4556 int GridCtrl::Find(const Value &v, Id id, int opt) const
4557 {
4558 	return Find(v, aliases.Get(id) - fixed_cols, 0, opt);
4559 }
4560 
FindInRow(const Value & v,int row,int start_from) const4561 int GridCtrl::FindInRow(const Value& v, int row, int start_from) const
4562 {
4563 	for(int i = fixed_cols + start_from; i < total_cols; i++)
4564 	{
4565 		if(!hitems[i].skip && items[row + fixed_rows][hitems[i].id].val == v)
4566 			return i - fixed_cols;
4567 	}
4568 	return -1;
4569 }
4570 
FindCurrent(Id id,int opt) const4571 int GridCtrl::FindCurrent(Id id, int opt) const
4572 {
4573 	int col = aliases.Get(id) - fixed_cols;
4574 	return Find(Get(col), col, 0, opt);
4575 }
4576 
Find(const Value & v0,Id id0,const Value & v1,Id id1,int opt) const4577 int GridCtrl::Find(const Value &v0, Id id0, const Value&v1, Id id1, int opt) const
4578 {
4579 	int col0 = aliases.Get(id0);
4580 	int col1 = aliases.Get(id1);
4581 
4582 	for(int i = fixed_rows; i < total_rows; i++)
4583 	{
4584 		if(opt & GF::SKIP_CURRENT_ROW && i == rowidx)
4585 			continue;
4586 		if(opt & GF::SKIP_HIDDEN && vitems[i].hidden)
4587 			continue;
4588 		if(!vitems[i].skip &&
4589 		   items[vitems[i].id][col0].val == v0 &&
4590 		   items[vitems[i].id][col1].val == v1)
4591 			return i - fixed_rows;
4592 	}
4593 	return -1;
4594 }
4595 
FindCurrent(Id id0,Id id1,int opt) const4596 int GridCtrl::FindCurrent(Id id0, Id id1, int opt) const
4597 {
4598 	int col0 = aliases.Get(id0);
4599 	int col1 = aliases.Get(id1);
4600 
4601 	const Value& val0 = items[vitems[rowidx].id][col0].val;
4602 	const Value& val1 = items[vitems[rowidx].id][col1].val;
4603 
4604 	return Find(val0, id0, val1, id1, opt);
4605 }
4606 
UpdateDefaults(int ri)4607 void GridCtrl::UpdateDefaults(int ri)
4608 {
4609 	for(int i = 1; i < total_cols; i++)
4610 		if(!IsNull(hitems[i].defval))
4611 			items[ri][hitems[i].id].val = hitems[i].defval;
4612 }
4613 
SetCtrlsData()4614 void GridCtrl::SetCtrlsData()
4615 {
4616 	if(!valid_cursor)
4617 		return;
4618 
4619 	for(int i = 1; i < total_cols; i++)
4620 	{
4621 		int idx = hitems[i].id;
4622 		Item &it = items[curid.y][idx];
4623 		Ctrl * ctrl = it.ctrl;
4624 		if(!ctrl)
4625 			ctrl = edits[idx].ctrl;
4626 		if(ctrl)
4627 		{
4628 			ctrl->SetData(it.val);
4629 			rowbkp[idx] = it.val;
4630 		}
4631 	}
4632 }
4633 
GetCtrlsData(bool samerow,bool doall,bool updates)4634 bool GridCtrl::GetCtrlsData(bool samerow, bool doall, bool updates)
4635 {
4636 	if(!valid_cursor || !HasCtrls())
4637 		return true;
4638 
4639 	bool newrow = newrow_inserted || newrow_appended;
4640 
4641 	if(focused_ctrl)
4642 	{
4643 		Item &it = items[curid.y][focused_ctrl_id];
4644 
4645 		if(updates && edit_mode == GE_CELL && !focused_ctrl->Accept())
4646 			return false;
4647 
4648 		Value v = focused_ctrl->GetData();
4649 
4650 		if(v.IsError())
4651 			v = Null;
4652 
4653 		bool was_modified = it.modified;
4654 
4655 		it.modified = edit_mode == GE_CELL
4656 			? it.val != v
4657 			: rowbkp[focused_ctrl_id] != v;
4658 
4659 		it.val = v;
4660 
4661 		if(it.modified)
4662 		{
4663 			if(!was_modified)
4664 				row_modified++;
4665 
4666 			//it.val = v;
4667 
4668 			if(updates)
4669 			{
4670 				#ifdef LOG_CALLBACKS
4671 				LGR(2, "WhenUpdateCell()");
4672 				LGR(2, Format("[row: %d, colid: %d]", curid.y, focused_ctrl_id));
4673 				LGR(2, Format("[oldval : %s]", AsString(rowbkp[focused_ctrl_id])));
4674 				LGR(2, Format("[newval : %s]", AsString(v)));
4675 				#endif
4676 				WhenUpdateCell();
4677 
4678 				if(cancel_update_cell)
4679 				{
4680 					it.val = rowbkp[focused_ctrl_id];
4681 					it.modified = false;
4682 					if(edit_mode == GE_CELL)
4683 						rowbkp[focused_ctrl_id] = it.val;
4684 					cancel_update_cell = false;
4685 					row_modified--;
4686 				}
4687 			}
4688 		}
4689 	}
4690 
4691 	if(!updates)
4692 		return false;
4693 
4694 	if(!samerow || doall)
4695 	{
4696 		if(edit_mode == GE_ROW)
4697 		{
4698 			for(int i = fixed_cols; i < total_cols; ++i)
4699 			{
4700 				int idx = hitems[i].id;
4701 				Ctrl * ctrl = GetCtrl(rowidx, i, true, false, false);
4702 				if(ctrl && !ctrl->Accept())
4703 				{
4704 					focused_ctrl = ctrl;
4705 					focused_ctrl_id = idx;
4706 					ctrl->SetFocus();
4707 					curpos.x = i;
4708 					return false;
4709 				}
4710 			}
4711 		}
4712 
4713 		if(row_modified)
4714 			vitems[curid.y].operation = GridOperation::UPDATE;
4715 
4716 		WhenAcceptRow();
4717 		if(cancel_accept)
4718 		{
4719 			cancel_accept = false;
4720 			return false;
4721 		}
4722 
4723 		bool removed = false;
4724 		if(newrow)
4725 		{
4726 			#ifdef LOG_CALLBACKS
4727 			LGR(2, Format("WhenInsertRow()", curid.y));
4728 			LGR(2, Format("[row: %d]", curid.y));
4729 			#endif
4730 			removed = !WhenInsertRow0();
4731 
4732 		}
4733 		else if(row_modified)
4734 		{
4735 			row_data = true;
4736 			SetModify();
4737 
4738 			#ifdef LOG_CALLBACKS
4739 			LGR(2, Format("WhenUpdateRow()", curid.y));
4740 			LGR(2, Format("[row: %d]", curid.y));
4741 			#endif
4742 			WhenUpdateRow();
4743 		}
4744 
4745 		if(!removed)
4746 		{
4747 			if(!cancel_accept && row_modified)
4748 			{
4749 				WhenAcceptedRow();
4750 				/*
4751 				if(auto_sorting)
4752 				{
4753 					GSort();
4754 					UpdateCursor();
4755 					Repaint(false, true);
4756 				}
4757 				*/
4758 			}
4759 
4760 			if(cancel_accept)
4761 			{
4762 				cancel_accept = false;
4763 				return false;
4764 			}
4765 
4766 			if(cancel_update)
4767 				CancelCtrlsData(true);
4768 			else
4769 				ClearModified();
4770 		}
4771 	}
4772 
4773 	if(newrow && (!samerow || doall))
4774 	{
4775 		newrow_inserted = false;
4776 		newrow_appended = false;
4777 	}
4778 
4779 	return true;
4780 }
4781 
CancelCtrlsData(bool all)4782 bool GridCtrl::CancelCtrlsData(bool all)
4783 {
4784 	cancel_update = false;
4785 
4786 	int ie = total_cols;
4787 	int is = 1;
4788 
4789 	if(!all && edit_mode == GE_CELL)
4790 	{
4791 		is = curpos.x;
4792 		ie = is + 1;
4793 	}
4794 
4795 	for(int i = is; i < ie; i++)
4796 	{
4797 		int id = hitems[i].id;
4798 		Item &it = items[curid.y][id];
4799 
4800 		Ctrl * ctrl = it.ctrl;
4801 		if(!ctrl)
4802 			ctrl = edits[id].ctrl;
4803 		if(ctrl)
4804 		{
4805 			ctrl->Reject();
4806 			ctrl->SetData(rowbkp[id]);
4807 
4808 			if(it.modified)
4809 			{
4810 				it.modified = false;
4811 				row_modified--;
4812 				it.val = rowbkp[id];
4813 			}
4814 		}
4815 	}
4816 
4817 	if(!row_modified)
4818 		vitems[curid.y].operation = GridOperation::NONE;
4819 
4820 	return true;
4821 }
4822 
UpdateCtrls(int opt)4823 void GridCtrl::UpdateCtrls(int opt /*= UC_CHECK_VIS | UC_SHOW | UC_CURSOR */)
4824 {
4825 	if(!valid_cursor)
4826 		return;
4827 
4828 	if((opt & UC_CHECK_VIS) && !HasCtrls())
4829 		return;
4830 
4831 	Point cp(opt & UC_OLDCUR ? oldcur : curpos);
4832 
4833 	bool show = opt & UC_SHOW;
4834 
4835 	ctrlid.y  = show ? (cp.y < 0 ? -1 : vitems[cp.y].id) : -1;
4836 	ctrlpos.y = show ? cp.y : -1;
4837 
4838 	if(cp.y < 0)
4839 		return;
4840 
4841 	Size sz = GetSize();
4842 
4843 	bool gofirst = (opt & UC_GOFIRST) && !IsCtrl(cp, false);
4844 
4845 	edit_ctrls = false;
4846 
4847 	bool nofocus = opt & UC_NOFOCUS;
4848 
4849 	focused_ctrl = NULL;
4850 	focused_ctrl_id = -1;
4851 	focused_col = -1;
4852 
4853 	for(int i = 1; i < total_cols; i++)
4854 	{
4855 		if(hitems[i].hidden)
4856 			continue;
4857 
4858 		Ctrl* ctrl = GetCtrl(cp.y, i, show == false);
4859 
4860 		if(!ctrl)
4861 			continue;
4862 
4863 		if(show)
4864 		{
4865 			if(newrow_appended || newrow_inserted)
4866 			{
4867 				if(!hitems[i].edit_insert)
4868 					continue;
4869 			}
4870 			else
4871 			{
4872 				if(!hitems[i].edit_update)
4873 					continue;
4874 			}
4875 		}
4876 
4877 		int id = hitems[i].id;
4878 
4879 		bool sync_ctrl = items[vitems[cp.y].id][id].ctrl;
4880 		bool dorect = false;
4881 		bool dorf = i == curpos.x;
4882 
4883 		if(gofirst)
4884 		{
4885 			dorf = true;
4886 			gofirst = false;
4887 		}
4888 
4889 		bool dofocus = !nofocus && !(opt & UC_HIDE) && dorf;
4890 
4891 		if(show)
4892 		{
4893 			dorect = edit_mode == GE_CELL ? dorf : (isedit || (opt & UC_CTRLS));
4894 			dorect = dorect && GetItem(cp.y, i).editable;
4895 		}
4896 
4897 		if(!sync_ctrl)
4898 		{
4899 			if(dorect)
4900 			{
4901 				Rect r = GetItemRect(ctrlpos.y, i, horz_grid, vert_grid, true, true);
4902 
4903 				if(!r.Intersects(sz))
4904 					r.Set(0, 0, 0, 0);
4905 
4906 				ctrl->SetRect(AlignRect(r, i));
4907 				ctrl->Show();
4908 				edit_ctrls = true;
4909 			}
4910 			else
4911 			{
4912 				ctrl->SetRect(0, 0, 0, 0);
4913 				ctrl->Hide();
4914 			}
4915 		}
4916 
4917 		if(dofocus && ctrl->IsShown())
4918 		{
4919 			ctrl->SetFocus();
4920 			focused_ctrl = ctrl;
4921 			focused_ctrl_id = id;
4922 			focused_ctrl_val = hitems[i].defval;
4923 			focused_col = i;
4924 
4925 			if(opt & UC_CURSOR)
4926 				SetCursor0(i, cp.y);
4927 		}
4928 	}
4929 
4930 	if(!nofocus && !focused_ctrl)
4931 		SetFocus();
4932 
4933 	if(opt & UC_CTRLS)
4934 		isedit = edit_ctrls;
4935 
4936 	if(opt & UC_CTRLS_OFF)
4937 		isedit = false;
4938 
4939 	if(!(opt & UC_SCROLL))
4940 		RebuildToolBar();
4941 
4942 	if(isedit)
4943 		popup.Close();
4944 }
4945 
SyncCtrls(int row,int col)4946 void GridCtrl::SyncCtrls(int row, int col)
4947 {
4948 	if(!genr_ctrls)
4949 		return;
4950 
4951 	Size sz = GetSize();
4952 
4953 	int js = row < 0 ? 0 : row;
4954 	int je = row < 0 ? total_rows : row + 1;
4955 
4956 	int is = col < 0 ? 1 : col;
4957 	int ie = col < 0 ? total_cols : col + 1;
4958 
4959 	for(int j = js; j < je; j++)
4960 	{
4961 		int idy = vitems[j].id;
4962 		bool fixed_row = j < fixed_rows;
4963 		bool create_row = !fixed_row && vitems[j].editable;
4964 
4965 		for(int i = is; i < ie; i++)
4966 		{
4967 			bool fixed_col = i < fixed_cols;
4968 			bool create_col = !fixed_col && hitems[i].editable;
4969 
4970 			int idx = hitems[i].id;
4971 
4972 			Item *it = &items[idy][idx];
4973 
4974 			if(it->isjoined)
4975 			{
4976 				it = &items[it->idy][it->idx];
4977 				idx = it->idx;
4978 			}
4979 
4980 			if(!it->ctrl && create_row && create_col && it->editable && edits[idx].factory)
4981 			{
4982 				One<Ctrl> newctrl;
4983 				edits[idx].factory(newctrl);
4984 				it->ctrl = newctrl.Detach();
4985 				it->ctrl_flag = IC_FACTORY | IC_INIT | IC_OWNED;
4986 			}
4987 
4988 			if(it->ctrl && (it->ctrl_flag & IC_INIT))
4989 			{
4990 				it->ctrl->SetData(it->val);
4991 				it->ctrl->WhenAction << Proxy(WhenCtrlsAction);
4992 				holder.AddChild(it->ctrl);
4993 				it->ctrl_flag &= ~IC_INIT;
4994 			}
4995 
4996 			if(it->ctrl)
4997 			{
4998 				if(it->isjoined && it->sync_flag == sync_flag)
4999 					continue;
5000 
5001 				Rect r = GetItemRect(j, i, horz_grid, vert_grid, true, true);
5002 				AlignRect(r, i);
5003 
5004 				if(r.Intersects(sz) && !fixed_col && !fixed_row && !vitems[j].hidden && !hitems[i].hidden)
5005 				{
5006 					it->ctrl->SetRect(r);
5007 					it->ctrl->Show();
5008 				}
5009 				else if(it->ctrl->IsShown())
5010 				{
5011 					it->ctrl->SetRect(0, 0, 0, 0);
5012 					it->ctrl->Hide();
5013 				}
5014 			}
5015 
5016 			if(it->isjoined)
5017 				it->sync_flag = sync_flag;
5018 		}
5019 	}
5020 	sync_flag = 1 - sync_flag;
5021 }
5022 
SyncSummary()5023 void GridCtrl::SyncSummary()
5024 {
5025 	if(!summary)
5026 		return;
5027 
5028 	if(WhenUpdateSummary)
5029 	{
5030 		WhenUpdateSummary();
5031 	}
5032 	else
5033 	{
5034 		for(int i = fixed_cols; i < total_cols; i++)
5035 		{
5036 			Value t = 0;
5037 
5038 			int idx = hitems[i].id;
5039 
5040 			int sop = hitems[i].sop;
5041 
5042 			if(sop == SOP_NONE)
5043 				continue;
5044 
5045 			int n = 0;
5046 
5047 			for(int j = fixed_rows; j < total_rows; j++)
5048 			{
5049 				if(vitems[j].IsHidden())
5050 					continue;
5051 
5052 				if(sop == SOP_CNT)
5053 				{
5054 					++n;
5055 					continue;
5056 				}
5057 
5058 				int idy = vitems[j].id;
5059 
5060 				Value v = items[idy][idx].val;
5061 
5062 				if(IsNull(v))
5063 					continue;
5064 
5065 				ProcessSummaryValue(v);
5066 
5067 				if(n == 0 && (sop == SOP_MIN || sop == SOP_MAX))
5068 					t = v;
5069 
5070 				if(IsNumber(v))
5071 				{
5072 					switch(sop)
5073 					{
5074 						case SOP_MIN:
5075 							if(double(v) < double(t))
5076 								t = v;
5077 							break;
5078 						case SOP_MAX:
5079 							if(double(v) > double(t))
5080 								t = v;
5081 							break;
5082 						case SOP_SUM:
5083 						case SOP_AVG:
5084 							t = double(t) + double(v);
5085 					}
5086 				}
5087 				else if(IsType<Date>(v))
5088 				{
5089 					switch(sop)
5090 					{
5091 						case SOP_MIN:
5092 							if((Date) v < (Date) t)
5093 								t = v;
5094 							break;
5095 						case SOP_MAX:
5096 							if((Date) v > (Date) t)
5097 								t = v;
5098 							break;
5099 						case SOP_SUM:
5100 						case SOP_AVG:
5101 							t = v;
5102 							break;
5103 					}
5104 				}
5105 			}
5106 
5107 			if(sop == SOP_AVG)
5108 			{
5109 				if(IsNumber(t))
5110 					t = double(t) / double(n);
5111 			}
5112 			else if(sop == SOP_CNT)
5113 			{
5114 				t = n;
5115 			}
5116 
5117 			summary[idx].val = t;
5118 		}
5119 	}
5120 
5121 	if(summary_row)
5122 		RefreshSummary();
5123 }
5124 
UpdateSummary(bool b)5125 void GridCtrl::UpdateSummary(bool b)
5126 {
5127 	update_summary = b;
5128 	if(b)
5129 		SyncSummary();
5130 }
5131 
HasCtrls()5132 bool GridCtrl::HasCtrls()
5133 {
5134 	return edit_ctrls || genr_ctrls;
5135 }
5136 
SetCtrlFocus(int col)5137 void GridCtrl::SetCtrlFocus(int col)
5138 {
5139 	oldcur.x = curpos.x;
5140 	Ctrl * ctrl = GetCtrl(col + fixed_cols, rowidx, false, false);
5141 	focused_ctrl = ctrl;
5142 	focused_ctrl_id = hitems[col + fixed_cols].id;
5143 	ctrl->SetFocus();
5144 	curpos.x = col + fixed_cols;
5145 }
5146 
SetCtrlFocus(Id id)5147 void GridCtrl::SetCtrlFocus(Id id)
5148 {
5149 	SetCtrlFocus(aliases.Get(id));
5150 }
5151 
Accept()5152 bool GridCtrl::Accept()
5153 {
5154 	if(!EndEdit())
5155 		return false;
5156 	return Ctrl::Accept();
5157 }
5158 
Reject()5159 void GridCtrl::Reject()
5160 {
5161 	CancelEdit();
5162 	Ctrl::Reject();
5163 }
5164 
RestoreFocus()5165 void GridCtrl::RestoreFocus()
5166 {
5167 	if(focused_ctrl && !focused_ctrl->HasFocusDeep())
5168 		focused_ctrl->SetFocus();
5169 }
5170 
ShowNextCtrl()5171 bool GridCtrl::ShowNextCtrl()
5172 {
5173 	if(GoRight(1, 1))
5174 	{
5175 		UpdateCtrls(UC_CHECK_VIS | UC_SHOW);
5176 		return true;
5177 	}
5178 	return false;
5179 }
5180 
ShowPrevCtrl()5181 bool GridCtrl::ShowPrevCtrl()
5182 {
5183 	if(GoLeft(1, 1))
5184 	{
5185 		UpdateCtrls(UC_CHECK_VIS | UC_SHOW);
5186 		return true;
5187 	}
5188 	return false;
5189 }
5190 
GetFocusedCtrlIndex()5191 int GridCtrl::GetFocusedCtrlIndex()
5192 {
5193 	for(int i = 1; i < total_cols; i++)
5194 	{
5195 		int id = hitems[i].id;
5196 
5197 		Ctrl * ctrl = items[0][id].ctrl;
5198 		if(ctrl && ctrl->HasFocusDeep())
5199 			return i;
5200 	}
5201 	return -1;
5202 }
5203 
GetCtrlPos(Ctrl * ctrl)5204 Point GridCtrl::GetCtrlPos(Ctrl * ctrl)
5205 {
5206 	for(int i = fixed_rows; i < total_rows; i++)
5207 	{
5208 		int idy = vitems[i].id;
5209 		for(int j = fixed_cols; j < total_cols; j++)
5210 		{
5211 			int idx = hitems[j].id;
5212 			Ctrl * ci = items[idy][idx].ctrl;
5213 			bool isedit = false;
5214 			if(!ci)
5215 			{
5216 				ci = edits[idx].ctrl;
5217 				isedit = true;
5218 			}
5219 			if(ci == ctrl || ci->HasChildDeep(ctrl))
5220 				return Point(j, isedit ? ctrlpos.y : i);
5221 		}
5222 	}
5223 	return Point(-1, -1);
5224 }
5225 
Split(int state,bool sync)5226 void GridCtrl::Split(int state, bool sync)
5227 {
5228 	if(resize_paint_mode < 2)
5229 	{
5230 		if(resize_paint_mode > 0 && state != GS_DOWN)
5231 		{
5232 			if(resizeCol) RefreshTop();
5233 			if(resizeRow) RefreshLeft();
5234 		}
5235 
5236 		if(state == GS_DOWN)
5237 			DrawLine(true, false);
5238 		else if(state == GS_MOVE)
5239 			DrawLine(true, true);
5240 		else
5241 			DrawLine(false, true);
5242 	}
5243 
5244 	if(state == GS_DOWN)
5245 	{
5246 		firstCol = firstRow = -1;
5247 		return;
5248 	}
5249 
5250 	if(state != GS_DOWN && (resize_paint_mode == 2 || state == GS_UP))
5251 	{
5252 		UpdateSizes();
5253 		UpdateHolder();
5254 		UpdateSb();
5255 		Refresh();
5256 	}
5257 
5258 	if((resize_paint_mode > 1 && state > GS_UP) || state == GS_UP)
5259 	{
5260 		SyncCtrls();
5261 		UpdateCtrls(UC_CHECK_VIS | UC_SHOW);
5262 	}
5263 
5264 	if(sync)
5265 		Sync();
5266 }
5267 
TabKey(bool enter_mode)5268 bool GridCtrl::TabKey(bool enter_mode)
5269 {
5270 	if(!HasFocus() && !holder.HasFocusDeep())
5271 		return false;
5272 
5273 	bool has_ctrls = HasCtrls();
5274 
5275 	if(has_ctrls)
5276 	{
5277 		bool isnext = ShowNextCtrl();
5278 		if(tab_adds_row && !isnext && curpos.y == lastVisRow)
5279 		{
5280 			DoAppend();
5281 			return true;
5282 		}
5283 		else
5284 			return focused_ctrl ? true : (genr_ctrls > 0 && !edit_ctrls) ? true : isnext;
5285 	}
5286 
5287 	if(tab_changes_row && ((enter_mode && has_ctrls) || (!enter_mode && !has_ctrls)))
5288 	{
5289 		bool isnext = false;
5290 		if(select_row)
5291 		{
5292 			isnext = GoNext();
5293 			if(!isnext && tab_adds_row)
5294 				DoAppendNoEdit();
5295 		}
5296 		else
5297 		{
5298 			isnext = GoRight();
5299 			if(!isnext && tab_adds_row)
5300 				DoAppendNoEdit();
5301 		}
5302 		ClearSelection();
5303 
5304 		if(isnext)
5305 			return true;
5306 	}
5307 	else if(!enter_mode)
5308 		return false;
5309 
5310 	if(enter_mode && !has_ctrls)
5311 	{
5312 		SwitchEdit();
5313 		return true;
5314 	}
5315 
5316 	return false;
5317 }
5318 
Key(dword key,int)5319 bool GridCtrl::Key(dword key, int)
5320 {
5321 	if(!IsReadOnly())
5322 	switch(key)
5323 	{
5324 		case K_ENTER:
5325 			ClearSelection();
5326 			WhenEnter();
5327 			#ifdef LOG_CALLBACKS
5328 			LGR(2, "WhenEnter()");
5329 			#endif
5330 
5331 			if(enter_like_tab)
5332 				return TabKey(true);
5333 			else if(!SwitchEdit())
5334 				return true;
5335 			/*
5336 			if(th.IsSorted())
5337 			{
5338 				th.Multisort();
5339 				Refresh();
5340 			}*/
5341 
5342 			return true;
5343 		case K_ESCAPE:
5344 		{
5345 			bool quit = true;
5346 			if(search_string.GetCount() > 0)
5347 			{
5348 				ClearFound();
5349 				quit = false;
5350 			}
5351 			else if(HasCtrls())
5352 			{
5353 				bool canceled = CancelEdit();
5354 				quit = !canceled;
5355 			}
5356 			if(quit)
5357 			{
5358 				WhenEscape();
5359 				return false;
5360 			}
5361 			else
5362 				return true;
5363 		}
5364 		case K_SHIFT|K_LEFT:
5365 			GoLeft();
5366 			DoShiftSelect();
5367 			return true;
5368 		case K_SHIFT|K_RIGHT:
5369 			GoRight();
5370 			DoShiftSelect();
5371 			return true;
5372 		case K_SHIFT|K_UP:
5373 			GoPrev();
5374 			DoShiftSelect();
5375 			return true;
5376 		case K_SHIFT|K_DOWN:
5377 			GoNext();
5378 			DoShiftSelect();
5379 			return true;
5380 		case K_SHIFT|K_PAGEUP:
5381 			GoPageUp();
5382 			DoShiftSelect();
5383 			return true;
5384 		case K_SHIFT|K_PAGEDOWN:
5385 			GoPageDn();
5386 			DoShiftSelect();
5387 			return true;
5388 		case K_SHIFT_HOME:
5389 			GoBegin();
5390 			DoShiftSelect();
5391 			return true;
5392 		case K_SHIFT_END:
5393 			GoEnd();
5394 			DoShiftSelect();
5395 			return true;
5396 		case K_CTRL|K_LEFT:
5397 			GoLeft();
5398 			DoCtrlSelect();
5399 			return true;
5400 		case K_CTRL|K_RIGHT:
5401 			GoRight();
5402 			DoCtrlSelect();
5403 			return true;
5404 		case K_CTRL|K_UP:
5405 			if(select_row)
5406 				break;
5407 			GoPrev();
5408 			DoCtrlSelect();
5409 			return true;
5410 		case K_CTRL|K_DOWN:
5411 			if(select_row)
5412 				break;
5413 			GoNext();
5414 			DoCtrlSelect();
5415 			return true;
5416 		case K_UP:
5417 			GoPrev();
5418 			ClearSelection();
5419 			return true;
5420 		case K_DOWN:
5421 			GoNext();
5422 			ClearSelection();
5423 			return true;
5424 		case K_LEFT:
5425 			GoLeft();
5426 			ClearSelection();
5427 			return true;
5428 		case K_RIGHT:
5429 			GoRight();
5430 			ClearSelection();
5431 			return true;
5432 
5433 		case K_HOME:
5434 		case K_CTRL_HOME:
5435 		case K_CTRL_PAGEUP:
5436 			GoBegin();
5437 			ClearSelection();
5438 			return true;
5439 		case K_END:
5440 		case K_CTRL_END:
5441 		case K_CTRL_PAGEDOWN:
5442 			GoEnd();
5443 			ClearSelection();
5444 			return true;
5445 		case K_PAGEUP:
5446 			GoPageUp();
5447 			ClearSelection();
5448 			return true;
5449 		case K_PAGEDOWN:
5450 			GoPageDn();
5451 			ClearSelection();
5452 			return true;
5453 		case K_TAB:
5454 			return TabKey(false);
5455 		case K_SHIFT|K_TAB:
5456 			if(HasCtrls())
5457 			{
5458 				bool isprev = ShowPrevCtrl();
5459 				return focused_ctrl ? true : isprev;
5460 			}
5461 			else if(tab_changes_row)
5462 			{
5463 				bool isprev = false;
5464 				if(select_row)
5465 					isprev = GoPrev();
5466 				else
5467 					isprev = GoLeft();
5468 				ClearSelection();
5469 
5470 				return isprev;
5471 			}
5472 			else
5473 				return false;
5474 		case K_CTRL|K_F:
5475 			if(searching)
5476 			{
5477 				find.SetFocus();
5478 				return true;
5479 			}
5480 			else
5481 				return false;
5482 		case K_BACKSPACE:
5483 		{
5484 			if(searching)
5485 			{
5486 				int cnt = search_string.GetCount();
5487 				if(cnt > 0)
5488 				{
5489 					search_string.Remove(cnt - 1);
5490 					find <<= search_string;
5491 					ShowMatchedRows(search_string);
5492 				}
5493 				return true;
5494 			}
5495 			else
5496 				return false;
5497 		}
5498 		case K_F3:
5499 			if(rowfnd >= 0)
5500 			{
5501 				for(int i = rowfnd + 1; i < total_rows; i++)
5502 				{
5503 					if(vitems[i].IsFound())
5504 					{
5505 						rowfnd = i;
5506 						SetCursor0(i);
5507 						CenterCursor();
5508 						WhenSearchCursor();
5509 						return true;
5510 					}
5511 				}
5512 				for(int i = fixed_rows; i < rowfnd; i++)
5513 				{
5514 					if(vitems[i].IsFound())
5515 					{
5516 						rowfnd = i;
5517 						SetCursor0(i);
5518 						CenterCursor();
5519 						WhenSearchCursor();
5520 						return true;
5521 					}
5522 				}
5523 
5524 				return true;
5525 			}
5526 			return false;
5527 		case K_CTRL_W:
5528 			WriteClipboardText(GetColumnWidths());
5529 			return true;
5530 		default:
5531 			if(searching && !isedit && Search(key))
5532 				return true;
5533 	}
5534 
5535 	return MenuBar::Scan(WhenMenuBar, key);
5536 }
5537 
Search(dword key)5538 bool GridCtrl::Search(dword key)
5539 {
5540 	//if(key & K_UP)
5541 	//	return false;
5542 
5543 	if(key >= 32 && key < 65536)
5544 	{
5545 		search_string += (wchar) key;
5546 		if(!ShowMatchedRows(search_string) && search_string.GetCount() > 0)
5547 			search_string.Remove(search_string.GetCount() - 1);
5548 		else
5549 			find <<= search_string;
5550 
5551 		return true;
5552 	}
5553 	return false;
5554 }
5555 
GetResizePanelHeight() const5556 int GridCtrl::GetResizePanelHeight() const
5557 {
5558 	return (resize_panel.GetHeight() + 2) * resize_panel_open;
5559 }
5560 
GetColumnName(int n) const5561 String GridCtrl::GetColumnName(int n) const
5562 {
5563 	return hitems[GetIdCol(n + fixed_cols)].GetName();
5564 }
5565 
GetColumnId(int n) const5566 Id GridCtrl::GetColumnId(int n) const
5567 {
5568 	return aliases.GetKey(n + fixed_cols);
5569 }
5570 
SwapCols(int n,int m)5571 void GridCtrl::SwapCols(int n, int m)
5572 {
5573 	if(m == n ||
5574 	   n < fixed_cols || n > total_cols - 1 ||
5575 	   m < fixed_cols || m > total_cols - 1)
5576 		return;
5577 
5578 	Swap(hitems[m], hitems[n]);
5579 	UpdateCursor();
5580 	Repaint(true, false);
5581 }
5582 
CanMoveCol(int n,int m)5583 bool GridCtrl::CanMoveCol(int n, int m)
5584 {
5585 	if(m == n || m == n - 1 ||
5586 	   n < 0 || n > total_cols - 1 ||
5587 	   m < 0 || m > total_cols - 1)
5588 		return false;
5589 	else
5590 	{
5591 		if(hitems[n].join > 0)
5592 		{
5593 			LG(2, "n=%d(%d) m=%d(%d)", n, hitems[n].join, m, hitems[m].join);
5594 			if(m == n - 2 && hitems[n].join == hitems[n - 1].join)
5595 				return true;
5596 
5597 			if(hitems[m].join != hitems[n].join)
5598 				return false;
5599 		}
5600 		//tu sprawdzic join
5601 		return true;
5602 	}
5603 }
5604 
MoveCol(int n,int m)5605 void GridCtrl::MoveCol(int n, int m)
5606 {
5607 	LG(0, "%d->%d", n, m);
5608 
5609 	if(!CanMoveCol(n, m))
5610 	{
5611 		Repaint();
5612 		return;
5613 	}
5614 
5615 	ItemRect ir = hitems[n];
5616 	if(m > total_cols)
5617 		hitems.Add(ir);
5618 	else
5619 		hitems.Insert(m + 1, ir);
5620 	if(m > n)
5621 		hitems.Remove(n);
5622 	else
5623 		hitems.Remove(n + 1);
5624 
5625 	UpdateCursor();
5626 	Repaint(true, false);
5627 }
5628 
MoveRow(int n,int m,bool repaint)5629 bool GridCtrl::MoveRow(int n, int m, bool repaint)
5630 {
5631 	LG(0, "%d->%d", n, m);
5632 
5633 	if(m == n || m == n - 1 ||
5634 	   n < 0 || n > total_rows - 1 ||
5635 	   m < -1 || m > total_rows - 1)
5636 	{
5637 		Repaint();
5638 		return false;
5639 	}
5640 
5641 	WhenMoveRow(n, m);
5642 
5643 	if(cancel_move)
5644 	{
5645 		cancel_move = false;
5646 		return false;
5647 	}
5648 
5649 	ItemRect ir = vitems[n];
5650 	if(m > total_rows)
5651 		vitems.Add(ir);
5652 	else
5653 		vitems.Insert(m + 1, ir);
5654 	if(m > n)
5655 		vitems.Remove(n);
5656 	else
5657 		vitems.Remove(n + 1);
5658 
5659 	if(repaint)
5660 	{
5661 		UpdateCursor();
5662 		Repaint(false, true);
5663 	}
5664 
5665 	SetOrder();
5666 	SetModify();
5667 
5668 	return true;
5669 }
5670 
MoveRows(int n,bool onerow)5671 void GridCtrl::MoveRows(int n, bool onerow)
5672 {
5673 	if(selected_rows && !onerow)
5674 	{
5675 		Vector<ItemRect> vi;
5676 		vi.Reserve(selected_rows);
5677 		for(int i = fixed_rows; i < total_rows; i++)
5678 			if(vitems[i].IsSelect())
5679 			{
5680 				WhenMoveRow(i, n);
5681 				if(cancel_move)
5682 				{
5683 					cancel_move = false;
5684 					return;
5685 				}
5686 				vi.Add(vitems[i]);
5687 			}
5688 
5689 		int cnt = 0;
5690 
5691 		for(int i = total_rows - 1; i >= fixed_rows; i--)
5692 			if(vitems[i].IsSelect())
5693 			{
5694 				vitems.Remove(i);
5695 				if(i < n)
5696 					cnt++;
5697 			}
5698 
5699 		vitems.Insert(n - cnt, vi);
5700 
5701 		SetOrder();
5702 		SetModify();
5703 
5704 		UpdateCursor();
5705 		Repaint(false, true);
5706 	}
5707 	else
5708 	{
5709 		MoveRow(curpos.y, n - 1);
5710 	}
5711 }
5712 
SwapRows(int n,int m,bool repaint)5713 bool GridCtrl::SwapRows(int n, int m, bool repaint)
5714 {
5715 	if(isedit || m == n ||
5716 	   n < fixed_rows || n > total_rows - 1 ||
5717 	   m < fixed_rows || m > total_rows - 1)
5718 		return false;
5719 
5720 	WhenMoveRow(n, m);
5721 
5722 	if(cancel_move)
5723 	{
5724 		cancel_move = false;
5725 		return false;
5726 	}
5727 
5728 	Swap(vitems[m], vitems[n]);
5729 	if(repaint)
5730 	{
5731 		UpdateCursor();
5732 		Repaint(false, true);
5733 	}
5734 	SetOrder();
5735 	SetModify();
5736 	return true;
5737 }
5738 
SwapUp(int cnt)5739 void GridCtrl::SwapUp(int cnt)
5740 {
5741 	int yp = 0;
5742 	bool first = false;
5743 	bool repaint = false;
5744 
5745 	if(selected_rows == 0)
5746 	{
5747 		if(SwapRows(curpos.y, curpos.y - cnt))
5748 			yp = vitems[curpos.y + cnt].nTop(sby + fixed_height);
5749 		else
5750 			return;
5751 	}
5752 	else
5753 	{
5754 		for(int i = fixed_rows; i < total_rows; i++)
5755 		{
5756 			if(vitems[i].IsSelect())
5757 			{
5758 				if(!SwapRows(i, i - cnt, false))
5759 					return;
5760 				if(!first)
5761 				{
5762 					yp = vitems[i].nTop(sby + fixed_height);
5763 					first = true;
5764 				}
5765 			}
5766 		}
5767 		repaint = true;
5768 	}
5769 
5770 	if(resize_row_mode == 0 && yp < 0)
5771 		sby.Set(sby + yp);
5772 
5773 	if(repaint)
5774 	{
5775 		UpdateCursor();
5776 		Repaint(false, true);
5777 	}
5778 }
5779 
SwapDown(int cnt)5780 void GridCtrl::SwapDown(int cnt)
5781 {
5782 	int yp = 0;
5783 	bool first = false;
5784 	bool repaint = false;
5785 
5786 	if(selected_rows == 0)
5787 	{
5788 		if(SwapRows(curpos.y, curpos.y + cnt))
5789 			yp = vitems[curpos.y - cnt].nBottom(sby);
5790 		else
5791 			return;
5792 	}
5793 	else
5794 	{
5795 		for(int i = total_rows - 1; i >= fixed_rows; i--)
5796 		{
5797 			if(vitems[i].IsSelect())
5798 			{
5799 				if(!SwapRows(i, i + cnt, false))
5800 					return;
5801 				if(!first)
5802 				{
5803 					yp = vitems[i].nBottom(sby);
5804 					first = true;
5805 				}
5806 			}
5807 		}
5808 		repaint = true;
5809 	}
5810 
5811 	int cy = GetSize().cy - bar.GetSize().cy;
5812 	if(resize_row_mode == 0 && yp > cy)
5813 		sby.Set(sby + yp - cy);
5814 
5815 	if(repaint)
5816 	{
5817 		UpdateCursor();
5818 		Repaint(false, true);
5819 	}
5820 }
5821 
MouseLeave()5822 void GridCtrl::MouseLeave()
5823 {
5824 	if(live_cursor)
5825 	{
5826 		LG(2, "MouseLeave:LiveCursor");
5827 		SetCursor0(-1, -1, CU_HIGHLIGHT);
5828 	}
5829 	UpdateHighlighting(GS_BORDER, Point(0, 0));
5830 	oldSplitCol = -1;
5831 	oldSplitRow = -1;
5832 	//popup.Close();
5833 }
5834 
MouseWheel(Point p,int zdelta,dword keyflags)5835 void GridCtrl::MouseWheel(Point p, int zdelta, dword keyflags)
5836 {
5837 	if(resize_row_mode == 0)
5838 	{
5839 		sby.Set(sby - zdelta / 4);
5840 	}
5841 }
5842 
GridColor(Color fg)5843 GridCtrl& GridCtrl::GridColor(Color fg)
5844 {
5845 	fg_grid = fg;
5846 	return *this;
5847 }
5848 
FocusColor(Color fg,Color bg)5849 GridCtrl& GridCtrl::FocusColor(Color fg, Color bg)
5850 {
5851 	fg_focus = fg;
5852 	bg_focus = bg;
5853 	return *this;
5854 }
5855 
LiveColor(Color fg,Color bg)5856 GridCtrl& GridCtrl::LiveColor(Color fg, Color bg)
5857 {
5858 	fg_live = fg;
5859 	bg_live = bg;
5860 	return *this;
5861 }
5862 
OddColor(Color fg,Color bg)5863 GridCtrl& GridCtrl::OddColor(Color fg, Color bg)
5864 {
5865 	fg_odd = fg;
5866 	bg_odd = bg;
5867 	return *this;
5868 }
5869 
EvenColor(Color fg,Color bg)5870 GridCtrl& GridCtrl::EvenColor(Color fg, Color bg)
5871 {
5872 	fg_even = fg;
5873 	bg_even = bg;
5874 	return *this;
5875 }
5876 
ColoringMode(int m)5877 GridCtrl& GridCtrl::ColoringMode(int m)
5878 {
5879 	coloring_mode = m;
5880 	return *this;
5881 }
5882 
ClearCursor(bool remove)5883 void GridCtrl::ClearCursor(bool remove)
5884 {
5885 	if(!remove && valid_cursor)
5886 	{
5887 		SetItemCursor(curpos, false, false);
5888 		RefreshRow(curpos.y, 0);
5889 	}
5890 
5891 	curpos.x = curpos.y = -1;
5892 	curid.x = curid.y = -1;
5893 	rowidx = -1;
5894 	valid_cursor = false;
5895 }
5896 
ClearRow(int r,int column_offset)5897 void GridCtrl::ClearRow(int r, int column_offset)
5898 {
5899 	if(r < 0)
5900 		r = rowidx;
5901 	else
5902 		r -= fixed_rows;
5903 	for(int i = fixed_cols + column_offset; i < total_cols; i++)
5904 		items[vitems[r].id][hitems[i].id].val = Null;
5905 
5906 	SetCtrlsData();
5907 	RefreshRow(rowidx, 0);
5908 }
5909 
Clear(bool columns)5910 void GridCtrl::Clear(bool columns)
5911 {
5912 	doscroll = false;
5913 
5914 	UpdateCtrls(UC_HIDE | UC_CTRLS);
5915 
5916 	int nrows = columns ? 1 : fixed_rows;
5917 	items.Remove(nrows, items.GetCount() - nrows);
5918 	vitems.Remove(nrows, vitems.GetCount() - nrows);
5919 
5920 	total_rows = nrows;
5921 	fixed_rows = nrows;
5922 
5923 	if(columns)
5924 	{
5925 		hitems.Remove(1, hitems.GetCount() - 1);
5926 		items[0].Remove(1, items[0].GetCount() - 1);
5927 		rowbkp.Remove(1, rowbkp.GetCount() - 1);
5928 		edits.Remove(1, edits.GetCount() - 1);
5929 		sortOrder.Clear();
5930 		total_cols = 1;
5931 		total_width = 0;
5932 		total_height = 0;
5933 		firstCol = -1;
5934 		lastCol = -1;
5935 		fixed_cols = 1;
5936 		coluid = 0;
5937 		hcol = -1;
5938 		sortCol = -1;
5939 		genr_ctrls = 0;
5940 		firstVisCol = fixed_cols;
5941 		lastVisCol = total_cols - 1;
5942 	}
5943 	else
5944 	{
5945 		total_height = fixed_height;
5946 	}
5947 
5948 	firstVisRow = fixed_rows;
5949 	lastVisRow = total_rows - 1;
5950 
5951 	focused_ctrl = NULL;
5952 
5953 	valid_cursor = false;
5954 
5955 	firstRow = -1;
5956 	lastRow = -1;
5957 
5958 	curpos.x = curpos.y = -1;
5959 	curid.x  = curid.y  = -1;
5960 
5961 	hrow = -1;
5962 
5963 	rowidx = -1;
5964 	rowuid = 0;
5965 
5966 	row_modified = 0;
5967 
5968 	UpdateSizes();
5969 	UpdateSb();
5970 
5971 	if(ready)
5972 	{
5973 		UpdateHolder();
5974 
5975 		oldpos.x = sbx;
5976 		oldpos.y = sby;
5977 
5978 		RebuildToolBar();
5979 		Refresh();
5980 	}
5981 
5982 	WhenEmpty();
5983 	WhenCursor();
5984 
5985 	doscroll = true;
5986 }
5987 
Reset()5988 void GridCtrl::Reset()
5989 {
5990 	Clear(true);
5991 }
5992 
ClearOperations()5993 void GridCtrl::ClearOperations()
5994 {
5995 	for(int i = fixed_rows; i < total_rows; i++)
5996 		vitems[i].operation.Clear();
5997 }
5998 
ClearVersions()5999 void GridCtrl::ClearVersions()
6000 {
6001 	for(int i = fixed_rows; i < total_rows; i++)
6002 		vitems[i].operation.ClearVersion();
6003 }
6004 
Begin()6005 void GridCtrl::Begin()
6006 {
6007 	bkp_rowidx = rowidx;
6008 	rowidx = fixed_rows;
6009 }
6010 
End()6011 void GridCtrl::End()
6012 {
6013 	rowidx = total_rows - 1;
6014 }
6015 
IsEnd()6016 bool GridCtrl::IsEnd()
6017 {
6018 	if(rowidx < total_rows)
6019 		return true;
6020 	else
6021 	{
6022 		rowidx = bkp_rowidx;
6023 		return false;
6024 	}
6025 }
6026 
Next()6027 void GridCtrl::Next()
6028 {
6029 	++rowidx;
6030 }
6031 
Prev()6032 void GridCtrl::Prev()
6033 {
6034 	--rowidx;
6035 }
6036 
Move(int r)6037 void GridCtrl::Move(int r)
6038 {
6039 	rowidx = r + fixed_rows;
6040 }
6041 
IsNext()6042 bool GridCtrl::IsNext()
6043 {
6044 	return rowidx < total_rows - 1;
6045 }
6046 
IsPrev()6047 bool GridCtrl::IsPrev()
6048 {
6049 	return rowidx > fixed_rows;
6050 }
6051 
IsFirst()6052 bool GridCtrl::IsFirst()
6053 {
6054 	return rowidx == fixed_rows;
6055 }
6056 
IsLast()6057 bool GridCtrl::IsLast()
6058 {
6059 	return rowidx == total_rows - 1;
6060 }
6061 
SetCursor0(int n)6062 int GridCtrl::SetCursor0(int n)
6063 {
6064 	int t = curpos.y;
6065 	SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x, n);
6066 	return t;
6067 }
6068 
SetCursor(int n)6069 int GridCtrl::SetCursor(int n)
6070 {
6071 	return SetCursor0(n + fixed_rows) - fixed_rows;
6072 }
6073 
SetCursor(const Point & p)6074 void GridCtrl::SetCursor(const Point& p)
6075 {
6076 	SetCursor0(Point(p.x + fixed_cols, p.y + fixed_rows), false);
6077 }
6078 
SetCursorId(int id)6079 int GridCtrl::SetCursorId(int id)
6080 {
6081 	id += fixed_rows;
6082 	for(int i = fixed_rows; i < total_rows; i++)
6083 	{
6084 		if(vitems[i].id == id)
6085 			return SetCursor(i - fixed_rows);
6086 	}
6087 	return -1;
6088 }
6089 
GetCursor(bool rel) const6090 int GridCtrl::GetCursor(bool rel) const
6091 {
6092 	if(rel)
6093 		return valid_cursor ? vitems[curpos.y].id - fixed_rows : -1;
6094 	else
6095 		return valid_cursor ? curpos.y - fixed_rows : -1;
6096 }
6097 
GetPrevCursor(bool rel) const6098 int GridCtrl::GetPrevCursor(bool rel) const
6099 {
6100 	if(rel)
6101 		return IsValidCursor(oldcur) ? vitems[oldcur.y].id - fixed_rows : -1;
6102 	else
6103 		return IsValidCursor(oldcur) ? oldcur.y - fixed_rows : -1;
6104 }
6105 
GetCursor(int uid) const6106 int GridCtrl::GetCursor(int uid) const
6107 {
6108 	for(int i = fixed_rows; i < total_rows; i++)
6109 		if(vitems[i].uid == uid)
6110 			return i - fixed_rows;
6111 	return -1;
6112 }
6113 
GetCursorPos() const6114 Point GridCtrl::GetCursorPos() const
6115 {
6116 	return valid_cursor ? Point(curpos.x - fixed_cols, curpos.y - fixed_rows) : Point(-1, -1);
6117 }
6118 
GetRowId() const6119 int GridCtrl::GetRowId() const
6120 {
6121 	return valid_cursor ? vitems[curpos.y].id - fixed_rows : -1;
6122 }
6123 
GetColId() const6124 int GridCtrl::GetColId() const
6125 {
6126 	return valid_cursor ? hitems[curpos.x].id - fixed_cols: -1;
6127 }
6128 
GetRowId(int n) const6129 int GridCtrl::GetRowId(int n) const { return vitems[n + fixed_rows].id - fixed_rows; }
GetColId(int n) const6130 int GridCtrl::GetColId(int n) const { return hitems[n + fixed_cols].id - fixed_cols; }
6131 
GetColUId() const6132 int GridCtrl::GetColUId() const
6133 {
6134 	return valid_cursor ? hitems[curpos.x].uid : -1;
6135 }
6136 
GetRowUId() const6137 int GridCtrl::GetRowUId() const
6138 {
6139 	return valid_cursor ? vitems[curpos.y].uid : -1;
6140 }
6141 
FindCol(int id) const6142 int GridCtrl::FindCol(int id) const
6143 {
6144 	for(int i = fixed_cols; i < total_cols; i++)
6145 		if(hitems[i].id == id)
6146 			return i - fixed_cols;
6147 	return -1;
6148 }
6149 
FindCol(const Id & id) const6150 int GridCtrl::FindCol(const Id& id) const
6151 {
6152 	for(int i = fixed_cols; i < total_cols; i++)
6153 		if(aliases.GetKey(i) == id)
6154 			return i - fixed_cols;
6155 	return -1;
6156 }
6157 
FindCol(const String & s) const6158 int GridCtrl::FindCol(const String& s) const
6159 {
6160 	for(int i = fixed_cols; i < total_cols; i++)
6161 		if(hitems[i].GetName() == s)
6162 			return i - fixed_cols;
6163 	return -1;
6164 }
6165 
FindRow(int id) const6166 int GridCtrl::FindRow(int id) const
6167 {
6168 	for(int i = fixed_rows; i < total_rows; i++)
6169 		if(vitems[i].id == id)
6170 			return i - fixed_rows;
6171 	return -1;
6172 }
6173 
GetNewRowPos()6174 int GridCtrl::GetNewRowPos()
6175 {
6176 	return rowidx > 0 ? rowidx - fixed_rows : -1;
6177 }
6178 
GetNewRowId()6179 int GridCtrl::GetNewRowId()
6180 {
6181 	return rowidx > 0 ? vitems[rowidx].id - fixed_rows : -1;
6182 }
6183 
GetRemovedRowPos()6184 int GridCtrl::GetRemovedRowPos()
6185 {
6186 	return rowidx > 0 ? rowidx - fixed_rows : -1;
6187 }
6188 
CenterCursor()6189 void GridCtrl::CenterCursor()
6190 {
6191 	if(IsEmpty() || !IsCursor())
6192 		return;
6193 
6194 	sbx.Set(hitems[curpos.x].nLeft() - GetSize().cx / 2);
6195 	sby.Set(vitems[curpos.y].nTop() - GetSize().cy / 2);
6196 }
6197 
Go0(int jump,bool scroll,bool goleft,bool ctrlmode)6198 bool GridCtrl::Go0(int jump, bool scroll, bool goleft, bool ctrlmode)
6199 {
6200 	if(IsEmpty())
6201 		return false;
6202 
6203 	if(!ready)
6204 	{
6205 		UpdateSizes();
6206 		UpdateSb();
6207 	}
6208 
6209 	if(jump == GO_LEFT || jump == GO_RIGHT)
6210 	{
6211 		if(select_row && !ctrlmode && !draw_focus)
6212 		{
6213 			if(jump == GO_LEFT)
6214 				sbx.Set(sbx.Get() - 5);
6215 			else
6216 				sbx.Set(sbx.Get() + 5);
6217 			return false;
6218 		}
6219 	}
6220 
6221 	if(jump == GO_PREV)
6222 		if(curpos.y >= 0 && curpos.y <= firstVisRow)
6223 			return false;
6224 
6225 	if(jump == GO_NEXT)
6226 		if(curpos.y >= 0 && curpos.y >= lastVisRow)
6227 			return false;
6228 
6229 	if(jump == GO_PAGEUP || jump == GO_PAGEDN)
6230 	{
6231 		if(jump == GO_PAGEDN && curpos.y == lastVisRow)
6232 			return false;
6233 
6234 		if(jump == GO_PAGEUP && curpos.y == firstVisRow)
6235 			return false;
6236 
6237 		if(!valid_cursor)
6238 		{
6239 			GoFirstVisible();
6240 			return true;
6241 		}
6242 	}
6243 
6244 	Size sz = GetSize();
6245 	int sy = -1;
6246 
6247 	int opt = /*ctrls*/ ctrlmode ? CU_CTRLMODE : 0;
6248 
6249 	switch(jump)
6250 	{
6251 		case GO_BEGIN:
6252 		{
6253 			if(!SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x, firstVisRow, opt, 0, 1))
6254 				return false;
6255 			sy = 0;
6256 
6257 			break;
6258 		}
6259 		case GO_END:
6260 		{
6261 			if(!SetCursor0((curpos.x < 0 || goleft) ? firstVisCol : curpos.x, lastVisRow, opt, 0, -1))
6262 				return false;
6263 			if(goleft)
6264 				GoCursorLeftRight();
6265 			else
6266 				sy = total_height - fixed_height - summary_height;
6267 
6268 			break;
6269 		}
6270 		case GO_NEXT:
6271 		{
6272 			if(!SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x,
6273 			               curpos.y < 0 ? firstVisRow : curpos.y + 1,
6274 					       opt, 0, 1))
6275 				return false;
6276 
6277 			int b = vitems[curpos.y].nBottom(sby);
6278 			int r = sz.cy - summary_height;
6279 
6280 			if(b > r)
6281 				sy = sby + b - r;
6282 
6283 			break;
6284 		}
6285 		case GO_PREV:
6286 		{
6287 			if(!SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x,
6288 			               curpos.y < 0 ? firstVisRow : curpos.y - 1,
6289 			               opt, 0, -1))
6290 				return false;
6291 
6292 			int t = vitems[curpos.y].nTop(sby + fixed_height);
6293 
6294 			if(t < 0)
6295 				sy = sby + t;
6296 
6297 			break;
6298 		}
6299 		case GO_LEFT:
6300 		{
6301 			if(!SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x - 1,
6302 						   curpos.y < 0 ? firstVisRow : curpos.y,
6303 						   opt, -1, 0))
6304 				return false;
6305 
6306 			break;
6307 		}
6308 		case GO_RIGHT:
6309 		{
6310 			if(!SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x + 1,
6311 						   curpos.y < 0 ? firstVisRow : curpos.y,
6312 						   ctrlmode ? CU_CTRLMODE : 0, 1, 0))
6313 				return false;
6314 
6315 			break;
6316 		}
6317 		case GO_PAGEUP:
6318 		{
6319 			int cp = curpos.y;
6320 			int c = cp;
6321 
6322 			int yn = vitems[c].nTop() - sz.cy;
6323 			int ya = vitems[c].nTop(sby);
6324 
6325 			bool found = false;
6326 			int i;
6327 			for(i = c - 1; i >= fixed_rows; i--)
6328 				if(yn >= vitems[i].nTop() && yn < vitems[i].nBottom())
6329 				{
6330 					found = true;
6331 					break;
6332 				}
6333 
6334 			c = found ? i : firstVisRow;
6335 
6336 			if(!SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x, c, opt, 0, 1))
6337 				return false;
6338 
6339 			c = curpos.y;
6340 
6341 			if(scroll && resize_row_mode == 0)
6342 			{
6343 				int yc = vitems[c].nTop();
6344 				int yt = vitems[cp].nTop(sby);
6345 				int yb = vitems[cp].nBottom(sby);
6346 
6347 				if(yt < 0 || yb > sz.cy - 1)
6348 					sby.Set(yc - sz.cy + vitems[c].nHeight());
6349 				else
6350 					sby.Set(yc - ya);
6351 			}
6352 
6353 			break;
6354 		}
6355 		case GO_PAGEDN:
6356 		{
6357 			int cp = curpos.y;
6358 			int c = cp;
6359 
6360 			int yn = vitems[c].nTop() + sz.cy;
6361 			int ya = vitems[c].nTop(sby);
6362 
6363 			bool found = false;
6364 			int i;
6365 			for(i = c + 1; i < total_rows; i++)
6366 				if(yn >= vitems[i].nTop() && yn < vitems[i].nBottom())
6367 				{
6368 					found = true;
6369 					break;
6370 				}
6371 
6372 			c = found ? i : lastVisRow;
6373 
6374 			if(!SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x, c, opt, 0, -1))
6375 				return false;
6376 
6377 			c = curpos.y;
6378 
6379 			if(scroll && resize_row_mode == 0)
6380 			{
6381 				int yc = vitems[c].nTop();
6382 				int yt = vitems[cp].nTop(sby);
6383 				int yb = vitems[cp].nBottom(sby);
6384 
6385 				if(yt < 0 || yb > sz.cy - 1)
6386 					sby.Set(yc);
6387 				else
6388 					sby.Set(yc - ya);
6389 			}
6390 
6391 			break;
6392 		}
6393 	}
6394 
6395 	if(jump == GO_LEFT || jump == GO_RIGHT)
6396 	{
6397 		if(scroll)
6398 			GoCursorLeftRight();
6399 	}
6400 	else
6401 	{
6402 		if(scroll && resize_row_mode == 0 && sy >= 0)
6403 			sby.Set(sy);
6404 	}
6405 
6406 	opt = UC_CHECK_VIS;
6407 	if(isedit)
6408 		opt |= UC_SHOW;
6409 	UpdateCtrls(opt);
6410 
6411 	return true;
6412 }
6413 
GoCursorLeftRight()6414 void GridCtrl::GoCursorLeftRight()
6415 {
6416 	if(resize_col_mode == 0)
6417 	{
6418 		int l = hitems[curpos.x].nLeft(sbx + fixed_width);
6419 		int r = hitems[curpos.x].nRight(sbx);
6420 		int w = GetSize().cx;
6421 
6422 		if(l < 0)
6423 			sbx.Set(sbx + l);
6424 		else if(r > w)
6425 			sbx.Set(sbx + r - w);
6426 	}
6427 
6428 	if(resize_row_mode == 0)
6429 	{
6430 		int t = vitems[curpos.y].nTop(sby + fixed_height);
6431 		int b = vitems[curpos.y].nBottom(sby);
6432 		int h = GetSize().cy - summary_height;
6433 
6434 		if(t < 0)
6435 			sby.Set(sby + t);
6436 		else if(b > h)
6437 			sby.Set(sby + b - h);
6438 	}
6439 }
6440 
GoFirstVisible(bool scroll)6441 bool GridCtrl::GoFirstVisible(bool scroll)
6442 {
6443 	if(IsEmpty())
6444 		return false;
6445 
6446 	SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x, max(firstVisRow, firstRow));
6447 	if(scroll && resize_row_mode == 0)
6448 		sby.Set(vitems[firstRow].nTop(/*fixed_height*/));
6449 	if(isedit)
6450 		UpdateCtrls();
6451 
6452 	return true;
6453 }
6454 
GoBegin(bool scroll)6455 bool GridCtrl::GoBegin(bool scroll)                { return Go0(GO_BEGIN, scroll);                  }
GoEnd(bool scroll,bool goleft)6456 bool GridCtrl::GoEnd(bool scroll, bool goleft)     { return Go0(GO_END, scroll, goleft);            }
GoNext(bool scroll)6457 bool GridCtrl::GoNext(bool scroll)                 { return Go0(GO_NEXT, scroll);                   }
GoPrev(bool scroll)6458 bool GridCtrl::GoPrev(bool scroll)                 { return Go0(GO_PREV, scroll);                   }
GoLeft(bool scroll,bool ctrlmode)6459 bool GridCtrl::GoLeft(bool scroll, bool ctrlmode)  { return Go0(GO_LEFT, scroll, false, ctrlmode);  }
GoRight(bool scroll,bool ctrlmode)6460 bool GridCtrl::GoRight(bool scroll, bool ctrlmode) { return Go0(GO_RIGHT, scroll, false, ctrlmode); }
GoPageUp(bool scroll)6461 bool GridCtrl::GoPageUp(bool scroll)               { return Go0(GO_PAGEUP, scroll);                 }
GoPageDn(bool scroll)6462 bool GridCtrl::GoPageDn(bool scroll)               { return Go0(GO_PAGEDN, scroll);                 }
6463 
GetCtrl(const Point & p,bool check_visibility,bool hrel,bool vrel,bool check_edits)6464 Ctrl * GridCtrl::GetCtrl(const Point &p, bool check_visibility, bool hrel, bool vrel, bool check_edits)
6465 {
6466 	return GetCtrl(p.y, p.x, check_visibility, hrel, vrel, check_edits);
6467 }
6468 
GetCtrl(int r,int c,bool check_visibility,bool hrel,bool vrel,bool check_edits)6469 Ctrl * GridCtrl::GetCtrl(int r, int c, bool check_visibility, bool hrel, bool vrel, bool check_edits)
6470 {
6471 	int idx = hrel ? fixed_cols + c : hitems[c].id;
6472 	int idy = vrel ? fixed_rows + r : vitems[r].id;
6473 	Ctrl * ctrl = items[idy][idx].ctrl;
6474 	if(check_edits && !ctrl)
6475 		ctrl = edits[idx].ctrl;
6476 	if(check_visibility && ctrl && !ctrl->IsShown())
6477 		ctrl = NULL;
6478 	return ctrl;
6479 }
6480 
GetCtrl(int r,int c)6481 Ctrl * GridCtrl::GetCtrl(int r, int c)
6482 {
6483 	return GetCtrl(r + fixed_rows, c, true, true, false);
6484 }
6485 
GetCtrlAt(int r,int c)6486 Ctrl * GridCtrl::GetCtrlAt(int r, int c)
6487 {
6488 	return GetCtrl(r + fixed_rows, c, false, true, false);
6489 }
6490 
GetCtrl(int c)6491 Ctrl * GridCtrl::GetCtrl(int c)
6492 {
6493 	return GetCtrl(rowidx, c, true, true, false);
6494 }
6495 
IsCtrl(Point & p,bool check_visibility)6496 bool GridCtrl::IsCtrl(Point &p, bool check_visibility)
6497 {
6498 	return GetCtrl(p, check_visibility, false, false) != NULL;
6499 }
6500 
GoTo(int r,bool setcursor,bool scroll)6501 void GridCtrl::GoTo(int r, bool setcursor, bool scroll)
6502 {
6503 	r += fixed_rows;
6504 
6505 	if(setcursor)
6506 		if(!SetCursor0(r))
6507 			return;
6508 
6509 	if(scroll)
6510 	{
6511 		Size sz = GetSize();
6512 		sby.Set(vitems[r].nTop() + vitems[r].nHeight() / 2 - sz.cy / 2);
6513 	}
6514 }
6515 
GoTo(int r,int c,bool setcursor,bool scroll)6516 void GridCtrl::GoTo(int r, int c, bool setcursor, bool scroll)
6517 {
6518 	c += fixed_cols;
6519 	r += fixed_rows;
6520 
6521 	if(setcursor)
6522 		if(!SetCursor0(c, r))
6523 			return;
6524 
6525 	if(scroll)
6526 	{
6527 		Size sz = GetSize();
6528 		sbx.Set(hitems[c].nLeft() + hitems[c].nWidth() / 2 - sz.cx / 2);
6529 		sby.Set(vitems[r].nTop() + vitems[r].nHeight() / 2 - sz.cy / 2);
6530 	}
6531 }
6532 
6533 
GetCount() const6534 int GridCtrl::GetCount() const      { return total_rows - fixed_rows; }
GetFixedCount() const6535 int GridCtrl::GetFixedCount() const { return fixed_rows;              }
GetTotalCount() const6536 int GridCtrl::GetTotalCount() const { return total_rows;              }
6537 
GetVisibleCount() const6538 int GridCtrl::GetVisibleCount() const
6539 {
6540 	int cnt = 0;
6541 	for(int i = fixed_rows; i < total_rows; i++)
6542 		if(!vitems[i].hidden)
6543 			++cnt;
6544 	return cnt;
6545 }
6546 
SetColsMin(int size)6547 GridCtrl& GridCtrl::SetColsMin(int size)
6548 {
6549 	for(int i = 1; i < total_cols; i++)
6550 		hitems[i].min = size;
6551 
6552 	return *this;
6553 }
6554 
SetColsMax(int size)6555 GridCtrl& GridCtrl::SetColsMax(int size)
6556 {
6557 	for(int i = 0; i < total_rows; i++)
6558 		hitems[i].max = size;
6559 
6560 	return *this;
6561 }
6562 
GotFocus()6563 void GridCtrl::GotFocus()
6564 {
6565 	LG(3, "GotFocus");
6566 	RestoreFocus();
6567 	if(valid_cursor)
6568 		RefreshRow(curpos.y, 0, 0);
6569 }
6570 
LostFocus()6571 void GridCtrl::LostFocus()
6572 {
6573 	LG(3, "LostFocus");
6574 	if(valid_cursor)
6575 		RefreshRow(curpos.y, 0, 0);
6576 	popup.Close();
6577 }
6578 
ChildGotFocus()6579 void GridCtrl::ChildGotFocus()
6580 {
6581 	LG(3, "ChildGotFocus");
6582 	if(valid_cursor)
6583 		RefreshRow(curpos.y, 0, 0);
6584 	Ctrl::ChildGotFocus();
6585 }
6586 
ChildLostFocus()6587 void GridCtrl::ChildLostFocus()
6588 {
6589 	LG(3, "ChildLostFocus");
6590 	if(valid_cursor)
6591 	{
6592 		//if(focus_lost_accepting && !HasFocusDeep())
6593 		//	EndEdit();
6594 		RefreshRow(curpos.y, 0, 0);
6595 	}
6596 	Ctrl::ChildLostFocus();
6597 }
6598 
Repaint(bool do_recalc_cols,bool do_recalc_rows,int opt)6599 void GridCtrl::Repaint(bool do_recalc_cols /* = false*/, bool do_recalc_rows /* = false*/, int opt)
6600 {
6601 	if(do_recalc_cols)
6602 	{
6603 		if(ready)
6604 		{
6605 			UpdateCols(true);
6606 			firstCol = fixed_cols;
6607 		}
6608 		else
6609 			recalc_cols = true;
6610 	}
6611 
6612 	if(do_recalc_rows)
6613 	{
6614 		if(ready)
6615 		{
6616 			UpdateRows(true);
6617 			firstRow = fixed_rows;
6618 		}
6619 		else
6620 			recalc_rows = true;
6621 	}
6622 
6623 	if(ready)
6624 	{
6625 		doscroll = false;
6626 		UpdateSizes();
6627 		UpdateSb();
6628 		UpdateHolder();
6629 		UpdateSummary();
6630 		if(opt & RP_UPDCTRLS)
6631 			UpdateCtrls();
6632 		SyncCtrls();
6633 		if(opt & RP_TOOLBAR)
6634 			RebuildToolBar();
6635 		doscroll = true;
6636 		Refresh();
6637 	}
6638 }
6639 
Ready(bool b)6640 void GridCtrl::Ready(bool b)
6641 {
6642 	ready = b;
6643 	if(b)
6644 		Repaint(true);
6645 }
6646 
ResizeColMode(int m)6647 GridCtrl& GridCtrl::ResizeColMode(int m)
6648 {
6649 	resize_col_mode = m;
6650 	recalc_cols = true;
6651 	RefreshLayout();
6652 	return *this;
6653 }
6654 
ResizeRowMode(int m)6655 GridCtrl& GridCtrl::ResizeRowMode(int m)
6656 {
6657 	resize_row_mode = m;
6658 	recalc_rows = true;
6659 	RefreshLayout();
6660 	return *this;
6661 }
6662 
UpdateSb(bool horz,bool vert)6663 void GridCtrl::UpdateSb(bool horz, bool vert)
6664 {
6665 	scrollbox.Width(ScrollBarSize());
6666 
6667 	if(horz)
6668 	{
6669 		sbx.SetTotal(resize_col_mode == 0 ? total_width - fixed_width : 0);
6670 		sbx.SetPage(GetSize().cx - fixed_width);
6671 	}
6672 
6673 	if(vert)
6674 	{
6675 		sby.SetTotal(resize_row_mode == 0 ? total_height - fixed_height + summary_height : 0);
6676 		sby.SetPage(GetSize().cy - fixed_height);
6677 	}
6678 
6679 	sbx.SetFrame(resize_row_mode == 0 && sby.GetTotal() > GetSize().cy - fixed_height ? scrollbox : NullFrame());
6680 }
6681 
SwitchEdit()6682 bool GridCtrl::SwitchEdit()
6683 {
6684 	if(!valid_cursor)
6685 		return false;
6686 
6687 	Ctrl * ctrl = items[curid.y][curid.x].ctrl;
6688 	if(ctrl)
6689 	{
6690 		if(ctrl->HasFocusDeep())
6691 			EndEdit(true, true);
6692 		SetFocus();
6693 		rowbkp[curid.x] = ctrl->GetData();
6694 		//items[curid.y][curid.x].val = ctrl->GetData();
6695 		focused_ctrl = ctrl;
6696 		focused_ctrl_id = curid.x;
6697 		focused_ctrl_val = ctrl->GetData();
6698 		ctrl->SetFocus();
6699 	}
6700 	else
6701 	{
6702 		if(isedit)
6703 			EndEdit(true, true);
6704 		else
6705 			StartEdit();
6706 	}
6707 	return true;
6708 }
6709 
StartEdit()6710 bool GridCtrl::StartEdit()
6711 {
6712 	if(!valid_cursor || !IsRowEditable())
6713 		return false;
6714 
6715 	WhenStartEdit();
6716 
6717 	SetCtrlsData();
6718 	UpdateCtrls(UC_SHOW | UC_CURSOR | UC_CTRLS | (goto_first_edit ? UC_GOFIRST : 0));
6719 	return true;
6720 }
6721 
EndEdit(bool accept,bool doall,bool remove_row)6722 bool GridCtrl::EndEdit(bool accept, bool doall, bool remove_row)
6723 {
6724 	if(!valid_cursor)
6725 		return true;
6726 
6727 	if(accept && !GetCtrlsData(false, doall, accept))
6728 		return false;
6729 
6730 	UpdateCtrls(UC_CTRLS);
6731 
6732 	if(!accept)
6733 	{
6734 		CancelCtrlsData();
6735 		if(newrow_inserted || newrow_appended)
6736 		{
6737 			newrow_inserted = false;
6738 			newrow_appended = false;
6739 			if(remove_row)
6740 			{
6741 				WhenCancelNewRow();
6742 				Remove0(curpos.y, 1, true, true, false);
6743 			}
6744 		}
6745 	}
6746 	WhenEndEdit();
6747 	SyncSummary();
6748 	return true;
6749 }
6750 
Insert0(int row,int cnt,bool recalc,bool refresh,int size)6751 void GridCtrl::Insert0(int row, int cnt /* = 1*/, bool recalc /* = true*/, bool refresh /* = true*/, int size /* = GD_ROW_HEIGHT*/)
6752 {
6753 	int id;
6754 
6755 	if(size < 0)
6756 		size = GD_ROW_HEIGHT;
6757 
6758 	if(row < total_rows)
6759 	{
6760 		id = vitems[row].id;
6761 		for(int i = 0; i < total_rows; i++)
6762 		{
6763 			if(vitems[i].id >= id)
6764 				vitems[i].id += cnt;
6765 		}
6766 	}
6767 	else
6768 		id = total_rows;
6769 
6770 	ItemRect ir;
6771 	ir.size = size;
6772 	vitems.Insert(row, ir, cnt);
6773 	items.InsertN(id, cnt);
6774 
6775 	for(int i = 0; i < cnt; i++)
6776 	{
6777 		int nid = id + i;
6778 		int r = row + i;
6779 		vitems[r].id = nid;
6780 		vitems[r].uid = rowuid++;
6781 		vitems[r].items = &items;
6782 		vitems[r].operation = ready ? GridOperation::INSERT : GridOperation::NONE;
6783 		items[nid].SetCount(total_cols);
6784 		UpdateDefaults(nid);
6785 		rowidx = r;
6786 		total_rows++;
6787 		WhenCreateRow();
6788 	}
6789 
6790 	UpdateJoins(row, -1, cnt);
6791 
6792 	firstRow = -1;
6793 
6794 	if(recalc)
6795 	{
6796 		if(ready)
6797 		{
6798 			RecalcRows();
6799 			UpdateSizes();
6800 
6801 			if(refresh)
6802 			{
6803 				UpdateSb();
6804 				SyncSummary();
6805 				SyncCtrls();
6806 				RefreshFrom(row);
6807 			}
6808 		}
6809 		else
6810 			recalc_rows = true;
6811 	}
6812 
6813 	SetOrder();
6814 	SetModify();
6815 }
6816 
Remove0(int row,int cnt,bool recalc,bool refresh,bool whens)6817 bool GridCtrl::Remove0(int row, int cnt /* = 1*/, bool recalc /* = true*/, bool refresh /* = true*/, bool whens /* = true*/)
6818 {
6819 	if(cnt < 0)
6820 		return false;
6821 
6822 	bool cancel = true;
6823 	int x = -1;
6824 	int y = -1;
6825 
6826 	for(int	i = 0; i < cnt; i++)
6827 	{
6828 		int rid = row + i;
6829 		rowidx = remove_hides ? rid : row;
6830 
6831 		if(vitems[rowidx].locked)
6832 			continue;
6833 
6834 		int id = vitems[rowidx].id;
6835 		int op = vitems[rowidx].operation;
6836 
6837 		vitems[rowidx].operation = GridOperation::REMOVE;
6838 
6839 		if(remove_hides)
6840 		{
6841 			vitems[rowidx].hidden = true;
6842 			vitems[rowidx].tsize = vitems[rowidx].size;
6843 			vitems[rowidx].size = 0;
6844 			vitems[rowidx].nsize = 0;
6845 			//for this row in hitems selected flag should be cleared too
6846 		}
6847 
6848 		if(whens)
6849 		{
6850 			if(call_whenremoverow)
6851 			{
6852 				#ifdef LOG_CALLBACKS
6853 				LGR(2, "WhenRemoveRow()");
6854 				LGR(2, Format("[row: %d]", rowidx));
6855 				#endif
6856 				WhenRemoveRow();
6857 			}
6858 
6859 			if(cancel_remove)
6860 			{
6861 				vitems[rowidx].operation = op;
6862 				cancel_remove = false;
6863 				cancel = false;
6864 				if(i == cnt - 1)
6865 					return cancel;
6866 				else
6867 					continue;
6868 			}
6869 			if(call_whenremoverow)
6870 				WhenAcceptRow();
6871 		}
6872 
6873 		if(vitems[rowidx].IsSelect())
6874 		{
6875 			int si = 0;
6876 			for(int j = fixed_cols; j < total_cols; j++)
6877 				if(GetItem(rowidx, j).IsSelect())
6878 					si++;
6879 
6880 			selected_items -= si;
6881 			selected_rows -= 1;
6882 		}
6883 
6884 		if(!remove_hides)
6885 		{
6886 			for(int j = 0; j < total_rows; j++)
6887 				if(vitems[j].id > id)
6888 					vitems[j].id--;
6889 		}
6890 		else
6891 			vitems[rowidx].style = 0; //bo IsSelect korzysta z pola style
6892 
6893 
6894 		total_height -= vitems[rowidx].nHeight();
6895 
6896 		if(rid == ctrlid.y)
6897 			UpdateCtrls(UC_HIDE | UC_CTRLS);
6898 
6899 		bool removed = false;
6900 
6901 		if(!remove_hides)
6902 		{
6903 			total_rows--;
6904 			vitems.Remove(rowidx);
6905 			items.Remove(id);
6906 			removed = true;
6907 		}
6908 
6909 		if(rid == curpos.y)
6910 		{
6911 			x = curpos.x;
6912 			y = remove_hides ? curpos.y + cnt : curpos.y;
6913 			ClearCursor(true);
6914 		}
6915 
6916 		if(rid == oldcur.y)
6917 		{
6918 			oldcur.x = oldcur.y = -1;
6919 		}
6920 
6921 		if(whens && removed)
6922 			WhenRemovedRow();
6923 	}
6924 
6925 	if(recalc)
6926 	{
6927 		if(ready)
6928 		{
6929 			RecalcRows();
6930 			UpdateSizes();
6931 
6932 			if(refresh)
6933 			{
6934 				UpdateSb();
6935 				SyncSummary();
6936 				SyncCtrls();
6937 				RefreshFrom(row);
6938 
6939 				if(x >= 0 && y >= 0)
6940 					SetCursor0(x, max(fixed_rows, min(total_rows - 1, y)), 0, 0, -1);
6941 
6942 				if(!valid_cursor)
6943 					RebuildToolBar();
6944 			}
6945 		}
6946 		else
6947 		{
6948 			UpdateVisColRow(false);
6949 			recalc_rows = true;
6950 		}
6951 	}
6952 
6953 	firstRow = -1;
6954 
6955 	if(IsEmpty())
6956 	{
6957 		WhenEmpty();
6958 		WhenCursor();
6959 	}
6960 
6961 	SetOrder();
6962 	SetModify();
6963 	return cancel;
6964 }
6965 
Append0(int cnt,int size,bool refresh)6966 int GridCtrl::Append0(int cnt, int size, bool refresh)
6967 {
6968 	if(size < 0)
6969 		size = GD_ROW_HEIGHT;
6970 
6971 	vitems.AddN(cnt);
6972 	items.AddN(cnt);
6973 
6974 	int j = total_rows;
6975 	int k = j;
6976 	int n = j > 0 ? vitems[j - 1].n + (int) vitems[j - 1].hidden : 0;
6977 	for(int i = 0; i < cnt; i++)
6978 	{
6979 		ItemRect &ir = vitems[j];
6980 		ir.parent = this;
6981 		ir.items = &items;
6982 		ir.size = ir.nsize = size;
6983 		ir.operation = ready ? GridOperation::INSERT : GridOperation::NONE;
6984 
6985 		if(total_rows > 0)
6986 		{
6987 			ir.pos = ir.npos = vitems[j - 1].nBottom();
6988 			ir.n = n;
6989 		}
6990 
6991 		if(size == 0)
6992 			ir.hidden = true;
6993 		else
6994 		{
6995 			lastVisRow = j;
6996 			if(firstVisRow < 0)
6997 				firstVisRow = lastVisRow;
6998 		}
6999 
7000 		items[j].SetCount(total_cols);
7001 		ir.id = j++;
7002 		ir.uid = rowuid++;
7003 		UpdateDefaults(ir.id);
7004 		rowidx = j - 1;
7005 		total_rows = j;
7006 		WhenCreateRow();
7007 	}
7008 
7009 	total_height += size * cnt;
7010 
7011 	if(refresh && ready)
7012 	{
7013 		if(resize_row_mode > 0)
7014 			UpdateRows(true);
7015 		UpdateSb();
7016 		SyncSummary();
7017 		SyncCtrls();
7018 		RefreshFrom(k);
7019 	}
7020 
7021 	SetOrder();
7022 	SetModify();
7023 	return total_rows - fixed_rows;
7024 }
7025 
Duplicate0(int row,int cnt,bool recalc,bool refresh)7026 bool GridCtrl::Duplicate0(int row, int cnt, bool recalc, bool refresh)
7027 {
7028 	int id;
7029 	int nrow = row + cnt;
7030 
7031 	if(nrow < total_rows)
7032 	{
7033 		id = vitems[nrow].id;
7034 		for(int i = 0; i < total_rows; i++)
7035 		{
7036 			if(vitems[i].id >= id)
7037 				vitems[i].id += cnt;
7038 		}
7039 	}
7040 	else
7041 		id = total_rows;
7042 
7043 	ItemRect ir;
7044 	vitems.Insert(nrow, ir, cnt);
7045 	items.InsertN(id, cnt);
7046 
7047 	int duplicated = 0;
7048 
7049 	for(int i = 0; i < cnt; i++)
7050 	{
7051 		int nid = id + i;
7052 		int r = nrow + i;
7053 		vitems[r].id = nid;
7054 		vitems[r].uid = rowuid++;
7055 		vitems[r].items = &items;
7056 		vitems[r].size = vitems[row + i].size;
7057 		items[nid].SetCount(total_cols);
7058 
7059 		int oid = vitems[row + i].id;
7060 		for(int j = 1; j < total_cols; j++)
7061 			items[nid][j].val = items[oid][j].val;
7062 
7063 		rowidx = r;
7064 		total_rows++;
7065 		WhenCreateRow();
7066 
7067 		duplicated++;
7068 		WhenDuplicateRow();
7069 		if(cancel_duplicate)
7070 		{
7071 			duplicated--;
7072 			Remove0(r, 1, false, false, false);
7073 			cancel_duplicate = false;
7074 		}
7075 
7076 	}
7077 
7078 	firstRow = -1;
7079 
7080 	if(recalc)
7081 	{
7082 		if(ready)
7083 		{
7084 			RecalcRows();
7085 			UpdateSizes();
7086 
7087 			if(refresh)
7088 			{
7089 				UpdateSb();
7090 				SyncSummary();
7091 				SyncCtrls();
7092 				RefreshFrom(nrow);
7093 			}
7094 		}
7095 		else
7096 			recalc_rows = true;
7097 	}
7098 
7099 	if(duplicated > 0)
7100 	{
7101 		SetOrder();
7102 		SetModify();
7103 	}
7104 
7105 	return duplicated > 0;
7106 }
7107 
Append(int cnt,bool refresh,int height)7108 int GridCtrl::Append(int cnt, bool refresh, int height)
7109 {
7110 	return Append0(cnt, height, refresh);
7111 }
7112 
Insert(int i,int cnt)7113 void GridCtrl::Insert(int i, int cnt)
7114 {
7115 	Insert0(fixed_rows + i, cnt);
7116 }
7117 
Remove(int i,int cnt)7118 void GridCtrl::Remove(int i, int cnt)
7119 {
7120 	Remove0(i < 0 ? rowidx : fixed_rows + i, cnt);
7121 }
7122 
RemoveFirst(int cnt)7123 void GridCtrl::RemoveFirst(int cnt /* = 1*/)
7124 {
7125 	Remove0(fixed_rows, min(total_rows - fixed_rows, cnt));
7126 }
7127 
RemoveLast(int cnt)7128 void GridCtrl::RemoveLast(int cnt /* = 1*/)
7129 {
7130 	Remove0(total_rows - cnt, min(total_rows - fixed_rows, cnt));
7131 }
7132 
Duplicate(int i,int cnt)7133 void GridCtrl::Duplicate(int i, int cnt)
7134 {
7135 	Duplicate0(fixed_rows + i, cnt);
7136 }
7137 
Select(int n,int cnt)7138 void GridCtrl::Select(int n, int cnt /* = 1*/)
7139 {
7140 	SelectCount(n + fixed_rows, cnt, true);
7141 }
7142 
SelectCount(int i,int cnt,bool sel)7143 void GridCtrl::SelectCount(int i, int cnt, bool sel)
7144 {
7145 	if(cnt <= 0)
7146 		return;
7147 	SelectRange(Point(fixed_cols, i), Point(total_cols - 1, i + cnt - 1), sel);
7148 }
7149 
SelectRange(int from,int to,bool sel)7150 void GridCtrl::SelectRange(int from, int to, bool sel)
7151 {
7152 	SelectRange(Point(fixed_cols, from), Point(total_cols - 1, to), sel);
7153 }
7154 
ShiftSelect(int from,int to)7155 void GridCtrl::ShiftSelect(int from, int to)
7156 {
7157 	ShiftSelect(Point(fixed_cols, from), Point(total_cols - 1, to));
7158 }
7159 
SelectRange(Point from,Point to,bool sel,bool fullrow)7160 void GridCtrl::SelectRange(Point from, Point to, bool sel /* = true*/, bool fullrow /* = false*/)
7161 {
7162 	Point f, t;
7163 
7164 	if(fullrow)
7165 	{
7166 		from.x = fixed_cols;
7167 		to.x = total_cols - 1;
7168 	}
7169 
7170 	if(from.y < to.y)
7171 	{
7172 		f = from;
7173 		t = to;
7174 	}
7175 	else
7176 	{
7177 		f = to;
7178 		t = from;
7179 	}
7180 
7181 	int ymin = f.y;
7182 	int ymax = t.y;
7183 
7184 	int xmin = f.x;
7185 	int xmax = t.x;
7186 
7187 	if(xmin > xmax)
7188 	{
7189 		int t = xmin;
7190 		xmin = xmax;
7191 		xmax = t;
7192 	}
7193 
7194 	for(int i = ymin; i <= ymax; i++)
7195 	{
7196 		ItemRect &ir = vitems[i];
7197 		int yid = ir.id;
7198 
7199 		bool is_row_selected = false;
7200 		bool do_refresh = false;
7201 
7202 		for(int j = fixed_cols; j < total_cols; j++)
7203 		{
7204 			int xid = hitems[j].id;
7205 			Item &it = items[yid][xid];
7206 
7207 			if(j >= xmin && j <= xmax)
7208 			{
7209 				if(it.IsSelect() != sel)
7210 				{
7211 					it.Select(sel);
7212 					do_refresh = true;
7213 				}
7214 				if(sel)
7215 				{
7216 					is_row_selected = true;
7217 					selected_items++;
7218 				}
7219 				else
7220 					selected_items--;
7221 			}
7222 			else if(it.IsSelect())
7223 				is_row_selected = true;
7224 		}
7225 
7226 		if(!ir.IsSelect())
7227 		{
7228 			if(is_row_selected)
7229 				selected_rows++;
7230 		}
7231 		else if(!is_row_selected)
7232 			selected_rows--;
7233 
7234 		ir.Select(is_row_selected);
7235 
7236 		if(do_refresh)
7237 			RefreshRow(i, false, false);
7238 
7239 	}
7240 }
7241 
SelectInverse(int from,int to)7242 void GridCtrl::SelectInverse(int from, int to)
7243 {
7244 	int nfrom = min(from, to);
7245 	int nto = max(from, to);
7246 
7247 	for(int i = nfrom ; i <= nto; i++)
7248 	{
7249 		vitems[i].Select(!vitems[i].IsSelect());
7250 		if(vitems[i].IsSelect())
7251 			selected_rows++;
7252 		else
7253 			selected_rows--;
7254 		RefreshRow(i, 0);
7255 	}
7256 }
7257 
ShiftSelect(Point from,Point to)7258 void GridCtrl::ShiftSelect(Point from, Point to)
7259 {
7260 	Point f, t;
7261 
7262 	if(from.y < to.y)
7263 	{
7264 		f = from;
7265 		t = to;
7266 	}
7267 	else
7268 	{
7269 		f = to;
7270 		t = from;
7271 	}
7272 
7273 	if(select_row)
7274 	{
7275 		f.x = fixed_cols;
7276 		t.x = total_cols;
7277 	}
7278 
7279 	int ymin = f.y;
7280 	int ymax = t.y;
7281 
7282 	int xmin = f.x;
7283 	int xmax = t.x;
7284 
7285 	if(ymin == ymax && xmin > xmax)
7286 	{
7287 		int t = xmin;
7288 		xmin = xmax;
7289 		xmax = t;
7290 	}
7291 
7292 	selected_rows = 0;
7293 	selected_items = 0;
7294 
7295 	for(int i = fixed_rows; i < total_rows; i++)
7296 	{
7297 		ItemRect &ir = vitems[i];
7298 		int yid = ir.id;
7299 
7300 		bool is_row_selected = false;
7301 		bool do_refresh = false;
7302 
7303 		if((i >= ymin && i <= ymax))
7304 		{
7305 			for(int j = fixed_cols; j < total_cols; j++)
7306 			{
7307 				int xid = hitems[j].id;
7308 
7309 				bool s = true;
7310 				if(i == ymin && ymin == ymax)
7311 					s = (j >= xmin && j <= xmax);
7312 				else if(i == ymin) s = (j >= xmin);
7313 				else if(i == ymax) s = (j <= xmax);
7314 
7315 				if(items[yid][xid].IsSelect() != s)
7316 				{
7317 					items[yid][xid].Select(s);
7318 					do_refresh = true;
7319 				}
7320 				if(s)
7321 				{
7322 					is_row_selected = true;
7323 					selected_items++;
7324 				}
7325 			}
7326 		}
7327 		else
7328 		{
7329 			for(int j = fixed_cols; j < total_cols; j++)
7330 				if(items[yid][j].IsSelect())
7331 				{
7332 					items[yid][j].Select(false);
7333 					do_refresh = true;
7334 				}
7335 		}
7336 
7337 		if(is_row_selected)
7338 			selected_rows++;
7339 
7340 		ir.Select(is_row_selected);
7341 
7342 		if(do_refresh)
7343 			RefreshRow(i, false, false);
7344 	}
7345 }
7346 
DoShiftSelect()7347 void GridCtrl::DoShiftSelect()
7348 {
7349 	if(!shiftmode)
7350 	{
7351 		if(!IsValidCursor(oldcur))
7352 			return;
7353 		shiftpos = oldcur;
7354 		shiftmode = true;
7355 		ShiftSelect(oldcur, curpos);
7356 	}
7357 	else
7358 		ShiftSelect(shiftpos, curpos);
7359 }
7360 
DoCtrlSelect()7361 void GridCtrl::DoCtrlSelect()
7362 {
7363 	if(!IsValidCursor(oldcur))
7364 		return;
7365 
7366 	if(shiftmode && !select_row)
7367 	{
7368 		ClearSelection();
7369 		shiftpos = oldcur;
7370 	}
7371 
7372 	if(!IsValidCursor(shiftpos))
7373 		shiftpos = oldcur;
7374 
7375 	SelectRange(shiftpos, oldcur, false, select_row);
7376 	SelectRange(shiftpos, curpos, true, select_row);
7377 }
7378 
IsSelected(int n,bool relative)7379 bool GridCtrl::IsSelected(int n, bool relative)
7380 {
7381 	//int id = vitems[n + (relative ? fixed_rows : 0)].id;
7382 	int id = n + (relative ? fixed_rows : 0);
7383 	return vitems[id].IsSelect() || vitems[id].IsCursor();
7384 }
7385 
IsSelected(int n,int m,bool relative)7386 bool GridCtrl::IsSelected(int n, int m, bool relative)
7387 {
7388 	int r = relative ? fixed_rows + n : n;
7389 	int c = relative ? fixed_cols + m : m;
7390 	Item &it = GetItem(r, c);
7391 	return it.IsSelect() || it.IsCursor();
7392 }
7393 
IsSelected()7394 bool GridCtrl::IsSelected()
7395 {
7396 	return IsSelected(rowidx, false);
7397 }
7398 
ClearSelection()7399 void GridCtrl::ClearSelection()
7400 {
7401 	LG(0, "Cleared %d", selected_rows);
7402 	shiftmode = false;
7403 	shiftpos = curpos;
7404 	if(selected_rows > 0)
7405 	{
7406 		for(int i = fixed_rows; i < total_rows; i++)
7407 		{
7408 			vitems[i].Select(0);
7409 			for(int j = fixed_cols; j < total_cols; j++)
7410 				items[i][j].Select(0);
7411 		}
7412 
7413 		Refresh();
7414 		selected_rows = 0;
7415 		selected_items = 0;
7416 	}
7417 }
7418 
DoInsert0(bool edit,bool after)7419 void GridCtrl::DoInsert0(bool edit, bool after)
7420 {
7421 	if(!valid_cursor)
7422 		return;
7423 
7424 	if(!EndEdit())
7425 		return;
7426 
7427 	SetItemCursor(curpos, false, false);
7428 	RefreshRow(curpos.y, false);
7429 	curpos.y += int(after);
7430 	Insert0(curpos.y, 1, true, true, GD_ROW_HEIGHT);
7431 	int y = curpos.y;
7432 	curpos.y = -1;
7433 	valid_cursor = false;
7434 	call_whenchangecol = false;
7435 	call_whenchangerow = false;
7436 	SetCursor0(curpos.x, y > total_rows - 1 ? total_rows - 1 : y);
7437 	call_whenchangecol = true;
7438 	call_whenchangerow = true;
7439 
7440 	newrow_inserted = true;
7441 
7442 	if(edit)
7443 		StartEdit();
7444 
7445 	if(!isedit)
7446 		WhenInsertRow0();
7447 
7448 	WhenNewRow();
7449 
7450 	if(!edit)
7451 		newrow_inserted = false;
7452 }
7453 
DoInsertBefore0(bool edit)7454 void GridCtrl::DoInsertBefore0(bool edit)
7455 {
7456 	DoInsert0(edit, false);
7457 }
7458 
DoInsertAfter0(bool edit)7459 void GridCtrl::DoInsertAfter0(bool edit)
7460 {
7461 	DoInsert0(edit, true);
7462 }
7463 
DoDuplicate0()7464 void GridCtrl::DoDuplicate0()
7465 {
7466 	int cy = 0;
7467 	if(selected_rows == 0)
7468 	{
7469 		if(!valid_cursor)
7470 			return;
7471 		cy = curpos.y + 1;
7472 		if(!Duplicate0(curpos.y, 1, false, false))
7473 			cy = 0;
7474 	}
7475 	else if(!multi_select)
7476 	{
7477 		cy = GetMinRowSelected() + selected_rows * 2 - 1;
7478 		if(!Duplicate0(GetMinRowSelected(), selected_rows, false, false))
7479 			cy = 0;
7480 	}
7481 
7482 	if(cy > 0)
7483 	{
7484 		Repaint(false, true);
7485 		SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x, max(fixed_rows, min(total_rows - 1, cy)));
7486 	}
7487 }
7488 
DoRemove()7489 void GridCtrl::DoRemove()
7490 {
7491 	if(keep_last_row && GetCount() == 1)
7492 		return;
7493 
7494 	if(!valid_cursor && selected_rows == 0)
7495 		return;
7496 
7497 	if(ask_remove)
7498 	{
7499 		if(!PromptYesNo(Format(t_("Do you really want to delete selected %s ?"), selected_rows > 1 ? t_("rows") : t_("row"))))
7500 			return;
7501 	}
7502 
7503 	bool newrow = IsNewRow();
7504 	CancelEdit(false);
7505 
7506 	int y = curpos.y;
7507 	int ocy = curpos.y;
7508 
7509 	if(selected_rows == 0)
7510 	{
7511 		//do not call WhenRemoveRow when not new (not inserted) row
7512 		Remove0(curpos.y, 1, true, true, !newrow);
7513 	}
7514 	else
7515 	{
7516 		int not_removed = 0;
7517 
7518 		minRowSelected = GetMinRowSelected();
7519 		maxRowSelected = GetMaxRowSelected();
7520 
7521 		if(keep_last_row && (maxRowSelected - minRowSelected + 1) == GetCount())
7522 			maxRowSelected--;
7523 
7524 		LG(0, "Min:%d, Max:%d", minRowSelected, maxRowSelected);
7525 
7526 		for(int i = minRowSelected; i <= maxRowSelected; i++)
7527 		{
7528 			int rid = remove_hides ? i : minRowSelected + not_removed;
7529 			if(vitems[rid].IsSelect())
7530 			{
7531 				sel_begin = i == minRowSelected;
7532 				sel_end = i == maxRowSelected;
7533 
7534 				if(Remove0(rid, 1, false, false))
7535 				{
7536 					/* curpos.y tez sie zmienia bo gdy w whenromoverow jest woloanie innego okna to
7537 					   grid traci fokus i wola sie lostfoucs, ktory wymaga poprawnego curpos.y */
7538 					if(i == ocy)
7539 						y = curpos.y = rid - 1;
7540 				}
7541 				else
7542 					not_removed++;
7543 			}
7544 			else
7545 				not_removed++;
7546 		}
7547 		RecalcRows();
7548 		//UpdateSizes();
7549 		SyncCtrls();
7550 		UpdateSb();
7551 		Refresh();
7552 		curpos.y = -1;
7553 		valid_cursor = false;
7554 		SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x, max(fixed_rows, min(total_rows - 1, y)), 0, 0, -1);
7555 		shiftpos = curpos;
7556 	}
7557 }
7558 
DoAppend0(bool edit)7559 void GridCtrl::DoAppend0(bool edit)
7560 {
7561 	if(!EndEdit())
7562 		return; //powinno byc zakomentowane ale wtedy goend wola endedit ale juz dla rowidx wiekszego o 1..
7563 
7564 	Append0(1, GD_ROW_HEIGHT, true);
7565 
7566 	call_whenchangecol = false;
7567 	call_whenchangerow = false;
7568 	GoEnd(true, true);
7569 	call_whenchangecol = true;
7570 	call_whenchangerow = true;
7571 
7572 	newrow_appended = true;
7573 
7574 	if(edit)
7575 	{
7576 		StartEdit();
7577 		GoCursorLeftRight();
7578 	}
7579 
7580 	if(!isedit)
7581 		WhenInsertRow0();
7582 
7583 	WhenNewRow();
7584 
7585 	if(!edit)
7586 		newrow_appended = false;
7587 
7588 }
7589 
DoAppend()7590 void GridCtrl::DoAppend()             { DoAppend0(edits_in_new_row); }
DoAppendNoEdit()7591 void GridCtrl::DoAppendNoEdit()       { DoAppend0(0);                }
DoInsertBefore()7592 void GridCtrl::DoInsertBefore()       { DoInsertBefore0(1);          }
DoInsertBeforeNoEdit()7593 void GridCtrl::DoInsertBeforeNoEdit() {	DoInsertBefore0(0);          }
DoInsertAfter()7594 void GridCtrl::DoInsertAfter()        { DoInsertAfter0(1);           }
DoInsertAfterNoEdit()7595 void GridCtrl::DoInsertAfterNoEdit()  { DoInsertAfter0(0);           }
DoDuplicate()7596 void GridCtrl::DoDuplicate()          { DoDuplicate0();              }
DoEdit()7597 void GridCtrl::DoEdit()               { StartEdit();                 }
DoEndEdit()7598 void GridCtrl::DoEndEdit()            { EndEdit();                   }
DoCancelEdit()7599 void GridCtrl::DoCancelEdit()         { EndEdit(false);              }
DoSwapUp()7600 void GridCtrl::DoSwapUp()             { SwapUp();                    }
DoSwapDown()7601 void GridCtrl::DoSwapDown()           { SwapDown();                  }
DoGoBegin()7602 void GridCtrl::DoGoBegin()            { GoBegin();                   }
DoGoEnd()7603 void GridCtrl::DoGoEnd()              { GoEnd();                     }
DoGoNext()7604 void GridCtrl::DoGoNext()             { GoNext();                    }
DoGoPrev()7605 void GridCtrl::DoGoPrev()             { GoPrev();                    }
DoGoLeft()7606 void GridCtrl::DoGoLeft()             { GoLeft();                    }
DoGoRight()7607 void GridCtrl::DoGoRight()            { GoRight();                   }
DoGoPageUp()7608 void GridCtrl::DoGoPageUp()           { GoPageUp();                  }
DoGoPageDn()7609 void GridCtrl::DoGoPageDn()           { GoPageDn();                  }
7610 
DoSelectAll()7611 void GridCtrl::DoSelectAll()
7612 {
7613 	SelectCount(fixed_rows, total_rows - fixed_rows);
7614 }
7615 
ShowRow(int n,bool refresh)7616 GridCtrl& GridCtrl::ShowRow(int n, bool refresh)
7617 {
7618 	if(!vitems[n].hidden)
7619 		return *this;
7620 	vitems[n].hidden = false;
7621 	vitems[n].size = vitems[n].tsize;
7622 	if(refresh)
7623 		Repaint(false, true);
7624 
7625 	return *this;
7626 }
7627 
HideRow(int n,bool refresh)7628 GridCtrl& GridCtrl::HideRow(int n, bool refresh)
7629 {
7630 	if(n < 0)
7631 		n = rowidx;
7632 
7633 	if(vitems[n].hidden)
7634 		return *this;
7635 	vitems[n].hidden = true;
7636 	vitems[n].tsize = vitems[n].size;
7637 	vitems[n].size = 0;
7638 	if(refresh)
7639 		Repaint(false, true);
7640 
7641 	return *this;
7642 }
7643 
ShowColumn(int n,bool refresh)7644 GridCtrl& GridCtrl::ShowColumn(int n, bool refresh)
7645 {
7646 	if(!hitems[n].hidden)
7647 		return *this;
7648 	hitems[n].hidden = false;
7649 	hitems[n].size = hitems[n].tsize;
7650 	if(refresh)
7651 		Repaint(true, false);
7652 
7653 	return *this;
7654 }
7655 
HideColumn(int n,bool refresh)7656 GridCtrl& GridCtrl::HideColumn(int n, bool refresh)
7657 {
7658 	if(hitems[n].hidden)
7659 		return *this;
7660 	hitems[n].hidden = true;
7661 	hitems[n].tsize = hitems[n].size;
7662 	if(refresh)
7663 		Repaint(true, false);
7664 
7665 	return *this;
7666 }
7667 
HideRows(bool repaint)7668 void GridCtrl::HideRows(bool repaint)
7669 {
7670 	bool change = false;
7671 	for(int i = fixed_rows; i < total_rows; i++)
7672 		if(!vitems[i].hidden)
7673 		{
7674 			change = true;
7675 			vitems[i].hidden = true;
7676 			vitems[i].tsize = vitems[i].size;
7677 			vitems[i].size = 0;
7678 		}
7679 	if(change || repaint)
7680 		Repaint(false, true);
7681 }
7682 
ShowRows(bool repaint)7683 void GridCtrl::ShowRows(bool repaint)
7684 {
7685 	bool change = false;
7686 	for(int i = fixed_rows; i < total_rows; i++)
7687 		if(vitems[i].hidden)
7688 		{
7689 			change = true;
7690 			vitems[i].hidden = false;
7691 			vitems[i].size = vitems[i].tsize;
7692 		}
7693 	if(change || repaint)
7694 		Repaint(false, true, UC_SCROLL);
7695 }
7696 
MenuHideColumn(int n)7697 void GridCtrl::MenuHideColumn(int n)
7698 {
7699 	if(hitems[n].hidden)
7700 		ShowColumn(n);
7701 	else
7702 		HideColumn(n);
7703 }
7704 
ShowMatchedRows(const WString & f)7705 int GridCtrl::ShowMatchedRows(const WString &f)
7706 {
7707 	if(f.IsEmpty())
7708 	{
7709 		if(search_highlight)
7710 			ClearFound();
7711 		else
7712 			ShowRows(true);
7713 		return 0;
7714 	}
7715 
7716 	if(!search_immediate && search_highlight)
7717 	{
7718 		ClearFound(true, false);
7719 		//search_string = f;
7720 	}
7721 
7722 	int first_matched_row = -1;
7723 
7724 	int s, e;
7725 	for(int i = fixed_rows; i < total_rows; i++)
7726 	{
7727 		if(!vitems[i].clickable)
7728 			continue;
7729 
7730 		int idv = vitems[i].id;
7731 
7732 		for(int j = fixed_cols; j < total_cols; j++)
7733 		{
7734 			int idh = hitems[j].id;
7735 			Item &it = items[idv][idh];
7736 			it.Found(false);
7737 			it.fs = it.fe = 0;
7738 
7739 			if(hitems[j].hidden || !hitems[j].clickable)
7740 				continue;
7741 
7742 			if(Match(f, (WString) GetStdConvertedColumn(idh, it.val), s, e))
7743 			{
7744 				first_matched_row = i;
7745 				rowfnd = i;
7746 				goto found_first_matched_row;
7747 			}
7748 		}
7749 	}
7750 
7751 	if(first_matched_row < 0)
7752 		return 0;
7753 
7754 	found_first_matched_row:
7755 
7756 	bool change = false;
7757 
7758 	int rows = 0;
7759 	for(int i = fixed_rows; i < total_rows; i++)
7760 	{
7761 		if(!vitems[i].clickable)
7762 			continue;
7763 
7764 		bool match = false;
7765 		int idv = vitems[i].id;
7766 		for(int j = fixed_cols; j < total_cols; j++)
7767 		{
7768 			int idh = hitems[j].id;
7769 			Item &it = items[idv][idh];
7770 			it.Found(false);
7771 			it.fs = it.fe = 0;
7772 
7773 			if(hitems[j].hidden || !hitems[j].clickable)
7774 				continue;
7775 
7776 			if(Match(f, (WString) GetStdConvertedColumn(idh, it.val), s, e))
7777 			{
7778 				match = true;
7779 				it.Found(search_highlight);
7780 				it.fs = s;
7781 				it.fe = e;
7782 
7783 				if(!search_highlight)
7784 					break;
7785 			}
7786 		}
7787 
7788 		if(match)
7789 		{
7790 			rows++;
7791 			if(search_hide && vitems[i].hidden)
7792 			{
7793 				vitems[i].hidden = false;
7794 				vitems[i].size = vitems[i].tsize;
7795 				change = true;
7796 			}
7797 
7798 			if(search_highlight_first)
7799 				break;
7800 		}
7801 		else if(search_hide && !vitems[i].hidden)
7802 		{
7803 			vitems[i].hidden = true;
7804 			vitems[i].tsize = vitems[i].size;
7805 			vitems[i].size = 0;
7806 			change = true;
7807 		}
7808 		vitems[i].found = match;
7809 	}
7810 
7811 	if(rows > 0)
7812 	{
7813 		if(change || search_highlight)
7814 		{
7815 			LG(0, "Repaint %d", search_hide);
7816 			Repaint(false, search_hide, 0);
7817 		}
7818 	}
7819 	else if(search_hide)
7820 	{
7821 		for(int i = fixed_rows; i < total_rows; i++)
7822 		{
7823 			vitems[i].hidden = false;
7824 			vitems[i].size = vitems[i].tsize;
7825 		}
7826 	}
7827 
7828 	if(search_move_cursor && first_matched_row >= 0)
7829 	{
7830 		SetCursor0(first_matched_row);
7831 		CenterCursor();
7832 		WhenSearchCursor();
7833 	}
7834 
7835 	LG(0, "Matched rows %d", rows);
7836 	return rows;
7837 }
7838 
ClearFound(bool showrows,bool clear_string)7839 void GridCtrl::ClearFound(bool showrows, bool clear_string)
7840 {
7841 	for(int i = 0; i < total_rows; i++)
7842 	{
7843 		vitems[i].found = false;
7844 		for(int j = 0; j < total_cols; j++)
7845 		{
7846 			items[i][j].Found(false);
7847 			items[i][j].fs = items[i][j].fe = 0;
7848 		}
7849 	}
7850 	if(showrows)
7851 		ShowRows(true);
7852 
7853 	if(clear_string)
7854 		search_string.Clear();
7855 
7856 	WhenSearchCursor();
7857 }
7858 
Match(const WString & f,const WString & s,int & fs,int & fe)7859 bool GridCtrl::Match(const WString &f, const WString &s, int &fs, int &fe)
7860 {
7861 	int i = 0;
7862 
7863 	int fl = f.GetLength();
7864 	int sl = s.GetLength();
7865 
7866 	if(fl > sl)
7867 		return false;
7868 
7869 	for(int j = find_offset; j < sl; j++)
7870 	{
7871 		bool match;
7872 		if(search_case)
7873 			match = f[i] == s[j];
7874 		else
7875 			match = ToUpper(f[i]) == ToUpper(s[j]);
7876 
7877 		if(match)
7878 		{
7879 			if(++i == fl)
7880 			{
7881 				fs = j + 1 - fl;
7882 				fe = j;
7883 				return true;
7884 			}
7885 		}
7886 		else
7887 			i = 0;
7888 	}
7889 	return false;
7890 }
7891 
DoFind()7892 void GridCtrl::DoFind()
7893 {
7894 	UpdateCtrls(UC_CHECK_VIS | UC_HIDE | UC_CTRLS | UC_SCROLL | UC_NOFOCUS);
7895 	ShowMatchedRows((WString) ~find);
7896 }
7897 
WhenInsertRow0()7898 bool GridCtrl::WhenInsertRow0()
7899 {
7900 	WhenInsertRow();
7901 	if(cancel_insert)
7902 	{
7903 		WhenCancelNewRow();
7904 		Remove0(curpos.y, 1, true, true, false);
7905 		cancel_insert = false;
7906 		return false;
7907 	}
7908 	return true;
7909 }
7910 
GetMinRowSelected()7911 int GridCtrl::GetMinRowSelected()
7912 {
7913 	int i = fixed_rows;
7914 	while(i < total_rows && !vitems[i].IsSelect()) i++;
7915 	return i > total_rows - 1 ? -1 : i;
7916 }
7917 
GetMaxRowSelected()7918 int GridCtrl::GetMaxRowSelected()
7919 {
7920 	int i = total_rows - 1;
7921 	while(i >= fixed_rows && !vitems[i].IsSelect()) i--;
7922 	return i < fixed_rows ? -1 : i;
7923 }
7924 
CloseGrid()7925 void GridCtrl::CloseGrid()
7926 {
7927 	GetTopWindow()->Close();
7928 }
7929 
UpdateVisColRow(bool col)7930 void GridCtrl::UpdateVisColRow(bool col)
7931 {
7932 	if(col)
7933 	{
7934 		firstVisCol = fixed_cols;
7935 		lastVisCol = total_cols - 1;
7936 
7937 		for(int i = fixed_cols; i < total_cols; i++)
7938 			if(!hitems[i].hidden)
7939 			{
7940 				firstVisCol = i;
7941 				break;
7942 			}
7943 
7944 		for(int i = total_cols - 1; i >= fixed_cols; i--)
7945 			if(!hitems[i].hidden)
7946 			{
7947 				lastVisCol = i;
7948 				break;
7949 			}
7950 	}
7951 	else
7952 	{
7953 		firstVisRow = fixed_rows;
7954 		lastVisRow = total_rows - 1;
7955 
7956 		for(int i = fixed_rows; i < total_rows; i++)
7957 			if(!vitems[i].hidden)
7958 			{
7959 				firstVisRow = i;
7960 				break;
7961 			}
7962 
7963 		for(int i = total_rows - 1; i >= fixed_rows; i--)
7964 			if(!vitems[i].hidden)
7965 			{
7966 				lastVisRow = i;
7967 				break;
7968 			}
7969 	}
7970 }
7971 
GetBarOffset()7972 Point GridCtrl::GetBarOffset()
7973 {
7974 	Size bsz = bar.GetSize();
7975 	return Point(bar.GetAlign() == BarCtrl::BAR_LEFT ? bsz.cx : 0,
7976 	             bar.GetAlign() == BarCtrl::BAR_TOP ? bsz.cy : 0);
7977 }
7978 
ClearModified()7979 void GridCtrl::ClearModified()
7980 {
7981 	row_modified = 0;
7982 	for(int i = 0; i < total_cols; ++i)
7983 		items[curid.y][i].modified = false;
7984 }
7985 
Debug(int n)7986 void GridCtrl::Debug(int n)
7987 {
7988 	if(n == 0)
7989 	{
7990 		LG("---- DEBUG 0 ----------");
7991 		LG("firstVisCol    %d", firstVisCol);
7992 		LG("lastVisCol     %d", lastVisCol);
7993 		LG("firstVisRow    %d", firstVisRow);
7994 		LG("lastVisRow     %d", lastVisRow);
7995 		LG("firstCol       %d", firstCol);
7996 		LG("firstRow       %d", firstRow);
7997 		LG("lastCol        %d", lastCol);
7998 		LG("lastRow        %d", lastRow);
7999 		LG("total_cols     %d", total_cols);
8000 		LG("total_rows     %d", total_rows);
8001 		LG("curpos         %d, %d", curpos.x, curpos.y);
8002 		LG("sbPos          %d, %d", sbx.Get(), sby.Get());
8003 		LG("sbTotal        %d, %d", sbx.GetTotal(), sby.GetTotal());
8004 		LG("sbPage         %d, %d", sbx.GetPage(), sby.GetPage());
8005 		LG("Size           %d, %d", GetSize().cx, GetSize().cy);
8006 		LG("fixed_width    %d", fixed_width);
8007 		LG("fixed_height   %d", fixed_height);
8008 		LG("total_width    %d", total_width);
8009 		LG("total_height   %d", total_height);
8010 		LG("row_modified   %d", row_modified);
8011 		LG("selected_rows  %d", selected_rows);
8012 		LG("selected_items %d", selected_items);
8013 		LG("---- END --------------");
8014 	}
8015 	if(n == 1)
8016 	{
8017 		LG("---- DEBUG 1 ----------");
8018 		for(int i = 0; i < total_cols; i++)
8019 		{
8020 			LG("Col %d h:%d p:%d s:%d", i, hitems[i].hidden, hitems[i].npos, hitems[i].nsize);
8021 			//LG("ismin %d ismax %d", hitems[i].ismin, hitems[i].ismax);
8022 		}
8023 		LG("---- END --------------");
8024 	}
8025 	if(n == 2)
8026 	{
8027 		LG("---- DEBUG 2 ----------");
8028 		for(int i = 0; i < total_rows; i++)
8029 		{
8030 			LG("Row %d p:%d s:%d", i, vitems[i].npos, vitems[i].nsize);
8031 		}
8032 		LG("---- END --------------");
8033 	}
8034 	if(n == 3)
8035 	{
8036 		Point p = GetCtrlPos(focused_ctrl);
8037 		LG(2, "Focused %x (%d, %d)", focused_ctrl, p.x, p.y);
8038 	}
8039 }
8040 
Serialize(Stream & s)8041 void GridCtrl::Serialize(Stream &s)
8042 {
8043 	if(s.IsStoring())
8044 	{
8045 		s % total_cols;
8046 		for(int i = 1; i < total_cols; i++)
8047 		{
8048 			int id = hitems[i].id;
8049 			//s % String(aliases[id]);
8050 			s % id;
8051 			s % hitems[id];
8052 		}
8053 	}
8054 	else
8055 	{
8056 		int tc;
8057 		s % tc;
8058 		for(int i = 1; i < tc; i++)
8059 		{
8060 			//String alias;
8061 			//s % alias;
8062 			//int n = aliases.Find(alias);
8063 			int id;
8064 			s % id;
8065 			//if(id >= 0 && id < total_cols)
8066 			s % hitems[id];
8067 		}
8068 	}
8069 }
8070 
JoinCells(int left,int top,int right,int bottom,bool relative)8071 void GridCtrl::JoinCells(int left, int top, int right, int bottom, bool relative)
8072 {
8073 	int fc = relative ? fixed_cols : 0;
8074 	int fr = relative ? fixed_rows : 0;
8075 
8076 	left   += fc;
8077 	top    += fr;
8078 	right  += fc;
8079 	bottom += fr;
8080 
8081 	int idx = hitems[left].id;
8082 	int idy = vitems[top].id;
8083 	int cx = right - left;
8084 	int cy = bottom - top;
8085 
8086 	for(int i = top; i <= bottom; ++i)
8087 	{
8088 		vitems[i].join++;
8089 
8090 		for(int j = left; j <= right; ++j)
8091 		{
8092 			Item &it = items[i][j];
8093 
8094 			it.idx = idx;
8095 			it.idy = idy;
8096 			it.cx  = cx;
8097 			it.cy  = cy;
8098 			it.group = join_group;
8099 			it.isjoined = true;
8100 
8101 			if(i == top)
8102 				hitems[j].join++;
8103 		}
8104 	}
8105 
8106 	JoinRect& jr = joins.Add();
8107 	jr.r.Set(left, top, right, bottom);
8108 	jr.group = join_group;
8109 	jr.idx = idx;
8110 	jr.idy = idy;
8111 
8112 	join_group++;
8113 }
8114 
JoinFixedCells(int left,int top,int right,int bottom)8115 void GridCtrl::JoinFixedCells(int left, int top, int right, int bottom)
8116 {
8117 	JoinCells(left, top, right, bottom, false);
8118 }
8119 
JoinRow(int n,int left,int right)8120 void GridCtrl::JoinRow(int n, int left, int right)
8121 {
8122 	if(n < 0)
8123 		n = rowidx;
8124 
8125 	if(left < 0)
8126 		left = fixed_cols;
8127 	else
8128 		left += fixed_cols;
8129 
8130 	if(right < 0)
8131 		right = total_cols - fixed_cols;
8132 	else
8133 		right = total_cols - fixed_cols - right;
8134 
8135 	JoinCells(left, n, right, n, false);
8136 }
8137 
JoinRow(int left,int right)8138 void GridCtrl::JoinRow(int left, int right)
8139 {
8140 	JoinRow(-1, left, right);
8141 }
8142 
8143 /*
8144 
8145 jr.r.top = 2;
8146 jr.r.bottom = 6
8147 
8148 2 -----------------------
8149 3 -----------------------
8150   +++++++++++++++++++++++  //insert at 4
8151   +++++++++++++++++++++++
8152 4 -----------------------
8153 5 -----------------------
8154 6 -----------------------
8155 
8156 */
8157 
UpdateJoins(int row,int col,int cnt)8158 void GridCtrl::UpdateJoins(int row, int col, int cnt)
8159 {
8160 	if(row >= 0)
8161 	{
8162 		for(int i = 0; i < joins.GetCount(); i++)
8163 		{
8164 			JoinRect& jr = joins[i];
8165 
8166 			int top = jr.r.top;
8167 			int bottom = jr.r.bottom;
8168 
8169 			if(row > top && row < bottom) // new row is inside cell rect, idy doesn't change but cy does
8170 			{
8171 				for(int r = jr.r.top; r <= jr.r.bottom; r++)
8172 				{
8173 					for(int c = jr.r.left; c <= jr.r.right; c++)
8174 					{
8175 						if(r < row || r > row)
8176 						{
8177 							Item& it = GetItem(r, c);
8178 							it.cy += cnt;
8179 						}
8180 						else if(r == row)
8181 						{
8182 							for(int n = 0; n < cnt; n++)
8183 							{
8184 								Item& it = GetItem(r + n, c);
8185 								it.group = jr.group;
8186 								it.idx = jr.idx;
8187 								it.idy = jr.idy;
8188 								it.cx = jr.r.Width();
8189 								it.cy = jr.r.Height() + cnt;
8190 								it.isjoined = true;
8191 
8192 								if(c == jr.r.left)
8193 									vitems[r + n].join++;
8194 							}
8195 						}
8196 					}
8197 				}
8198 
8199 				jr.r.bottom += cnt;
8200 			}
8201 			else if(row <= top) // idy changes
8202 			{
8203 				jr.idy += cnt;
8204 
8205 				for(int r = jr.r.top; r <= jr.r.bottom; r++)
8206 				{
8207 					for(int c = jr.r.left; c <= jr.r.right; c++)
8208 					{
8209 						Item& it = GetItem(r + cnt, c);
8210 						it.idy = jr.idy;
8211 					}
8212 				}
8213 
8214 				jr.r.top += cnt;
8215 				jr.r.bottom += cnt;
8216 			}
8217 		}
8218 	}
8219 
8220 	if(col >= 0)
8221 	{
8222 		for(int i = 0; i < joins.GetCount(); i++)
8223 		{
8224 			JoinRect& jr = joins[i];
8225 
8226 			int left = jr.r.left;
8227 			int right = jr.r.right;
8228 
8229 			if(col > left && col < right)
8230 			{
8231 				for(int c = jr.r.left; c <= jr.r.right; c++)
8232 				{
8233 					for(int r = jr.r.top; r <= jr.r.bottom; r++)
8234 					{
8235 						if(c < col || c > col)
8236 						{
8237 							Item& it = GetItem(r, c);
8238 							it.cx += cnt;
8239 						}
8240 						else if(c == col)
8241 						{
8242 							for(int n = 0; n < cnt; n++)
8243 							{
8244 								Item& it = GetItem(r, c + n);
8245 								it.group = jr.group;
8246 								it.idx = jr.idx;
8247 								it.idy = jr.idy;
8248 								it.cx = jr.r.Width() + cnt;
8249 								it.cy = jr.r.Height();
8250 								it.isjoined = true;
8251 
8252 								if(r == jr.r.top)
8253 									hitems[c + n].join++;
8254 							}
8255 						}
8256 					}
8257 				}
8258 
8259 				jr.r.right += cnt;
8260 			}
8261 			else if(col <= left)
8262 			{
8263 				jr.idx += cnt;
8264 
8265 				for(int c = jr.r.left; c <= jr.r.right; c++)
8266 				{
8267 					for(int r = jr.r.top; r <= jr.r.bottom; r++)
8268 					{
8269 						Item& it = GetItem(r, c + cnt);
8270 						it.idx = jr.idx;
8271 					}
8272 				}
8273 
8274 				jr.r.left += cnt;
8275 				jr.r.right += cnt;
8276 			}
8277 		}
8278 	}
8279 }
8280 
8281 /*----------------------------------------------------------------------------------------*/
8282 
Paint(Draw & w)8283 void GridPopUp::Paint(Draw &w)
8284 {
8285 	Size sz = GetSize();
8286 	if(gd->row < 0 || gd->col < 0)
8287 		gd->PaintFixed(w, false, false, 1, 1, sz.cx - 2, sz.cy - 2, val, style | GD::WRAP | GD::VCENTER, fnt);
8288 	else
8289 		gd->Paint(w, 1, 1, sz.cx - 2, sz.cy - 2, val, style | GD::WRAP | GD::VCENTER, fg, bg, fnt);
8290 	DrawBorder(w, sz, BlackBorder);
8291 }
8292 
Offset(Point p)8293 Point GridPopUp::Offset(Point p)
8294 {
8295 	return p + GetScreenView().TopLeft() - ctrl->GetScreenView().TopLeft();
8296 }
8297 
LeftDown(Point p,dword flags)8298 void GridPopUp::LeftDown(Point p, dword flags)
8299 {
8300 	ctrl->LeftDown(Offset(p), flags);
8301 }
8302 
LeftDrag(Point p,dword flags)8303 void GridPopUp::LeftDrag(Point p, dword flags)
8304 {
8305 	Close();
8306 	ctrl->LeftDrag(Offset(p), flags);
8307 }
8308 
LeftDouble(Point p,dword flags)8309 void GridPopUp::LeftDouble(Point p, dword flags)
8310 {
8311 	ctrl->LeftDouble(Offset(p), flags);
8312 }
8313 
RightDown(Point p,dword flags)8314 void GridPopUp::RightDown(Point p, dword flags)
8315 {
8316 	ctrl->RightDown(Offset(p), flags);
8317 }
8318 
LeftUp(Point p,dword flags)8319 void GridPopUp::LeftUp(Point p, dword flags)
8320 {
8321 	ctrl->LeftUp(Offset(p), flags);
8322 }
8323 
MouseWheel(Point p,int zdelta,dword flags)8324 void GridPopUp::MouseWheel(Point p, int zdelta, dword flags)
8325 {
8326 	ctrl->MouseWheel(Offset(p), zdelta, flags);
8327 }
8328 
MouseLeave()8329 void GridPopUp::MouseLeave()
8330 {
8331 	ctrl->MouseLeave();
8332 	Close();
8333 }
8334 
MouseEnter(Point p,dword flags)8335 void GridPopUp::MouseEnter(Point p, dword flags)
8336 {
8337 	ctrl->MouseEnter(Offset(p), flags);
8338 }
8339 
MouseMove(Point p,dword flags)8340 void GridPopUp::MouseMove(Point p, dword flags)
8341 {
8342 	ctrl->MouseMove(Offset(p), flags);
8343 }
8344 
CursorImage(Point p,dword flags)8345 Image GridPopUp::CursorImage(Point p, dword flags)
8346 {
8347 	return ctrl->CursorImage(Offset(p), flags);
8348 }
8349 
LostFocus()8350 void GridPopUp::LostFocus()
8351 {
8352 	Close();
8353 }
8354 
PopUp(Ctrl * owner,int x,int y,int width,int height)8355 void GridPopUp::PopUp(Ctrl *owner, int x, int y, int width, int height)
8356 {
8357 	Rect r(x, y, x + width, y + height);
8358 	if(r != GetRect())
8359 		SetRect(r);
8360 
8361 	if(!open)
8362 	{
8363 		ctrl = owner;
8364 		open = true;
8365 		Ctrl::PopUp(owner, true, false, GUI_DropShadows());
8366 		SetAlpha(230);
8367 	}
8368 }
8369 
Close()8370 void GridPopUp::Close()
8371 {
8372 	open = false;
8373 	Ctrl::Close();
8374 }
8375 
UpdateHighlighting(int mode,Point p)8376 void GridCtrl::UpdateHighlighting(int mode, Point p)
8377 {
8378 	if(resize_paint_mode < 1 && (resizeCol || resizeRow))
8379 		return;
8380 
8381 	bool refresh = false;
8382 
8383 	if(hcol >= hitems.GetCount())
8384 		hcol = -1;
8385 
8386 	if(mode == GS_UP)
8387 	{
8388 		if((p.x < 0 || p.x > GetSize().cx - 1) && hcol >= 0 && hitems[hcol].IsHighlight())
8389 		{
8390 			hitems[hcol].Highlight(0);
8391 			refresh = true;
8392 			hcol = -1;
8393 		}
8394 	}
8395 	else if(mode == GS_MOVE)
8396 	{
8397 		int col = GetMouseCol(p, true, true, false);
8398 		int row = hrow = GetMouseRow(p, false, true, true);
8399 
8400 		if(hcol >= 0 && (hcol != col || row != 0) && hitems[hcol].IsHighlight())
8401 		{
8402 			hitems[hcol].Highlight(0);
8403 			refresh = true;
8404 		}
8405 		if(col >= 0 && row == 0 && !hitems[col].IsHighlight())
8406 		{
8407 			hitems[col].Highlight(1);
8408 			hcol = col;
8409 			refresh = true;
8410 		}
8411 	}
8412 	else if(mode == GS_POPUP)
8413 	{
8414 		if(hcol >= 0)
8415 			hitems[hcol].Highlight(0);
8416 		refresh = true;
8417 		hcol = moveCol;
8418 	}
8419 	else if(mode == GS_BORDER)
8420 	{
8421 		if(hcol >= 0 && hitems[hcol].IsHighlight())
8422 		{
8423 			hitems[hcol].Highlight(0);
8424 			refresh = true;
8425 		}
8426 	}
8427 	if(refresh)
8428 		RefreshRow(0, 0, 1);
8429 }
8430 
GetColumnWidths()8431 String GridCtrl::GetColumnWidths()
8432 {
8433 	String s;
8434 	for(int i = fixed_cols; i < total_cols; i++)
8435 	{
8436 		s += AsString(hitems[i].nsize);
8437 		if(i < total_cols - 1)
8438 			s += " ";
8439 	}
8440 	return s;
8441 }
8442 
ColumnWidths(const char * s)8443 void GridCtrl::ColumnWidths(const char* s)
8444 {
8445 	Vector<String> w = UPP::Split(s, ' ');
8446 	for(int i = 0; i < min(w.GetCount(), GetColumnCount()); i++)
8447 		hitems[i + fixed_cols].Width(atoi(w[i]));
8448 }
8449 
SetDisplay(int r,int c,GridDisplay & gd)8450 void GridCtrl::SetDisplay(int r, int c, GridDisplay& gd)
8451 {
8452 	GetItem(r + fixed_rows, c + fixed_cols).SetDisplay(gd);
8453 }
8454 
8455 #ifdef flagGRIDSQL
FieldLayout(FieldOperator & f)8456 void GridCtrl::FieldLayout(FieldOperator& f)
8457 {
8458 	for(int i = 1; i < total_cols; i++)
8459 	{
8460 		if(hitems[i].hidden)
8461 			continue;
8462 		int id = hitems[i].id;
8463 		const Id& key = aliases.GetKey(id);
8464 		f(key, items[vitems[rowidx].id][id].val);
8465 	}
8466 }
8467 
GetColumnList(bool skip_hidden) const8468 SqlSet GridCtrl::GetColumnList(bool skip_hidden) const
8469 {
8470 	SqlSet s;
8471 	for(int i = 1; i < total_cols; i++)
8472 	{
8473 		if(skip_hidden && hitems[i].hidden)
8474 			continue;
8475 		int id = hitems[i].id;
8476 		s.Cat(SqlId(aliases.GetKey(id)));
8477 	}
8478 	return s;
8479 }
8480 
8481 #endif
8482 
8483 
8484 /*----------------------------------------------------------------------------------------*/
GridFind()8485 GridFind::GridFind()
8486 {
8487 	MultiButton::SubButton& btn = button.AddButton().Main();
8488 	btn.WhenPush = THISBACK(Push);
8489 	btn.SetMonoImage(CtrlImg::smallright());
8490 
8491 	button.SetStyle(button.StyleFrame());
8492 	button.NoDisplay();
8493 	button.AddTo(*this);
8494 }
8495 
Key(dword key,int count)8496 bool GridFind::Key(dword key, int count)
8497 {
8498 	if(key == K_ENTER && WhenEnter)
8499 	{
8500 		WhenEnter();
8501 		return true;
8502 	}
8503 
8504 	return EditString::Key(key, count);
8505 }
8506 
GetMinSize() const8507 Size GridFind::GetMinSize() const
8508 {
8509 	return button.GetMinSize();
8510 }
8511 
Push()8512 void GridFind::Push()
8513 {
8514 	MenuBar::Execute(WhenBar, GetScreenRect().TopRight());
8515 }
8516 
8517 /*----------------------------------------------------------------------------------------*/
Paint(Draw & w)8518 void GridPopUpHeader::Paint(Draw &w)
8519 {
8520 	Size sz = GetSize();
8521 	dword style = chameleon ? GD::CHAMELEON : 0;
8522 	Font stdfont(StdFont());
8523 	display->PaintFixed(w, 1, 1, 0, 0, sz.cx, sz.cy,
8524 		                val, style, stdfont, false, true,
8525 		                sortmode, sortcol, sortcnt, true);
8526 }
8527 
PopUp(Ctrl * owner,int x,int y,int width,int height)8528 void GridPopUpHeader::PopUp(Ctrl *owner, int x, int y, int width, int height)
8529 {
8530 	SetRect(Rect(x, y, x + width, y + height));
8531 	if(open)
8532 		return;
8533 	Ctrl::PopUp(owner, true, true, GUI_DropShadows());
8534 	SetAlpha(200);
8535 	open = true;
8536 }
8537 
Close()8538 void GridPopUpHeader::Close()
8539 {
8540 	open = false;
8541 	Ctrl::Close();
8542 }
8543 
8544 /*----------------------------------------------------------------------------------------*/
GridButton()8545 GridButton::GridButton()
8546 {
8547 	n = 0;
8548 	img = 0;
8549 }
8550 
Paint(Draw & w)8551 void GridButton::Paint(Draw& w)
8552 {
8553 	static Image (*vimg[])() = {
8554 		&GridImg::Btn0N, &GridImg::Btn0H, &GridImg::Btn0P,
8555 		&GridImg::Btn1N, &GridImg::Btn1H, &GridImg::Btn1P
8556 	};
8557 
8558 	Size sz = GetSize();
8559 	w.DrawImage(0, 0, sz.cx, sz.cy, vimg[img + n * 3]);
8560 }
8561 
LeftDown(Point p,dword flags)8562 void GridButton::LeftDown(Point p, dword flags)
8563 {
8564 	img = 2;
8565 	Refresh();
8566 }
8567 
LeftUp(Point p,dword flags)8568 void GridButton::LeftUp(Point p, dword flags)
8569 {
8570 	img = 1;
8571 	Refresh();
8572 	Action();
8573 }
8574 
MouseEnter(Point p,dword flags)8575 void GridButton::MouseEnter(Point p, dword flags)
8576 {
8577 	img = flags & K_MOUSELEFT ? 2 : 1;
8578 	Refresh();
8579 }
8580 
MouseLeave()8581 void GridButton::MouseLeave()
8582 {
8583 	img = 0;
8584 	Refresh();
8585 }
8586 
State(int reason)8587 void GridButton::State(int reason)
8588 {
8589 	if(reason == CLOSE)
8590 		img = 0;
8591 }
8592 
GetStdSize() const8593 Size GridButton::GetStdSize() const
8594 {
8595 	return n > 0 ? Size(14, 11) : Size(17, 17); //FIXME
8596 }
8597 
SetButton(int b)8598 void GridButton::SetButton(int b)
8599 {
8600 	n = b;
8601 }
8602 
GridResizePanel()8603 GridResizePanel::GridResizePanel()
8604 {
8605 	int h = max(16, Draw::GetStdFontCy()) + 5;
8606 	Height(h);
8607 	Size csz = close.GetStdSize();
8608 	Add(close.LeftPos(1, csz.cx).TopPos((h - csz.cy) / 2, csz.cy));
8609 	minsize.Clear();
8610 	close.WhenAction = Proxy(WhenClose);
8611 }
8612 
Paint(Draw & w)8613 void GridResizePanel::Paint(Draw& w)
8614 {
8615 	Size sz = GetSize();
8616 	w.DrawRect(sz, SColorFace);
8617 	Size isz = CtrlsImg::SizeGrip().GetSize();
8618 	w.DrawImage(sz.cx - isz.cx, sz.cy - isz.cy, CtrlsImg::SizeGrip());
8619 }
8620 
MouseOnGrip(const Point & p)8621 bool GridResizePanel::MouseOnGrip(const Point &p)
8622 {
8623 	Size isz = CtrlsImg::SizeGrip().GetSize();
8624 	Size sz = GetSize();
8625 	return (p.x > sz.cx - isz.cx && p.y > sz.cy - isz.cy);
8626 }
8627 
SetMinSize(Size sz)8628 void GridResizePanel::SetMinSize(Size sz)
8629 {
8630 	minsize = sz;
8631 }
8632 
CursorImage(Point p,dword flags)8633 Image GridResizePanel::CursorImage(Point p, dword flags)
8634 {
8635 	if(MouseOnGrip(p) || HasCapture())
8636 		return Image::SizeBottomRight();
8637 	return Image::Arrow();
8638 }
8639 
LeftDown(Point p,dword flags)8640 void GridResizePanel::LeftDown(Point p, dword flags)
8641 {
8642 	if(MouseOnGrip(p))
8643 	{
8644 		r = GetParent()->GetScreenRect();
8645 		pos = GetMousePos();
8646 		SetCapture();
8647 	}
8648 }
8649 
LeftUp(Point p,dword flags)8650 void GridResizePanel::LeftUp(Point p, dword flags)
8651 {
8652 	ReleaseCapture();
8653 }
8654 
MouseMove(Point p,dword flags)8655 void GridResizePanel::MouseMove(Point p, dword flags)
8656 {
8657 	if(HasCapture())
8658 	{
8659 		Point curpos = GetMousePos();
8660 		Point diff = curpos - pos;
8661 		Rect r(this->r);
8662 		r.right += diff.x;
8663 		r.bottom += diff.y;
8664 		bool setmin = false;
8665 
8666 		if(!minsize.IsEmpty())
8667 		{
8668 			if(r.right < r.left + minsize.cx)
8669 			{
8670 				r.right = r.left + minsize.cx;
8671 				setmin = true;
8672 			}
8673 			if(r.bottom < r.top + minsize.cy)
8674 			{
8675 				r.bottom = r.top + minsize.cy;
8676 				setmin = true;
8677 			}
8678 		}
8679 		if(this->r != r || setmin)
8680 		{
8681 			GetParent()->SetRect(r);
8682 			GetParent()->Sync();
8683 		}
8684 	}
8685 }
8686 
8687 /*----------------------------------------------------------------------------------------*/
8688 
Xmlize(XmlIO & xml,GridCtrl & g)8689 template<> void Xmlize(XmlIO& xml, GridCtrl& g) {
8690 	Vector< Vector<Value> > v;
8691 
8692 	if(xml.IsLoading()) {
8693 		xml("data", v);
8694 		g.SetValues(v);
8695 	} else {
8696 		v = g.GetValues();
8697 		xml("data", v);
8698 	}
8699 }
8700 
Jsonize(JsonIO & json,GridCtrl & g)8701 template<> void Jsonize(JsonIO& json, GridCtrl& g) {
8702 	Vector< Vector<Value> > v;
8703 
8704 	if(json.IsLoading()) {
8705 		json("data", v);
8706 		g.SetValues(v);
8707 	} else {
8708 		v = g.GetValues();
8709 		json("data", v);
8710 	}
8711 }
8712 
8713 /* after this assist++ sees nothing */
8714 //$-
8715 #define E__Addv0(I)    Set(q, I - 1, p##I)
8716 #define E__AddF0(I) \
8717 GridCtrl& GridCtrl::Add(__List##I(E__Value)) { \
8718 	int q = GetCount(); \
8719 	__List##I(E__Addv0); \
8720 	return *this; \
8721 }
8722 __Expand(E__AddF0)
8723 
8724 #define E__Addv1(I)    Set(q, I - 1, p##I)
8725 #define E__AddF1(I) \
8726 GridCtrl::ItemRect& GridCtrl::AddRow(__List##I(E__Value)) { \
8727 	int q = GetCount(); \
8728 	ItemRect& ir = AddRow(); \
8729 	__List##I(E__Addv1); \
8730 	return ir; \
8731 }
8732 __Expand(E__AddF1)
8733 
8734 }
8735