// This file is part of VSTGUI. It is subject to the license terms // in the LICENSE file found in the top-level directory of this // distribution and at http://github.com/steinbergmedia/vstgui/LICENSE #include "cdatabrowser.h" #include "vstkeycode.h" #include "cframe.h" #include "controls/ctextedit.h" #include "controls/cscrollbar.h" #include "ifocusdrawing.h" #include "cgraphicspath.h" #include "cdrawcontext.h" #include "idatabrowserdelegate.h" #include #include namespace VSTGUI { /// @cond ignore //----------------------------------------------------------------------------------------------- class CDataBrowserView : public CView, public IFocusDrawing, public IDropTarget //----------------------------------------------------------------------------------------------- { public: CDataBrowserView (const CRect& size, IDataBrowserDelegate* db, CDataBrowser* browser); void draw (CDrawContext* context) override; void drawRect (CDrawContext* context, const CRect& updateRect) override; CMouseEventResult onMouseDown (CPoint &where, const CButtonState& buttons) override; CMouseEventResult onMouseMoved (CPoint &where, const CButtonState& buttons) override; CMouseEventResult onMouseUp (CPoint &where, const CButtonState& buttons) override; CMouseEventResult onMouseExited (CPoint &where, const CButtonState& buttons) override; SharedPointer getDropTarget () override { return this; } DragOperation onDragEnter (DragEventData data) override; DragOperation onDragMove (DragEventData data) override; void onDragLeave (DragEventData data) override; bool onDrop (DragEventData data) override; void onKeyboardEvent (KeyboardEvent& keyCode) override; CRect getRowBounds (int32_t row); void invalidateRow (int32_t row); bool getCell (const CPoint& where, CDataBrowser::Cell& cell); bool drawFocusOnTop () override; bool getFocusPath (CGraphicsPath& outPath) override; protected: IDataBrowserDelegate* db; CDataBrowser* browser; }; //----------------------------------------------------------------------------------------------- class CDataBrowserHeader : public CView //----------------------------------------------------------------------------------------------- { public: CDataBrowserHeader (const CRect& size, IDataBrowserDelegate* db, CDataBrowser* browser); void draw (CDrawContext* context) override; void drawRect (CDrawContext* context, const CRect& updateRect) override; CMouseEventResult onMouseDown (CPoint &where, const CButtonState& buttons) override; CMouseEventResult onMouseMoved (CPoint &where, const CButtonState& buttons) override; CMouseEventResult onMouseUp (CPoint &where, const CButtonState& buttons) override; CMouseEventResult onMouseExited (CPoint &where, const CButtonState& buttons) override; protected: int32_t getColumnAtPoint (CPoint& where); IDataBrowserDelegate* db; CDataBrowser* browser; CPoint startMousePoint; int32_t mouseColumn {0}; CCoord startWidth {0}; }; /// @endcond //----------------------------------------------------------------------------------------------- /** * @param size size of data browser * @param db data browser interface. If db is inheritated from CBaseObject it will be remembered and released if data browser is destroyed * @param style data browser and scroll view style see #CDataBrowserStyle and #CScrollViewStyle * @param scrollbarWidth width of scrollbars * @param pBackground background bitmap */ CDataBrowser::CDataBrowser (const CRect& size, IDataBrowserDelegate* db, int32_t style, CCoord scrollbarWidth, CBitmap* pBackground) : CScrollView (size, CRect (0, 0, 0, 0), style, scrollbarWidth, pBackground) , db (db) , dbView (nullptr) , dbHeader (nullptr) , dbHeaderContainer (nullptr) { setTransparency (true); dbView = new CDataBrowserView (CRect (0, 0, 0, 0), db, this); dbView->setAutosizeFlags (kAutosizeLeft|kAutosizeRight|kAutosizeBottom); addView (dbView); auto obj = dynamic_cast(db); if (obj) obj->remember (); } //----------------------------------------------------------------------------------------------- CDataBrowser::~CDataBrowser () noexcept { auto obj = dynamic_cast(db); if (obj) obj->forget (); } //----------------------------------------------------------------------------------------------- void CDataBrowser::setAutosizeFlags (int32_t flags) { CScrollView::setAutosizeFlags (flags); } //----------------------------------------------------------------------------------------------- void CDataBrowser::setViewSize (const CRect& size, bool invalid) { if (getViewSize () != size) { CScrollView::setViewSize (size, invalid); recalculateLayout (true); } } //----------------------------------------------------------------------------------------------- void CDataBrowser::setWantsFocus (bool state) { if (dbView) { dbView->setWantsFocus (state); } } //------------------------------------------------------------------------ bool CDataBrowser::wantsFocus () const { return dbView ? dbView->wantsFocus () : false; } //----------------------------------------------------------------------------------------------- bool CDataBrowser::attached (CView *parent) { bool result = CScrollView::attached (parent); if (result) { recalculateLayout (true); db->dbAttached (this); } return result; } //----------------------------------------------------------------------------------------------- bool CDataBrowser::removed (CView* parent) { if (isAttached ()) db->dbRemoved (this); return CScrollView::removed (parent); } //------------------------------------------------------------------------ void CDataBrowser::onKeyboardEvent (KeyboardEvent& event) { if (dbView) dbView->onKeyboardEvent (event); } //----------------------------------------------------------------------------- CMouseEventResult CDataBrowser::onMouseDown (CPoint& where, const CButtonState& buttons) { CMouseEventResult result = CViewContainer::onMouseDown (where, buttons); if (auto frame = getFrame ()) { CView* focusView = frame->getFocusView (); if (focusView != dbView && !isChild (focusView, true)) frame->setFocusView (dbView); } return result; } //----------------------------------------------------------------------------- void CDataBrowser::valueChanged (CControl *pControl) { CPoint origOffset = getScrollOffset (); CScrollView::valueChanged (pControl); CPoint offset = getScrollOffset (); if (origOffset != offset) { switch (pControl->getTag ()) { case kHSBTag: { if (dbHeader) { CRect viewSize = dbHeader->getViewSize (); CCoord width = viewSize.getWidth (); viewSize.left = offset.x; viewSize.setWidth (width); dbHeader->setViewSize (viewSize); dbHeader->setMouseableArea (viewSize); dbHeader->invalid (); } break; } } if (isAttached () && (getMouseDownView () == dbView || getMouseDownView () == nullptr)) { CPoint where; getFrame ()->getCurrentMouseLocation (where); if (getFrame ()->getViewAt (where, GetViewOptions ().deep ()) == dbView) { CDataBrowser::Cell cell; dbView->frameToLocal (where); dbView->getCell (where, cell); db->dbOnMouseMoved (where, getFrame ()->getCurrentMouseButtons (), cell.row, cell.column, this); } } } } //----------------------------------------------------------------------------------------------- /** * @param rememberSelection if true selection will be remembered */ void CDataBrowser::recalculateLayout (bool rememberSelection) { CCoord lineWidth = 0; CColor lineColor; db->dbGetLineWidthAndColor (lineWidth, lineColor, this); CCoord rowHeight = db->dbGetRowHeight (this); CCoord headerHeight = db->dbGetHeaderHeight (this); int32_t numRows = db->dbGetNumRows (this); int32_t numColumns = db->dbGetNumColumns (this); CCoord allRowsHeight = rowHeight * numRows; if (getStyle () & kDrawRowLines) allRowsHeight += numRows * lineWidth; CCoord allColumnsWidth = 0; for (int32_t i = 0; i < numColumns; i++) allColumnsWidth += db->dbGetCurrentColumnWidth (i, this); if (getStyle () & kDrawColumnLines) allColumnsWidth += numColumns * lineWidth; CRect newContainerSize (0, 0, allColumnsWidth, allRowsHeight); if (getStyle () & kDrawHeader) { CRect headerSize (0, 0, newContainerSize.getWidth (), headerHeight + lineWidth); if (dbHeader == nullptr) { dbHeaderContainer = new CViewContainer (headerSize); dbHeaderContainer->setAutosizeFlags (kAutosizeLeft|kAutosizeRight|kAutosizeTop); dbHeaderContainer->setTransparency (true); dbHeader = new CDataBrowserHeader (headerSize, db, this); dbHeaderContainer->addView (dbHeader); setEdgeView (Edge::Top, dbHeaderContainer); } else { headerSize.offset (getScrollOffset ().x, 0); dbHeader->setViewSize (headerSize); dbHeader->setMouseableArea (headerSize); } } setContainerSize (newContainerSize, true); if (dbView->getParentView ()) { CRect ps = dbView->getParentView ()->getViewSize (); if (newContainerSize.getWidth () < ps.getWidth ()) newContainerSize.setWidth (ps.getWidth ()); if (newContainerSize.getHeight () < ps.getHeight ()) newContainerSize.setHeight (ps.getHeight ()); if (newContainerSize != getContainerSize ()) setContainerSize (newContainerSize, true); } newContainerSize.offset (getScrollOffset ().x, -getScrollOffset ().y); dbView->setViewSize (newContainerSize); dbView->setMouseableArea (newContainerSize); CControl* scrollbar = getVerticalScrollbar (); if (scrollbar && newContainerSize.getHeight () > 0.) { float wheelInc = (float)(rowHeight / newContainerSize.getHeight ()); scrollbar->setWheelInc (wheelInc); } if (getStyle () & kDrawHeader) { for (const auto& pV : getChildren ()) { CRect viewSize = pV->getViewSize (); if (pV != dbHeaderContainer && viewSize.top < headerHeight+lineWidth) { if (getStyle () & kOverlayScrollbars && pV.cast ()) continue; bool autoSizingEnabled = false; if (auto container = pV->asViewContainer ()) { autoSizingEnabled = container->getAutosizingEnabled (); container->setAutosizingEnabled (false); } viewSize.top += headerHeight+lineWidth; pV->setViewSize (viewSize); pV->setMouseableArea (viewSize); if (auto container = pV->asViewContainer ()) container->setAutosizingEnabled (autoSizingEnabled); } } } if (isAttached ()) invalid (); validateSelection (); if (!rememberSelection) unselectAll (); } //----------------------------------------------------------------------------------------------- /** * @param cell cell to invalidate */ void CDataBrowser::invalidate (const Cell& cell) { invalidRect (getCellBounds (cell)); } //----------------------------------------------------------------------------------------------- /** * @param row row to invalidate */ void CDataBrowser::invalidateRow (int32_t row) { dbView->invalidateRow (row); } //----------------------------------------------------------------------------------------------- /** * @param row row to make visible */ void CDataBrowser::makeRowVisible (int32_t row) { CRect r = dbView->getRowBounds (row); makeRectVisible (r); } //----------------------------------------------------------------------------------------------- /** * @param index row to select * @param makeVisible if true makes row visible */ void CDataBrowser::setSelectedRow (int32_t index, bool makeVisible) { if (index == kNoSelection) { unselectAll (); return; } int32_t numRows = db->dbGetNumRows (this); if (index >= numRows) index = numRows-1; bool hasChanged = true; Selection::iterator alreadySelected = std::find (selection.begin (), selection.end (), index); if (alreadySelected != selection.end ()) { selection.erase (alreadySelected); hasChanged = !selection.empty (); } else { invalidateRow (index); } for (auto row : selection) { dbView->invalidateRow (row); } selection.clear (); selection.emplace_back (index); if (hasChanged) db->dbSelectionChanged (this); if (makeVisible) makeRowVisible (index); } //----------------------------------------------------------------------------------------------- int32_t CDataBrowser::getSelectedRow () const { if (!selection.empty ()) return selection[0]; return kNoSelection; } //----------------------------------------------------------------------------------------------- void CDataBrowser::selectRow (int32_t row) { if (row > db->dbGetNumRows (this)) return; Selection::iterator alreadySelected = std::find (selection.begin (), selection.end (), row); if (alreadySelected == selection.end ()) { if (getStyle () & kMultiSelectionStyle) { selection.emplace_back (row); dbView->invalidateRow (row); db->dbSelectionChanged (this); } else { setSelectedRow (row); } } } //----------------------------------------------------------------------------------------------- void CDataBrowser::unselectRow (int32_t row) { if (row > db->dbGetNumRows (this)) return; Selection::iterator alreadySelected = std::find (selection.begin (), selection.end (), row); if (alreadySelected != selection.end ()) { if (getStyle () & kMultiSelectionStyle) { selection.erase (alreadySelected); dbView->invalidateRow (row); db->dbSelectionChanged (this); } else { unselectAll (); } } } //----------------------------------------------------------------------------------------------- void CDataBrowser::unselectAll () { if (!selection.empty ()) { for (auto row : selection) { dbView->invalidateRow (row); } selection.clear (); db->dbSelectionChanged (this); } } //----------------------------------------------------------------------------------------------- void CDataBrowser::validateSelection () { bool selectionChanged = false; int32_t numRows = db->dbGetNumRows (this); for (Selection::iterator it = selection.begin (); it != selection.end ();) { if (*it >= numRows) { it = selection.erase (it); selectionChanged = true; } else { ++it; } } if (selectionChanged) db->dbSelectionChanged (this); } //----------------------------------------------------------------------------------------------- /** * @param cell cell * @return bounds of cell */ CRect CDataBrowser::getCellBounds (const Cell& cell) { CCoord lineWidth = 0; if (getStyle () & kDrawRowLines || getStyle () & kDrawColumnLines) { CColor lineColor; db->dbGetLineWidthAndColor (lineWidth, lineColor, this); } CCoord rowHeight = db->dbGetRowHeight (this); if (getStyle () & kDrawRowLines) rowHeight += lineWidth; CRect result (0, rowHeight * cell.row, 0, rowHeight * (cell.row+1)); for (int32_t i = 0; i <= cell.column; i++) { CCoord colWidth = db->dbGetCurrentColumnWidth (i, this); if (i != cell.column) { result.offset (colWidth, 0); if (getStyle () & kDrawColumnLines) result.offset (lineWidth, 0); } result.setWidth (colWidth); } CRect viewSize = dbView->getViewSize (); result.offset (viewSize.left, viewSize.top); return result; } //----------------------------------------------------------------------------------------------- CDataBrowser::Cell CDataBrowser::getCellAt (const CPoint& where) const { Cell pos; if (dbView) { CPoint w (where); localToFrame (w); dbView->frameToLocal (w); if (dbView->hitTest (w)) dbView->getCell (w, pos); } return pos; } //----------------------------------------------------------------------------------------------- /** * @param cell cell * @param initialText UTF-8 string the text edit field will be initialized with */ void CDataBrowser::beginTextEdit (const Cell& cell, UTF8StringPtr initialText) { CRect r = getCellBounds (cell); makeRectVisible (r); CRect cellRect = getCellBounds (cell); auto* te = new CTextEdit (cellRect, nullptr, -1, initialText); db->dbCellSetupTextEdit (cell.row, cell.column, te, this); addView (te); getFrame ()->setFocusView (te); // save row and column te->setAttribute ('row ', cell.row); te->setAttribute ('col ', cell.column); } //----------------------------------------------------------------------------------------------- CMessageResult CDataBrowser::notify (CBaseObject* sender, IdStringPtr message) { if (message == kMsgLooseFocus) { if (auto* te = dynamic_cast(sender)) { // get row and column int32_t row = kNoSelection; int32_t col = kNoSelection; te->getAttribute ('row ', row); te->getAttribute ('col ', col); UTF8StringPtr newText = te->getText (); db->dbCellTextChanged (row, col, newText, this); removeView (te); getFrame ()->setFocusView (dbView); return kMessageNotified; } } return CViewContainer::notify (sender, message); } /// @cond ignore //----------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------- CDataBrowserHeader::CDataBrowserHeader (const CRect& size, IDataBrowserDelegate* db, CDataBrowser* browser) : CView (size) , db (db) , browser (browser) { setTransparency (true); } //----------------------------------------------------------------------------------------------- void CDataBrowserHeader::draw (CDrawContext* context) { drawRect (context, getViewSize ()); } //----------------------------------------------------------------------------------------------- void CDataBrowserHeader::drawRect (CDrawContext* context, const CRect& updateRect) { CColor lineColor; CCoord lineWidth = 0; if (browser->getStyle () & CDataBrowser::kDrawRowLines || browser->getStyle () & CDataBrowser::kDrawColumnLines) { db->dbGetLineWidthAndColor (lineWidth, lineColor, browser); } CCoord headerHeight = db->dbGetHeaderHeight (browser); if (browser->getStyle () & CDataBrowser::kDrawRowLines) headerHeight += lineWidth; int32_t numColumns = db->dbGetNumColumns (browser); CRect r (getViewSize ().left, getViewSize ().top, 0, 0); r.setHeight (headerHeight); for (int32_t col = 0; col < numColumns; col++) { CCoord columnWidth = db->dbGetCurrentColumnWidth (col, browser); r.setWidth (columnWidth); if (browser->getStyle () & CDataBrowser::kDrawColumnLines) r.right += lineWidth; CRect testRect (r); testRect.bound (updateRect); if (!testRect.isEmpty ()) { db->dbDrawHeader (context, r, col, 0, browser); } r.offset (r.getWidth (), 0); } setDirty (false); } //----------------------------------------------------------------------------------------------- int32_t CDataBrowserHeader::getColumnAtPoint (CPoint& where) { // calculate column at point CCoord lineWidth = 0; if (browser->getStyle () & CDataBrowser::kDrawRowLines || browser->getStyle () & CDataBrowser::kDrawColumnLines) { CColor lineColor; db->dbGetLineWidthAndColor (lineWidth, lineColor, browser); } int32_t col = -1; int32_t numColumns = db->dbGetNumColumns (browser); CRect r (getViewSize ().left, getViewSize ().top, 0, getViewSize ().bottom); for (int32_t c = 0; c < numColumns; c++) { CCoord columnWidth = db->dbGetCurrentColumnWidth (c, browser); if (browser->getStyle () & CDataBrowser::kDrawColumnLines) columnWidth += lineWidth; r.setWidth (columnWidth); if (r.pointInside (where)) { if (r.right - where.x < 5) col = c; break; } r.offset (columnWidth, 0); } return col; } //----------------------------------------------------------------------------------------------- CMouseEventResult CDataBrowserHeader::onMouseDown (CPoint &where, const CButtonState& buttons) { mouseColumn = getColumnAtPoint (where); CCoord minWidth; CCoord maxWidth; if (mouseColumn >= 0 && db->dbGetColumnDescription (mouseColumn, minWidth, maxWidth, browser) && minWidth != maxWidth) { startWidth = db->dbGetCurrentColumnWidth (mouseColumn, browser); startMousePoint = where; getFrame ()->setCursor (kCursorHSize); return onMouseMoved (where, buttons); } return kMouseDownEventHandledButDontNeedMovedOrUpEvents; } //----------------------------------------------------------------------------------------------- CMouseEventResult CDataBrowserHeader::onMouseMoved (CPoint &where, const CButtonState& buttons) { if (buttons & kLButton) { CCoord xOffset = where.x - startMousePoint.x; if (xOffset != 0) { CCoord minWidth; CCoord maxWidth; if (db->dbGetColumnDescription (mouseColumn, minWidth, maxWidth, browser)) { CCoord columnWidth = db->dbGetCurrentColumnWidth (mouseColumn, browser); CCoord newWidth = startWidth + xOffset; if (newWidth < minWidth) newWidth = minWidth; if (newWidth > maxWidth) newWidth = maxWidth; if (newWidth != columnWidth) { db->dbSetCurrentColumnWidth (mouseColumn, newWidth, browser); browser->recalculateLayout (true); } } } return kMouseEventHandled; } else { int32_t col = getColumnAtPoint (where); CCoord minWidth; CCoord maxWidth; if (col >= 0 && db->dbGetColumnDescription (col, minWidth, maxWidth, browser) && minWidth != maxWidth) getFrame ()->setCursor (kCursorHSize); else getFrame ()->setCursor (kCursorDefault); } return kMouseEventNotHandled; } //----------------------------------------------------------------------------------------------- CMouseEventResult CDataBrowserHeader::onMouseExited (CPoint &where, const CButtonState& buttons) { getFrame ()->setCursor (kCursorDefault); return kMouseEventHandled; } //----------------------------------------------------------------------------------------------- CMouseEventResult CDataBrowserHeader::onMouseUp (CPoint &where, const CButtonState& buttons) { getFrame ()->setCursor (kCursorDefault); return kMouseEventHandled; } //----------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------- //----------------------------------------------------------------------------------------------- CDataBrowserView::CDataBrowserView (const CRect& size, IDataBrowserDelegate* db, CDataBrowser* browser) : CView (size) , db (db) , browser (browser) { setTransparency (true); setWantsFocus (true); } //----------------------------------------------------------------------------------------------- CRect CDataBrowserView::getRowBounds (int32_t row) { CCoord lineWidth = 0; if (browser->getStyle () & CDataBrowser::kDrawRowLines || browser->getStyle () & CDataBrowser::kDrawColumnLines) { CColor lineColor; db->dbGetLineWidthAndColor (lineWidth, lineColor, browser); } CCoord rowHeight = db->dbGetRowHeight (browser); CRect where (getViewSize ()); where.originize (); if (browser->getStyle () & CDataBrowser::kDrawRowLines) rowHeight+=lineWidth; CRect r (getViewSize ().left, getViewSize ().top + rowHeight * row, getViewSize ().right, getViewSize ().top + rowHeight * (row+1)); return r; } //----------------------------------------------------------------------------------------------- void CDataBrowserView::invalidateRow (int32_t row) { if (row == CDataBrowser::kNoSelection) return; CRect ia = getRowBounds (row); invalidRect (ia); } //----------------------------------------------------------------------------------------------- void CDataBrowserView::draw (CDrawContext* context) { drawRect (context, getViewSize ()); } //----------------------------------------------------------------------------------------------- void CDataBrowserView::drawRect (CDrawContext* context, const CRect& updateRect) { const bool drawRowLines = (browser->getStyle () & CDataBrowser::kDrawRowLines) ? true : false; CCoord lineWidth = 0; CColor lineColor; if (browser->getStyle () & CDataBrowser::kDrawRowLines || browser->getStyle () & CDataBrowser::kDrawColumnLines) { db->dbGetLineWidthAndColor (lineWidth, lineColor, browser); } CCoord rowHeight = db->dbGetRowHeight (browser); if (drawRowLines) rowHeight += lineWidth; int32_t numRows = db->dbGetNumRows (browser); int32_t numColumns = db->dbGetNumColumns (browser); const CDataBrowser::Selection& selection = browser->getSelection (); CDrawContext::LineList lines; CRect r (getViewSize ()); r.setHeight (rowHeight - lineWidth); for (int32_t row = 0; row < numRows; row++) { CRect testRect (r); testRect.bound (updateRect); if (testRect.isEmpty () == false) { bool isSelected = std::find (selection.begin (), selection.end (), row) != selection.end (); for (int32_t col = 0; col < numColumns; col++) { CCoord columnWidth = db->dbGetCurrentColumnWidth (col, browser); r.setWidth (columnWidth); testRect = r; testRect.bound (updateRect); if (testRect.isEmpty () == false) { context->setClipRect (testRect); CRect cellSize (r); cellSize.bottom++; cellSize.right++; db->dbDrawCell (context, cellSize, row, col, isSelected ? IDataBrowserDelegate::kRowSelected : 0, browser); } r.offset (columnWidth, 0); if (browser->getStyle () & CDataBrowser::kDrawColumnLines) r.offset (lineWidth, 0); } } r.left = getViewSize ().left; r.setWidth (getWidth ()); if (drawRowLines) lines.emplace_back (r.getBottomLeft (), r.getBottomRight ()); r.offset (0, rowHeight); } if (browser->getStyle () & CDataBrowser::kDrawColumnLines) { CPoint p1; CPoint p2; if (browser->getStyle () & CDataBrowser::kDrawColumnLines) { p1 (getViewSize ().left - lineWidth, getViewSize ().top); p2 (getViewSize ().left - lineWidth, getViewSize ().bottom); for (int32_t col = 0; col < numColumns - 1; col++) { p1.x = p2.x = p1.x + db->dbGetCurrentColumnWidth (col, browser) + lineWidth; lines.emplace_back (p1, p2); } } } if (!lines.empty ()) { context->setClipRect (updateRect); context->setDrawMode (kAntiAliasing); context->setLineWidth (lineWidth); context->setFrameColor (lineColor); context->setLineStyle (kLineSolid); context->drawLines (lines); } setDirty (false); } //----------------------------------------------------------------------------------------------- bool CDataBrowserView::getCell (const CPoint& where, CDataBrowser::Cell& cell) { CPoint _where (where); _where.offset (-getViewSize ().left, -getViewSize ().top); if (_where.x < 0) return false; CCoord lineWidth = 0; if (browser->getStyle () & CDataBrowser::kDrawRowLines || browser->getStyle () & CDataBrowser::kDrawColumnLines) { CColor lineColor; db->dbGetLineWidthAndColor (lineWidth, lineColor, browser); } CCoord rowHeight = db->dbGetRowHeight (browser); int32_t numColumns = db->dbGetNumColumns (browser); if (browser->getStyle () & CDataBrowser::kDrawRowLines) rowHeight += lineWidth; int32_t rowNum = (int32_t)(_where.y / rowHeight); int32_t colNum = 0; CCoord cw = 0; for (int32_t i = 0; i < numColumns; i++) { cw += db->dbGetCurrentColumnWidth (i, browser); if (browser->getStyle () & CDataBrowser::kDrawColumnLines) cw += lineWidth; if (_where.x < cw && rowNum < db->dbGetNumRows (browser) && colNum < db->dbGetNumColumns (browser)) { colNum = i; cell.row = rowNum; cell.column = colNum; return true; } } return false; } //----------------------------------------------------------------------------------------------- CMouseEventResult CDataBrowserView::onMouseDown (CPoint &where, const CButtonState& buttons) { getFrame ()->setFocusView (this); CDataBrowser::Cell cell; if (getCell (where, cell)) { const CDataBrowser::Selection& selection = browser->getSelection (); bool alreadySelected = std::find (selection.begin (), selection.end (), cell.row) != selection.end (); if (browser->getStyle () & CDataBrowser::kMultiSelectionStyle) { if (buttons.getModifierState () == kControl) { if (alreadySelected) browser->unselectRow (cell.row); else browser->selectRow (cell.row); } else if (buttons.getModifierState () == kShift) { int32_t lastSelectedRow = !selection.empty () ? selection.back () : -1; if (lastSelectedRow < cell.row) { for (int32_t i = lastSelectedRow; i <= cell.row; i++) { browser->selectRow (i); } } else if (lastSelectedRow > cell.row) { for (int32_t i = cell.row; i < lastSelectedRow; i++) { browser->selectRow (i); } } } else { browser->setSelectedRow (cell.row); } } else if (!alreadySelected) { invalidateRow (cell.row); invalidateRow (browser->getSelectedRow ()); browser->setSelectedRow (cell.row); } return db->dbOnMouseDown (where, buttons, cell.row, cell.column, browser); } return kMouseEventHandled; } //----------------------------------------------------------------------------------------------- CMouseEventResult CDataBrowserView::onMouseMoved (CPoint &where, const CButtonState& buttons) { CDataBrowser::Cell cell; if (getCell (where, cell)) { return db->dbOnMouseMoved (where, buttons, cell.row, cell.column, browser); } return kMouseEventHandled; } //----------------------------------------------------------------------------------------------- CMouseEventResult CDataBrowserView::onMouseUp (CPoint &where, const CButtonState& buttons) { CDataBrowser::Cell cell; if (getCell (where, cell)) { return db->dbOnMouseUp (where, buttons, cell.row, cell.column, browser); } return kMouseEventNotHandled; } //----------------------------------------------------------------------------------------------- CMouseEventResult CDataBrowserView::onMouseExited (CPoint &where, const CButtonState& buttons) { return db->dbOnMouseMoved (where, buttons, -1, -1, browser); } //----------------------------------------------------------------------------------------------- bool CDataBrowserView::onDrop (DragEventData data) { CPoint cellPoint (data.pos); CDataBrowser::Cell cell; if (getCell (data.pos, cell)) { CRect r = browser->getCellBounds (cell); cellPoint.x -= r.left; cellPoint.y -= r.top; } return db->dbOnDropInCell (cell.row, cell.column, cellPoint, data.drag, browser); } //----------------------------------------------------------------------------------------------- static const CViewAttributeID kDataBrowserViewDragRow = 'vddr'; static const CViewAttributeID kDataBrowserViewDragColumn = 'vddc'; //----------------------------------------------------------------------------------------------- DragOperation CDataBrowserView::onDragEnter (DragEventData data) { db->dbOnDragEnterBrowser (data.drag, browser); CDataBrowser::Cell cell; getCell (data.pos, cell); CRect r = browser->getCellBounds (cell); CPoint cellPoint (data.pos); cellPoint.x -= r.left; cellPoint.y -= r.top; auto result = db->dbOnDragEnterCell (cell.row, cell.column, cellPoint, data.drag, browser); setAttribute (kDataBrowserViewDragRow, cell.row); setAttribute (kDataBrowserViewDragColumn, cell.column); return result; } //----------------------------------------------------------------------------------------------- void CDataBrowserView::onDragLeave (DragEventData data) { int32_t oldRowNum = -1; int32_t oldColNum = -1; getAttribute (kDataBrowserViewDragRow, oldRowNum); getAttribute (kDataBrowserViewDragColumn, oldColNum); db->dbOnDragExitCell (oldRowNum, oldColNum, data.drag, browser); removeAttribute (kDataBrowserViewDragRow); removeAttribute (kDataBrowserViewDragColumn); db->dbOnDragExitBrowser (data.drag, browser); } //----------------------------------------------------------------------------------------------- DragOperation CDataBrowserView::onDragMove (DragEventData data) { DragOperation result = DragOperation::None; int32_t oldRowNum = -1; int32_t oldColNum = -1; getAttribute (kDataBrowserViewDragRow, oldRowNum); getAttribute (kDataBrowserViewDragColumn, oldColNum); CDataBrowser::Cell cell; getCell (data.pos, cell); CRect r = browser->getCellBounds (cell); CPoint cellPoint (data.pos); cellPoint.x -= r.left; cellPoint.y -= r.top; if (oldRowNum != cell.row || oldColNum != cell.column) { if (oldRowNum != -1 && oldColNum != -1) db->dbOnDragExitCell (oldRowNum, oldColNum, data.drag, browser); result = db->dbOnDragEnterCell (cell.row, cell.column, cellPoint, data.drag, browser); setAttribute (kDataBrowserViewDragRow, cell.row); setAttribute (kDataBrowserViewDragColumn, cell.column); } else { result = db->dbOnDragMoveInCell (cell.row, cell.column, cellPoint, data.drag, browser); } return result; } //----------------------------------------------------------------------------------------------- void CDataBrowserView::onKeyboardEvent (KeyboardEvent& event) { db->dbOnKeyboardEvent (event, browser); if (event.consumed || event.type != EventType::KeyDown) return; if (!event.modifiers.empty ()) return; if (event.virt == VirtualKey::Up || event.virt == VirtualKey::Down || event.virt == VirtualKey::PageUp || event.virt == VirtualKey::PageDown) { int32_t numRows = db->dbGetNumRows (browser); int32_t selRow = browser->getSelectedRow (); int32_t changeRow = 0; if (event.virt == VirtualKey::Up) changeRow = -1; else if (event.virt == VirtualKey::Down) changeRow = 1; else if (event.virt == VirtualKey::PageUp) changeRow = (int32_t) (-browser->getHeight () / db->dbGetRowHeight (browser)); else if (event.virt == VirtualKey::PageDown) changeRow = (int32_t) (browser->getHeight () / db->dbGetRowHeight (browser)); int32_t newSelRow = selRow + changeRow; newSelRow = std::min (newSelRow, numRows); newSelRow = std::max (newSelRow, 0); if (newSelRow != selRow) { invalidateRow (selRow); invalidateRow (newSelRow); browser->setSelectedRow (newSelRow, true); CRect rect = getRowBounds (newSelRow); browser->makeRectVisible (rect); } event.consumed = true; } } //----------------------------------------------------------------------------------------------- bool CDataBrowserView::drawFocusOnTop () { return true; } //----------------------------------------------------------------------------------------------- bool CDataBrowserView::getFocusPath (CGraphicsPath& outPath) { CRect r = getVisibleViewSize (); outPath.addRect (r); CCoord focusWidth = getFrame ()->getFocusWidth (); r.inset (focusWidth, focusWidth); outPath.addRect (r); return true; } /// @endcond //----------------------------------------------------------------------------------------------- } // VSTGUI