#include #include "GridCtrl.h" NAMESPACE_UPP #define IMAGECLASS GridImg #define IMAGEFILE #include #define TFILE #include LineEdit *dlog = NULL; DropList *dlev = NULL; static int pos = 0; void Log(const char *fmt, ...) { if(!dlog || !dlev || int(~(*dlev)) > 0) return; char buffer[1024]; va_list argptr; va_start(argptr, fmt); int l = vsprintf(buffer, fmt, argptr); va_end(argptr); dlog->Insert(pos, buffer); pos += l; dlog->Insert(pos, "\n"); pos++; dlog->SetCursor(pos); } void Log(int level, const char *fmt, ...) { if(!dlog || !dlev || level < int(~(*dlev))) return; char buffer[1024]; va_list argptr; va_start(argptr, fmt); int l = vsprintf(buffer, fmt, argptr); va_end(argptr); dlog->Insert(pos, buffer); pos += l; dlog->Insert(pos, "\n"); pos++; dlog->SetCursor(pos); } void LogUpp(const char *fmt, ...) { char buffer[1024]; va_list argptr; va_start(argptr, fmt); int l = vsprintf(buffer, fmt, argptr); va_end(argptr); LOG(buffer); LOG("\n"); } void LogUpp(int level, const char *fmt, ...) { char buffer[1024]; va_list argptr; va_start(argptr, fmt); int l = vsprintf(buffer, fmt, argptr); va_end(argptr); LOG(buffer); LOG("\n"); } int GridCtrl::GD_COL_WIDTH; int GridCtrl::GD_ROW_HEIGHT; int GridCtrl::GD_HDR_HEIGHT; int GridCtrl::GD_IND_WIDTH; GridCtrl::GridCtrl() : holder(*this) { sortCol = -1; hcol = -1; hrow = -1; fixed_click = false; fixed_top_click = false; fixed_left_click = false; fixed_size_changed = true; valid_cursor = false; synced = false; sc_fr = -1; sc_lr = -1; resizeCol = false; resizeRow = false; ready = false; doscroll = true; scrollxdir = 0; scrollydir = 0; firstRow = lastRow = -1; firstCol = lastCol = -1; firstVisCol = -1; lastVisCol = -1; firstVisRow = -1; lastVisRow = -1; rowidx = -1; GD_COL_WIDTH = 50; GD_ROW_HEIGHT = Draw::GetStdFontCy() + 4; GD_HDR_HEIGHT = GD_ROW_HEIGHT + 2; GD_IND_WIDTH = 9; display = new GridDisplay(); display->SetTextAlign(GD::VCENTER); orgdisp = display; sbx.Horz(); sby.Vert(); sbx.WhenScroll = THISBACK(Scroll); sby.WhenScroll = THISBACK(Scroll); sbx.SetLine(5); sby.SetLine(GridCtrl::GD_ROW_HEIGHT); fixed_cols = 1; fixed_rows = 1; total_cols = 0; total_rows = 0; minRowSelected = -1; maxRowSelected = -1; bains = 0; coluid = 0; rowuid = 0; close.SetLabel(t_("Close")); close <<= THISBACK(CloseGrid); oldpos.Clear(); indicator = false; resizing_cols = true; resizing_rows = true; resizing_fixed_cols = true; resizing_fixed_rows = true; resize_paint_mode = 2; resize_col_mode = 1; resize_row_mode = 0; multi_select = false; select_row = true; moving_cols = false; moving_rows = false; dragging = false; horz_grid = true; vert_grid = true; sorting = false; live_cursor = false; edit_mode = 0; one_click_edit = false; coloringMode = 0; ctrls = false; genr_ctrls = false; edit_ctrls = false; sorting = true; sorting_multicol = true; cancel_operation = false; disable_childgotfocus = false; inserting = false; appending = false; duplicating = false; removing = false; accepting = false; canceling = false; moving = false; navigating = false; searching = false; editing = false; closing = false; hiding = false; clipboard = false; extra_paste = true; fixed_paste = false; search_hide = true; search_highlight = true; search_immediate = true; search_case = false; roworder = false; reject_null_row = true; tab_changes_row = true; tab_adds_row = true; enter_like_tab = false; keep_last_row = false; full_col_resizing = true; full_row_resizing = false; chameleon = false; mouse_move = false; row_modified = false; curpos.x = curpos.y = -1; oldcur.x = oldcur.y = -1; curid.x = curid.y = -1; oldid.x = oldid.y = -1; ctrlid.x = ctrlid.y = -1; osz.cx = osz.cy = -1; livecur.x = livecur.y = -1; leftpnt.x = leftpnt.y = -1; fixed_width = 0; fixed_height = 0; total_width = 0; total_height = 0; ItemRect &ir = vitems.Add(); ir.parent = this; ir.edits = &edits; items.Add(); /* add indicator, total_cols = 1 */ AddColumn("", 0); focuscol = 0; shiftmode = false; recalc_cols = false; recalc_rows = false; selected_rows = 0; selected_items = 0; WhenMenuBar = THISBACK(StdMenuBar); WhenToolBar = THISBACK(StdToolBar); findstring <<= THISBACK(DoFind); findstring.NoWantFocus(); StdAppend = THISBACK(DoAppend); StdRemove = THISBACK(DoRemove); StdInsert = THISBACK(DoInsertBefore); newrow_inserted = false; newrow_appended = false; row_removed = false; just_clicked = false; call_whenchangerow = true; call_whenremoverow = true; sel_begin = false; sel_end = false; moving_header = false; moving_body = false; moving_allowed = false; join_group = 0; curSplitCol = -1; curSplitRow = -1; moveCol = moveRow = -1; find_offset = 0; scrollLeftRight = false; fg_focus = SColorHighlightText; bg_focus = SColorHighlight; fg_select = Black; bg_select = Color(217, 198, 251); fg_live = SColorText; bg_live = Blend(SColorHighlight, White, 100); fg_found = Color(0, 0, 0); bg_found = Color(189,231,237); fg_even = SColorText; fg_odd = SColorText; bg_even = SColorPaper; bg_odd = SColorPaper; fg_grid = SColorShadow; focused_ctrl = NULL; focused_ctrl_id = -1; findstring.AddFrame(findimg); findstring.AddFrame(findopts); findopts.SetMonoImage(CtrlImg::smallright()).NoWantFocus(); findimg.SetImage(GridImg::ImgGridFind3()); findopts <<= THISBACK(ShowFindOpts); /* frames added at the very end, otherwise there will be strange crash in optimal mode... */ sbx.AutoHide(); sby.AutoHide(); SetFrame(ViewFrame()); AddFrame(sbx); AddFrame(sby); Ctrl::Add(holder); bar.Set(WhenToolBar); // WhenCtrlsAction = THISBACK(CtrlsAction); resizing = false; selecting = false; is_clipboard = false; sync_flag = 0; paint_flag = 0; } GridCtrl::~GridCtrl() { delete orgdisp; } void GridCtrl::StdToolBar(Bar &bar) { bool e = IsEditable(); bool c = IsCursor(); bool d = e && c; if(inserting || appending) bar.Add(t_("Append"), GridImg::ImgGridAdd(), StdAppend); if(removing) bar.Add(t_("Delete"), GridImg::ImgGridDel(), StdRemove); if(editing) { bar.Add(!ctrls, t_("Edit"), GridImg::ImgGridMod(), THISBACK(DoEdit)); bar.Add(ctrls, t_("Accept"), GridImg::ImgGridCommit(), THISBACK(DoEndEdit)); bar.Add(ctrls, t_("Cancel"), GridImg::ImgGridCancel(), THISBACK(CancelEdit)); } if(searching) { if(inserting || appending || removing) bar.Separator(); FindBar(bar, 150, 19); } if(moving) { if(searching) bar.Separator(); bar.Add(t_("Move up"), GridImg::ImgGridMoveUp(), THISBACK(DoSwapUp)); bar.Add(t_("Move down"), GridImg::ImgGridMoveDn(), THISBACK(DoSwapDown)); } if(navigating) { if(!closing) bar.GapRight(); else bar.Separator(); NavigatingBar(bar); } if(closing) { bar.GapRight(); bar.Add(close, 76, 24); } } void GridCtrl::FindBar(Bar &bar, int cx, int cy) { bar.Add(findstring, cx, cy); } void GridCtrl::InfoBar(Bar &bar, int cx, int cy) { bar.Add(info, cx, cy); } void GridCtrl::SetToolBarInfo(String inf) { info.SetLabel(inf); } void GridCtrl::NavigatingBar(Bar &bar) { bar.Add(RowFormat(t_("First %s")), GridImg::ImgGridFirstRec(), THISBACK(DoGoBegin)); bar.Add(RowFormat(t_("Previous %s")), GridImg::ImgGridPrevRec(), THISBACK(DoGoPrev)); bar.Add(RowFormat(t_("Next %s")), GridImg::ImgGridNextRec(), THISBACK(DoGoNext)); bar.Add(RowFormat(t_("Last %s")), GridImg::ImgGridLastRec(), THISBACK(DoGoEnd)); } GridCtrl& GridCtrl::SetToolBar(bool b, int align, int frame) { RemoveFrame(bar); if(!b) return *this; InsertFrame(frame, bar.Align(align)); if(frame == 1) switch(align) { case BarCtrl::BAR_TOP: RemoveFrame(TopSeparatorFrame()); InsertFrame(2, TopSeparatorFrame()); break; case BarCtrl::BAR_BOTTOM: RemoveFrame(BottomSeparatorFrame()); InsertFrame(2, BottomSeparatorFrame()); break; case BarCtrl::BAR_LEFT: RemoveFrame(LeftSeparatorFrame()); InsertFrame(2, LeftSeparatorFrame()); break; case BarCtrl::BAR_RIGHT: RemoveFrame(RightSeparatorFrame()); InsertFrame(2, RightSeparatorFrame()); break; } WhenToolBar(bar); return *this; } void GridCtrl::FindOptsBar(Bar &bar) { bar.Add(t_("Immediate search"), THISBACK1(SetFindOpts, 0)).Check(search_immediate); bar.Add(t_("Hide rows"), THISBACK1(SetFindOpts, 1)).Check(search_hide); bar.Add(t_("Higlight found cells"), THISBACK1(SetFindOpts, 2)).Check(search_highlight); bar.Add(t_("Case sensitive"), THISBACK1(SetFindOpts, 3)).Check(search_case); } void GridCtrl::SetFindOpts(int n) { switch(n) { case 0: search_immediate = !search_immediate; if(!search_immediate) { findstring <<= THISBACK(Nothing); findstring.WhenEnter = THISBACK(DoFind); } else { findstring <<= THISBACK(DoFind); findstring.WhenEnter = THISBACK(Nothing); } break; case 1: search_hide = !search_hide; if(!String(~findstring).IsEmpty()) { if(!search_hide) ShowRows(); else DoFind(); } break; case 2: search_highlight = !search_highlight; if(!search_highlight) { ClearFound(false); Refresh(); } else DoFind(); break; case 3: search_case = !search_case; DoFind(); break; } } void GridCtrl::ShowFindOpts() { MenuBar::Execute(THISBACK(FindOptsBar)); } String GridCtrl::RowFormat(const char *s) { String row = t_("row"); return Sprintf(s, ~row); } void GridCtrl::StdMenuBar(Bar &bar) { bool e = IsEditable(); bool c = IsCursor(); bool d = e && c; bool isitem = false; if(inserting) { bar.Add(c, t_("Insert"), StdInsert) .Help(RowFormat(t_("Insert a new %s into the table."))) .Key(K_INSERT); isitem = true; } if(bains == 1) { bar.Add(c, t_("Insert before"), THISBACK(DoInsertBefore)) .Help(RowFormat(t_("Insert a new %s into the table before the actual one"))) .Key(K_INSERT); bar.Add(c, t_("Insert after"), THISBACK(DoInsertAfter)) .Help(RowFormat(t_("Insert a new %s into the table after the actual one"))) .Key(K_SHIFT_INSERT); isitem = true; } if(bains == 2) { bar.Add(c, t_("Insert after"), THISBACK(DoInsertAfter)) .Help(RowFormat(t_("Insert a new %s into the table after the actual one"))) .Key(K_INSERT); bar.Add(c, t_("Insert before"), THISBACK(DoInsertBefore)) .Help(RowFormat(t_("Insert a new %s into the table before the actual one"))) .Key(K_SHIFT_INSERT); isitem = true; } if(appending) { bar.Add(t_("Append"), StdAppend) .Image(GridImg::ImgGridAdd()) .Help(RowFormat(t_("Append a new %s at the end of the table."))) .Key(inserting ? K_SHIFT_INSERT : K_INSERT); isitem = true; } if(duplicating) { bar.Add(c, t_("Duplicate"), THISBACK(DoDuplicate)) .Help(RowFormat(t_("Duplicate current table %s."))) .Key(K_CTRL_INSERT); isitem = true; } if(editing) { bar.Add(d, t_("Edit"), THISBACK(DoEdit)) .Image(GridImg::ImgGridMod()) .Help(RowFormat(t_("Edit active %s."))) .Key(K_ENTER); isitem = true; } if(removing) { RemovingMenu(bar); isitem = true; } if(moving) { MovingMenu(bar); isitem = true; } if(multi_select) { SelectMenu(bar); isitem = true; } if(clipboard) { if(isitem) bar.Separator(); ClipboardMenu(bar); isitem = true; } if(hiding) { if(isitem) bar.Separator(); ColumnsMenu(bar); } } void GridCtrl::RemovingMenu(Bar &bar) { bool c = IsCursor(); bar.Add(c && (keep_last_row ? GetCount() > 1 : true), t_("Delete"), StdRemove) .Image(GridImg::ImgGridDel()) .Help(RowFormat(t_("Delete active %s."))) .Key(K_DELETE); } void GridCtrl::MovingMenu(Bar &bar) { bool c = IsCursor(); bar.Add(c && curpos.y > fixed_rows, t_("Move up"), THISBACK(DoSwapUp)) .Key(K_CTRL_UP); bar.Add(c && curpos.y >= fixed_rows && curpos.y < total_rows - 1, t_("Move down"), THISBACK(DoSwapDown)) .Key(K_CTRL_DOWN); } void GridCtrl::SelectMenu(Bar &bar) { bar.Add(total_rows > fixed_rows, RowFormat(t_("Select all")), THISBACK(DoSelectAll)) .Help(t_("Select all table rows")) .Key(K_CTRL_A); } void GridCtrl::ColumnsMenu(Bar &bar) { bar.Add(t_("Columns"), THISBACK(ColumnList)); } void GridCtrl::ColumnList(Bar &bar) { int cnt = 0; for(int i = fixed_cols; i < total_cols; i++) if(!hitems[i].index && !hitems[i].hidden) cnt++; for(int i = fixed_cols; i < total_cols; i++) { if(!hitems[i].index) bar.Add((String) items[0][hitems[i].id].val, THISBACK1(MenuHideColumn, i)) .Check(!hitems[i].hidden) .Enable(cnt > 1 || (cnt == 1 && hitems[i].hidden)); } } void GridCtrl::ClipboardMenu(Bar &bar) { bool c = IsCursor(); bool s = c || IsSelection(); bar.Add(t_("Copy"), THISBACK(DoCopy)).Image(CtrlImg::copy()).Key(K_CTRL_C).Enable(s); bar.Add(t_("Cut"), THISBACK(Nothing)).Image(CtrlImg::cut()).Key(K_CTRL_X).Enable(s); bar.Add(t_("Paste"), THISBACK(DoPaste)).Image(CtrlImg::paste()).Key(K_CTRL_V).Enable(c && IsClipboardAvailable()); if(extra_paste) bar.Add(t_("Paste as"), THISBACK(PasteAsMenu)); } void GridCtrl::PasteAsMenu(Bar &bar) { bool c = IsCursor(); bool s = IsClipboardAvailable(); bar.Add(t_("appended rows"), THISBACK(DoPasteAppendedRows)).Key(K_CTRL_E).Enable(s); bar.Add(t_("inserted rows"), THISBACK(DoPasteInsertedRows)).Key(K_CTRL_I).Enable(c && s); } bool GridCtrl::IsClipboardAvailable() { return IsClipboardFormatAvailable() || IsClipboardAvailableText(); } GridClipboard GridCtrl::GetClipboard() { GridClipboard gc = ReadClipboardFormat(); return gc; } void GridCtrl::SetClipboard() { if(!clipboard) return; GridClipboard gc; Point minpos(total_cols, total_rows); Point maxpos(fixed_cols, fixed_rows); for(int i = fixed_rows; i < total_rows; i++) { bool row_selected = select_row && IsSelected(i, false); for(int j = fixed_cols; j < total_cols; j++) if(row_selected || IsSelected(i, j, false)) { GridClipboard::ClipboardData &d = gc.data.Add(); d.col = j; d.row = i; d.v = Get0(i, j); if(i < minpos.y) minpos.y = i; else if(i > maxpos.y) maxpos.y = i; if(j < minpos.x) minpos.x = j; else if(j > maxpos.x) maxpos.x = j; } } gc.minpos = minpos; gc.maxpos = maxpos; bool row_selected = select_row && IsSelected(curpos.y, false); gc.shiftmode = row_selected ? true : shiftmode; WriteClipboardFormat(gc); Color c0 = bg_select; Color c1 = White; Color c2 = bg_focus; for(int i = 0; i < 256; i += 64) { bg_select = Blend(c0, c1, i); bg_focus = Blend(c2, c1, i); Refresh(); Sync(); Sleep(1); } for(int i = 0; i < 256; i += 32) { bg_select = Blend(c1, c0, i); bg_focus = Blend(c1, c2, i); Refresh(); Sync(); Sleep(1); } } void GridCtrl::Paste(int mode) { if(!clipboard) return; GridClipboard gc = GetClipboard(); if(gc.data.IsEmpty()) { if(IsClipboardAvailableText()) { Vector lines = Upp::Split(ReadClipboardText(), '\n'); for(int i = 0; i < lines.GetCount(); i++) { Vector cells = Upp::Split(lines[i], ' '); //todo.. } } return; } if(select_row && !gc.shiftmode) return; Point cp(curpos); if(cp.x < 0 || select_row) cp.x = fixed_cols; if(cp.y < 0) cp.y = fixed_rows; if(mode == 1) { int dy = gc.maxpos.y - gc.minpos.y + 1; Insert0(curpos.y, dy); curpos.y += dy; } else if(mode == 2) cp.y = total_rows; int lc = -1, lr = -1; if(!select_row && gc.shiftmode) { lc = cp.x; lr = cp.y; for(int i = 0; i < gc.data.GetCount(); i++) { Set0(lr, lc, gc.data[i].v, true); bool data_end = i == gc.data.GetCount() - 1; bool new_row = ++lc > total_cols - 1; if(new_row || data_end) { if(new_row && lr + 1 >= total_rows) { if(fixed_paste) break; #ifdef LOG_CALLBACKS LGR(2, "WhenInsertRow() - paste"); #endif WhenInsertRow(); } else { #ifdef LOG_CALLBACKS LGR(2, "WhenUpdateRow() - paste"); #endif WhenUpdateRow(); } if(!data_end) { lc = fixed_cols; ++lr; } } } } else { int pr = gc.data[0].row - gc.minpos.y; bool new_row = false; for(int i = 0; i < gc.data.GetCount(); i++) { int r = gc.data[i].row - gc.minpos.y; int c = gc.data[i].col - gc.minpos.x; if(cp.x + c < total_cols) { lc = cp.x + c; lr = cp.y + r; new_row = lr >= total_rows; if(fixed_paste && new_row) break; Set0(lr, lc, gc.data[i].v, true); } if(r > pr || i == gc.data.GetCount() - 1) { if(new_row) { #ifdef LOG_CALLBACKS LGR(2, "WhenInsertRow() - paste"); #endif WhenInsertRow(); } else { #ifdef LOG_CALLBACKS LGR(2, "WhenUpdateRow() - paste"); #endif WhenUpdateRow(); } pr = r; } } } if(lc >= 0 && lr >= 0) { SetCursor0(lc, lr); sby.Set(vitems[curpos.y].nBottom() - GetSize().cy); } } void GridCtrl::DoCopy() { SetClipboard(); } void GridCtrl::DoPaste() { Paste(0); } void GridCtrl::DoPasteInsertedRows() { Paste(1); } void GridCtrl::DoPasteAppendedRows() { Paste(2); } void GridCtrl::Nothing() { } bool GridCtrl::IsEditable() { for(int i = 1; i < total_cols; i++) { int id = hitems[i].id; Ctrl * ctrl = items[0][id].ctrl; if(ctrl) return true; } return false; } void GridCtrl::DrawLine(bool iniLine, bool delLine) { if((resizeCol || resizeRow) && resize_paint_mode < 2) { int sx = resize_paint_mode == 1 ? fixed_width : 0; int sy = resize_paint_mode == 1 ? fixed_height : 0; ViewDraw w(this); Size sz = GetSize(); Point curPnt; static Point oldPnt = curPnt; if(resizeCol) { curPnt.x = hitems[splitCol].nRight(sbx) - 1; if(delLine) w.DrawRect(oldPnt.x, sy, 1, sz.cy, InvertColor()); if(iniLine) w.DrawRect(curPnt.x, sy, 1, sz.cy, InvertColor()); } if(resizeRow) { curPnt.y = vitems[splitRow].nBottom(sby) - 1; if(delLine) w.DrawRect(sx, oldPnt.y, sz.cx, 1, InvertColor()); if(iniLine) w.DrawRect(sx, curPnt.y, sz.cx, 1, InvertColor()); } oldPnt = curPnt; } } Item& GridCtrl::GetItemYCY(int &r, int c, int &y, int &cy, bool setrow /* = true*/, bool relative /* = true*/) { int idx = hitems[c].id; Item &it = items[vitems[r].id][idx]; int ro = r; if(it.isjoined) { while(r >= 0 && items[vitems[r].id][idx].isjoined) --r; ++r; it = items[it.idy][it.idx]; y = vitems[r].nTop(); cy = vitems[r + it.cy].nBottom() - y; } else { y = vitems[r].nTop(); cy = vitems[r].nHeight(); } if(relative) y -= sby; if(!setrow) r = ro; return it; } Item& GridCtrl::GetItemXCX(int r, int &c, int &x, int &cx, bool setcol /* = true*/, bool relative /* = true*/) { int idy = vitems[r].id; Item &it = items[idy][hitems[c].id]; int co = c; if(it.isjoined) { while(c >= 0 && items[idy][hitems[c].id].isjoined) --c; ++c; it = items[it.idy][it.idx]; x = hitems[c].nLeft(); cx = hitems[c + it.cx].nRight() - x; } else { x = hitems[c].nLeft(); cx = hitems[c].nWidth(); } if(relative) x -= sbx; if(!setcol) c = co; return it; } Item& GridCtrl::GetItemSize(int r, int c, int &x, int &y, int &cx, int &cy, bool &skip, bool relx, bool rely) { int idx = hitems[c].id; int idy = vitems[r].id; int dx = 0; int dy = 0; Item &it = items[idy][idx]; if(it.isjoined) { it = items[it.idy][it.idx]; skip = it.paint_flag == paint_flag; it.paint_flag = paint_flag; if(skip) return it; while(c >= 0 && items[idy][hitems[c].id].isjoined) --c; ++c; while(r >= 0 && items[vitems[r].id][idx].isjoined) --r; ++r; dx = it.cx; dy = it.cy; } else skip = false; x = hitems[c].nLeft(); y = vitems[r].nTop(); cx = hitems[c + dx].nRight() - x; cy = vitems[r + dy].nBottom() - y; if(relx) x -= sbx; if(rely) y -= sby; return it; } void GridCtrl::Paint(Draw &w) { static int paintcnt = 0; Size sz = GetSize(); Rect rc = Rect(sz); //w.GetClip() - it always returns view rect now. bug?? int i, j, cx, cy, x, y; bool skip; Rect r; LG("---- Paint(%d)", ++paintcnt); if(total_cols <= 1 || total_rows == 0) { LG("empty"); w.DrawRect(sz, SColorPaper); return; } if(UpdateCols() || UpdateRows()) UpdateSizes(); if(scrollxdir != 0 || firstCol < 0) { firstCol = GetFirstVisCol(fixed_width, firstCol, scrollxdir); LG("fc %d, scx %d", firstCol, scrollxdir); scrollxdir = 0; } if(scrollydir != 0 || firstRow < 0) { firstRow = GetFirstVisRow(fixed_height, firstRow, scrollydir); LG("fr %d, scy %d", firstRow, scrollydir); scrollydir = 0; } int en = IsShowEnabled() ? 0 : GD::READONLY; //--------------------------------------------------------------------------------------- if(fixed_width > 0 && fixed_height > 0) { w.Clip(0, 0, fixed_width, fixed_height); display->PaintFixed(w, 1, 1, 0, 0, fixed_width, fixed_height, Value(""), 0, false, false, 0, -1, 0, true); w.End(); } r.Set(fixed_width, 0, sz.cx, fixed_height); LG("painting %d, tc %d", (int)w.IsPainting(r), total_cols); LG("%d, %d, %d, %d", r.left, r.top, r.right, r.bottom); if(w.IsPainting(r) && total_cols > 1) { LG("Top header"); w.Clip(r); x = hitems[total_cols - 1].nRight(sbx); int rx = x; int firstcol = indicator ? 0 : (fixed_cols > 1 ? 1 : firstVisCol); if(firstCol < 0) firstCol = 0; int jfc = chameleon ? 0 : firstCol; for(i = 0; i < fixed_rows; i++) { for(j = jfc; j < total_cols; j++) { if(hitems[j].hidden) continue; Item &it = GetItemSize(i, j, x, y, cx, cy, skip, true, false); if(skip) continue; if(x >= rc.right) break; if(w.IsPainting(x, y, cx, cy)) { int id = hitems[j].id; GridDisplay *cd = items[0][id].display; GridDisplay *gd = cd ? cd : it.display != NULL ? it.display : display; dword style = hitems[j].style | hitems[j].align; if(i > 0) style &= ~GD::HIGHLIGHT; if(chameleon) style |= GD::CHAMELEON; gd->PaintFixed(w, j == firstcol, i == 0, x, y, cx, cy, i == 0 ? it.val : GetConvertedColumn(id, it.val), style | en, false, false, i == 0 ? hitems[j].sortmode : 0, hitems[j].sortcol, sortOrder.GetCount(), true); } } } if(rx < sz.cx || chameleon) { int cx = sz.cx - rx + 1; dword style = 0; if(chameleon) { cx = max(10, cx); style = GD::CHAMELEON; } display->PaintFixed(w, 0, 1, rx, 0, cx, fixed_height, Value(""), style, false, false, 0, -1, 0, true); } w.End(); } //--------------------------------------------------------------------------------------- bool can_paint = firstCol >= 0 && firstRow >= 0; r.Set(0, fixed_height, fixed_width, sz.cy); if(can_paint && w.IsPainting(r)) { LG("Left header"); w.Clip(r); y = vitems[total_rows - 1].nBottom(sby); if(y < sz.cy) w.DrawRect(Rect(0, y, fixed_width, sz.cy), SColorPaper); for(i = 0; i < fixed_cols; i++) { bool firstx = (i == !indicator); bool indicator = (i == 0); int id = hitems[i].id; for(j = firstRow; j < total_rows; j++) { if(vitems[j].hidden) continue; Item &it = GetItemSize(j, i, x, y, cx, cy, skip, false, true); if(skip) continue; if(y >= rc.bottom) break; if(w.IsPainting(x, y, cx, cy)) { GridDisplay *gd = it.display != NULL ? it.display : display; dword style = vitems[j].style; if(i > 0) style &= ~GD::HIGHLIGHT; gd->PaintFixed(w, firstx, j == 0, x, y, cx, cy, GetConvertedColumn(id, it.val), style | en, indicator, false, 0, -1, 0, false); } } } w.End(); } //--------------------------------------------------------------------------------------- r.Set(fixed_width, fixed_height, sz.cx, sz.cy); if(can_paint && w.IsPainting(r)) { LG("Body"); w.Clip(r); x = hitems[total_cols - 1].nRight(sbx); y = vitems[total_rows - 1].nBottom(sby); if(x < sz.cx) w.DrawRect(Rect(max(x, rc.left), max(fixed_height, rc.top), sz.cx, sz.cy), SColorPaper); if(y < sz.cy) w.DrawRect(Rect(max(fixed_width, rc.left), max(y, rc.top), sz.cx, sz.cy), SColorPaper); bool hasfocus = HasFocusDeep(); for(i = max(firstRow, fixed_rows); i < total_rows; i++) { if(vitems[i].hidden) continue; bool even = coloringMode == 2 ? (i - vitems[i].n - fixed_rows) & 1 : false; for(j = max(firstCol, fixed_cols); j < total_cols; j++) { if(hitems[j].hidden) continue; Item &it = GetItemSize(i, j, x, y, cx, cy, skip); if(skip) continue; if(y >= rc.bottom) goto end_paint; if(x >= rc.right) break; if(!w.IsPainting(0, y, sz.cx, cy)) break; if(w.IsPainting(x, y, cx, cy)) { if(coloringMode == 1) even = (j - hitems[j].n - fixed_cols) & 1; int id = hitems[j].id; dword style = (select_row ? vitems[i].style : it.style) | hitems[j].align; dword istyle = it.style; if(hitems[j].wrap) style |= GD::WRAP; if(ctrlpos.y == i && edits[id].ctrl && edits[id].ctrl->IsShown()) style |= GD::NOTEXT; if(it.ctrl) style |= GD::NOTEXT; if(coloringMode > 0) style |= (even ? GD::EVEN : GD::ODD); if(hasfocus) style |= GD::FOCUS; Color cfg = IsNull(vitems[i].fg) ? hitems[j].fg : vitems[i].fg; Color cbg = IsNull(vitems[i].bg) ? hitems[j].bg : vitems[i].bg; Font fnt = StdFont(); if(!IsNull(vitems[i].fnt)) fnt = vitems[i].fnt; else if(!IsNull(hitems[j].fnt)) fnt = hitems[i].fnt; Color fg = SColorText; Color bg = SColorPaper; bool custom = true; if(style & GD::CURSOR) { if(style & GD::FOCUS) { fg = fg_focus; bg = bg_focus; } else { fg = fg_focus; bg = Blend(bg_focus, White, 170); } custom = false; } else if(style & GD::LIVE) { fg = fg_live; bg = bg_live; custom = false; } else if(istyle & GD::FOUND) { fg = fg_found; bg = bg_found; custom = false; } else if(style & GD::SELECT) { fg = fg_select; bg = bg_select; custom = false; } else if(style & GD::EVEN) { fg = fg_even; bg = bg_even; } else if(style & GD::ODD) { fg = fg_odd; bg = bg_odd; } if(custom) { if(!IsNull(cfg)) fg = cfg; if(!IsNull(cbg)) bg = cbg; } GridDisplay * cd = items[0][id].display; GridDisplay * gd = cd ? cd : (it.display ? it.display : display); int gcx = cx - (int) vert_grid; int gcy = cy - (int) horz_grid; gd->col = j - fixed_rows; gd->row = i - fixed_cols; gd->parent = this; gd->Paint(w, x, y, gcx, gcy, hitems[j].IsConvertion() ? GetConvertedColumn(id, it.val) : it.val, style | en, fg, bg, fnt, it.style & GD::FOUND, it.fs, it.fe); if(vert_grid) w.DrawRect(x + gcx, y, 1, cy, fg_grid); if(horz_grid) w.DrawRect(x, y + gcy, cx, 1, fg_grid); } } } end_paint: w.End(); lastCol = j - 1; lastRow = i - 1; } if(moving_header && fixed_top_click && curSplitCol >= 0) { int dx = -1; if(curSplitCol < firstVisCol) dx = 0; else if(curSplitCol == lastVisCol) dx = -2; int x = hitems[curSplitCol].nRight(sbx) + dx; if(x >= fixed_width - 1) DrawHorzDragLine(w, x, vitems[fixed_rows - 1].nBottom(), moving_allowed ? LtBlue : Red); } if(moving_header && fixed_left_click) { int dy = curSplitRow == lastVisRow ? -2 : -1; int y = curSplitRow >= 0 ? vitems[curSplitRow].nBottom(sby) + dy : 0; if(y >= fixed_height - 1) DrawVertDragLine(w, y, hitems[fixed_cols - 1].nRight(), 0, moving_allowed ? LtBlue : Red); } if(moving_body) { int dy = curSplitRow == lastVisRow ? -2 : -1; int y = curSplitRow >= 0 ? vitems[curSplitRow].nBottom(sby) + dy : 0; if(y >= fixed_height - 1) DrawVertDragLine(w, y, sz.cx, fixed_width - 1, LtBlue); } if(!can_paint) w.DrawRect(Rect(0, total_cols > 1 ? fixed_height : 0, sz.cx, sz.cy), SColorPaper); if(++paint_flag > 100) paint_flag = 0; LG("---- Paint(%d).", paintcnt); } void GridCtrl::DrawHorzDragLine(Draw &w, int pos, int size, Color c) { w.DrawRect(pos, 0, 2, size - 1, c); } void GridCtrl::DrawVertDragLine(Draw &w, int pos, int size, int dx, Color c) { w.DrawRect(1 + dx, pos, size - dx - 1, 2, c); } Value GridCtrl::GetConvertedColumn(int col, Value &v) { Convert *conv = edits[col].convert; Value val = conv ? conv->Format(v) : v; return IsString(val) ? val : StdConvert().Format(val); } ItemRect& GridCtrl::InsertColumn(int pos, const char *name, int size, bool idx) { return hitems[0]; } ItemRect& GridCtrl::AddColumn(const char *name, int size, bool idx) { ItemRect::aliases = &aliases; if(total_rows > 1) return hitems[total_cols - 1]; total_rows = 1; Item &it = items[0].Add(); it.val = name; ItemRect &ib = hitems.Add(); ib.parent = &holder; ib.items = &items; ib.edits = &edits; ib.prop = size; ib.id = total_cols; ib.uid = coluid++; ib.index = idx; ib.Size(size); if(!ib.hidden) { lastVisCol = total_cols; if(firstVisCol < 0) firstVisCol = lastVisCol; } if(vitems[0].nsize == 0) vitems[0].size = vitems[0].nsize = GD_HDR_HEIGHT; edits.Add(); String lname(name); aliases.Add(ToLower(lname), ib.id); total_cols++; recalc_cols = true; RefreshLayout(); return ib; } ItemRect& GridCtrl::AddColumn(Id id, const char *name, int size, bool idx) { return AddColumn(name, size, idx).Alias(id); } void GridCtrl::RemoveColumn(int n, int cnt /* = 1*/) { items.Remove(n + fixed_cols); hitems.Remove(n + fixed_cols); } GridCtrl& GridCtrl::AddRow(int n, int size) { Append0(n, size); return *this; } void GridCtrl::SetRowCount(int n, int size) { Clear(); Append0(n, size); } void GridCtrl::SetColCount(int n, int size) { Reset(); for(int i = 0; i < n; i++) AddColumn(NULL, size, false); } void GridCtrl::MouseMove(Point p, dword keyflags) { //LG("MouseMove"); mouse_move = true; if(resizing) { int si, lp, mp, off; RectItems *its; static int sub = 0; if(resizeCol) { mp = p.x; lp = leftpnt.x; si = splitCol; its = &hitems; bool top = fixed_top_click || (!fixed_left_click && full_col_resizing); off = top ? sbx : 0; } else { mp = p.y; lp = leftpnt.y; si = splitRow; its = &vitems; bool left = fixed_left_click || (!fixed_top_click && full_row_resizing); off = left ? sby : 0; } int right = (*its)[si].nRight(off); if(just_clicked) { sub = right - lp; just_clicked = false; } if(SetDiffItemSize(resizeCol, *its, si, mp - right + sub)) { Split(GS_MOVE); } return; } else if(fixed_click) { if((fixed_top_click && !moving_cols) || (fixed_left_click && !moving_rows) || moveCol < 0 || moveRow < 0) return; if(!moving_header) { int diffx = p.x - leftpnt.x; int diffy = p.y - leftpnt.y; if(abs(diffx) < 5 && abs(diffy) < 5) return; p -= Point(diffx, diffy); moving_header = true; int idx = hitems[moveCol].id; int idy = vitems[moveRow].id; pophdr.val = idy > 0 ? GetConvertedColumn(moveCol, items[idy][idx].val) : items[idy][idx].val; if(fixed_top_click) { pophdr.sortmode = hitems[moveCol].sortmode; pophdr.sortcol = hitems[moveCol].sortcol; pophdr.sortcnt = sortOrder.GetCount(); UpdateHighlighting(GS_POPUP, p); } else { pophdr.sortmode = 0; pophdr.sortcol = -1; pophdr.sortcnt = 0; } dx = hitems[moveCol].nLeft(fixed_top_click ? sbx : 0) - p.x; dy = vitems[moveRow].nTop(fixed_left_click ? sby : 0) - p.y; } Point pt = p + GetScreenRect().TopLeft() + GetBarOffset(); pophdr.display = display; pophdr.chameleon = chameleon; pophdr.PopUp(this, pt.x + dx, pt.y + dy, hitems[moveCol].nWidth(), vitems[moveRow].nHeight()); if(fixed_top_click && curSplitCol != oldMoveCol) { moving_allowed = CanMoveCol(moveCol, curSplitCol); RefreshTop(); //Refresh(oldMoveCol >= 0 ? hitems[oldMoveCol].nRight(sbx) : 0, 0, hitems[curSplitCol].nRight(sbx), fixed_height); oldMoveCol = curSplitCol; } if(fixed_left_click && curSplitRow != oldMoveRow) { Refresh(0, 0, fixed_width, fixed_height); RefreshLeft(); oldMoveRow = curSplitRow; } return; } if(leftpnt != p && p.y < fixed_height) { UpdateHighlighting(GS_MOVE, p); } if(live_cursor) { if(IsMouseBody(p)) SetCursor0(p, true, true); else SetCursor0(-1, -1, false, true); } if(HasCapture()) { if(!moving_body) { if(keyflags & K_SHIFT) { if(SetCursor0(p, true)) { DoShiftSelect(); selecting = true; } return; } if(multi_select && (keyflags & K_CTRL)) { if(SetCursor0(p, true)) { DoCtrlSelect(); selecting = true; } return; } } if(moveCol < 0 || moveRow < 0) return; if(!dragging) return; if(!moving_body) { if(!top_click && valid_cursor && p.x < total_width && p.y < total_height && (abs(p.y - leftpnt.y) > 5 || abs(p.x - leftpnt.x) > 5)) moving_body = true; oldMoveRow = -1; } else { Point pt = p + GetScreenRect().TopLeft(); int row = curSplitRow - fixed_rows + 2; if(select_row) { int count = max(1, selected_rows); if(vitems[curpos.y].IsSelect()) popup.text = Format(t_("Moving selection (%d %s) before row %d"), count, count == 1 ? t_("row") : t_("rows"), row); else popup.text = Format(t_("Moving row %d before row %d"), curpos.y - fixed_rows + 1, row); } else { int count = max(1, selected_items); popup.text = Format(t_("Moving %d %s before row %d"), count, count == 1 ? t_("cell") : t_("cells"), row); } int px = pt.x + 15; int py = pt.y + GD_ROW_HEIGHT; popup.PopUp(this, px, py, GetTextSize(popup.text, StdFont()).cx + 6, GD_ROW_HEIGHT); SetFocus(); if(curSplitRow != oldMoveRow || scrollLeftRight) { int dy = sby; if(oldMoveRow >= 0) Refresh(Rect(0, vitems[oldMoveRow].nBottom(dy) - 5, GetSize().cx, vitems[oldMoveRow].nBottom(dy) + 5)); else Refresh(Rect(0, 0, GetSize().cx, 5)); if(curSplitRow >= 0) Refresh(Rect(0, vitems[curSplitRow].nBottom(dy) - 5, GetSize().cx, vitems[curSplitRow].nBottom(dy) + 5)); else Refresh(Rect(0, 0, GetSize().cx, 5)); oldMoveRow = curSplitRow; popup.Refresh(); scrollLeftRight = false; } } } } void GridCtrl::LeftDown(Point p, dword keyflags) { LG("LeftDown"); SetCapture(); leftpnt = p; just_clicked = true; selecting = false; fixed_top_click = p.x >= fixed_width && p.y < fixed_height; fixed_left_click = p.x < fixed_width && p.y >= fixed_height; fixed_click = fixed_top_click || fixed_left_click; top_click = p.y < fixed_height; resizing = curResizeCol || curResizeRow; if(resizing) { splitCol = curSplitCol; splitRow = curSplitRow; resizeCol = curResizeCol; resizeRow = curResizeRow; Split(GS_DOWN); return; } else if(fixed_click) { moveCol = oldMoveCol = GetMouseCol(p, fixed_top_click, 1); moveRow = oldMoveRow = GetMouseRow(p, fixed_left_click, 1); return; } if(IsEmpty()) { SetFocus(); return; } int ri = rowidx; bool newcur = SetCursor0(p, true); if(newcur) { moveCol = curpos.x; moveRow = curpos.y; } bool samerow = ctrlid.y == curid.y && newcur; GetCtrlsData(ctrlid.y, samerow, !samerow, ri); UpdateCtrls(UC_HIDE | UC_CTRLS); if(valid_cursor) { if(multi_select && (keyflags & K_CTRL)) { if(!select_row) ClearSelection(); shiftpos = curpos; Point from(select_row ? fixed_cols : curpos.x, curpos.y); Point to(select_row ? total_cols - 1 : curpos.x, curpos.y); SelectRange(from, to, !vitems[curpos.y].IsSelect()); selecting = true; } else if(keyflags & K_SHIFT) { DoShiftSelect(); selecting = true; } #ifdef LOG_CALLBACKS //LGR(2, "WhenLeftClick()"); #endif WhenLeftClick(); } SetFocus(); if(newcur && one_click_edit && IsRowEditable()) { SetCtrlsData(curid.y); UpdateCtrls(UC_SHOW | UC_FOCUS | UC_CURSOR | UC_CTRLS | UC_GOFIRST | UC_MOUSE); } } void GridCtrl::LeftUp(Point p, dword keyflags) { LG("LeftUp"); ReleaseCapture(); fixed_click = false; UpdateHighlighting(GS_UP, p); if(moving_header) { LG("moving_header"); pophdr.Close(); moving_header = false; if(fixed_top_click) MoveCol(moveCol, curSplitCol); else MoveRow(moveRow, curSplitRow); if(focused_ctrl) focused_ctrl->SetFocus(); fixed_top_click = false; fixed_left_click = false; return; } if(resizing) { Split(GS_UP); resizeCol = resizeRow = resizing = false; return; } if(fixed_top_click && sorting /*&& leftpnt == p*/) { int i = GetMouseRow(leftpnt, false, true); int j = GetMouseCol(leftpnt, true, false); if(j >= fixed_cols && i == 0 && hitems[i].sortable) { LG("Sorting"); int newSortCol = hitems[j].id; if(sorting_multicol && (keyflags & K_CTRL)) { if(sortCol >= 0) { sortOrder.Add(sortCol); sortCol = -1; } int colidx = InMultisort(newSortCol); if(colidx < 0) sortOrder.Add(newSortCol); int cnt = sortOrder.GetCount(); hitems[j].ChangeSortMode(newSortCol == sortOrder[cnt - 1]); if(colidx >= 0) { if(hitems[j].sortmode == 0) { sortOrder.Remove(colidx); cnt--; } if(hitems[j].sortmode > 0 && colidx == cnt - 1) GSort(); else Multisort(); } else { hitems[j].sortcol = cnt; GSort(); } } else { if(sortCol >= 0 && sortCol != newSortCol) { int idx = GetIdCol(sortCol, true); hitems[idx].sortmode = 0; } ClearMultisort(); hitems[j].ChangeSortMode(); hitems[j].sortcol = 1; if(hitems[j].sortmode == 0) sortCol = -1; else sortCol = newSortCol; sortOrder.Clear(); GSort(newSortCol, hitems[j].sortmode, fixed_rows); } UpdateCursor(); Repaint(false, true); } } if(moving_body) { popup.Close(); moving_body = false; MoveRows(curSplitRow + 1, !vitems[curpos.y].IsSelect()); return; } if(selected_rows > 0 && !selecting) ClearSelection(); if(multi_select && keyflags & K_CTRL) shiftpos = curpos; } void GridCtrl::LeftDouble(Point p, dword keyflags) { if(full_col_resizing && curSplitCol >= 0) return; if(full_row_resizing && curSplitRow >= 0) return; if(IsEmpty() || !IsMouseBody(p)) return; if(IsRowEditable()) { SetCtrlsData(curid.y); UpdateCtrls(UC_SHOW | UC_FOCUS | UC_CURSOR | UC_CTRLS | UC_GOFIRST | UC_MOUSE); } if(!IsCtrl(curpos)) { #ifdef LOG_CALLBACKS LGR(2, "WhenLeftDouble()"); #endif WhenLeftDouble(); } } void GridCtrl::LeftRepeat(Point p, dword keyflags) { if(!moving_header && !resizeCol && !resizeRow) MouseAccel(p, resize_col_mode == 0, resize_row_mode == 0, keyflags); } void GridCtrl::RightDown(Point p, dword keyflags) { if(total_rows > fixed_rows) { //ClearSelection(); int ri = rowidx; bool newcur = SetCursor0(p, true); bool samerow = ctrlid.y == curid.y && newcur; GetCtrlsData(ctrlid.y, samerow, !samerow, ri); SetCtrlsData(curid.y); UpdateCtrls(UC_HIDE | UC_CTRLS); } SetFocus(); //jak nie bedzie menu to fokous zostanie na danym wierszu MenuBar::Execute(WhenMenuBar); } void GridCtrl::Init() { bar.Set(WhenToolBar); UpdateCols(true); /* recalc_rows bo przed otworzeniem grida moglo zostac wywolane setrowheight */ UpdateRows(resize_row_mode > 0 || recalc_rows); UpdateSizes(); UpdateSb(); UpdateHolder(true); SyncCtrls(); } void GridCtrl::State(int reason) { if(reason == OPEN) { Init(); ready = true; //ready po init - updatesb wola layout() a ten syncctrl //(ktory w sumie wola sie 3 razy zanim grid sie wyswietli - niepotrzebnie) } else if(reason == CLOSE) { EndEdit(); } } void GridCtrl::Layout() { if(!ready) return; LG("Layout"); UpdateCols(); UpdateRows(); UpdateSizes(); UpdateSb(); UpdateHolder(); SyncCtrls(); } void GridCtrl::ChildMouseEvent(Ctrl *child, int event, Point p, int zdelta, dword keyflags) { if(child->GetParent() == &holder && (event == LEFTDOWN || event == RIGHTDOWN) && child != focused_ctrl) { Point cp = GetCtrlPos(child); if(cp.x < 0 || cp.y < 0) return; bool samerow = curpos.y == cp.y; int ocy = curpos.y; GetCtrlsData(ctrlid.y, samerow, !samerow); SetCursor0(cp, false); UpdateCtrls(UC_SHOW | UC_FOCUS | UC_CTRLS_OFF); if(ocy != cp.y) SetCtrlsData(ctrlid.y); } return Ctrl::ChildMouseEvent(child, event, p, zdelta, keyflags); } Rect GridCtrl::GetItemRect(int r, int c, bool hgrid, bool vgrid, bool ctrlmode) { int dx = sbx + (ctrlmode ? fixed_width : 0); int dy = sby + (ctrlmode ? fixed_height : 0); int idx = hitems[c].id; int idy = vitems[r].id; Item &it = items[idy][idx]; int left, top, right, bottom; if(it.isjoined) { while(r > fixed_rows && items[vitems[r].id][idx].isjoined) --r; ++r; top = vitems[r].nTop(dy); bottom = vitems[r + it.cy].nBottom(dy); while(c > fixed_cols && items[idy][hitems[c].id].isjoined) --c; ++c; left = hitems[c].nLeft(dx); right = hitems[c + it.cx].nRight(dx); } else { left = hitems[c].nLeft(dx); top = vitems[r].nTop(dy); right = hitems[c].nRight(dx); bottom = vitems[r].nBottom(dy); } return Rect(left, top, right - (int) vgrid, bottom - (int) hgrid); } Rect& GridCtrl::AlignRect(Rect &r, int i) { Rect c(r); int align = hitems[i].calign; int sx = hitems[i].sx; int sy = hitems[i].sy; if(sx > 0) { if(align & GD::HCENTER) { int d = (r.Width() - sx) / 2; r.left += d; r.right -= d; } else if(align & GD::LEFT) { r.left += hitems[i].sl; r.right = r.left + sx; } else if(align & GD::RIGHT) { r.right -= hitems[i].sr; r.left = r.right - sx; } else if(align & GD::HPOS) { r.left += hitems[i].sl; r.right -= hitems[i].sr; } } if(sy > 0) { if(align & GD::VCENTER) { int d = (r.Height() - sy) / 2; r.top += d; r.bottom -= d; } else if(align & GD::TOP) { r.top += hitems[i].st; r.bottom = r.top + sy; } else if(align & GD::RIGHT) { r.bottom -= hitems[i].sb; r.top = r.bottom - sy; } else if(align & GD::VPOS) { r.top += hitems[i].st; r.bottom -= hitems[i].sb; } } if(r.left < c.left) r.left = c.left; if(r.right > c.right) r.right = c.right; if(r.top < c.top) r.top = c.top; if(r.bottom > c.bottom) r.bottom = c.bottom; return r; } void GridCtrl::Scroll() { Point newpos(sbx, sby); Size delta = oldpos - newpos; oldpos = newpos; if(!doscroll) return; LG("Scroll (%d, %d)", delta.cx, delta.cy); scrollxdir = delta.cx == 0 ? scrollxdir : delta.cx < 0 ? 1 : -1; scrollydir = delta.cy == 0 ? scrollydir : delta.cy < 0 ? 1 : -1; SyncCtrls(); UpdateCtrls(UC_CHECK_VIS | UC_SHOW | UC_SCROLL); if(resizeCol || resizeRow) return; if(!IsFullRefresh()) { Size sz = GetSize(); holder.ScrollView(delta); if(delta.cx != 0) { ScrollView(Rect(fixed_width, 0, sz.cx, fixed_height), delta.cx, 0); scrollLeftRight = true; } if(delta.cy != 0) { ScrollView(Rect(0, fixed_height, fixed_width, sz.cy), 0, delta.cy); } } } void GridCtrl::SetFixedRows(int n) { if(n >= 0 && n <= total_rows) { LG("SetFixedRows"); fixed_rows = n; scrollydir = 0; firstRow = -1; UpdateSizes(); UpdateHolder(); UpdateVisColRow(false); if(ready) SyncCtrls(); Refresh(); } } void GridCtrl::SetFixedCols(int n) { if(n >= 0 && n < total_cols) { LG("SetFixedCols"); fixed_cols = n + 1; /* +1 - indicator! */ scrollxdir = 0; firstCol = -1; UpdateSizes(); UpdateHolder(); UpdateVisColRow(true); if(ready) SyncCtrls(); Refresh(); } } void GridCtrl::Set0(int r, int c, const Value &val, bool paste) { if(c > total_cols - 1) return; if(r > total_rows - 1) AddRow(r - total_rows + 1); int ri = vitems[r].id; Item &it = items[ri][c]; if(it.isjoined) { ri = it.idy; c = it.idx; } Ctrl * ctrl = items[ri][c].ctrl; if(ctrl) ctrl->SetData(val); else { ctrl = edits[c].ctrl; if(ctrl && ctrlid.y == ri) ctrl->SetData(val); } items[ri][c].val = val; RefreshItem(r, c, false); if(paste) WhenUpdateCell(); } void GridCtrl::Set(int r, int c, const Value &val) { Set0(r + fixed_rows, c + fixed_cols, val); } void GridCtrl::Set(int c, const Value &val) { Set0(rowidx, c + fixed_cols, val); } void GridCtrl::Set(Id id, const Value &val) { Set0(rowidx, aliases.Get(id), val); } void GridCtrl::Set(int r, const Vector &v, int data_offset /* = 0*/, int column_offset /* = 0*/) { r += fixed_rows; int cnt = min(v.GetCount(), total_cols - fixed_cols); int r0 = vitems[r].id; int c = fixed_cols + column_offset; for(int i = data_offset; i < cnt; i++) items[r0][c++].val = v[i]; RefreshRow(r, false, 0); } void GridCtrl::Set(const Vector &v, int data_offset /* = 0*/, int column_offset /* = 0*/) { int r = rowidx - fixed_rows; Set(r, v, data_offset, column_offset); } void GridCtrl::SetCtrlValue(int r, int c, const Value &val) { c += fixed_cols; int ri = vitems[r].id; Ctrl * ctrl = items[ri][c].ctrl; if(ctrl) ctrl->SetData(val); else { ctrl = edits[c].ctrl; if(ctrl && ctrlid.y == ri) ctrl->SetData(val); } } void GridCtrl::SetCtrlValue(int c, const Value &val) { SetCtrlValue(rowidx, c, val); } void GridCtrl::SetLast(int c, const Value &val) { c += fixed_cols; items[vitems[rowidx].id][c].val = val; RefreshItem(rowidx, c, false); } void GridCtrl::SetFixed(int r, int c, const Value &val) { items[r][c + 1].val = val; Refresh(); } Value GridCtrl::GetFixed(int r, int c) { return items[vitems[r].id][c + fixed_cols].val; } Value GridCtrl::GetFixed(int c) { return items[0][c + fixed_cols].val; } Value GridCtrl::Get0(int r, int c) { r = vitems[r].id; Item &it = items[r][c]; if(it.isjoined) { r = it.idy; c = it.idx; } Ctrl * ctrl = items[r][c].ctrl; if(!ctrl && ctrls && ctrlid.y == r) ctrl = edits[c].ctrl; return ctrl ? ctrl->GetData() : items[r][c].val; } Value GridCtrl::Get(int r, int c) { return Get0(r + fixed_rows, c + fixed_cols); } Value GridCtrl::Get(int c) { return Get0(rowidx, c + fixed_cols); } Value GridCtrl::Get(Id id) { return Get0(rowidx, aliases.Get(id)); } Value GridCtrl::Get(int r, Id id) { return Get0(r + fixed_rows, aliases.Get(id)); } Value GridCtrl::Get() { return Get0(curpos.y, curpos.x); } Value GridCtrl::Get(const char * alias) { return Get0(rowidx, aliases.Get(alias)); } Value GridCtrl::Get(int r, const char * alias) { return Get0(r, aliases.Get(alias)); } Value GridCtrl::GetFirst(int c) { return Get0(fixed_rows, c + fixed_cols); } Value GridCtrl::GetLast(int c) { return Get0(total_rows - 1, c + fixed_cols); } Value GridCtrl::GetNew(int c) { return Get0(rowidx, c + fixed_cols); } Value& GridCtrl::operator() (int r, int c) { return items[vitems[r + fixed_rows].id][c + fixed_cols].val; } Value& GridCtrl::operator() (int c) { return items[vitems[rowidx].id][c + fixed_cols].val; } Value& GridCtrl::operator() (Id id) { return items[vitems[rowidx].id][aliases.Get(id)].val; } Value& GridCtrl::operator() (const char * alias) { return items[vitems[rowidx].id][aliases.Get(alias)].val; } Value& GridCtrl::operator() (int r, const char * alias) { return items[vitems[r + fixed_rows].id][aliases.Get(alias)].val; } Vector GridCtrl::ReadRow(int n) const { Vector v; for(int i = fixed_cols; i < total_cols; i++) v.Add(items[vitems[n].id][i].val); return v; } GridCtrl& GridCtrl::Add(const Vector &v, int offset) { Append0(1, GD_ROW_HEIGHT); int cnt = min(v.GetCount(), total_cols - fixed_cols); int r0 = total_rows - 1; int r = vitems[r0].id; for(int i = offset; i < cnt; i++) items[r][i + fixed_cols].val = v[i]; RefreshRow(r0, 0, 0); return *this; } #define E__Addv(I) Set(q, I - 1, p##I) #define E__AddF(I) \ GridCtrl& GridCtrl::Add(__List##I(E__Value)) { \ int q = GetCount(); \ __List##I(E__Addv); \ return *this; \ } __Expand(E__AddF) int GridCtrl::GetMouseCol(Point &p, bool relative, bool fixed, bool full) { if(!full && p.x < fixed_width) { LG("%d %d", fixed_width, p.x); return -1; } int dx = 0; if(relative) dx += sbx; int first_col = fixed ? 0 : max(firstVisCol, fixed_cols); int last_col = max(lastVisCol, fixed_cols - 1); for(int i = first_col; i <= last_col; i++) { if(p.x >= hitems[i].nLeft(dx) && p.x < hitems[i].nRight(dx)) return i; } return -1; } int GridCtrl::GetMouseRow(Point &p, bool relative, bool fixed, bool full) { if(!full && p.y < fixed_height) return -1; int dy = 0; if(relative) dy += sby; int first_row = fixed ? 0 : max(firstVisRow, fixed_rows); int last_row = max(lastVisRow, fixed_rows - 1); for(int i = first_row; i <= last_row; i++) { if(p.y >= vitems[i].nTop(dy) && p.y < vitems[i].nBottom(dy)) return i; } return -1; } void GridCtrl::MouseAccel(const Point &p, bool horz, bool vert, dword keyflags) { Size sz = GetSize(); int speedx = 0, speedy = 0; const int bound = 5; if(horz) { if(p.x > sz.cx - bound) speedx = p.x - (sz.cx - bound); else if(p.x < fixed_width + bound) speedx = -(bound - p.x + fixed_width); } if(vert) { if(p.y > sz.cy - bound) speedy = p.y - (sz.cy - bound); else if(p.y < fixed_height + bound) speedy = -(bound - p.y + fixed_height); } if(speedx) sbx.Set(sbx + speedx); if(speedy) sby.Set(sby + speedy); if(speedx || speedy) { LG("speedx %d, speedy %d", speedx, speedy); MouseMove(p, keyflags); } } Image GridCtrl::CursorImage(Point p, dword keyflags) { if(!moving_header && !moving_body && HasCapture()) { if(resizing_cols && curSplitCol >= 0) return GridImg::HorzPos(); if(resizing_rows && curSplitRow >= 0) return GridImg::VertPos(); else return Image::Arrow(); } if(moving_header) { curSplitCol = GetSplitCol(p, -1); curSplitRow = GetSplitRow(p, -1); if(resize_col_mode == 0 || resize_row_mode == 0) MouseAccel(p, fixed_top_click, fixed_left_click, keyflags); return Image::Arrow(); } else if(moving_body) { curSplitRow = GetSplitRow(Point(0, p.y), -1); return Image::Arrow(); } else if(mouse_move) { curSplitCol = GetSplitCol(p); curSplitRow = GetSplitRow(p); mouse_move = false; } curResizeCol = curResizeRow = false; if(resizing_cols && curSplitCol >= 0 || resizeCol) { if(hitems[curSplitCol].join > 0) { int idy = GetMouseRow(p, true, p.y < fixed_height, true); if(idy >= 0) { Item &it = items[vitems[idy].id][hitems[curSplitCol].id]; if(it.isjoined && it.idx + it.cx != curSplitCol) return Image::Arrow(); } } curResizeCol = true; return GridImg::HorzPos(); } else if(resizing_rows && curSplitRow >= 0 || resizeRow) { if(vitems[curSplitRow].join > 0) { int idx = GetMouseCol(p, true, p.x < fixed_width, true); if(idx >= 0) { Item &it = items[vitems[curSplitRow].id][hitems[idx].id]; if(it.isjoined && it.idy + it.cy != curSplitRow) return Image::Arrow(); } } curResizeRow = true; return GridImg::VertPos(); } return Image::Arrow(); } void GridCtrl::UpdateHolder(bool force) { if(fixed_size_changed || force) { holder.SetOffset(Point(fixed_width, fixed_height)); holder.HSizePos(fixed_width, 0).VSizePos(fixed_height, 0); fixed_size_changed = false; } } bool GridCtrl::SetCursor0(int x, int y, bool mouse, bool highlight, int dirx, int diry, bool ctrlmode) { return SetCursor0(Point(x, y), mouse, highlight, dirx, diry, ctrlmode); } bool GridCtrl::SetCursor0(Point p, bool mouse, bool highlight, int dirx, int diry, bool ctrlmode) { Point tmpcur; oldcur = highlight ? livecur : curpos; if(mouse) { tmpcur.x = GetMouseCol(p, true, false); tmpcur.y = GetMouseRow(p, true, false); if(tmpcur.x < 0 || tmpcur.y < 0) return false; } else tmpcur = p; bool oldvalid = IsValidCursorAll(oldcur); bool newvalid = false; Item *nit = NULL; if(!mouse && !highlight) { if(dirx == -2) dirx = tmpcur.x >= oldcur.x ? 1 : -1; if(diry == -2) diry = tmpcur.y >= oldcur.y ? 1 : -1; bool quit = false; int fc = max(fixed_cols, firstVisCol); int lc = lastVisCol; int fr = max(fixed_rows, firstVisRow); int lr = lastVisRow; Item *oit = oldvalid ? &GetItem(oldcur) : NULL; while(true) { bool cur = IsValidCursor(tmpcur, fc, lc, fr, lr); bool hidden = true; bool hx = true; bool clickable = true; bool editable = true; bool group = true; if(cur) { hx = hitems[tmpcur.x].hidden; hidden = hx && vitems[tmpcur.y].hidden; clickable = hitems[tmpcur.x].clickable && vitems[tmpcur.y].clickable; editable = hitems[tmpcur.x].editable && vitems[tmpcur.y].editable; if(oit && oit->group >= 0 && !select_row) { nit = &GetItem(tmpcur); group = nit->group != oit->group; } } newvalid = cur && !hidden && clickable && group; if(ctrlmode) { if(newvalid) { int idx = hitems[tmpcur.x].id; int idy = vitems[tmpcur.y].id; Item &it = items[idy][idx]; Ctrl * ctrl = it.ctrl; if(!ctrl && ctrls) ctrl = edits[idx].ctrl; if(ctrl && it.editable && it.clickable) break; } } else if(newvalid) break; if(quit) return false; if(dirx != 0 || hx) { if(hx && dirx == 0) dirx = 1; tmpcur.x += dirx; if(tmpcur.x > lc) { if(tab_changes_row && diry == 0) { tmpcur.y += 1; if(tmpcur.y > lr) { tmpcur.y = lr; tmpcur.x = lc; quit = true; } else tmpcur.x = fc; } else quit = true; } else if(tmpcur.x < fc) { if(tab_changes_row && diry == 0) { tmpcur.y -= 1; if(tmpcur.y < fr) { tmpcur.y = fr; tmpcur.x = fc; quit = true; } else tmpcur.x = lc; } else quit = true; } continue; } if(diry != 0) { tmpcur.y += diry; if(tmpcur.y < fr) { tmpcur.y = fr; quit = true; } else if(tmpcur.y > lr) { tmpcur.y = lr; quit = true; } } } } else newvalid = IsValidCursor(tmpcur); if(!newvalid) (highlight ? livecur : curpos) = tmpcur; if(tmpcur == oldcur) return false; if(!highlight) valid_cursor = newvalid; if(oldvalid) { SetItemCursor(oldcur, false, highlight); if(!newvalid) { RefreshRow(oldcur.y, 0); return false; } } if(!newvalid) return false; SetItemCursor(tmpcur, true, highlight); if(oldvalid && oldcur.y != tmpcur.y) { RefreshRow(oldcur.y, 0); } if(highlight) { if(oldcur.y != tmpcur.y) RefreshRow(tmpcur.y, 0, 0); else if(!select_row && oldcur.x != tmpcur.x) RefreshRow(tmpcur.y, 0, 0); } else { RefreshRow(tmpcur.y, 0); } if(highlight) { livecur = tmpcur; LG("lcur(%d, %d)", livecur.x, livecur.y); } else { oldid = curid; bool isnewrow = curpos.y != tmpcur.y; curpos = tmpcur; rowidx = curpos.y; curid.x = hitems[curpos.x].id; curid.y = vitems[curpos.y].id; if(call_whenchangerow && isnewrow) { #ifdef LOG_CALLBACKS LGR(2, "WhenChangeRow()"); LGR(2, Format("[row: %d]", rowidx)); #endif WhenChangeRow(); } LG("cur(%d, %d)", curpos.x, curpos.y); } return true; } int GridCtrl::GetWidth(int n) { if(n < 0) n = total_cols; if(n == 0) return 0; return hitems[n - 1].nRight(); } int GridCtrl::GetHeight(int n) { if(n < 0) n = total_rows; if(n == 0) return 0; return vitems[n - 1].nBottom(); } int GridCtrl::GetFixedWidth() { return GetWidth(fixed_cols); } int GridCtrl::GetFixedHeight() { return GetHeight(fixed_rows); } int GridCtrl::GetFirst0(Vector &its, int total, int sb, int p, int is, int dir) { int l = 0; int r = total - 1; if(is < 0) is = 0; if(dir < 0) r = is; else l = is; /* LG("p:%d", p); LG("is:%d", is); LG("dir:%d", dir); LG("l:%d r:%d", l, r); */ while(l <= r) { int i = (l + r) / 2; int p0 = its[i].nLeft(sb); int p1 = its[i].nRight(sb); if(p0 <= p && p1 >= p) { if(!its[i].hidden) return i; else { if(dir == 0) dir = 1; int ie = dir < 0 ? -1 : total; int pi = i; for(; i != ie; i += dir) if(!its[i].hidden) return i; dir = -dir; ie = dir < 0 ? -1 : total; for(i = pi; i != ie; i += dir) if(!its[i].hidden) return i; return -1; } } if(p1 < p) l = i + 1; else r = i - 1; } return -1; } int GridCtrl::GetFirstVisCol(int p, int is, int dir) { return total_cols <= 2 ? fixed_cols : GetFirst0(hitems, total_cols, sbx, p, is, dir); } int GridCtrl::GetFirstVisRow(int p, int is, int dir) { return total_rows <= 1 ? fixed_rows : GetFirst0(vitems, total_rows, sby, p, is, dir); } GridCtrl& GridCtrl::SetColWidth(int n, int width, bool recalc /* = true */) { if(resize_col_mode > 0 && n >= fixed_cols) return *this; hitems[n].Width(width); Repaint(true, false); return *this; } GridCtrl& GridCtrl::SetRowHeight(int n, int height, bool recalc) { LG("SetRowHeight %d %d", n, height); if(resize_row_mode > 0 && n >= fixed_rows) return *this; vitems[n].Height(height); Repaint(false, true); return *this; } bool GridCtrl::SetDiffItemSize(bool horizontal, RectItems &its, int n, int diff, bool newsize) { if(diff == 0) return false; if(diff < 0 && its[n].IsMin()) { LG("Min1"); return false; } if(diff > 0 && its[n].IsMax()) { LG("Max1"); return false; } int resize_mode = horizontal ? resize_col_mode : resize_row_mode; if(resize_mode > 0 && diff > 0) { bool ismin = true; for(int i = n + 1; i < (horizontal ? total_cols : total_rows); i++) if(!its[i].IsMin()) { ismin = false; break; } if(ismin) { LG("Min2"); return false; } } double size = its[n].size + diff; if(size <= its[n].min) { size = its[n].min; its[n].ismin = true; } else if(size >= its[n].max) { size = its[n].max; its[n].ismax = true; } else { its[n].ismin = false; its[n].ismax = false; } double ddiff = size - its[n].size; if(ddiff != 0) { Recalc(horizontal, its, n, size, ddiff); return true; } return false; } void GridCtrl::Recalc(bool horizontal, RectItems &its, int n, double size, double diff) { its[n].size = size; Size sz = GetSize(); int cnt = horizontal ? total_cols : total_rows; int maxsize = horizontal ? sz.cx : sz.cy; int tcnt = cnt; int resize_mode = horizontal ? resize_col_mode : resize_row_mode; if(resize_mode == 0) { for(int i = n + 1; i < cnt; i++) its[i].pos += diff; } else if(resize_mode == 1) { double imaxsize = 1.0 / (double) maxsize; double ms = maxsize; loop: double sumprop = 0; for(int i = cnt - 1; i >= n + 1; --i) { if(its[i].hidden) continue; bool prop = (diff > 0 && its[i].IsMin() || diff < 0 && its[i].IsMax()); if(!prop) sumprop += its[i].prop; } double cps = sumprop != 0 ? -diff / sumprop : 0; bool isminmax = false; for(int i = cnt - 1; i >= n + 1; --i) { if(its[i].hidden) { its[i].pos = ms; continue; } if(!(diff > 0 && its[i].IsMin() || diff < 0 && its[i].IsMax())) { double size = its[i].size + its[i].prop * cps; bool minsize = (diff > 0 && size < its[i].min); bool maxsize = (diff < 0 && size > its[i].max); its[i].ismin = minsize; its[i].ismax = maxsize; if(minsize || maxsize) { diff += size - its[i].size; double ns = minsize ? its[i].min : its[i].max; its[i].size = ns; its[i].prop = ns * imaxsize; cnt = i + 1; goto loop; } its[i].size = size; its[i].prop = size * imaxsize; } ms -= its[i].size; its[i].pos = ms; } its[n].size -= its[n].pos + its[n].size - ms; its[n].prop = its[n].size * imaxsize; } // LGR(Format("Time diff %d", tm.Elapsed())); CalcIntPos(its, n, maxsize, tcnt - 1, resize_mode, false); } void GridCtrl::CalcIntPos(RectItems &its, int n, int maxsize, int cnt, int resize_mode, bool renumber) { its[0].npos = 0; int last_vis = 1; if(!renumber) { for(int i = max(1, n); i <= cnt ; i++) { its[i].npos = Round(its[i].Left()); its[i - 1].nsize = its[i].npos - its[i - 1].npos; if(!its[i].hidden) last_vis = i; } } else { int hidden = 0; for(int i = 1; i <= cnt ; i++) { its[i].npos = Round(its[i].Left()); its[i - 1].nsize = its[i].npos - its[i - 1].npos; its[i].n = hidden; if(its[i].hidden) hidden++; else last_vis = i; } } last_vis = cnt; if(resize_mode > 0) { int size = maxsize - its[last_vis].npos; its[last_vis].nsize = size ;//>= its[cnt].min && size <= its[cnt].max ? size : Round(its[cnt].size); } else its[last_vis].nsize = Round(its[last_vis].size); } bool GridCtrl::UpdateSizes() { total_width = total_cols ? hitems[total_cols - 1].nRight() : 0; total_height = total_rows ? vitems[total_rows - 1].nRight() : 0; int new_fixed_width = fixed_cols ? hitems[fixed_cols - 1].nRight() : 0; int new_fixed_height = fixed_rows ? vitems[fixed_rows - 1].nRight() : 0; fixed_size_changed = false; if(new_fixed_width != fixed_width) { fixed_width = new_fixed_width; fixed_size_changed = true; } if(new_fixed_height != fixed_height) { fixed_height = new_fixed_height; fixed_size_changed = true; } return fixed_size_changed; } bool GridCtrl::UpdateCols(bool force) { Size sz = GetSize(); bool change = false;; if((osz.cx != sz.cx && resize_col_mode > 0) || force || recalc_cols) { LG(" RecalcCols"); RecalcCols(-1); recalc_cols = false; change = true; } osz.cx = sz.cx; return change; } bool GridCtrl::UpdateRows(bool force) { Size sz = GetSize(); bool change = false;; if((osz.cy != sz.cy && resize_row_mode > 0) || force || recalc_rows) { LG(" RecalcRows"); RecalcRows(-1); recalc_rows = false; change = true; } osz.cy = sz.cy; return change; } bool GridCtrl::Recalc(bool horizontal, RectItems &its, int resize_mode) { Size sz = GetSize(); if(resize_mode < 0) resize_mode = horizontal ? resize_col_mode : resize_row_mode; int fixed = horizontal ? fixed_cols : fixed_rows; int cnt = horizontal ? total_cols : total_rows; its[0].pos = 0; if(resize_mode == 0) { for(int i = 1; i < cnt; i++) its[i].pos = its[i - 1].pos + its[i - 1].size; } else if(resize_mode == 1) { int cs = horizontal ? sz.cx - fixed_width : sz.cy - fixed_height; if(cs <= 0) return false; double ics = 1.0 / cs; for(int i = fixed; i < cnt; i++) { its[i].ismin = false; its[i].ismax = false; } loop: double sumprop = 0; for(int i = fixed; i < cnt; i++) { if(its[i].hidden) continue; its[i].prop = its[i].size * ics; sumprop += its[i].prop; } ics = sumprop <= 0 ? 0 : cs / sumprop; for(int i = fixed; i < cnt; i++) { its[i].pos = i == 0 ? 0 : its[i - 1].Right(); if(its[i].hidden || its[i].ismin || its[i].ismax) continue; its[i].size = its[i].prop * ics; if(its[i].size < its[i].min) { its[i].size = its[i].min; its[i].ismin = true; goto loop; } else if(its[i].size > its[i].max) { its[i].size = its[i].max; its[i].ismax = true; goto loop; } } } CalcIntPos(its, 0, horizontal ? GetSize().cx : GetSize().cy, cnt - 1, resize_mode, true); UpdateVisColRow(horizontal); oldpos.x = sbx; oldpos.y = sby; return true; } bool GridCtrl::RecalcCols(int mode) { return Recalc(true, hitems, mode); } bool GridCtrl::RecalcRows(int mode) { return Recalc(false, vitems, mode); } int GridCtrl::GetSplitCol(const Point &p, int splitSize, bool full) { if(total_cols < 2) return -1; int diff = 0; int size = splitSize >= 0 ? splitSize : 0; if(p.x > fixed_width || moving_body || moving_header) { if(!full && !full_col_resizing && p.y > fixed_height) return -1; diff = sbx; } else if(p.y < fixed_height - splitSize) return -1; int tc = splitSize >= 0 ? (resize_col_mode == 0 ? 0 : 1) : 0; int fc = lastVisCol - tc; int lc = resizing_fixed_cols ? 1 : firstVisCol; if(splitSize >= 0) { for(int i = fc; i >= lc; i--) { if(hitems[i].hidden) continue; int x = hitems[i].nRight(diff); if(p.x >= x - splitSize && p.x <= x + splitSize) return i; } } else { int c = fc; for(int i = fc; i >= lc; i--) { if(!hitems[i].hidden) c = i; int x = hitems[c].nLeft(diff) + hitems[c].nWidth() / 2; if(p.x >= x) return c < fixed_cols ? firstVisCol - 1 : c; else if(i == lc) return c - 1; } } return -1; } int GridCtrl::GetSplitRow(const Point &p, int splitSize, bool full) { if(total_rows < 2) return -1; int diff = 0; int size = splitSize >= 0 ? splitSize : 0; if(p.y > fixed_height || moving_body || moving_header) { if(!full && !moving_header && !full_row_resizing && p.x > fixed_width) return -1; diff = sby; } else if(p.x < fixed_width) return -1; int tr = splitSize >= 0 ? (resize_row_mode == 0 ? 0 : 1) : 0; int fr = lastVisRow - tr; int lr = p.y < fixed_height ? 0 : firstVisRow; if(splitSize >= 0) { for(int i = fr; i >= lr; i--) { if(vitems[i].hidden) continue; int y = vitems[i].nBottom(diff); if(p.y >= y - splitSize && p.y <= y + splitSize) return i; } } else { int c = fr; for(int i = fr; i >= lr; i--) { if(!vitems[i].hidden) c = i; int y = vitems[c].nTop(diff) + vitems[c].nHeight() / 2; if(p.y >= y) return c < fixed_rows ? firstVisRow - 1 : c; else if(i == lr) return c - 1; } } return -1; } bool GridCtrl::IsValidCursor(Point &p, int fc, int lc, int fr, int lr) { return p.x >= fc && p.x <= lc && p.y >= fr && p.y <= lr; } bool GridCtrl::IsValidCursorVis(Point &p) { return p.x >= firstVisCol && p.x <= lastVisCol && p.y >= firstVisRow && p.y <= lastVisRow; } bool GridCtrl::IsValidCursorAll(Point &p) { return p.x >= fixed_cols && p.x < total_cols && p.y >= fixed_rows && p.y < total_rows; } bool GridCtrl::IsValidCursor(Point &p) { return ready ? IsValidCursorVis(p) : IsValidCursorAll(p); } bool GridCtrl::IsRowEditable() { return vitems[curpos.y].editable && hitems[curpos.x].editable && (select_row || (!select_row && edits[curid.x].ctrl)); } void GridCtrl::SetItemCursor(Point p, bool b, bool highlight) { if(highlight) { hitems[p.x].Live(b); vitems[p.y].Live(b); GetItem(p).Live(b); } else { hitems[p.x].Cursor(b); vitems[p.y].Cursor(b); GetItem(p).Cursor(b); } } GridCtrl& GridCtrl::Indicator(bool b, int size) { indicator = b; fixed_width += (size + 1) * (b ? 1 : -1); SetColWidth(0, b ? size : 0); return *this; } void GridCtrl::RefreshRow(int n, bool relative, bool fixed) { if(n < 0) { n = rowidx; relative = false; } if(relative) n += fixed_rows; if(vitems[n].hidden) return; int dy = fixed ? 0 : sby; int join = vitems[n].join; if(join > 0) { int s = n; while(s >= 0 && vitems[s].join > 0) s--; s++; int e = n; while(e < total_rows && vitems[e].join > 0) e++; e--; Refresh(Rect(0, vitems[s].nTop(dy), GetSize().cx, vitems[e].nBottom(dy))); } else Refresh(Rect(0, vitems[n].nTop(dy), GetSize().cx, vitems[n].nBottom(dy))); } void GridCtrl::RefreshCol(int n, bool relative, bool fixed) { if(n < 0) { n = curpos.x; relative = false; } if(relative) n += fixed_cols; if(hitems[n].hidden) return; int dx = fixed ? 0 : sbx; Refresh(Rect(hitems[n].nLeft(dx), 0, hitems[n].nRight(dx), GetSize().cy)); } void GridCtrl::RefreshRows(int from, int to, bool relative, bool fixed) { if(relative) { from += fixed_rows; to += fixed_rows; } Refresh(Rect(0, vitems[from].nTop(sby), GetSize().cx, vitems[to].nBottom(sby))); } void GridCtrl::RefreshFrom(int from) { Size sz = GetSize(); int y = 0; if(resize_row_mode == 0) if(from > 2 && from <= total_rows) y = vitems[from - 1].nBottom(sby); Refresh(Rect(0, y, sz.cx, sz.cy)); } void GridCtrl::RefreshItem(int r, int c, bool relative) { if(relative) { c += fixed_cols; r += fixed_rows; } Refresh(GetItemRect(r, c)); } void GridCtrl::RefreshNewRow() { RefreshRow(rowidx, 0); } void GridCtrl::RefreshTop() { Refresh(0, 0, GetSize().cx, fixed_height); } void GridCtrl::RefreshLeft() { Refresh(0, fixed_height, fixed_width, GetSize().cy - fixed_height); } bool GridCtrl::IsMouseBody(Point &p) { return p.x >= fixed_width && p.x < total_width && p.y >= fixed_height && p.y < total_height; } int GridCtrl::GetIdCol(int id, bool checkall) { for(int i = checkall ? 1 : fixed_cols; i < total_cols; i++) { if(id == hitems[i].id) return i; } return -1; } int GridCtrl::GetIdRow(int id, bool checkall) { for(int i = checkall ? 0 : fixed_rows; i < total_rows; i++) { if(id == vitems[i].id) return i; } return -1; } int GridCtrl::GetVisIdCol(int id) { for(int i = firstCol; i <= lastCol; i++) { if(id == hitems[i].id) return i; } return -1; } int GridCtrl::GetVisIdRow(int id) { for(int i = firstRow; i <= lastRow; i++) { if(id == hitems[i].id) return i; } return -1; } int GridCtrl::GetNextRow(int n) { n += fixed_rows; for(int i = n + 1; i < total_rows; i++) if(!vitems[i].hidden) return i - fixed_rows; return -1; } int GridCtrl::GetPrevRow(int n) { n += fixed_rows; for(int i = n - 1; i >= fixed_rows; i--) if(!vitems[i].hidden) return i - fixed_rows; return -1; } void GridCtrl::UpdateCursor() { curpos.x = GetIdCol(curid.x); curpos.y = GetIdRow(curid.y); rowidx = curpos.y; shiftpos = curpos; ctrlid.y = curpos.y < 0 ? -1 : vitems[curpos.y].id; ctrlpos.y = curpos.y; } int GridCtrl::Find(const Value &v, int col) { for(int i = fixed_rows; i < total_rows; i++) { if(items[vitems[i].id][col + fixed_cols].val == v) return i - fixed_rows; } return -1; } int GridCtrl::Find(const Value &v, Id id) { return Find(v, aliases.Get(id) - fixed_cols); } void GridCtrl::UpdateDefaults(int ri) { for(int i = 1; i < total_cols; i++) if(!IsNull(hitems[i].defval)) items[ri][hitems[i].id].val = hitems[i].defval; } void GridCtrl::SetCtrlsData(int row) { if(row < 0) return; for(int i = 1; i < total_cols; i++) { int idx = hitems[i].id; Ctrl * ctrl = items[row][idx].ctrl; if(!ctrl) ctrl = edits[idx].ctrl; if(ctrl) ctrl->SetData(items[row][idx].val); } } bool GridCtrl::GetCtrlsData(int row, bool samerow, bool doall, int ri) { if(row < 0 || row > items.GetCount() - 1 || !HasCtrls()) return false; int cri = rowidx; if(ri >= 0) rowidx = ri; bool newrow = newrow_inserted || newrow_appended; bool modified = false; if(focused_ctrl) { Value v = focused_ctrl->GetData(); if(v.IsNull() && newrow) v = focused_ctrl_val; focused_ctrl->Accept(); #ifdef LOG_CALLBACKS Value oldval = items[row][focused_ctrl_id].val; #endif if(items[row][focused_ctrl_id].val != v) { modified = row_modified = true; items[row][focused_ctrl_id].val = v; } if((samerow || doall) && modified) { #ifdef LOG_CALLBACKS LGR(2, "WhenUpdateCell()"); LGR(2, Format("[row: %d, colid: %d]", row, focused_ctrl_id)); LGR(2, Format("[oldval : %s]", AsString(oldval))); LGR(2, Format("[newval : %s]", AsString(v))); #endif WhenUpdateCell(); } } if(edit_mode == 0 && !samerow) for(int i = 1; i < total_cols; i++) { int idx = hitems[i].id; Ctrl * ctrl = items[row][idx].ctrl; if(!ctrl) ctrl = edits[idx].ctrl; if(ctrl && ctrl->IsShown()) { Value v = ctrl->GetData(); if(v.IsNull() && newrow) v = hitems[i].defval; ctrl->Accept(); if(items[row][idx].val != v) { modified = row_modified = true; items[row][idx].val = v; } } } if(!samerow || doall) { if(newrow) { #ifdef LOG_CALLBACKS LGR(2, Format("WhenInsertRow()", row)); LGR(2, Format("[row: %d]", row)); #endif WhenInsertRow(); row_modified = false; } else if(row_modified) { #ifdef LOG_CALLBACKS LGR(2, Format("WhenUpdateRow()", row)); LGR(2, Format("[row: %d]", row)); #endif WhenUpdateRow(); row_modified = false; } WhenAcceptRow(); WhenModification(); } if(newrow && (!samerow || doall)) { newrow_inserted = false; newrow_appended = false; } if(ri >= 0) rowidx = cri; return modified; } bool GridCtrl::CancelCtrlsData(int row) { bool null = true; for(int i = 1; i < total_cols; i++) { Ctrl * ctrl = edits[hitems[i].id].ctrl; if(ctrl && ctrl->IsShown() && ctrl->HasFocus()) { ctrl->Reject(); ctrl->SetData(items[row][hitems[i].id].val); if(!IsNull(ctrl->GetData())) null = false; } } return null; } void GridCtrl::UpdateCtrls(int opt /*= UC_CHECK_VIS | UC_SHOW | UC_CURSOR | UC_FOCUS*/) { if(opt & UC_CHECK_VIS && !ctrls) return; ctrlid.y = curpos.y < 0 ? -1 : vitems[curpos.y].id; ctrlpos.y = curpos.y; if(curpos.y < 0) return; Size sz = GetSize(); bool isctrl = false; Ctrl * first_ctrl = NULL; focused_ctrl = NULL; focused_ctrl_id = -1; bool gofirst = opt & UC_GOFIRST && select_row && !ctrls; if(opt & UC_MOUSE) gofirst = gofirst && !GetCtrl(curpos, false, false); bool show = opt & UC_SHOW; for(int i = 1; i < total_cols; i++) { if(hitems[i].hidden) continue; Ctrl * ctrl = GetCtrl(curpos.y, i, show == false); if(!ctrl) continue; int id = hitems[i].id; if(!first_ctrl) first_ctrl = ctrl; bool dorect = false; bool dofocus = false; bool factory = edits[id].factory; if(show) { bool dorf = gofirst ? ctrl == first_ctrl : i == curpos.x; dofocus = dorf; dorect = edit_mode == 1 ? dorf : (ctrls || opt & UC_CTRLS); } if(dorect) { Rect r = GetItemRect(ctrlpos.y, i, horz_grid, vert_grid, true); if(!r.Intersects(sz)) r.Set(0, 0, 0, 0); if(!factory) { ctrl->SetRect(AlignRect(r, i)); ctrl->Show(); isctrl = true; } } else if(!factory) { bool hide = true; if(edit_mode == 2 && !show) hide = false; if(hide) { ctrl->SetRect(0, 0, 0, 0); ctrl->Hide(); } } if(dofocus) { LG(2, "Focus on %d", i); ctrl->SetFocus(); focused_ctrl = ctrl; focused_ctrl_id = id; focused_ctrl_val = hitems[i].defval; if(opt & UC_CURSOR) SetCursor0(i, curpos.y); } } edit_ctrls = isctrl; if(opt & UC_CTRLS) ctrls = isctrl; if(opt & UC_CTRLS_OFF && !isctrl) ctrls = false; if(!show) SetFocus(); if(!(opt & UC_SCROLL)) RebuildToolBar(); } void GridCtrl::SyncCtrls() { Size sz = GetSize(); genr_ctrls = false; Vector cols; for(int i = 1; i < total_cols; i++) if(edits[hitems[i].id].factory && hitems[i].editable) cols.Add(i); if(cols.IsEmpty()) return; int dy = sby + fixed_height; int dx = sbx + fixed_width; Rect r; for(int j = 0; j < total_rows; j++) { int idy = vitems[j].id; bool fixed = j < fixed_rows; bool create = !fixed && vitems[j].editable; for(int i = 0; i < cols.GetCount(); i++) { int c = cols[i]; int idx = hitems[c].id; Item &it = items[idy][idx]; if(it.isjoined) { it = items[it.idy][it.idx]; idx = it.idx; } if(!it.ctrl && create && edits[idx].factory) { One newctrl; edits[idx].factory(newctrl); newctrl->SetData(it.val); newctrl->WhenAction << Proxy(WhenCtrlsAction); it.ctrl = newctrl.Detach(); holder.AddChild(it.ctrl); } if(it.ctrl) { if(it.isjoined && it.sync_flag == sync_flag) continue; r = GetItemRect(j, c, horz_grid, vert_grid, true); AlignRect(r, c); if(r.Intersects(sz) && !fixed && !vitems[j].hidden && !hitems[c].hidden) { it.ctrl->SetRect(r); it.ctrl->Show(); } else if(it.ctrl->IsShown()) { it.ctrl->SetRect(0, 0, 0, 0); it.ctrl->Hide(); } genr_ctrls = true; } if(it.isjoined) it.sync_flag = sync_flag; } } sync_flag = 1 - sync_flag; } bool GridCtrl::HasCtrls() { return edit_ctrls || genr_ctrls; } void GridCtrl::RestoreFocus() { if(focused_ctrl) focused_ctrl->SetFocus(); } bool GridCtrl::ShowNextCtrl() { if(GoRight(1, 1)) { UpdateCtrls(UC_CHECK_VIS | UC_SHOW | UC_FOCUS); return true; } return false; } bool GridCtrl::ShowPrevCtrl() { if(GoLeft(1, 1)) { UpdateCtrls(UC_CHECK_VIS | UC_SHOW | UC_FOCUS); return true; } return false; } int GridCtrl::GetFocusedCtrlIndex() { for(int i = 1; i < total_cols; i++) { int id = hitems[i].id; Ctrl * ctrl = items[0][id].ctrl; if(ctrl && ctrl->HasFocusDeep()) return i; } return -1; } Point GridCtrl::GetCtrlPos(Ctrl * ctrl) { for(int i = fixed_rows; i < total_rows; i++) { int idy = vitems[i].id; for(int j = fixed_cols; j < total_cols; j++) { int idx = hitems[j].id; Ctrl * ci = items[idy][idx].ctrl; bool isedit = false; if(!ci) { ci = edits[idx].ctrl; isedit = true; } if(ci == ctrl) return Point(j, isedit ? ctrlpos.y : i); } } return Point(-1, -1); } void GridCtrl::Split(int state, bool sync) { if(resize_paint_mode < 2) { if(resize_paint_mode > 0 && state != GS_DOWN) { if(resizeCol) RefreshTop(); if(resizeRow) RefreshLeft(); } if(state == GS_DOWN) DrawLine(true, false); else if(state == GS_MOVE) DrawLine(true, true); else DrawLine(false, true); } if(state == GS_DOWN) { firstCol = firstRow = -1; return; } if(state != GS_DOWN && (resize_paint_mode == 2 || state == GS_UP)) { UpdateSizes(); UpdateHolder(); UpdateSb(); Refresh(); } if((resize_paint_mode > 1 && state > GS_UP) || state == GS_UP) { SyncCtrls(); UpdateCtrls(UC_CHECK_VIS | UC_SHOW | (UC_FOCUS * int(state == GS_UP))); } if(sync) Sync(); } bool GridCtrl::Key(dword key, int) { switch(key) { case K_ENTER: ClearSelection(); #ifdef LOG_CALLBACKS LGR(2, "WhenEnter()"); WhenEnter(); #endif if(enter_like_tab) { if(ctrls) { bool isnext = ShowNextCtrl(); if(tab_adds_row && !isnext && curpos.y == lastVisRow) { DoAppend(); return true; } else return isnext; if(tab_changes_row) { if(select_row) { if(!GoNext() && tab_adds_row) DoAppendNoEdit(); } else if(!GoRight() && tab_adds_row) DoAppendNoEdit(); ClearSelection(); return true; } } else { SwitchEdit(); return true; } return false; } else if(!SwitchEdit()) return true; /* if(th.IsSorted()) { th.Multisort(); Refresh(); }*/ return true; case K_ESCAPE: if(ctrls) { CancelEdit(); return true; } else return false; case K_SHIFT|K_LEFT: GoLeft(); DoShiftSelect(); return true; case K_SHIFT|K_RIGHT: GoRight(); DoShiftSelect(); return true; case K_SHIFT|K_UP: GoPrev(); DoShiftSelect(); return true; case K_SHIFT|K_DOWN: GoNext(); DoShiftSelect(); return true; case K_SHIFT|K_PAGEUP: GoPageUp(); DoShiftSelect(); return true; case K_SHIFT|K_PAGEDOWN: GoPageDn(); DoShiftSelect(); return true; case K_SHIFT_HOME: GoBegin(); DoShiftSelect(); return true; case K_SHIFT_END: GoEnd(); DoShiftSelect(); return true; case K_CTRL_INSERT: DoCopy(); return true; case K_SHIFT_INSERT: DoPaste(); return true; case K_SHIFT_DELETE: DoRemove(); return true; case K_UP: //case K_CTRL|K_UP: GoPrev(); ClearSelection(); return true; case K_DOWN: //case K_CTRL|K_DOWN: GoNext(); ClearSelection(); return true; case K_LEFT: GoLeft(); ClearSelection(); return true; case K_RIGHT: GoRight(); ClearSelection(); return true; case K_HOME: case K_CTRL_HOME: case K_CTRL_PAGEUP: GoBegin(); ClearSelection(); return true; case K_END: case K_CTRL_END: case K_CTRL_PAGEDOWN: GoEnd(); ClearSelection(); return true; case K_PAGEUP: GoPageUp(); ClearSelection(); return true; case K_PAGEDOWN: GoPageDn(); ClearSelection(); return true; case K_CTRL|K_LEFT: SwapCols(curpos.x, curpos.x - 1); return true; case K_CTRL|K_RIGHT: SwapCols(curpos.x, curpos.x + 1); return true; case K_TAB: if(HasCtrls()) { bool isnext = ShowNextCtrl(); if(tab_adds_row && !isnext && curpos.y == lastVisRow) { DoAppend(); return true; } else return isnext; } else if(tab_changes_row) { if(select_row) { if(!GoNext() && tab_adds_row) DoAppendNoEdit(); } else if(!GoRight() && tab_adds_row) DoAppendNoEdit(); ClearSelection(); return true; } else return false; case K_SHIFT|K_TAB: if(HasCtrls()) return ShowPrevCtrl(); else if(tab_changes_row) { if(select_row) GoPrev(); else GoLeft(); ClearSelection(); return true; } else return false; case K_CTRL|K_F: if(searching) { findstring.SetFocus(); return true; } else return false; } return MenuBar::Scan(WhenMenuBar, key); } void GridCtrl::SwapCols(int n, int m) { if(m == n || n < fixed_cols || n > total_cols - 1 || m < fixed_cols || m > total_cols - 1) return; Swap(hitems[m], hitems[n]); UpdateCursor(); Repaint(true, false); } bool GridCtrl::CanMoveCol(int n, int m) { if(m == n || m == n - 1 || n < 0 || n > total_cols - 1 || m < 0 || m > total_cols - 1) return false; else { if(hitems[n].join > 0) { LG(2, "n=%d(%d) m=%d(%d)", n, hitems[n].join, m, hitems[m].join); if(m == n - 2 && hitems[n].join == hitems[n - 1].join) return true; if(hitems[m].join != hitems[n].join) return false; } //tu sprawdzic join return true; } } void GridCtrl::MoveCol(int n, int m) { LG("%d->%d", n, m); if(!CanMoveCol(n, m)) { Repaint(); return; } LG("Moved"); ItemRect ir = hitems[n]; if(m > total_cols) hitems.Add(ir); else hitems.Insert(m + 1, ir); if(m > n) hitems.Remove(n); else hitems.Remove(n + 1); UpdateCursor(); Repaint(true, false); } bool GridCtrl::MoveRow(int n, int m, bool repaint) { LG("%d->%d", n, m); if(m == n || m == n - 1 || n < 0 || n > total_rows - 1 || m < -1 || m > total_rows - 1) { Repaint(); return false; } LG("Moved"); ItemRect ir = vitems[n]; if(m > total_rows) vitems.Add(ir); else vitems.Insert(m + 1, ir); if(m > n) vitems.Remove(n); else vitems.Remove(n + 1); if(repaint) { UpdateCursor(); Repaint(false, true); } roworder = true; return true; } void GridCtrl::MoveRows(int n, bool onerow) { if(selected_rows && !onerow) { Vector vi; vi.Reserve(selected_rows); for(int i = fixed_rows; i < total_rows; i++) if(vitems[i].IsSelect()) vi.Add(vitems[i]); int cnt = 0; for(int i = total_rows - 1; i >= fixed_rows; i--) if(vitems[i].IsSelect()) { vitems.Remove(i); if(i < n) cnt++; } vitems.Insert(n - cnt, vi); roworder = true; UpdateCursor(); Repaint(false, true); } else { MoveRow(curpos.y, n - 1); } } bool GridCtrl::SwapRows(int n, int m, bool repaint) { if(ctrls || m == n || n < fixed_rows || n > total_rows - 1 || m < fixed_rows || m > total_rows - 1) return false; Swap(vitems[m], vitems[n]); if(repaint) { UpdateCursor(); Repaint(false, true); } roworder = true; return true; } void GridCtrl::SwapUp(int cnt) { int yp = 0; bool first = false; bool repaint = false; if(selected_rows == 0) { if(SwapRows(curpos.y, curpos.y - cnt)) yp = vitems[curpos.y + cnt].nTop(sby + fixed_height); else return; } else { for(int i = fixed_rows; i < total_rows; i++) { if(vitems[i].IsSelect()) { if(!SwapRows(i, i - cnt, false)) return; if(!first) { yp = vitems[i].nTop(sby + fixed_height); first = true; } } } repaint = true; } if(resize_row_mode == 0 && yp < 0) sby.Set(sby + yp); if(repaint) { UpdateCursor(); Repaint(false, true); } } void GridCtrl::SwapDown(int cnt) { int yp = 0; bool first = false; bool repaint = false; if(selected_rows == 0) { if(SwapRows(curpos.y, curpos.y + cnt)) yp = vitems[curpos.y - cnt].nBottom(sby); else return; } else { for(int i = total_rows - 1; i >= fixed_rows; i--) { if(vitems[i].IsSelect()) { if(!SwapRows(i, i + cnt, false)) return; if(!first) { yp = vitems[i].nBottom(sby); first = true; } } } repaint = true; } int cy = GetSize().cy; if(resize_row_mode == 0 && yp > cy) sby.Set(sby + yp - cy); if(repaint) { UpdateCursor(); Repaint(false, true); } } void GridCtrl::MouseLeave() { if(live_cursor) SetCursor0(-1, -1, false, true); UpdateHighlighting(GS_BORDER, Point(0, 0)); } void GridCtrl::MouseWheel(Point p, int zdelta, dword keyflags) { if(resize_row_mode == 0) sby.Set(sby - zdelta / 4); } inline Item& GridCtrl::GetItem(int n, int m) { return items[vitems[n].id][hitems[m].id]; } GridCtrl& GridCtrl::GridColor(Color fg) { fg_grid = fg; return *this; } GridCtrl& GridCtrl::FocusColor(Color fg, Color bg) { fg_focus = fg; bg_focus = bg; return *this; } GridCtrl& GridCtrl::LiveColor(Color fg, Color bg) { fg_live = fg; bg_live = bg; return *this; } GridCtrl& GridCtrl::OddColor(Color fg, Color bg) { fg_odd = fg; bg_odd = bg; return *this; } GridCtrl& GridCtrl::EvenColor(Color fg, Color bg) { fg_even = fg; bg_even = bg; return *this; } GridCtrl& GridCtrl::ColoringMode(int m) { coloringMode = m; return *this; } void GridCtrl::Clear(bool columns) { doscroll = false; scrollxdir = scrollydir = 0; UpdateCtrls(UC_HIDE | UC_CTRLS); items.Remove(1, items.GetCount() - 1); vitems.Remove(1, vitems.GetCount() - 1); if(columns) { hitems.Remove(1, hitems.GetCount() - 1); items[0].Remove(1, items[0].GetCount() - 1); total_cols = 1; total_width = 0; total_height = 0; firstVisCol = 0; lastVisCol = -1; firstCol = -1; lastCol = -1; fixed_cols = 1; firstVisRow = -1; lastVisRow = -1; } else { total_height = fixed_height; firstVisRow = 1; lastVisRow = 1; } total_rows = 1; fixed_rows = 1; firstRow = -1; lastRow = -1; curpos.y = curpos.x = -1; curid.x = curid.y = -1; hcol = -1; hrow = -1; rowidx = -1; UpdateSizes(); UpdateHolder(); UpdateSb(); Refresh(); doscroll = true; } void GridCtrl::Reset() { Clear(true); } void GridCtrl::First() { rowidx = fixed_rows; } void GridCtrl::Next() { ++rowidx; } bool GridCtrl::IsEnd() { return rowidx == total_rows; } int GridCtrl::SetCursor(int n) { int t = curpos.y; SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x, n + fixed_rows); return t; } int GridCtrl::SetCursorId(int id) { for(int i = fixed_rows; i < total_rows; i++) { if(vitems[i].id == id) return SetCursor(i); } return -1; } int GridCtrl::GetCursor(bool rel) { if(rel) return valid_cursor ? vitems[curpos.y].id - fixed_rows : -1; else return valid_cursor ? curpos.y - fixed_rows : -1; } int GridCtrl::GetCursor(int uid) { for(int i = fixed_rows; i < total_rows; i++) if(vitems[i].uid == uid) return i - fixed_rows; return -1; } Point GridCtrl::GetCursorPos() { return valid_cursor ? Point(curpos.x - fixed_cols, curpos.y - fixed_rows) : Point(-1, -1); } int GridCtrl::GetRowId() { return valid_cursor ? vitems[curpos.y].id - fixed_rows : -1; } int GridCtrl::GetColId() { return valid_cursor ? hitems[curpos.x].id - fixed_cols: -1; } int GridCtrl::GetRowId(int n) { return vitems[n + fixed_rows].id - fixed_rows; } int GridCtrl::GetColId(int n) { return hitems[n + fixed_cols].id - fixed_cols; } int GridCtrl::GetColUId() { return valid_cursor ? hitems[curpos.x].uid : -1; } int GridCtrl::GetRowUId() { return valid_cursor ? vitems[curpos.y].uid : -1; } int GridCtrl::GetNewRowPos() { return rowidx > 0 ? rowidx - fixed_rows : -1; } int GridCtrl::GetRemovedRowPos() { return rowidx > 0 ? rowidx - fixed_rows : -1; } void GridCtrl::CenterCursor() { if(IsEmpty() || !IsCursor()) return; int posx = hitems[curpos.x].nLeft(); int posy = vitems[curpos.y].nTop(); sbx.Set((posx + GetSize().cx) / 2); sby.Set((posy + GetSize().cy) / 2); if(ctrls) { GetCtrlsData(oldid.y); SetCtrlsData(curid.y); UpdateCtrls(); } } bool GridCtrl::Go0(int jump, bool scroll, bool goleft, bool ctrlmode) { if(IsEmpty()) return false; bool samerow = false; bool doall = true; if(jump == GO_LEFT || jump == GO_RIGHT) { if(select_row && !ctrlmode) { if(jump == GO_LEFT) sbx.Set(sbx.Get() - 5); else sbx.Set(sbx.Get() + 5); return false; } samerow = true; doall = false; } if(jump == GO_PREV) if(curpos.y >= 0 && curpos.y <= firstVisRow) return false; if(jump == GO_NEXT) if(curpos.y >= 0 && curpos.y >= lastVisRow) return false; if(jump == GO_PAGEUP || jump == GO_PAGEDN) { if(jump == GO_PAGEDN && curpos.y == lastVisRow) return false; if(jump == GO_PAGEUP && curpos.y == firstVisRow) return false; if(!valid_cursor) { GoFirstVisible(); return true; } } Size sz = GetSize(); int cid = curid.y; int rid = rowidx; int sy = -1; GetCtrlsData(curid.y, samerow, doall); focused_ctrl = NULL; focused_ctrl_id = -1; switch(jump) { case GO_BEGIN: { SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x, firstVisRow, false, false, 0, -1, ctrls); sy = 0; break; } case GO_END: { SetCursor0((curpos.x < 0 || goleft) ? firstVisCol : curpos.x, lastVisRow, false, false, 0, 1, ctrls); if(goleft) GoCursorLeftRight(); else sy = total_height - fixed_height; break; } case GO_NEXT: { SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x, curpos.y < 0 ? firstVisRow : curpos.y + 1, 0, 0, 0, 1, ctrls); int b = vitems[curpos.y].nBottom(sby); int r = sz.cy; if(b > r) sy = sby + b - r; break; } case GO_PREV: { SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x, curpos.y < 0 ? firstVisRow : curpos.y - 1, 0, 0, 0, -1, ctrls); int t = vitems[curpos.y].nTop(sby + fixed_height); if(t < 0) sy = sby + t; break; } case GO_LEFT: { if(!SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x - 1, curpos.y < 0 ? firstVisRow : curpos.y, 0, 0, -1, 0, ctrlmode)) return false; break; } case GO_RIGHT: { if(!SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x + 1, curpos.y < 0 ? firstVisRow : curpos.y, 0, 0, 1, 0, ctrlmode)) return false; break; } case GO_PAGEUP: { int c = curpos.y; int yn = vitems[c].nTop() - sz.cy; int ya = vitems[c].nTop(sby); bool found = false; int i; for(i = c - 1; i >= fixed_rows; i--) if(yn >= vitems[i].nTop() && yn < vitems[i].nBottom() && !vitems[i].hidden) { found = true; break; } c = found ? i : firstVisRow; if(scroll && resize_row_mode == 0) { int yc = vitems[c].nTop(); int yt = vitems[curpos.y].nTop(sby); int yb = vitems[curpos.y].nBottom(sby); if(yt < 0 || yb > sz.cy - 1) sby.Set(yc - sz.cy + vitems[c].nHeight()); else sby.Set(yc - ya); } SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x, c, 0, 0, -2, -2, ctrls); break; } case GO_PAGEDN: { int c = curpos.y; int yn = vitems[c].nTop() + sz.cy; int ya = vitems[c].nTop(sby); bool found = false; int i; for(i = c + 1; i < total_rows; i++) if(yn >= vitems[i].nTop() && yn < vitems[i].nBottom() && !vitems[i].hidden) { found = true; break; } c = found ? i : lastVisRow; if(scroll && resize_row_mode == 0) { int yc = vitems[c].nTop(); int yt = vitems[curpos.y].nTop(sby); int yb = vitems[curpos.y].nBottom(sby); if(yt < 0 || yb > sz.cy - 1) sby.Set(yc); else sby.Set(yc - ya); } SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x, c, 0, 0, -2, -2, ctrls); break; } } if(jump == GO_LEFT || jump == GO_RIGHT) { if(cid != curid.y) { rowidx = rid; GetCtrlsData(cid, false); rowidx = curpos.y; SetCtrlsData(curid.y); } if(scroll) GoCursorLeftRight(); } else { if(scroll && resize_row_mode == 0 && sy >= 0) sby.Set(sy); if(ctrls) { SetCtrlsData(curid.y); UpdateCtrls(); } } Ctrl * ctrl = valid_cursor ? GetItem(curpos).ctrl : NULL; if(ctrl) { focused_ctrl = ctrl; focused_ctrl_id = hitems[curpos.x].id;//GetColId(curpos.x); ctrl->SetFocus(); } else if(!focused_ctrl) SetFocus(); return true; } void GridCtrl::GoCursorLeftRight() { if(resize_col_mode == 0) { int l = hitems[curpos.x].nLeft(sbx + fixed_width); int r = hitems[curpos.x].nRight(sbx); int w = GetSize().cx; if(l < 0) sbx.Set(sbx + l); else if(r > w) sbx.Set(sbx + r - w); } if(resize_row_mode == 0) { int t = vitems[curpos.y].nTop(sby + fixed_height); int b = vitems[curpos.y].nBottom(sby); int h = GetSize().cy; if(t < 0) sby.Set(sby + t); else if(b > h) sby.Set(sby + b - h); } } bool GridCtrl::GoFirstVisible(bool scroll) { if(IsEmpty()) return false; GetCtrlsData(curid.y); SetCursor0(curpos.x < 0 ? firstVisCol : curpos.x, max(firstVisRow, firstRow)); if(scroll && resize_row_mode == 0) sby.Set(vitems[firstRow].nTop(/*fixed_height*/)); if(ctrls) { SetCtrlsData(curid.y); UpdateCtrls(); } return true; } bool GridCtrl::GoBegin(bool scroll) { return Go0(GO_BEGIN, scroll); } bool GridCtrl::GoEnd(bool scroll, bool goleft) { return Go0(GO_END, scroll, goleft); } bool GridCtrl::GoNext(bool scroll) { return Go0(GO_NEXT, scroll); } bool GridCtrl::GoPrev(bool scroll) { return Go0(GO_PREV, scroll); } bool GridCtrl::GoLeft(bool scroll, bool ctrlmode) { return Go0(GO_LEFT, scroll, false, ctrlmode); } bool GridCtrl::GoRight(bool scroll, bool ctrlmode) { return Go0(GO_RIGHT, scroll, false, ctrlmode); } bool GridCtrl::GoPageUp(bool scroll) { return Go0(GO_PAGEUP, scroll); } bool GridCtrl::GoPageDn(bool scroll) { return Go0(GO_PAGEDN, scroll); } Ctrl * GridCtrl::GetCtrl(const Point &p, bool check_visibility, bool relative) { return GetCtrl(p.y, p.x, check_visibility, relative); } Ctrl * GridCtrl::GetCtrl(int r, int c, bool check_visibility, bool relative) { int idx = relative ? fixed_cols + c : hitems[c].id; int idy = vitems[r].id; Ctrl * ctrl = items[idy][idx].ctrl; if(!ctrl) ctrl = edits[idx].ctrl; if(check_visibility && ctrl && !ctrl->IsShown()) ctrl = NULL; return ctrl; } Ctrl * GridCtrl::GetCtrl(int r, int c) { return GetCtrl(r, c, true, true); } Ctrl * GridCtrl::GetCtrl(int c) { return GetCtrl(curpos.y, c, true, true); } bool GridCtrl::IsCtrl(Point &p) { return GetCtrl(p, true); } void GridCtrl::GoTo(int n, bool setcursor, bool scroll) { if(setcursor) SetCursor(n); if(scroll) { Size sz = GetSize(); n += fixed_rows; sby.Set(vitems[n].nTop() + vitems[n].nHeight() / 2 - sz.cy / 2); } } int GridCtrl::GetCount() { return total_rows - fixed_rows; } int GridCtrl::GetFixedCount() { return fixed_rows; } int GridCtrl::GetTotalCount() { return total_rows; } GridCtrl& GridCtrl::SetColsMin(int size) { for(int i = 1; i < total_cols; i++) hitems[i].min = size; return *this; } GridCtrl& GridCtrl::SetColsMax(int size) { for(int i = 0; i < total_rows; i++) hitems[i].max = size; return *this; } void GridCtrl::GotFocus() { LG("GotFocus"); RestoreFocus(); if(valid_cursor) RefreshRow(curpos.y, 0, 0); } void GridCtrl::LostFocus() { LG("Lostfocus"); if(valid_cursor) RefreshRow(curpos.y, 0, 0); } void GridCtrl::ChildGotFocus() { GotFocus(); } void GridCtrl::ChildLostFocus() { LostFocus(); } void GridCtrl::Repaint(bool do_recalc_cols /* = false*/, bool do_recalc_rows /* = false*/) { if(do_recalc_cols) { if(ready) { UpdateCols(true); firstCol = fixed_cols; scrollxdir = 1; } else recalc_cols = true; } if(do_recalc_rows) { if(ready) { UpdateRows(true); firstRow = fixed_rows; scrollydir = 1; } else recalc_rows = true; } if(ready) { doscroll = false; UpdateSizes(); UpdateSb(); UpdateHolder(); UpdateCtrls(); SyncCtrls(); doscroll = true; } Refresh(); } GridCtrl& GridCtrl::ResizeColMode(int m) { resize_col_mode = m; recalc_cols = true; RefreshLayout(); return *this; } GridCtrl& GridCtrl::ResizeRowMode(int m) { resize_row_mode = m; recalc_rows = true; RefreshLayout(); return *this; } void GridCtrl::UpdateSb(bool horz, bool vert) { if(horz) { sbx.SetTotal(resize_col_mode == 0 ? total_width - fixed_width : 0); sbx.SetPage(GetSize().cx - fixed_width); } if(vert) { sby.SetTotal(resize_row_mode == 0 ? total_height - fixed_height : 0); sby.SetPage(GetSize().cy - fixed_height); } } bool GridCtrl::SwitchEdit() { if(!valid_cursor) return false; if(ctrls) EndEdit(true, true); else if(IsRowEditable()) { SetCtrlsData(curid.y); UpdateCtrls(UC_SHOW | UC_FOCUS | UC_CURSOR | UC_CTRLS | UC_GOFIRST); } return true; } bool GridCtrl::StartEdit(int focusmode) { if(!valid_cursor) return false; EndEdit(true); //SetFocus(); //zeby wywolac GotFocus i ustawic zmienna isfocus na true; SetCtrlsData(curid.y); UpdateCtrls(UC_SHOW | UC_GOFIRST | UC_CURSOR | UC_CTRLS); return true; } void GridCtrl::EndEdit(bool accept, bool doall) { if(ctrls) { if(accept) { GetCtrlsData(curid.y, false, doall); //if(th.ClearSorted()) // th.Refresh(); } UpdateCtrls(UC_HIDE | UC_CTRLS); if(!accept && CancelCtrlsData(curid.y) && reject_null_row && (newrow_inserted || newrow_appended)) { newrow_inserted = false; newrow_appended = false; call_whenremoverow = false; DoRemove(); //DoRemove wola EndEdit ale wtedy ctrls jest juz false call_whenremoverow = true; } } } void GridCtrl::Insert0(int row, int cnt /* = 1*/, bool recalc /* = true*/, bool refresh /* = true*/, int size /* = GD_ROW_HEIGHT*/) { int id = vitems[row].id; for(int i = 0; i < total_rows; i++) { if(vitems[i].id >= id) vitems[i].id += cnt; } ItemRect ir; ir.size = size; vitems.Insert(row, ir, cnt); items.InsertN(id, cnt); for(int i = 0; i < cnt; i++) { int nid = id + i; int r = row + i; vitems[r].id = nid; vitems[r].items = &items; items[nid].SetCount(total_cols); UpdateDefaults(nid); } total_rows += cnt; rowidx = row + cnt; firstRow = -1; if(ready && recalc) { RecalcRows(); UpdateSizes(); if(refresh) { UpdateSb(); SyncCtrls(); RefreshFrom(row); } } roworder = true; } bool GridCtrl::Remove0(int row, int cnt /* = 1*/, bool recalc /* = true*/, bool refresh /* = true*/, bool whens /* = true*/) { if(cnt < 0) return false; rowidx = row; int minid = total_rows; bool cancel = true; for(int i = 0; i < cnt; i++) { int id = vitems[rowidx].id; if(whens) { if(call_whenremoverow) { #ifdef LOG_CALLBACKS LGR(2, "WhenRemoveRow()"); LGR(2, Format("[row: %d]", rowidx)); WhenRemoveRow(); #endif } if(cancel_operation) { cancel_operation = false; cancel = false; if(i == cnt - 1) return cancel; else continue; } if(call_whenremoverow) WhenModification(); } for(int j = 0; j < total_rows; j++) { if(vitems[j].id > id) vitems[j].id--; } total_height -= vitems[rowidx].nHeight(); total_rows--; vitems.Remove(rowidx); items.Remove(id); if(rowidx == ctrlid.y) focused_ctrl = NULL; } if(ready && recalc) { RecalcRows(); UpdateSizes(); if(refresh) { UpdateSb(); SyncCtrls(); RefreshFrom(row); } } firstRow = -1; scrollydir = 1; return cancel; } int GridCtrl::Append0(int cnt, int size, bool refresh) { vitems.AddN(cnt); items.AddN(cnt); int j = total_rows; int k = j; int n = j > 0 ? vitems[j - 1].n + (int) vitems[j - 1].hidden : 0; for(int i = 0; i < cnt; i++) { ItemRect &ir = vitems[j]; ir.items = &items; ir.size = ir.nsize = size; if(total_rows > 0) { ir.pos = ir.npos = vitems[j - 1].nBottom(); ir.n = n; } if(size == 0) ir.hidden = true; else { lastVisRow = total_rows + i; if(firstVisRow < 0) firstVisRow = lastVisRow; } items[j].SetCount(total_cols); ir.id = j++; ir.uid = rowuid++; UpdateDefaults(ir.id); } total_rows = j; rowidx = j - 1; total_height += size * cnt; if(refresh && ready) { if(resize_row_mode > 0) UpdateRows(true); UpdateSb(); SyncCtrls(); RefreshFrom(k); } return total_rows - fixed_rows; } void GridCtrl::Duplicate0(int i, int cnt) { } int GridCtrl::Append(int cnt, bool refresh) { return Append0(cnt, GD_ROW_HEIGHT, refresh); } void GridCtrl::Insert(int i, int cnt) { Insert0(fixed_rows + i, cnt); } void GridCtrl::Remove(int i, int cnt) { Remove0(fixed_rows + i, cnt); } void GridCtrl::RemoveFirst(int cnt /* = 1*/) { Remove0(fixed_rows, min(total_rows - fixed_rows, cnt)); } void GridCtrl::RemoveLast(int cnt /* = 1*/) { Remove0(total_rows - cnt, min(total_rows - fixed_rows, cnt)); } void GridCtrl::Duplicate(int i, int cnt) { Duplicate0(fixed_rows + i, cnt); } void GridCtrl::Select(int n, int cnt /* = 1*/) { SelectCount(n + fixed_rows, cnt, true); } void GridCtrl::SelectCount(int i, int cnt, bool sel) { if(cnt <= 0) return; SelectRange(Point(fixed_cols, i), Point(total_cols - 1, i + cnt - 1), sel); } void GridCtrl::SelectRange(int from, int to, bool sel) { SelectRange(Point(0, from), Point(0, to), sel); } void GridCtrl::ShiftSelect(int from, int to) { ShiftSelect(Point(0, from), Point(0, to)); } void GridCtrl::SelectRange(Point from, Point to, bool sel /* = true*/) { Point f, t; if(from.y < to.y) { f = from; t = to; } else { f = to; t = from; } int ymin = f.y; int ymax = t.y; int xmin = f.x; int xmax = t.x; if(xmin > xmax) { int t = xmin; xmin = xmax; xmax = t; } for(int i = ymin; i <= ymax; i++) { ItemRect &ir = vitems[i]; int yid = ir.id; bool is_row_selected = false; bool do_refresh = false; for(int j = fixed_cols; j < total_cols; j++) { int xid = hitems[j].id; Item &it = items[yid][xid]; if(j >= xmin && j <= xmax) { if(it.IsSelect() != sel) { it.Select(sel); do_refresh = true; } if(sel) { is_row_selected = true; selected_items++; } else selected_items--; } else if(it.IsSelect()) is_row_selected = true; } if(!ir.IsSelect()) { if(is_row_selected) selected_rows++; } else if(!is_row_selected) selected_rows--; ir.Select(is_row_selected); if(do_refresh) RefreshRow(i, false, false); } } void GridCtrl::SelectInverse(int from, int to) { int nfrom = min(from, to); int nto = max(from, to); for(int i = nfrom ; i <= nto; i++) { vitems[i].Select(!vitems[i].IsSelect()); if(vitems[i].IsSelect()) selected_rows++; else selected_rows--; RefreshRow(i, 0); } } void GridCtrl::ShiftSelect(Point from, Point to) { Point f, t; if(from.y < to.y) { f = from; t = to; } else { f = to; t = from; } if(select_row) { f.x = fixed_cols; t.x = total_cols; } int ymin = f.y; int ymax = t.y; int xmin = f.x; int xmax = t.x; if(ymin == ymax && xmin > xmax) { int t = xmin; xmin = xmax; xmax = t; } selected_rows = 0; selected_items = 0; for(int i = fixed_rows; i < total_rows; i++) { ItemRect &ir = vitems[i]; int yid = ir.id; bool is_row_selected = false; bool do_refresh = false; if((i >= ymin && i <= ymax)) { for(int j = fixed_cols; j < total_cols; j++) { int xid = hitems[j].id; bool s = true; if(i == ymin && ymin == ymax) s = (j >= xmin && j <= xmax); else if(i == ymin) s = (j >= xmin); else if(i == ymax) s = (j <= xmax); if(items[yid][xid].IsSelect() != s) { items[yid][xid].Select(s); do_refresh = true; } if(s) { is_row_selected = true; selected_items++; } } } else { for(int j = fixed_cols; j < total_cols; j++) if(items[yid][j].IsSelect()) { items[yid][j].Select(false); do_refresh = true; } } if(is_row_selected) selected_rows++; ir.Select(is_row_selected); if(do_refresh) RefreshRow(i, false, false); } } void GridCtrl::DoShiftSelect() { if(!shiftmode) { if(!IsValidCursor(oldcur)) return; shiftpos = oldcur; shiftmode = true; ShiftSelect(oldcur, curpos); } else ShiftSelect(shiftpos, curpos); } void GridCtrl::DoCtrlSelect() { SelectRange(shiftpos, oldcur, false); SelectRange(shiftpos, curpos, true); } bool GridCtrl::IsSelected(int n, bool relative) { int id = vitems[n + (relative ? fixed_rows : 0)].id; return vitems[id].IsSelect() || vitems[id].IsCursor(); } bool GridCtrl::IsSelected(int n, int m, bool relative) { int r = relative ? fixed_rows + n : n; int c = relative ? fixed_cols + m : m; Item &it = GetItem(r, c); return it.IsSelect() || it.IsCursor(); } void GridCtrl::ClearSelection() { LG("Cleared %d", selected_rows); shiftmode = false; if(selected_rows > 0) { for(int i = fixed_rows; i < total_rows; i++) { vitems[i].Select(0); for(int j = fixed_cols; j < total_cols; j++) items[i][j].Select(0); } Refresh(); selected_rows = 0; selected_items = 0; } } void GridCtrl::DoInsertBefore0(bool edit) { if(!valid_cursor) return; EndEdit(); SetItemCursor(curpos, false, false); Insert0(curpos.y); int y = curpos.y; curpos.y = -1; call_whenchangerow = false; SetCursor0(curpos.x, y > total_rows - 1 ? total_rows - 1 : y); call_whenchangerow = true; // Refresh(); newrow_inserted = true; if(edit) StartEdit(); if(!ctrls) WhenInsertRow(); WhenNewRow(); if(!edit) newrow_inserted = false; } void GridCtrl::DoRemove() { if(keep_last_row && GetCount() == 1) return; if(!valid_cursor && selected_rows == 0) return; row_removed = true; EndEdit(); int y = curpos.y; int ocy = curpos.y; if(selected_rows == 0) { Remove0(curpos.y); } else { int removed = 0; int not_removed = 0; int tc = total_rows; minRowSelected = GetMinRowSelected(); maxRowSelected = GetMaxRowSelected(); if(keep_last_row && (maxRowSelected - minRowSelected + 1) == GetCount()) maxRowSelected--; LG("Min:%d, Max:%d", minRowSelected, maxRowSelected); for(int i = minRowSelected; i <= maxRowSelected; i++) { int rid = minRowSelected + not_removed; if(vitems[rid].IsSelect()) { sel_begin = i == minRowSelected; sel_end = i == maxRowSelected; int si = 0; if(vitems[rid].IsSelect()) for(int i = fixed_cols; i < total_cols; i++) if(GetItem(rid, i).IsSelect()) si++; if(Remove0(rid, 1, false, false)) { /* curpos.y tez sie zmienia bo gdy w whenromoverow jest woloanie innego okna to grid traci fokus i wola sie lostfoucs, ktory wymaga poprawnego curpos.y */ if(i == ocy) y = curpos.y = rid - 1; removed++; selected_items -= si; } else not_removed++; } else not_removed++; } selected_rows -= removed; RecalcRows(); SyncCtrls(); Refresh(); } curpos.y = -1; SetCursor0(curpos.x, max(fixed_rows, min(total_rows - 1, y))); row_removed = false; } void GridCtrl::DoAppend0(bool edit) { EndEdit(); Append0(); call_whenchangerow = false; GoEnd(true, true); call_whenchangerow = true; newrow_appended = true; if(edit) { StartEdit(); GoCursorLeftRight(); } if(!ctrls) WhenInsertRow(); WhenNewRow(); if(!edit) newrow_appended = false; } void GridCtrl::DoAppend() { DoAppend0(1); } void GridCtrl::DoAppendNoEdit() { DoAppend0(0); } void GridCtrl::DoInsertBefore() { DoInsertBefore0(1); } void GridCtrl::DoInsertBeforeNoEdit() { DoInsertBefore0(0); } void GridCtrl::DoInsertAfter() { } void GridCtrl::DoDuplicate() { } void GridCtrl::DoEdit() { StartEdit(); } void GridCtrl::DoEndEdit() { EndEdit(); } void GridCtrl::DoSwapUp() { SwapUp(); } void GridCtrl::DoSwapDown() { SwapDown(); } void GridCtrl::DoGoBegin() { GoBegin(); } void GridCtrl::DoGoEnd() { GoEnd(); } void GridCtrl::DoGoNext() { GoNext(); } void GridCtrl::DoGoPrev() { GoPrev(); } void GridCtrl::DoGoLeft() { GoLeft(); } void GridCtrl::DoGoRight() { GoRight(); } void GridCtrl::DoGoPageUp() { GoPageUp(); } void GridCtrl::DoGoPageDn() { GoPageDn(); } void GridCtrl::DoSelectAll() { SelectCount(fixed_rows, total_rows - fixed_rows); } GridCtrl& GridCtrl::ShowRow(int n, bool refresh) { if(!vitems[n].hidden) return *this; vitems[n].hidden = false; vitems[n].size = vitems[n].tsize; vitems[n].tsize = vitems[n].tnsize; if(refresh) Repaint(false, true); return *this; } GridCtrl& GridCtrl::HideRow(int n, bool refresh) { if(vitems[n].hidden) return *this; vitems[n].hidden = true; vitems[n].tsize = vitems[n].size; vitems[n].tnsize = vitems[n].nsize; vitems[n].size = 0; if(refresh) Repaint(false, true); return *this; } GridCtrl& GridCtrl::ShowColumn(int n, bool refresh) { if(!hitems[n].hidden) return *this; hitems[n].hidden = false; hitems[n].size = hitems[n].tsize; hitems[n].nsize = hitems[n].tnsize; if(refresh) Repaint(true, false); return *this; } GridCtrl& GridCtrl::HideColumn(int n, bool refresh) { if(hitems[n].hidden) return *this; hitems[n].hidden = true; hitems[n].tsize = hitems[n].size; hitems[n].tnsize = hitems[n].nsize; hitems[n].size = 0; if(refresh) Repaint(true, false); return *this; } void GridCtrl::HideRows(bool repaint) { bool change = false; for(int i = fixed_rows; i < total_rows; i++) if(!vitems[i].hidden) { change = true; vitems[i].hidden = true; vitems[i].tsize = vitems[i].size; vitems[i].tprop = vitems[i].prop; } if(change || repaint) Repaint(false, true); } void GridCtrl::ShowRows(bool repaint) { bool change = false; for(int i = fixed_rows; i < total_rows; i++) if(vitems[i].hidden) { change = true; vitems[i].hidden = false; vitems[i].size = vitems[i].tsize; vitems[i].prop = vitems[i].tprop; } if(change || repaint) Repaint(false, true); } void GridCtrl::MenuHideColumn(int n) { if(hitems[n].hidden) ShowColumn(n); else HideColumn(n); } int GridCtrl::ShowMatchedRows(const WString &f) { if(f.IsEmpty()) { if(search_highlight) ClearFound(); else { ShowRows(true); } return 0; } bool change = false; int rows = 0; int s, e; for(int i = fixed_rows; i < total_rows; i++) { bool match = false; int idv = vitems[i].id; for(int j = fixed_cols; j < total_cols; j++) { int idh = hitems[j].id; Item &it = items[idv][idh]; it.Found(false); it.fs = it.fe = 0; if(hitems[j].hidden) continue; if(Match(f, (WString) GetConvertedColumn(j, it.val), s, e)) { match = true; it.Found(search_highlight); it.fs = s; it.fe = e; if(!search_highlight) break; } } if(match) { rows++; if(search_hide && vitems[i].hidden) { vitems[i].hidden = false; vitems[i].size = vitems[i].tsize; change = true; } } else if(search_hide && !vitems[i].hidden) { vitems[i].hidden = true; vitems[i].tsize = vitems[i].size; vitems[i].size = 0; change = true; } } if(change || (search_highlight && rows > 0)) { LG("Repaint %d", search_hide); Repaint(false, search_hide); } LG("Matched rows %d", rows); return rows; } void GridCtrl::ClearFound(bool showrows) { for(int i = 0; i < total_rows; i++) for(int j = 0; j < total_cols; j++) { items[i][j].Found(false); items[i][j].fs = items[i][j].fe = 0; } if(showrows) ShowRows(true); } bool GridCtrl::Match(const WString &f, const WString &s, int &fs, int &fe) { int i = 0; int fl = f.GetLength(); int sl = s.GetLength(); if(fl > sl) return false; for(int j = find_offset; j < sl; j++) { bool match; if(search_case) match = f[i] == s[j]; else match = ToUpper(f[i]) == ToUpper(s[j]); if(match) { if(++i == fl) { fs = j + 1 - fl; fe = j; return true; } } else i = 0; } return false; } void GridCtrl::DoFind() { UpdateCtrls(UC_CHECK_VIS | UC_HIDE); ShowMatchedRows((WString) ~findstring); } int GridCtrl::GetMinRowSelected() { int i = fixed_rows; while(i < total_rows && !vitems[i].IsSelect()) i++; return i > total_rows - 1 ? -1 : i; } int GridCtrl::GetMaxRowSelected() { int i = total_rows - 1; while(i >= fixed_rows && !vitems[i].IsSelect()) i--; return i < fixed_rows ? -1 : i; } void GridCtrl::CloseGrid() { GetTopWindow()->Close(); } void GridCtrl::UpdateVisColRow(bool col) { if(col) { firstVisCol = fixed_cols; lastVisCol = total_cols - 1; for(int i = fixed_cols; i < total_cols; i++) if(!hitems[i].hidden) { firstVisCol = i; break; } for(int i = total_cols - 1; i >= fixed_cols; i--) if(!hitems[i].hidden) { lastVisCol = i; break; } } else { firstVisRow = fixed_rows; lastVisRow = total_rows - 1; for(int i = fixed_rows; i < total_rows; i++) if(!vitems[i].hidden) { firstVisRow = i; break; } for(int i = total_rows - 1; i >= fixed_rows; i--) if(!vitems[i].hidden) { lastVisRow = i; break; } } } Point GridCtrl::GetBarOffset() { Size bsz = bar.GetSize(); return Point(bar.GetAlign() == BarCtrl::BAR_LEFT ? bsz.cx : 0, bar.GetAlign() == BarCtrl::BAR_TOP ? bsz.cy : 0); } void GridCtrl::Debug(int n) { if(n == 0) { LG("---- DEBUG 0 ----------"); LG("firstVisCol %d", firstVisCol); LG("lastVisCol %d", lastVisCol); LG("firstVisRow %d", firstVisRow); LG("lastVisRow %d", lastVisRow); LG("firstCol %d", firstCol); LG("firstRow %d", firstRow); LG("lastCol %d", lastCol); LG("lastRow %d", lastRow); LG("total_cols %d", total_cols); LG("total_rows %d", total_rows); LG("curpos %d, %d", curpos.x, curpos.y); LG("sbPos %d, %d", sbx.Get(), sby.Get()); LG("sbTotal %d, %d", sbx.GetTotal(), sby.GetTotal()); LG("sbPage %d, %d", sbx.GetPage(), sby.GetPage()); LG("Size %d, %d", GetSize().cx, GetSize().cy); LG("fixed_width %d", fixed_width); LG("fixed_height %d", fixed_height); LG("total_width %d", total_width); LG("total_height %d", total_height); LG("scrollxdir %d", scrollxdir); LG("scrollydir %d", scrollydir); LG("---- END --------------"); } if(n == 1) { LG("---- DEBUG 1 ----------"); for(int i = 0; i < total_cols; i++) { LG("Col %d h:%d p:%d s:%d", i, hitems[i].hidden, hitems[i].npos, hitems[i].nsize); //LG("ismin %d ismax %d", hitems[i].ismin, hitems[i].ismax); } LG("---- END --------------"); } if(n == 2) { LG("---- DEBUG 2 ----------"); for(int i = 0; i < total_rows; i++) { LG("Row %d p:%d s:%d", i, vitems[i].npos, vitems[i].nsize); } LG("---- END --------------"); } if(n == 3) { Point p = GetCtrlPos(focused_ctrl); LG(2, "Focused %x (%d, %d)", focused_ctrl, p.x, p.y); /* LG(2, "Value from col 1 : %s", AsString(GetConvertedColumn(fixed_cols + 0, Get(0)))); LG(2, "Value from col 2 : %s", AsString(GetConvertedColumn(fixed_cols + 1, Get(1)))); LG(2, "Value from col 3 : %s", AsString(GetConvertedColumn(fixed_cols + 2, Get(2)))); */ } } void GridCtrl::Serialize(Stream &s) { if(s.IsStoring()) { s % total_cols; for(int i = 1; i < total_cols; i++) { int id = hitems[i].id; //s % String(aliases[id]); s % id; s % hitems[id]; } } else { int tc; s % tc; for(int i = 1; i < tc; i++) { //String alias; //s % alias; //int n = aliases.Find(alias); int id; s % id; //PromptOK(Format("items: %d, id: %d", hitems.GetCount(), id)); //if(id >= 0 && id < total_cols) s % hitems[id]; } } } void GridCtrl::JoinCells(int left, int top, int right, int bottom, bool relative) { int fc = relative ? fixed_cols : 0; int fr = relative ? fixed_rows : 0; left += fc; top += fr; right += fc; bottom += fr; for(int i = top; i <= bottom; ++i) { vitems[i].join++; for(int j = left; j <= right; ++j) { Item &it = items[i][j]; it.idx = hitems[left].id; it.idy = vitems[top].id; it.cx = right - left; it.cy = bottom - top; it.group = join_group; it.isjoined = true; if(i == top) hitems[j].join++; } } join_group++; } void GridCtrl::JoinFixedCells(int left, int top, int right, int bottom) { JoinCells(left, top, right, bottom, false); } /*----------------------------------------------------------------------------------------*/ void GridPopUp::Paint(Draw &w) { Size sz = GetSize(); DrawBorder(w, sz, BlackBorder); w.DrawRect(1, 1, sz.cx - 2, sz.cy - 2, Color(240, 240, 240)); Size tsz = GetTextSize(text, StdFont()); w.DrawText((sz.cx - tsz.cx) / 2, (sz.cy - tsz.cy) / 2, text); } void GridPopUp::PopUp(Ctrl *owner, int x, int y, int width, int height) { SetRect(Rect(x, y, x + width, y + height)); if(open) return; Ctrl::PopUp(owner, true, true, GUI_DropShadows()); SetAlpha(230); open = true; } void GridPopUp::Close() { open = false; Ctrl::Close(); } void GridCtrl::UpdateHighlighting(int mode, Point p) { if(resize_paint_mode < 1 && (resizeCol || resizeRow)) return; bool refresh = false; if(mode == GS_UP) { if((p.x < 0 || p.x > GetSize().cx - 1) && hcol >= 0 && hitems[hcol].IsHighlight()) { hitems[hcol].Highlight(0); refresh = true; hcol = -1; } } else if(mode == GS_MOVE) { int col = GetMouseCol(p, true, true, false); int row = hrow = GetMouseRow(p, false, true, true); if(hcol >= 0 && (hcol != col || row != 0) && hitems[hcol].IsHighlight()) { hitems[hcol].Highlight(0); refresh = true; } if(col >= 0 && row == 0 && !hitems[col].IsHighlight()) { hitems[col].Highlight(1); hcol = col; refresh = true; } } else if(mode == GS_POPUP) { if(hcol >= 0) hitems[hcol].Highlight(0); refresh = true; hcol = moveCol; } else if(mode == GS_BORDER) { if(hcol >= 0 && hitems[hcol].IsHighlight()) { hitems[hcol].Highlight(0); refresh = true; } } if(refresh) RefreshRow(0, 0, 1); } /*----------------------------------------------------------------------------------------*/ void GridPopUpHeader::Paint(Draw &w) { Size sz = GetSize(); dword style = chameleon ? GD::CHAMELEON : 0; display->PaintFixed(w, 1, 1, 0, 0, sz.cx, sz.cy, val, style, false, true, sortmode, sortcol, sortcnt, true); } void GridPopUpHeader::PopUp(Ctrl *owner, int x, int y, int width, int height) { SetRect(Rect(x, y, x + width, y + height)); if(open) return; Ctrl::PopUp(owner, true, true, GUI_DropShadows()); SetAlpha(200); open = true; } void GridPopUpHeader::Close() { open = false; Ctrl::Close(); } END_UPP_NAMESPACE