#include "StdAfx.h" #include "T2DlgItemHScr.h" #include "T2DlgItemTable.h" #include "T2DlgItemVScr.h" #include T2DlgItemTable::T2DlgItemTable(T2TowerDoc* towerDoc, T2ImageObj* imageObj, CPalette* palette) : T2DlgItem(towerDoc, imageObj, palette) { InitTable(0, 0, 16, 100, 0); } T2DlgItemTable::T2DlgItemTable(int rows, int cols, int rowHeight, int colWidth, int cellDataSize, T2TowerDoc* towerDoc, T2ImageObj* imageObj, CPalette* palette) : T2DlgItem(towerDoc, imageObj, palette) { InitTable(rows, cols, rowHeight, colWidth, cellDataSize); } /*virtual*/ T2DlgItemTable::~T2DlgItemTable() { delete mCellData; delete mMultiSelectedCells; } void T2DlgItemTable::InitTable(int rows, int cols, int rowHeight, int colWidth, int cellDataSize) { mVScrollerStyle = ShowIfNeeded; mHScrollerStyle = ShowIfNeeded; mScrollOffset.x = 0; mScrollOffset.y = 0; mShowHScroller = false; mShowVScroller = false; mResizeCount = 0; mRowHeight = rowHeight; mColWidth = colWidth; mCellData = NULL; mMultiSelectedCells = NULL; mSelectedCell.col = 0; mSelectedCell.row = 0; mClickedCell.col = 1; mClickedCell.row = 1; mCols = 0; mRows = 0; InsertRows(rows, 0, NULL); InsertCols(cols, 0, NULL); SetCellDataSize(cellDataSize); } /*virtual*/ BOOL T2DlgItemTable::Create(const char* windowName, DWORD style, const RECT& rect, CWnd* parentWnd, UINT nId) { return T2DlgItem::Create(windowName, style | WS_CLIPCHILDREN, rect, parentWnd, nId); } /*virtual*/ BOOL T2DlgItemTable::OnT2DlgItemCreate(CREATESTRUCT*) { CRect rect; GetClientRect(rect); rect.left = rect.right - 16; mVScroller = new T2DlgItemVScr(mTowerDoc, mImageObj, mPalette); mVScroller->Create("", WS_CHILD, rect, this, 100); GetClientRect(rect); rect.top = rect.bottom - 16; mHScroller = new T2DlgItemHScr(mTowerDoc, mImageObj, mPalette); mHScroller->Create("", WS_CHILD, rect, this, 101); return false; } void T2DlgItemTable::GetTableSize(UINT& outRows, UINT& outCols) const { outRows = mRows; outCols = mCols; } BOOL T2DlgItemTable::IsMultiSelectable() const { return (mMultiSelectedCells != NULL); } void T2DlgItemTable::EnableMultiSelect(BOOL enabled) { if (IsMultiSelectable()) { if (!enabled) { delete mMultiSelectedCells; mMultiSelectedCells = NULL; } } else { if (enabled) { mMultiSelectedCells = new LArray(sizeof(TableCellT)); } } } /*virtual*/ void T2DlgItemTable::ClearSelectedCells() { if (IsValidCell(mSelectedCell)) InvalidateCell(mSelectedCell); mSelectedCell.col = 0; mSelectedCell.row = 0; if (IsMultiSelectable()) { LArrayIterator iter(*mMultiSelectedCells); TableCellT iterCell; while (iter.Next(&iterCell)) InvalidateCell(iterCell); mMultiSelectedCells->RemoveItemsAt(mMultiSelectedCells->GetCount(), 1); } } BOOL T2DlgItemTable::IsValidCell(const TableCellT& cell) const { return (cell.row > 0) && (cell.row <= mRows) && (cell.col > 0) && (cell.col <= mCols); } BOOL T2DlgItemTable::EqualCell(const TableCellT& a, const TableCellT& b) const { return (a.row == b.row) && (a.col == b.col); } /*virtual*/ void T2DlgItemTable::InsertRows(int count, UINT where, void* data) { if (mCellData) mCellData->InsertItemsAt(count * mCols, where * mCols + 1, data); mRows += count; ClearSelectedCells(); Resized(); } /*virtual*/ void T2DlgItemTable::InsertCols(int count, UINT where, void* data) { if (mCellData) { UINT index = where + 1; for (UINT i = 1; i <= mRows; i++) { mCellData->InsertItemsAt(count, index, data); index += (mCols + count); } } mCols += count; ClearSelectedCells(); Resized(); } /*virtual*/ void T2DlgItemTable::RemoveRows(int count, UINT where) { if (mCellData) mCellData->RemoveItemsAt(count * mCols, (where - 1) * mCols + 1); mRows -= count; ClearSelectedCells(); Resized(); } /*virtual*/ void T2DlgItemTable::RemoveCols(int count, UINT where) { if (mCellData) { UINT index = where; for (UINT n = 1; n <= mRows; n++) { mCellData->RemoveItemsAt(count, index); index += (mCols - count); } } mCols -= count; ClearSelectedCells(); Resized(); } /*virtual*/ void T2DlgItemTable::SetRowHeight(int height, unsigned int, unsigned int) { mRowHeight = height; } /*virtual*/ void T2DlgItemTable::SetColWidth(int width, unsigned int, unsigned int) { mColWidth = width; } void T2DlgItemTable::SetCellDataSize(int size) { if (!mCellData && size > 0) { mCellData = new LArray(size); mCellData->InsertItemsAt(mRows * mCols, 1, NULL); } } /*virtual*/ void T2DlgItemTable::SetCellData(const TableCellT& cell, void* data) { if (mCellData) mCellData->AssignItemsAt(1, FetchCellDataIndex(cell), data); } /*virtual*/ void T2DlgItemTable::GetCellData(const TableCellT& cell, void* data) { if (mCellData) mCellData->FetchItemAt(FetchCellDataIndex(cell), data); } /*virtual*/ BOOL T2DlgItemTable::FetchLocalCellFrame(const TableCellT& cell, RECT& rect) { rect.left = (cell.col - 1) * mColWidth; rect.top = (cell.row - 1) * mRowHeight; ViewToClient((POINT *) &rect.left, 1); rect.right = rect.left + mColWidth; rect.bottom = rect.top + mRowHeight; return true; } /*virtual*/ void T2DlgItemTable::FetchCellHitBy(const POINT& pt, TableCellT& cell) { cell.row = pt.y / mRowHeight + 1; cell.col = pt.x / mColWidth + 1; } int T2DlgItemTable::FetchCellDataIndex(const TableCellT& cell) { return (cell.row - 1) * mCols + cell.col; } TableCellIterator::TableCellIterator(const TableCellT& start, const TableCellT& end) { mLastRow = (start.row > end.row) ? start.row : end.row; mFirstCol = (start.col < end.col) ? start.col : end.col; mLastCol = (start.col > end.col) ? start.col : end.col; mCurrentRow = ((start.row < end.row) ? start.row : end.row) - 1; mCurrentCol = mLastCol; } BOOL TableCellIterator::Next(TableCellT& newCurrent) { mCurrentCol++; if (mCurrentCol > mLastCol) { mCurrentRow++; mCurrentCol = mFirstCol; } BOOL isValid = (mCurrentRow <= mLastRow); if (isValid) { newCurrent.row = mCurrentRow; newCurrent.col = mCurrentCol; } return isValid; } /*virtual*/ void T2DlgItemTable::ClickSelf(const POINT& pt, UINT nFlags) { UINT keyFlags = IsMultiSelectable() ? (nFlags & (MK_SHIFT | MK_CONTROL)) : 0; POINT ptView = pt; ClientToView(&ptView, 1); TableCellT cellClicked; FetchCellHitBy(ptView, cellClicked); if (IsValidCell(cellClicked)) { switch (keyFlags) { case MK_SHIFT: ClearSelectedCells(); case (MK_SHIFT | MK_CONTROL): { TableCellIterator cellIterator(mClickedCell, cellClicked); TableCellT cell; while (cellIterator.Next(cell)) SelectCell(cell); } break; case MK_CONTROL: ToggleCell(cellClicked); mClickedCell = cellClicked; break; default: ClickCell(cellClicked, pt); mClickedCell = cellClicked; break; } } } /*virtual*/ BOOL T2DlgItemTable::OnT2DlgItemEraseBkgnd(CDC* dc) { #pragma var_order(clipRect, bottomRightCell, bottomRight, pen, save, topLeft, topLeftCell, clientRect, i) int save = dc->SaveDC(); dc->SelectPalette(mPalette, false); dc->RealizePalette(); CPen pen; pen.CreatePen(PS_SOLID, 0, PALETTERGB(255, 255, 255)); if (mShowHScroller && mShowVScroller) { CRect insideRect; GetClientRect(insideRect); insideRect.left = insideRect.right - 16; insideRect.top = insideRect.bottom - 16; dc->SelectObject(pen); dc->MoveTo(insideRect.right - 1, insideRect.top); dc->LineTo(insideRect.right - 1, insideRect.bottom - 1); dc->LineTo(insideRect.left, insideRect.bottom - 1); insideRect.right -= 1; insideRect.bottom -= 1; dc->FillSolidRect(&insideRect, PALETTERGB(204, 204, 204)); } CRect clipRect; dc->GetClipBox(clipRect); CRect clientRect; GetClientRect(clientRect); clientRect.right -= mShowVScroller ? 16 : 0; clientRect.bottom -= mShowHScroller ? 16 : 0; clipRect.bottom = min(clipRect.bottom, clientRect.bottom); clipRect.right = min(clipRect.right, clientRect.right); dc->IntersectClipRect(&clientRect); CPoint topLeft; topLeft = clipRect.TopLeft(); ClientToView(&topLeft, 1); TableCellT topLeftCell; FetchCellHitBy(topLeft, topLeftCell); if (topLeftCell.row < 1) topLeftCell.row = 1; if (topLeftCell.col < 1) topLeftCell.col = 1; CPoint bottomRight = clipRect.BottomRight(); ClientToView(&bottomRight, 1); TableCellT bottomRightCell; FetchCellHitBy(bottomRight, bottomRightCell); if (bottomRightCell.row > mRows) bottomRightCell.row = mRows; if (bottomRightCell.col > mCols) bottomRightCell.col = mCols; TableCellT i; for (i.row = topLeftCell.row; i.row <= bottomRightCell.row; i.row++) { for (i.col = topLeftCell.col; i.col <= bottomRightCell.col; i.col++) { DrawCell(dc, i); } } if (IsMultiSelectable()) { LArrayIterator iter(*mMultiSelectedCells); HiliteCells(dc, iter); } else { HiliteCell(dc, mSelectedCell); } dc->RestoreDC(save); return true; } /*virtual*/ void T2DlgItemTable::HiliteCell(CDC* dc, const TableCellT& cell) { RECT cellFrameRect; if (IsValidCell(cell) && FetchLocalCellFrame(cell, cellFrameRect)) dc->InvertRect(&cellFrameRect); } /*virtual*/ void T2DlgItemTable::UnhiliteCell(CDC* dc, const TableCellT& cell) { RECT cellFrameRect; if (IsValidCell(cell) && FetchLocalCellFrame(cell, cellFrameRect)) dc->InvertRect(&cellFrameRect); } /*virtual*/ void T2DlgItemTable::HiliteCells(CDC* dc, LArrayIterator& iterator) { TableCellT cell; while (iterator.Next(&cell)) HiliteCell(dc, cell); } /*virtual*/ void T2DlgItemTable::UnhiliteCells(CDC* dc, LArrayIterator& iterator) { TableCellT cell; while (iterator.Next(&cell)) UnhiliteCell(dc, cell); } /*virtual*/ void T2DlgItemTable::ClickCell(const TableCellT& cell, const POINT&) { ClearSelectedCells(); SelectCell(cell); } /*virtual*/ void T2DlgItemTable::DrawCell(CDC* dc, const TableCellT& cell) { CRect rect; if (FetchLocalCellFrame(cell, rect)) { int save = dc->SaveDC(); CBrush brush; brush.CreateSolidBrush(PALETTEINDEX(0)); CPen pen0; pen0.CreatePen(PS_SOLID, 0, PALETTEINDEX(255)); dc->SelectObject(&brush); dc->SelectObject(&pen0); dc->SetBkMode(TRANSPARENT); dc->Rectangle(rect); CString text; text.Format("%d,%d", cell.row, cell.col); dc->DrawText(text, rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE | DT_NOPREFIX); dc->RestoreDC(save); } } /*virtual*/ void T2DlgItemTable::SelectCell(const TableCellT& cell) { if (IsMultiSelectable()) { if (!IsSelected(cell)) { mSelectedCell = cell; InvalidateCell(mSelectedCell); mMultiSelectedCells->Add(&cell); } } else { if (!EqualCell(cell, mSelectedCell)) { InvalidateCell(mSelectedCell); mSelectedCell = cell; InvalidateCell(mSelectedCell); Notify(0, &mSelectedCell); } } } /*virtual*/ void T2DlgItemTable::ToggleCell(const TableCellT& cell) { if (IsSelected(cell)) { mMultiSelectedCells->Remove(&cell); if (EqualCell(cell, mSelectedCell)) { mSelectedCell.col = 0; mSelectedCell.row = 0; mMultiSelectedCells->FetchItemAt(1, &mSelectedCell); } } else { mMultiSelectedCells->Add(&cell); } InvalidateCell(cell); } BOOL T2DlgItemTable::IsSelected(const TableCellT& cell) const { return IsMultiSelectable() ? (mMultiSelectedCells->FetchIndexOf(&cell) != 0) : EqualCell(mSelectedCell, cell); } void T2DlgItemTable::GetSelectedCell(TableCellT& cell) const { cell = mSelectedCell; } void T2DlgItemTable::InvalidateCell(const TableCellT& cell) { CRect rect; if (FetchLocalCellFrame(cell, rect)) InvalidateRect(rect); } BOOL T2DlgItemTable::Resized() { if (!m_hWnd) return false; BOOL changed = false; mResizeCount++; CRect clientRect; GetClientRect(clientRect); clientRect.right -= mShowVScroller ? 16 : 0; clientRect.bottom -= mShowHScroller ? 16 : 0; CRect interiorRect; interiorRect.left = 0; interiorRect.top = 0; interiorRect.right = mColWidth * mCols; interiorRect.bottom = mRowHeight * mRows; if ( (mHScrollerStyle == ShowIfNeeded && interiorRect.Width() > clientRect.Width()) || mHScrollerStyle == AlwaysShow ) { if (!mShowHScroller && !changed) { mShowHScroller = true; changed = true; } } if ( (mHScrollerStyle == ShowIfNeeded && interiorRect.Width() <= clientRect.Width()) || (mHScrollerStyle != ShowIfNeeded && mHScrollerStyle != AlwaysShow) ) { if (mShowHScroller && !changed) { mShowHScroller = false; changed = true; } } if ( (mVScrollerStyle == ShowIfNeeded && interiorRect.Height() > clientRect.Height()) || mVScrollerStyle == AlwaysShow ) { if (!mShowVScroller && !changed) { mShowVScroller = true; changed = true; } } if ( (mVScrollerStyle == ShowIfNeeded && interiorRect.Height() <= clientRect.Height()) || (mVScrollerStyle != ShowIfNeeded && mVScrollerStyle != AlwaysShow) ) { if (mShowVScroller && !changed) { mShowVScroller = false; changed = true; } } if (changed) Resized(); mResizeCount--; GetClientRect(clientRect); clientRect.right -= mShowVScroller ? 16 : 0; clientRect.bottom -= mShowHScroller ? 16 : 0; if (mHScrollerStyle != AttachedNoUpdates) { mHScroller->SetPage(clientRect.Width() / ((mColWidth > 0) ? mColWidth : 1)); mHScroller->SetRange(mCols); } if (mVScrollerStyle != AttachedNoUpdates) { mVScroller->SetPage(clientRect.Height() / ((mRowHeight > 0) ? mRowHeight : 1)); mVScroller->SetRange(mRows); } if (IsOwnHScroller()) mHScroller->SetWindowPos(NULL, 0, 0, clientRect.Width(), 16, SWP_NOMOVE | SWP_NOZORDER); if (IsOwnVScroller()) mVScroller->SetWindowPos(NULL, 0, 0, 16, clientRect.Height(), SWP_NOMOVE | SWP_NOZORDER); if (mResizeCount == 0 && changed) { if ((mHScrollerStyle == ShowIfNeeded && !mShowHScroller) || (mHScrollerStyle == AlwaysHide)) mHScroller->ShowWindow(SW_HIDE); if ((mVScrollerStyle == ShowIfNeeded && !mShowVScroller) || (mVScrollerStyle == AlwaysHide)) mVScroller->ShowWindow(SW_HIDE); if ((mHScrollerStyle == ShowIfNeeded && mShowHScroller) || (mHScrollerStyle == AlwaysShow)) mHScroller->ShowWindow(SW_SHOW); if ((mVScrollerStyle == ShowIfNeeded && mShowVScroller) || (mVScrollerStyle == AlwaysShow)) mVScroller->ShowWindow(SW_SHOW); } return changed; } void T2DlgItemTable::ViewToClient(POINT* pt, int n) { for (int i = 0; i < n; i++) { pt->x -= mScrollOffset.x; pt->y -= mScrollOffset.y; pt++; } } void T2DlgItemTable::ClientToView(POINT* pt, int n) { for (int i = 0; i < n; i++) { pt->x += mScrollOffset.x; pt->y += mScrollOffset.y; pt++; } } /*virtual*/ void T2DlgItemTable::OnT2DlgItemLButtonDown(UINT nFlags, CPoint pt) { ClickSelf(pt, nFlags); } /*virtual*/ void T2DlgItemTable::OnT2DlgItemLButtonUp(UINT, CPoint) { } /*virtual*/ void T2DlgItemTable::OnT2DlgItemMouseMove(UINT, CPoint) { } /*virtual*/ BOOL T2DlgItemTable::OnCommand(UINT, long) { DoScroll(); return true; } /*virtual*/ void T2DlgItemTable::OnT2Size(unsigned int, int, int) { Resized(); } void T2DlgItemTable::DoScroll() { POINT prevOffset = mScrollOffset; mScrollOffset.x = mHScroller->GetValue() * ((mColWidth > 0) ? mColWidth : 1); mScrollOffset.y = mVScroller->GetValue() * ((mRowHeight > 0) ? mRowHeight : 1); CRect clientRect; GetClientRect(clientRect); clientRect.right -= mShowVScroller ? 16 : 0; clientRect.bottom -= mShowHScroller ? 16 : 0; ScrollWindowEx(prevOffset.x - mScrollOffset.x, prevOffset.y - mScrollOffset.y, &clientRect, NULL, NULL, NULL, SW_INVALIDATE | SW_ERASE); } /*virtual*/ void T2DlgItemTable::ListenToMessage(unsigned int msg, void*) { if ( (!IsOwnHScroller() && msg == mHScroller->GetDlgCtrlID()) || (!IsOwnVScroller() && msg == mVScroller->GetDlgCtrlID()) ) DoScroll(); } T2DlgItemHScr* T2DlgItemTable::GetHScroller() const { return mHScroller; } T2DlgItemVScr* T2DlgItemTable::GetVScroller() const { return mVScroller; } void T2DlgItemTable::AttachHScroller(T2DlgItemHScr* scroller, BOOL flag) { if (scroller) { if (IsOwnHScroller()) delete mHScroller; mHScroller = scroller; mHScrollerStyle = flag ? AttachedNoUpdates : Attached; mHScroller->AddListener(this); Resized(); } } void T2DlgItemTable::AttachVScroller(T2DlgItemVScr* scroller, BOOL flag) { if (scroller) { if (IsOwnVScroller()) delete mVScroller; mVScroller = scroller; mVScrollerStyle = flag ? AttachedNoUpdates : Attached; mVScroller->AddListener(this); Resized(); } } void T2DlgItemTable::SetHScrollerStyle(ScrollerStyle style) { switch (style) { case ShowIfNeeded: case AlwaysHide: case AlwaysShow: if (IsOwnHScroller() && mHScrollerStyle != style) { mHScrollerStyle = style; Resized(); } } } void T2DlgItemTable::SetVScrollerStyle(ScrollerStyle style) { switch (style) { case ShowIfNeeded: case AlwaysHide: case AlwaysShow: if (IsOwnVScroller() && mVScrollerStyle != style) { mVScrollerStyle = style; Resized(); } } } BOOL T2DlgItemTable::IsOwnHScroller() const { BOOL result = false; switch (mHScrollerStyle) { case ShowIfNeeded: case AlwaysHide: case AlwaysShow: result = true; } return result; } BOOL T2DlgItemTable::IsOwnVScroller() const { BOOL result = false; switch (mVScrollerStyle) { case ShowIfNeeded: case AlwaysHide: case AlwaysShow: result = true; } return result; }