/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include "editbrowseboximpl.hxx" #include namespace svt { namespace { GetFocusFlags getRealGetFocusFlags( vcl::Window* _pWindow ) { GetFocusFlags nFlags = GetFocusFlags::NONE; while ( _pWindow && nFlags == GetFocusFlags::NONE ) { nFlags = _pWindow->GetGetFocusFlags( ); _pWindow = _pWindow->GetParent(); } return nFlags; } } using namespace ::com::sun::star::uno; using namespace com::sun::star::accessibility::AccessibleEventId; IEditImplementation::~IEditImplementation() { } //= EditBrowserHeader void EditBrowserHeader::DoubleClick() { sal_uInt16 nColId = GetCurItemId(); if (nColId) { sal_uInt32 nAutoWidth = static_cast(GetParent())->GetAutoColumnWidth(nColId); if (nAutoWidth != static_cast(GetParent())->GetColumnWidth(nColId)) { static_cast(GetParent())->SetColumnWidth(nColId, nAutoWidth); static_cast(GetParent())->ColumnResized(nColId); } } } //= EditBrowseBox void EditBrowseBox::BrowserMouseEventPtr::Clear() { pEvent.reset(); } void EditBrowseBox::BrowserMouseEventPtr::Set(const BrowserMouseEvent* pEvt, bool bIsDown) { if (pEvt == pEvent.get()) { bDown = bIsDown; return; } pEvent.reset(); if (pEvt) { pEvent.reset(new BrowserMouseEvent(pEvt->GetWindow(), *pEvt, pEvt->GetRow(), pEvt->GetColumn(), pEvt->GetColumnId(), pEvt->GetRect())); bDown = bIsDown; } } EditBrowseBox::EditBrowseBox( vcl::Window* pParent, EditBrowseBoxFlags nBrowserFlags, WinBits nBits, BrowserMode _nMode ) :BrowseBox( pParent, nBits, _nMode ) ,nStartEvent(nullptr) ,nEndEvent(nullptr) ,nCellModifiedEvent(nullptr) ,m_pFocusWhileRequest(nullptr) ,nPaintRow(-1) ,nEditRow(-1) ,nEditCol(0) ,bHasFocus(false) ,bPaintStatus(true) ,bActiveBeforeTracking( false ) ,m_nBrowserFlags(nBrowserFlags) ,pHeader(nullptr) { m_aImpl.reset(new EditBrowseBoxImpl); SetCompoundControl(true); ImplInitSettings(true, true, true); pCheckBoxPaint = VclPtr::Create(&GetDataWindow()); pCheckBoxPaint->SetPaintTransparent( true ); pCheckBoxPaint->SetBackground(); } void EditBrowseBox::Init() { // late construction } EditBrowseBox::~EditBrowseBox() { disposeOnce(); } void EditBrowseBox::dispose() { if (nStartEvent) Application::RemoveUserEvent(nStartEvent); if (nEndEvent) Application::RemoveUserEvent(nEndEvent); if (nCellModifiedEvent) Application::RemoveUserEvent(nCellModifiedEvent); pCheckBoxPaint.disposeAndClear(); m_pFocusWhileRequest.clear(); pHeader.clear(); BrowseBox::dispose(); } void EditBrowseBox::RemoveRows() { BrowseBox::Clear(); nEditRow = nPaintRow = -1; nEditCol = 0; } VclPtr EditBrowseBox::CreateHeaderBar(BrowseBox* pParent) { pHeader = imp_CreateHeaderBar(pParent); if (!IsUpdateMode()) pHeader->SetUpdateMode(false); return pHeader; } VclPtr EditBrowseBox::imp_CreateHeaderBar(BrowseBox* pParent) { return VclPtr::Create(pParent); } void EditBrowseBox::LoseFocus() { BrowseBox::LoseFocus(); DetermineFocus(); } void EditBrowseBox::GetFocus() { BrowseBox::GetFocus(); // This should handle the case that the BrowseBox (or one of its children) // gets the focus from outside by pressing Tab if (IsEditing() && Controller()->GetWindow().IsVisible()) Controller()->GetWindow().GrabFocus(); DetermineFocus(getRealGetFocusFlags(this)); } bool EditBrowseBox::SeekRow(sal_Int32 nRow) { nPaintRow = nRow; return true; } IMPL_LINK_NOARG(EditBrowseBox, StartEditHdl, void*, void) { nStartEvent = nullptr; if (IsEditing()) { EnableAndShow(); if (!ControlHasFocus() && (m_pFocusWhileRequest.get() == Application::GetFocusWindow())) aController->GetWindow().GrabFocus(); } } void EditBrowseBox::PaintField( vcl::RenderContext& rDev, const tools::Rectangle& rRect, sal_uInt16 nColumnId ) const { if (nColumnId == HandleColumnId) { if (bPaintStatus) PaintStatusCell(rDev, rRect); } else { // don't paint the current cell if (rDev.GetOwnerWindow() == &GetDataWindow()) // but only if we're painting onto our data win (which is the usual painting) if (nPaintRow == nEditRow) { if (IsEditing() && nEditCol == nColumnId && aController->GetWindow().IsVisible()) return; } PaintCell(rDev, rRect, nColumnId); } } Image EditBrowseBox::GetImage(RowStatus eStatus) const { BitmapEx aBitmap; bool bNeedMirror = IsRTLEnabled(); switch (eStatus) { case CURRENT: aBitmap = BitmapEx(BMP_CURRENT); break; case CURRENTNEW: aBitmap = BitmapEx(BMP_CURRENTNEW); break; case MODIFIED: aBitmap = BitmapEx(BMP_MODIFIED); bNeedMirror = false; // the pen is not mirrored break; case NEW: aBitmap = BitmapEx(BMP_NEW); break; case DELETED: aBitmap = BitmapEx(BMP_DELETED); break; case PRIMARYKEY: aBitmap = BitmapEx(BMP_PRIMARYKEY); break; case CURRENT_PRIMARYKEY: aBitmap = BitmapEx(BMP_CURRENT_PRIMARYKEY); break; case FILTER: aBitmap = BitmapEx(BMP_FILTER); break; case HEADERFOOTER: aBitmap = BitmapEx(BMP_HEADERFOOTER); break; case CLEAN: break; } if ( bNeedMirror ) { aBitmap.Mirror( BmpMirrorFlags::Horizontal ); } return Image(aBitmap); } void EditBrowseBox::PaintStatusCell(OutputDevice& rDev, const tools::Rectangle& rRect) const { if (nPaintRow < 0) return; RowStatus eStatus = GetRowStatus( nPaintRow ); EditBrowseBoxFlags nBrowserFlags = GetBrowserFlags(); if (nBrowserFlags & EditBrowseBoxFlags::NO_HANDLE_COLUMN_CONTENT) return; // draw the text of the header column if (nBrowserFlags & EditBrowseBoxFlags::HANDLE_COLUMN_TEXT ) { rDev.DrawText( rRect, GetCellText( nPaintRow, 0 ), DrawTextFlags::Center | DrawTextFlags::VCenter | DrawTextFlags::Clip ); } // draw an image else if (eStatus != CLEAN && rDev.GetOutDevType() == OUTDEV_WINDOW) { Image aImage(GetImage(eStatus)); // calc the image position Size aImageSize(aImage.GetSizePixel()); aImageSize.setWidth( CalcZoom(aImageSize.Width()) ); aImageSize.setHeight( CalcZoom(aImageSize.Height()) ); Point aPos( rRect.TopLeft() ); if ( ( aImageSize.Width() > rRect.GetWidth() ) || ( aImageSize.Height() > rRect.GetHeight() ) ) rDev.SetClipRegion(vcl::Region(rRect)); if ( aImageSize.Width() < rRect.GetWidth() ) aPos.AdjustX(( rRect.GetWidth() - aImageSize.Width() ) / 2 ); if ( aImageSize.Height() < rRect.GetHeight() ) aPos.AdjustY(( rRect.GetHeight() - aImageSize.Height() ) / 2 ); if ( IsZoom() ) rDev.DrawImage( aPos, aImageSize, aImage ); else rDev.DrawImage( aPos, aImage ); if (rDev.IsClipRegion()) rDev.SetClipRegion(); } } void EditBrowseBox::ImplStartTracking() { bActiveBeforeTracking = IsEditing(); if ( bActiveBeforeTracking ) { DeactivateCell(); PaintImmediately(); } BrowseBox::ImplStartTracking(); } void EditBrowseBox::ImplEndTracking() { if ( bActiveBeforeTracking ) ActivateCell(); bActiveBeforeTracking = false; BrowseBox::ImplEndTracking(); } void EditBrowseBox::RowHeightChanged() { if ( IsEditing() ) { tools::Rectangle aRect( GetCellRect( nEditRow, nEditCol, false ) ); CellControllerRef aCellController( Controller() ); ResizeController( aCellController, aRect ); aCellController->GetWindow().GrabFocus(); } BrowseBox::RowHeightChanged(); } EditBrowseBox::RowStatus EditBrowseBox::GetRowStatus(sal_Int32) const { return CLEAN; } void EditBrowseBox::KeyInput( const KeyEvent& rEvt ) { sal_uInt16 nCode = rEvt.GetKeyCode().GetCode(); bool bShift = rEvt.GetKeyCode().IsShift(); bool bCtrl = rEvt.GetKeyCode().IsMod1(); switch (nCode) { case KEY_RETURN: if (!bCtrl && !bShift && IsTabAllowed(true)) { Dispatch(BROWSER_CURSORRIGHT); } else BrowseBox::KeyInput(rEvt); return; case KEY_TAB: if (!bCtrl && !bShift) { if (IsTabAllowed(true)) Dispatch(BROWSER_CURSORRIGHT); else // do NOT call BrowseBox::KeyInput : this would handle the tab, but we already now // that tab isn't allowed here. So give the Control class a chance Control::KeyInput(rEvt); return; } else if (!bCtrl && bShift) { if (IsTabAllowed(false)) Dispatch(BROWSER_CURSORLEFT); else // do NOT call BrowseBox::KeyInput : this would handle the tab, but we already now // that tab isn't allowed here. So give the Control class a chance Control::KeyInput(rEvt); return; } [[fallthrough]]; default: BrowseBox::KeyInput(rEvt); } } void EditBrowseBox::ChildFocusIn() { DetermineFocus(getRealGetFocusFlags(this)); } void EditBrowseBox::ChildFocusOut() { DetermineFocus(); } void EditBrowseBox::MouseButtonDown(const BrowserMouseEvent& rEvt) { // absorb double clicks if (rEvt.GetClicks() > 1 && rEvt.GetRow() >= 0) return; // we are about to leave the current cell. If there is a "this cell has been modified" notification // pending (asynchronously), this may be deadly -> do it synchronously if ( nCellModifiedEvent ) { Application::RemoveUserEvent( nCellModifiedEvent ); nCellModifiedEvent = nullptr; LINK( this, EditBrowseBox, CellModifiedHdl ).Call( nullptr ); } if (rEvt.GetColumnId() == HandleColumnId) { // it was the handle column. save the current cell content if necessary // (clicking on the handle column results in selecting the current row) if (IsEditing() && aController->IsValueChangedFromSaved()) SaveModified(); } aMouseEvent.Set(&rEvt,true); BrowseBox::MouseButtonDown(rEvt); aMouseEvent.Clear(); if (m_nBrowserFlags & EditBrowseBoxFlags::ACTIVATE_ON_BUTTONDOWN) { // the base class does not travel upon MouseButtonDown, but implActivateCellOnMouseEvent assumes we traveled ... GoToRowColumnId( rEvt.GetRow(), rEvt.GetColumnId() ); if (rEvt.GetRow() >= 0) implActivateCellOnMouseEvent(rEvt, false); } } void EditBrowseBox::MouseButtonUp( const BrowserMouseEvent& rEvt ) { // absorb double clicks if (rEvt.GetClicks() > 1 && rEvt.GetRow() >= 0) return; aMouseEvent.Set(&rEvt,false); BrowseBox::MouseButtonUp(rEvt); aMouseEvent.Clear(); if (!(m_nBrowserFlags & EditBrowseBoxFlags::ACTIVATE_ON_BUTTONDOWN)) if (rEvt.GetRow() >= 0) implActivateCellOnMouseEvent(rEvt, true); } bool EditBrowseBox::ControlHasFocus() const { Window* pControlWindow = aController ? &aController->GetWindow() : nullptr; if (ControlBase* pControlBase = dynamic_cast(pControlWindow)) return pControlBase->ControlHasFocus(); return pControlWindow && pControlWindow->HasChildPathFocus(); } void EditBrowseBox::implActivateCellOnMouseEvent(const BrowserMouseEvent& _rEvt, bool _bUp) { if (!IsEditing()) ActivateCell(); else if (IsEditing() && !aController->GetWindow().IsEnabled()) DeactivateCell(); else if (IsEditing() && !ControlHasFocus()) AsynchGetFocus(); if (!IsEditing() || !aController->GetWindow().IsEnabled()) return; // forwards the event to the control aController->ActivatingMouseEvent(_rEvt, _bUp); } void EditBrowseBox::Dispatch( sal_uInt16 _nId ) { if ( _nId == BROWSER_ENHANCESELECTION ) { // this is a workaround for the bug in the base class: // if the row selection is to be extended (which is what BROWSER_ENHANCESELECTION tells us) // then the base class does not revert any column selections, while, for doing a "simple" // selection (BROWSER_SELECT), it does. In fact, it does not only revert the col selection then, // but also any current row selections. // This clearly tells me that the both ids are for row selection only - there this behaviour does // make sense. // But here, where we have column selection, too, we take care of this ourself. if ( GetSelectColumnCount( ) ) { while ( GetSelectColumnCount( ) ) SelectColumnPos( sal::static_int_cast< sal_uInt16 >(FirstSelectedColumn()), false ); Select(); } } BrowseBox::Dispatch( _nId ); } bool EditBrowseBox::ProcessKey(const KeyEvent& rKeyEvent) { sal_uInt16 nCode = rKeyEvent.GetKeyCode().GetCode(); bool bShift = rKeyEvent.GetKeyCode().IsShift(); bool bCtrl = rKeyEvent.GetKeyCode().IsMod1(); bool bAlt = rKeyEvent.GetKeyCode().IsMod2(); bool bLocalSelect = false; bool bNonEditOnly = false; sal_uInt16 nId = BROWSER_NONE; if (!bAlt && !bCtrl && !bShift ) switch ( nCode ) { case KEY_DOWN: nId = BROWSER_CURSORDOWN; break; case KEY_UP: nId = BROWSER_CURSORUP; break; case KEY_PAGEDOWN: nId = BROWSER_CURSORPAGEDOWN; break; case KEY_PAGEUP: nId = BROWSER_CURSORPAGEUP; break; case KEY_HOME: nId = BROWSER_CURSORHOME; break; case KEY_END: nId = BROWSER_CURSOREND; break; case KEY_TAB: // ask if traveling to the next cell is allowed if (IsTabAllowed(true)) nId = BROWSER_CURSORRIGHT; break; case KEY_RETURN: // save the cell content (if necessary) if (IsEditing() && aController->IsValueChangedFromSaved() && !SaveModified()) { // maybe we're not visible ... EnableAndShow(); aController->GetWindow().GrabFocus(); return true; } // ask if traveling to the next cell is allowed if (IsTabAllowed(true)) nId = BROWSER_CURSORRIGHT; break; case KEY_RIGHT: nId = BROWSER_CURSORRIGHT; break; case KEY_LEFT: nId = BROWSER_CURSORLEFT; break; case KEY_SPACE: nId = BROWSER_SELECT; bNonEditOnly = bLocalSelect = true; break; } if ( !bAlt && !bCtrl && bShift ) switch ( nCode ) { case KEY_DOWN: nId = BROWSER_SELECTDOWN; bLocalSelect = true; break; case KEY_UP: nId = BROWSER_SELECTUP; bLocalSelect = true; break; case KEY_HOME: nId = BROWSER_SELECTHOME; bLocalSelect = true; break; case KEY_END: nId = BROWSER_SELECTEND; bLocalSelect = true; break; case KEY_TAB: if (IsTabAllowed(false)) nId = BROWSER_CURSORLEFT; break; } if ( !bAlt && bCtrl && bShift ) switch ( nCode ) { case KEY_SPACE: nId = BROWSER_SELECTCOLUMN; bLocalSelect = true; break; } if ( !bAlt && bCtrl && !bShift ) switch ( nCode ) { case KEY_DOWN: nId = BROWSER_SCROLLUP; break; case KEY_UP: nId = BROWSER_SCROLLDOWN; break; case KEY_PAGEDOWN: nId = BROWSER_CURSORENDOFFILE; break; case KEY_PAGEUP: nId = BROWSER_CURSORTOPOFFILE; break; case KEY_HOME: nId = BROWSER_CURSORTOPOFSCREEN; break; case KEY_END: nId = BROWSER_CURSORENDOFSCREEN; break; case KEY_SPACE: nId = BROWSER_ENHANCESELECTION; bLocalSelect = true; break; } if ( ( nId != BROWSER_NONE ) && ( !IsEditing() || ( !bNonEditOnly && aController->MoveAllowed(rKeyEvent) ) ) ) { if (nId == BROWSER_SELECT || BROWSER_SELECTCOLUMN == nId ) { // save the cell content (if necessary) if (IsEditing() && aController->IsValueChangedFromSaved() && !SaveModified()) { // maybe we're not visible ... EnableAndShow(); aController->GetWindow().GrabFocus(); return true; } } Dispatch(nId); if (bLocalSelect && (GetSelectRowCount() || GetSelection() != nullptr)) DeactivateCell(); return true; } return false; } bool EditBrowseBox::PreNotify(NotifyEvent& rEvt) { if (rEvt.GetType() == NotifyEventType::KEYINPUT) { if ( (IsEditing() && ControlHasFocus()) || rEvt.GetWindow() == &GetDataWindow() || (!IsEditing() && HasChildPathFocus()) ) { if (ProcessKey(*rEvt.GetKeyEvent())) return true; } } return BrowseBox::PreNotify(rEvt); } bool EditBrowseBox::IsTabAllowed(bool) const { return true; } bool EditBrowseBox::EventNotify(NotifyEvent& rEvt) { switch (rEvt.GetType()) { case NotifyEventType::GETFOCUS: DetermineFocus(getRealGetFocusFlags(this)); break; case NotifyEventType::LOSEFOCUS: DetermineFocus(); break; default: break; } return BrowseBox::EventNotify(rEvt); } void EditBrowseBox::StateChanged( StateChangedType nType ) { BrowseBox::StateChanged( nType ); bool bNeedCellReActivation = false; if ( nType == StateChangedType::Mirroring ) { bNeedCellReActivation = true; } else if ( nType == StateChangedType::Zoom ) { ImplInitSettings( true, false, false ); bNeedCellReActivation = true; } else if ( nType == StateChangedType::ControlFont ) { ImplInitSettings( true, false, false ); Invalidate(); } else if ( nType == StateChangedType::ControlForeground ) { ImplInitSettings( false, true, false ); Invalidate(); } else if ( nType == StateChangedType::ControlBackground ) { ImplInitSettings( false, false, true ); Invalidate(); } else if (nType == StateChangedType::Style) { WinBits nStyle = GetStyle(); if (!(nStyle & WB_NOTABSTOP) ) nStyle |= WB_TABSTOP; SetStyle(nStyle); } if ( bNeedCellReActivation ) { if ( IsEditing() ) { DeactivateCell(); ActivateCell(); } } } void EditBrowseBox::DataChanged( const DataChangedEvent& rDCEvt ) { BrowseBox::DataChanged( rDCEvt ); if ((( rDCEvt.GetType() == DataChangedEventType::SETTINGS ) || ( rDCEvt.GetType() == DataChangedEventType::DISPLAY )) && ( rDCEvt.GetFlags() & AllSettingsFlags::STYLE )) { ImplInitSettings( true, true, true ); Invalidate(); } } void EditBrowseBox::ImplInitSettings( bool bFont, bool bForeground, bool bBackground ) { const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); if (bFont) { vcl::Font aFont = rStyleSettings.GetFieldFont(); if (IsControlFont()) { GetDataWindow().SetControlFont(GetControlFont()); aFont.Merge(GetControlFont()); } else GetDataWindow().SetControlFont(); GetDataWindow().SetZoomedPointFont(*GetDataWindow().GetOutDev(), aFont); } if (bFont || bForeground) { Color aTextColor = rStyleSettings.GetFieldTextColor(); if (IsControlForeground()) { aTextColor = GetControlForeground(); GetDataWindow().SetControlForeground(aTextColor); } else GetDataWindow().SetControlForeground(); GetDataWindow().SetTextColor( aTextColor ); } if (!bBackground) // FIXME: Outside of Paint Hierarchy return; if (GetDataWindow().IsControlBackground()) { GetDataWindow().SetControlBackground(GetControlBackground()); GetDataWindow().SetBackground(GetDataWindow().GetControlBackground()); GetDataWindow().GetOutDev()->SetFillColor(GetDataWindow().GetControlBackground()); } else { GetDataWindow().SetControlBackground(); GetDataWindow().SetBackground(rStyleSettings.GetFieldColor()); GetDataWindow().GetOutDev()->SetFillColor(rStyleSettings.GetFieldColor()); } } bool EditBrowseBox::IsCursorMoveAllowed(sal_Int32 nNewRow, sal_uInt16 nNewColId) const { sal_uInt16 nInfo = 0; if (GetSelectColumnCount() || (aMouseEvent.Is() && aMouseEvent->GetRow() < 0)) nInfo |= COLSELECT; if ((GetSelection() != nullptr && GetSelectRowCount()) || (aMouseEvent.Is() && aMouseEvent->GetColumnId() == HandleColumnId)) nInfo |= ROWSELECT; if (!nInfo && nNewRow != nEditRow) nInfo |= ROWCHANGE; if (!nInfo && nNewColId != nEditCol) nInfo |= COLCHANGE; if (nInfo == 0) // nothing happened return true; // save the cell content if (IsEditing() && aController->IsValueChangedFromSaved() && !const_cast(this)->SaveModified()) { // maybe we're not visible ... EnableAndShow(); aController->GetWindow().GrabFocus(); return false; } EditBrowseBox * pTHIS = const_cast (this); // save the cell content if // a) a selection is being made // b) the row is changing if (IsModified() && (nInfo & (ROWCHANGE | COLSELECT | ROWSELECT)) && !pTHIS->SaveRow()) { if (nInfo & COLSELECT || nInfo & ROWSELECT) { // cancel selected pTHIS->SetNoSelection(); } if (IsEditing()) { if (!Controller()->GetWindow().IsVisible()) { EnableAndShow(); } aController->GetWindow().GrabFocus(); } return false; } if (nNewRow != nEditRow) { vcl::Window& rWindow = GetDataWindow(); if ((nEditRow >= 0) && !(GetBrowserFlags() & EditBrowseBoxFlags::NO_HANDLE_COLUMN_CONTENT)) { tools::Rectangle aRect = GetFieldRectPixel(nEditRow, 0, false ); // status cell should be painted if and only if text is displayed pTHIS->bPaintStatus = ( GetBrowserFlags() & EditBrowseBoxFlags::HANDLE_COLUMN_TEXT ) == EditBrowseBoxFlags::HANDLE_COLUMN_TEXT; rWindow.Invalidate(aRect); pTHIS->bPaintStatus = true; } // don't paint during row change rWindow.EnablePaint(false); // the last veto chance for derived classes if (!pTHIS->CursorMoving(nNewRow, nNewColId)) { pTHIS->InvalidateStatusCell(nEditRow); rWindow.EnablePaint(true); return false; } else { rWindow.EnablePaint(true); return true; } } else return pTHIS->CursorMoving(nNewRow, nNewColId); } void EditBrowseBox::ColumnMoved(sal_uInt16 nId) { BrowseBox::ColumnMoved(nId); if (IsEditing()) { tools::Rectangle aRect( GetCellRect(nEditRow, nEditCol, false)); CellControllerRef aControllerRef = Controller(); ResizeController(aControllerRef, aRect); Controller()->GetWindow().GrabFocus(); } } bool EditBrowseBox::SaveRow() { return true; } bool EditBrowseBox::CursorMoving(sal_Int32, sal_uInt16) { DeactivateCell(false); return true; } void EditBrowseBox::CursorMoved() { sal_Int32 nNewRow = GetCurRow(); if (nEditRow != nNewRow) { if (!(GetBrowserFlags() & EditBrowseBoxFlags::NO_HANDLE_COLUMN_CONTENT)) InvalidateStatusCell(nNewRow); nEditRow = nNewRow; } ActivateCell(); GetDataWindow().EnablePaint(true); // should not be called here because the descant event is not needed here //BrowseBox::CursorMoved(); } void EditBrowseBox::EndScroll() { if (IsEditing()) { tools::Rectangle aRect = GetCellRect(nEditRow, nEditCol, false); ResizeController(aController,aRect); AsynchGetFocus(); } BrowseBox::EndScroll(); } void EditBrowseBox::ActivateCell(sal_Int32 nRow, sal_uInt16 nCol, bool bCellFocus) { if (IsEditing()) return; nEditCol = nCol; if ((GetSelectRowCount() && GetSelection() != nullptr) || GetSelectColumnCount() || (aMouseEvent.Is() && (aMouseEvent.IsDown() || aMouseEvent->GetClicks() > 1))) // nothing happens on MouseDown { return; } if (nEditRow < 0 || nEditCol <= HandleColumnId) return; aController = GetController(nRow, nCol); if (aController.is()) { tools::Rectangle aRect( GetCellRect(nEditRow, nEditCol, false)); ResizeController(aController, aRect); InitController(aController, nEditRow, nEditCol); aController->SaveValue(); aController->SetModifyHdl(LINK(this,EditBrowseBox,ModifyHdl)); EnableAndShow(); if ( isAccessibleAlive() ) implCreateActiveAccessible(); // activate the cell only of the browser has the focus if ( bHasFocus && bCellFocus ) AsynchGetFocus(); } else { // no controller -> we have a new "active descendant" if ( isAccessibleAlive() && HasFocus() ) { commitTableEvent( ACTIVE_DESCENDANT_CHANGED, Any( CreateAccessibleCell( nRow, GetColumnPos( nCol -1) ) ), Any() ); } } } void EditBrowseBox::DeactivateCell(bool bUpdate) { if (!IsEditing()) return; if ( isAccessibleAlive() ) { commitBrowseBoxEvent( CHILD, Any(), Any( m_aImpl->m_xActiveCell ) ); m_aImpl->clearActiveCell(); } aOldController = aController; aController.clear(); // reset the modify handler aOldController->SetModifyHdl(Link()); if (bHasFocus) GrabFocus(); // ensure that we have (and keep) the focus aOldController->suspend(); // update if requested if (bUpdate) PaintImmediately(); // release the controller (asynchronously) if (nEndEvent) Application::RemoveUserEvent(nEndEvent); nEndEvent = Application::PostUserEvent(LINK(this,EditBrowseBox,EndEditHdl), nullptr, true); } tools::Rectangle EditBrowseBox::GetCellRect(sal_Int32 nRow, sal_uInt16 nColId, bool bRel) const { tools::Rectangle aRect( GetFieldRectPixel(nRow, nColId, bRel)); if ((GetMode() & BrowserMode::CURSOR_WO_FOCUS) == BrowserMode::CURSOR_WO_FOCUS) { aRect.AdjustTop(1 ); aRect.AdjustBottom( -1 ); } return aRect; } IMPL_LINK_NOARG(EditBrowseBox, EndEditHdl, void*, void) { nEndEvent = nullptr; aOldController = CellControllerRef(); } IMPL_LINK_NOARG(EditBrowseBox, ModifyHdl, LinkParamNone*, void) { if (nCellModifiedEvent) Application::RemoveUserEvent(nCellModifiedEvent); nCellModifiedEvent = Application::PostUserEvent(LINK(this,EditBrowseBox,CellModifiedHdl), nullptr, true); } IMPL_LINK_NOARG(EditBrowseBox, CellModifiedHdl, void*, void) { nCellModifiedEvent = nullptr; CellModified(); } void EditBrowseBox::ColumnResized( sal_uInt16 ) { if (IsEditing()) { tools::Rectangle aRect( GetCellRect(nEditRow, nEditCol, false)); CellControllerRef aControllerRef = Controller(); ResizeController(aControllerRef, aRect); // don't grab focus if Field Properties panel is being // resized by split pane drag resizing if (Application::IsUICaptured()) return; Controller()->GetWindow().GrabFocus(); } } sal_uInt16 EditBrowseBox::AppendColumn(const OUString& rName, sal_uInt16 nWidth, sal_uInt16 nPos, sal_uInt16 nId) { if (nId == BROWSER_INVALIDID) { // look for the next free id for (nId = ColCount(); nId > 0 && GetColumnPos(nId) != BROWSER_INVALIDID; nId--) ; if (!nId) { // if there is no handle column // increment the id if ( ColCount() == 0 || GetColumnId(0) != HandleColumnId ) nId = ColCount() + 1; } } DBG_ASSERT(nId, "EditBrowseBox::AppendColumn: invalid id!"); tools::Long w = nWidth; if (!w) w = GetDefaultColumnWidth(rName); InsertDataColumn(nId, rName, w, (HeaderBarItemBits::CENTER | HeaderBarItemBits::CLICKABLE), nPos); return nId; } void EditBrowseBox::Resize() { BrowseBox::Resize(); // if the window is smaller than "title line height" + "control area", // do nothing if (GetOutputSizePixel().Height() < (GetControlArea().GetHeight() + GetDataWindow().GetPosPixel().Y())) return; // the size of the control area Point aPoint(GetControlArea().TopLeft()); sal_uInt16 nX = static_cast(aPoint.X()); ArrangeControls(nX, static_cast(aPoint.Y())); if (!nX) nX = USHRT_MAX; bool bChanged = ReserveControlArea(nX); //tdf#97731 if the reserved area changed size, give the controls a //chance to adapt to the new size if (bChanged) { nX = static_cast(aPoint.X()); ArrangeControls(nX, static_cast(aPoint.Y())); } } void EditBrowseBox::ArrangeControls(sal_uInt16&, sal_uInt16) { } CellController* EditBrowseBox::GetController(sal_Int32, sal_uInt16) { return nullptr; } void EditBrowseBox::ResizeController(CellControllerRef const & rController, const tools::Rectangle& rRect) { Point aPoint(rRect.TopLeft()); Size aSize(rRect.GetSize()); Control& rControl = rController->GetWindow(); auto nMinHeight = rControl.get_preferred_size().Height(); if (nMinHeight > aSize.Height()) { auto nOffset = (nMinHeight - aSize.Height()) / 2; aPoint.AdjustY(-nOffset); aSize.setHeight(nMinHeight); } rControl.SetPosSizePixel(aPoint, aSize); } void EditBrowseBox::InitController(CellControllerRef&, sal_Int32, sal_uInt16) { } void EditBrowseBox::CellModified() { } bool EditBrowseBox::SaveModified() { return true; } void EditBrowseBox::DoubleClick(const BrowserMouseEvent& rEvt) { // when double clicking on the column, the optimum size will be calculated sal_uInt16 nColId = rEvt.GetColumnId(); if (nColId != HandleColumnId) SetColumnWidth(nColId, GetAutoColumnWidth(nColId)); } sal_uInt32 EditBrowseBox::GetAutoColumnWidth(sal_uInt16 nColId) { sal_uInt32 nCurColWidth = GetColumnWidth(nColId); sal_uInt32 nMinColWidth = CalcZoom(20); // minimum sal_uInt32 nNewColWidth = nMinColWidth; sal_Int32 nMaxRows = std::min(sal_Int32(GetVisibleRows()), GetRowCount()); sal_Int32 nLastVisRow = GetTopRow() + nMaxRows - 1; if (GetTopRow() <= nLastVisRow) // calc the column with using the cell contents { for (tools::Long i = GetTopRow(); i <= nLastVisRow; ++i) nNewColWidth = std::max(nNewColWidth,GetTotalCellWidth(i,nColId) + 12); if (nNewColWidth == nCurColWidth) // size has not changed nNewColWidth = GetDefaultColumnWidth(GetColumnTitle(nColId)); } else nNewColWidth = GetDefaultColumnWidth(GetColumnTitle(nColId)); return nNewColWidth; } sal_uInt32 EditBrowseBox::GetTotalCellWidth(sal_Int32, sal_uInt16) { return 0; } void EditBrowseBox::InvalidateHandleColumn() { tools::Rectangle aHdlFieldRect( GetFieldRectPixel( 0, 0 )); tools::Rectangle aInvalidRect( Point(0,0), GetOutputSizePixel() ); aInvalidRect.SetRight( aHdlFieldRect.Right() ); Invalidate( aInvalidRect ); } void EditBrowseBox::PaintTristate(const tools::Rectangle& rRect, const TriState& eState, bool _bEnabled) const { pCheckBoxPaint->SetState(eState); pCheckBoxPaint->GetBox().set_sensitive(_bEnabled); Size aBoxSize = pCheckBoxPaint->GetBox().get_preferred_size(); tools::Rectangle aRect(Point(rRect.Left() + ((rRect.GetWidth() - aBoxSize.Width()) / 2), rRect.Top() + ((rRect.GetHeight() - aBoxSize.Height()) / 2)), aBoxSize); pCheckBoxPaint->SetPosSizePixel(aRect.TopLeft(), aRect.GetSize()); pCheckBoxPaint->Draw(GetDataWindow().GetOutDev(), aRect.TopLeft(), SystemTextColorFlags::NONE); } void EditBrowseBox::AsynchGetFocus() { if (nStartEvent) Application::RemoveUserEvent(nStartEvent); m_pFocusWhileRequest = Application::GetFocusWindow(); nStartEvent = Application::PostUserEvent(LINK(this,EditBrowseBox,StartEditHdl), nullptr, true); } void EditBrowseBox::SetBrowserFlags(EditBrowseBoxFlags nFlags) { if (m_nBrowserFlags == nFlags) return; bool RowPicturesChanges = ((m_nBrowserFlags & EditBrowseBoxFlags::NO_HANDLE_COLUMN_CONTENT) != (nFlags & EditBrowseBoxFlags::NO_HANDLE_COLUMN_CONTENT)); m_nBrowserFlags = nFlags; if (RowPicturesChanges) InvalidateStatusCell(GetCurRow()); } inline void EditBrowseBox::EnableAndShow() const { Controller()->resume(); } CellController::CellController(ControlBase* pW) : pWindow(pW) , bSuspended( true ) { DBG_ASSERT(pWindow, "CellController::CellController: missing the window!"); DBG_ASSERT(!pWindow->IsVisible(), "CellController::CellController: window should not be visible!"); } CellController::~CellController() { } void CellController::suspend( ) { DBG_ASSERT( bSuspended == !GetWindow().IsVisible(), "CellController::suspend: inconsistence!" ); if ( !isSuspended( ) ) { CommitModifications(); GetWindow().Hide( ); GetWindow().Disable( ); bSuspended = true; } } void CellController::resume( ) { DBG_ASSERT( bSuspended == !GetWindow().IsVisible(), "CellController::resume: inconsistence!" ); if ( isSuspended( ) ) { GetWindow().Enable( ); GetWindow().Show( ); bSuspended = false; } } void CellController::CommitModifications() { // nothing to do in this base class } void CellController::ActivatingMouseEvent(const BrowserMouseEvent& /*rEvt*/, bool /*bUp*/) { // nothing to do in this base class } bool CellController::MoveAllowed(const KeyEvent&) const { return true; } } // namespace svt /* vim:set shiftwidth=4 softtabstop=4 expandtab: */