1
0
Fork 0
libreoffice/dbaccess/source/ui/querydesign/TableWindow.cxx
Daniel Baumann 8e63e14cf6
Adding upstream version 4:25.2.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-22 16:20:04 +02:00

709 lines
23 KiB
C++

/* -*- 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 <TableWindow.hxx>
#include <TableWindowListBox.hxx>
#include <TableWindowData.hxx>
#include <imageprovider.hxx>
#include <JoinController.hxx>
#include <JoinTableView.hxx>
#include <JoinDesignView.hxx>
#include <osl/diagnose.h>
#include <utility>
#include <vcl/svapp.hxx>
#include <vcl/settings.hxx>
#include <vcl/commandevent.hxx>
#include <vcl/event.hxx>
#include <vcl/ptrstyle.hxx>
#include <vcl/wall.hxx>
#include <vcl/weldutils.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <com/sun/star/container/XContainer.hpp>
#include <com/sun/star/container/XNameAccess.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/sdb/application/DatabaseObject.hpp>
#include <bitmaps.hlst>
#include <TableWindowAccess.hxx>
#include <connectivity/dbtools.hxx>
using namespace dbaui;
using namespace ::com::sun::star;
using namespace ::com::sun::star::sdb;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::accessibility;
namespace DatabaseObject = css::sdb::application::DatabaseObject;
#define TABWIN_SIZING_AREA 4
#define TABWIN_WIDTH_MIN 90
#define TABWIN_HEIGHT_MIN 80
namespace {
void Draw3DBorder(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
{
// Use the System Style-Settings for my colours
const StyleSettings& aSystemStyle = Application::GetSettings().GetStyleSettings();
// Black lines for bottom and right
rRenderContext.SetLineColor(aSystemStyle.GetDarkShadowColor());
rRenderContext.DrawLine(rRect.BottomLeft(), rRect.BottomRight());
rRenderContext.DrawLine(rRect.BottomRight(), rRect.TopRight());
// Dark grey lines over the black lines
rRenderContext.SetLineColor(aSystemStyle.GetShadowColor());
Point aEHvector(1, 1);
rRenderContext.DrawLine(rRect.BottomLeft() + Point(1, -1), rRect.BottomRight() - aEHvector);
rRenderContext.DrawLine(rRect.BottomRight() - aEHvector, rRect.TopRight() + Point(-1, 1));
// Light grey lines for top and left
rRenderContext.SetLineColor(aSystemStyle.GetLightColor());
rRenderContext.DrawLine(rRect.BottomLeft() + Point(1, -2), rRect.TopLeft() + aEHvector);
rRenderContext.DrawLine(rRect.TopLeft() + aEHvector, rRect.TopRight() + Point(-2, 1));
}
}
OTableWindow::OTableWindow( vcl::Window* pParent, TTableWindowData::value_type pTabWinData )
: ::comphelper::OContainerListener(m_aMutex)
, Window( pParent, WB_3DLOOK|WB_MOVEABLE )
, m_xTitle( VclPtr<OTableWindowTitle>::Create(this) )
, m_pData(std::move( pTabWinData ))
, m_nMoveCount(0)
, m_nMoveIncrement(1)
, m_nSizingFlags( SizingFlags::NONE )
{
// Set position and size
if( GetData()->HasPosition() )
SetPosPixel( GetData()->GetPosition() );
if( GetData()->HasSize() )
SetSizePixel( GetData()->GetSize() );
// Set background
const StyleSettings& aSystemStyle = Application::GetSettings().GetStyleSettings();
SetBackground(Wallpaper(aSystemStyle.GetFaceColor()));
// Set the text colour even though there is no text,
// because derived classes might need it
SetTextColor(aSystemStyle.GetButtonTextColor());
EnableClipSiblings();
}
OTableWindow::~OTableWindow()
{
disposeOnce();
}
void OTableWindow::dispose()
{
if (m_xListBox)
{
OSL_ENSURE(m_xListBox->get_widget().n_children()==0,"Forgot to call EmptyListbox()!");
}
m_xListBox.disposeAndClear();
if ( m_pContainerListener.is() )
m_pContainerListener->dispose();
m_xTitle.disposeAndClear();
vcl::Window::dispose();
}
const OJoinTableView* OTableWindow::getTableView() const
{
OSL_ENSURE(static_cast<OJoinTableView*>(GetParent()),"No OJoinTableView!");
return static_cast<OJoinTableView*>(GetParent());
}
OJoinTableView* OTableWindow::getTableView()
{
OSL_ENSURE(static_cast<OJoinTableView*>(GetParent()),"No OJoinTableView!");
return static_cast<OJoinTableView*>(GetParent());
}
OJoinDesignView* OTableWindow::getDesignView()
{
OSL_ENSURE(static_cast<OJoinDesignView*>(GetParent()->GetParent()->GetParent()),"No OJoinDesignView!");
return static_cast<OJoinDesignView*>(GetParent()->GetParent()->GetParent());
}
void OTableWindow::SetPosPixel( const Point& rNewPos )
{
Point aNewPosData = rNewPos + getTableView()->GetScrollOffset();
GetData()->SetPosition( aNewPosData );
Window::SetPosPixel( rNewPos );
}
void OTableWindow::SetSizePixel( const Size& rNewSize )
{
Size aOutSize(rNewSize);
if( aOutSize.Width() < TABWIN_WIDTH_MIN )
aOutSize.setWidth( TABWIN_WIDTH_MIN );
if( aOutSize.Height() < TABWIN_HEIGHT_MIN )
aOutSize.setHeight( TABWIN_HEIGHT_MIN );
GetData()->SetSize( aOutSize );
Window::SetSizePixel( aOutSize );
}
void OTableWindow::SetPosSizePixel( const Point& rNewPos, const Size& rNewSize )
{
SetPosPixel( rNewPos );
SetSizePixel( rNewSize );
}
void OTableWindow::FillListBox()
{
clearListBox();
weld::TreeView& rTreeView = m_xListBox->get_widget();
assert(!rTreeView.n_children());
if ( !m_pContainerListener.is() )
{
Reference< XContainer> xContainer(m_pData->getColumns(),UNO_QUERY);
if ( xContainer.is() )
m_pContainerListener = new ::comphelper::OContainerListenerAdapter(this,xContainer);
}
// mark all primary keys with special image
OUString aPrimKeyImage(BMP_PRIMARY_KEY);
if (GetData()->IsShowAll())
{
rTreeView.append(weld::toId(createUserData(nullptr,false)), u"*"_ustr);
}
Reference<XNameAccess> xPKeyColumns;
try
{
xPKeyColumns = dbtools::getPrimaryKeyColumns_throw(m_pData->getTable());
}
catch(Exception&)
{
TOOLS_WARN_EXCEPTION( "dbaccess", "");
}
try
{
Reference< XNameAccess > xColumns = m_pData->getColumns();
if( xColumns.is() )
{
for (auto& column : xColumns->getElementNames())
{
bool bPrimaryKeyColumn = xPKeyColumns.is() && xPKeyColumns->hasByName(column);
OUString sId;
Reference<XPropertySet> xColumn(xColumns->getByName(column), UNO_QUERY);
if (xColumn.is())
sId = weld::toId(createUserData(xColumn, bPrimaryKeyColumn));
rTreeView.append(sId, column);
// is this column in the primary key
if ( bPrimaryKeyColumn )
rTreeView.set_image(rTreeView.n_children() - 1, aPrimKeyImage);
}
}
}
catch(Exception&)
{
TOOLS_WARN_EXCEPTION( "dbaccess", "");
}
}
void* OTableWindow::createUserData(const Reference< XPropertySet>& /*_xColumn*/,bool /*_bPrimaryKey*/)
{
return nullptr;
}
void OTableWindow::deleteUserData(void*& _pUserData)
{
OSL_ENSURE(!_pUserData,"INVALID call. Need to delete the userclass!");
_pUserData = nullptr;
}
void OTableWindow::clearListBox()
{
if ( !m_xListBox )
return;
weld::TreeView& rTreeView = m_xListBox->get_widget();
rTreeView.all_foreach([this, &rTreeView](weld::TreeIter& rEntry){
void* pUserData = weld::fromId<void*>(rTreeView.get_id(rEntry));
deleteUserData(pUserData);
return false;
});
rTreeView.clear();
}
void OTableWindow::impl_updateImage()
{
weld::Image& rImage = m_xTitle->GetImage();
ImageProvider aImageProvider( getDesignView()->getController().getConnection() );
rImage.set_from_icon_name(aImageProvider.getImageId(GetComposedName(), m_pData->isQuery() ? DatabaseObject::QUERY : DatabaseObject::TABLE));
rImage.show();
}
bool OTableWindow::Init()
{
// create list box if necessary
if ( !m_xListBox )
{
m_xListBox = VclPtr<OTableWindowListBox>::Create(this);
assert(m_xListBox && "OTableWindow::Init() : CreateListBox returned NULL !");
m_xListBox->get_widget().set_selection_mode(SelectionMode::Multiple);
}
// Set the title
weld::Label& rLabel = m_xTitle->GetLabel();
rLabel.set_label(m_pData->GetWinName());
rLabel.set_tooltip_text(GetComposedName());
m_xTitle->Show();
m_xListBox->Show();
// add the fields to the ListBox
FillListBox();
m_xListBox->get_widget().unselect_all();
impl_updateImage();
return true;
}
void OTableWindow::DataChanged(const DataChangedEvent& rDCEvt)
{
if (rDCEvt.GetType() == DataChangedEventType::SETTINGS)
{
// In the worst-case the colours have changed so
// adapt myself to the new colours
const StyleSettings& aSystemStyle = Application::GetSettings().GetStyleSettings();
SetBackground(Wallpaper(aSystemStyle.GetFaceColor()));
SetTextColor(aSystemStyle.GetButtonTextColor());
}
}
void OTableWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
{
tools::Rectangle aRect(Point(0,0), GetOutputSizePixel());
Window::Paint(rRenderContext, rRect);
Draw3DBorder(rRenderContext, aRect);
}
tools::Rectangle OTableWindow::getSizingRect(const Point& _rPos,const Size& _rOutputSize) const
{
tools::Rectangle aSizingRect( GetPosPixel(), GetSizePixel() );
if( m_nSizingFlags & SizingFlags::Top )
{
if( _rPos.Y() < 0 )
aSizingRect.SetTop( 0 );
else
aSizingRect.SetTop( _rPos.Y() );
}
if( m_nSizingFlags & SizingFlags::Bottom )
{
if( _rPos.Y() > _rOutputSize.Height() )
aSizingRect.SetBottom( _rOutputSize.Height() );
else
aSizingRect.SetBottom( _rPos.Y() );
}
if( m_nSizingFlags & SizingFlags::Right )
{
if( _rPos.X() > _rOutputSize.Width() )
aSizingRect.SetRight( _rOutputSize.Width() );
else
aSizingRect.SetRight( _rPos.X() );
}
if( m_nSizingFlags & SizingFlags::Left )
{
if( _rPos.X() < 0 )
aSizingRect.SetLeft( 0 );
else
aSizingRect.SetLeft( _rPos.X() );
}
return aSizingRect;
}
void OTableWindow::setSizingFlag(const Point& _rPos)
{
Size aOutSize = GetOutputSizePixel();
// Set the flags when the mouse cursor is in the sizing area
m_nSizingFlags = SizingFlags::NONE;
if( _rPos.X() < TABWIN_SIZING_AREA )
m_nSizingFlags |= SizingFlags::Left;
if( _rPos.Y() < TABWIN_SIZING_AREA )
m_nSizingFlags |= SizingFlags::Top;
if( _rPos.X() > aOutSize.Width()-TABWIN_SIZING_AREA )
m_nSizingFlags |= SizingFlags::Right;
if( _rPos.Y() > aOutSize.Height()-TABWIN_SIZING_AREA )
m_nSizingFlags |= SizingFlags::Bottom;
}
void OTableWindow::MouseMove( const MouseEvent& rEvt )
{
Window::MouseMove(rEvt);
OJoinTableView* pCont = getTableView();
if (pCont->getDesignView()->getController().isReadOnly())
return;
Point aPos = rEvt.GetPosPixel();
setSizingFlag(aPos);
PointerStyle aPointer = PointerStyle::Arrow;
// Set the mouse cursor when it is in the sizing area
if ( m_nSizingFlags == SizingFlags::Top ||
m_nSizingFlags == SizingFlags::Bottom )
aPointer = PointerStyle::SSize;
else if ( m_nSizingFlags == SizingFlags::Left ||
m_nSizingFlags ==SizingFlags::Right )
aPointer = PointerStyle::ESize;
else if ( m_nSizingFlags == (SizingFlags::Left | SizingFlags::Top) ||
m_nSizingFlags == (SizingFlags::Right | SizingFlags::Bottom) )
aPointer = PointerStyle::SESize;
else if ( m_nSizingFlags == (SizingFlags::Right | SizingFlags::Top) ||
m_nSizingFlags == (SizingFlags::Left | SizingFlags::Bottom) )
aPointer = PointerStyle::NESize;
SetPointer( aPointer );
}
void OTableWindow::MouseButtonDown( const MouseEvent& rEvt )
{
// When resizing, the parent must be informed that
// the window size of its child has changed
if( m_nSizingFlags != SizingFlags::NONE )
getTableView()->BeginChildSizing( this, GetPointer() );
Window::MouseButtonDown( rEvt );
}
void OTableWindow::Resize()
{
// The window must not disappear so we enforce a minimum size
Size aOutSize = GetOutputSizePixel();
aOutSize = Size(CalcZoom(aOutSize.Width()),CalcZoom(aOutSize.Height()));
tools::Long nTitleHeight = CalcZoom( GetTextHeight() )+ CalcZoom( 4 );
// Set the title and ListBox
tools::Long n5Pos = CalcZoom(5);
tools::Long nPositionX = n5Pos;
tools::Long nPositionY = n5Pos;
Size aPreferredSize = m_xTitle->get_preferred_size();
if (nTitleHeight < aPreferredSize.Height())
nTitleHeight = aPreferredSize.Height();
m_xTitle->SetPosSizePixel( Point( nPositionX, nPositionY ), Size( aOutSize.Width() - nPositionX - n5Pos, nTitleHeight ) );
tools::Long nTitleToList = CalcZoom( 3 );
m_xListBox->SetPosSizePixel(
Point( n5Pos, nPositionY + nTitleHeight + nTitleToList ),
Size( aOutSize.Width() - 2 * n5Pos, aOutSize.Height() - ( nPositionY + nTitleHeight + nTitleToList ) - n5Pos )
);
Window::Invalidate();
}
void OTableWindow::SetBoldTitle( bool bBold )
{
weld::Label& rLabel = m_xTitle->GetLabel();
vcl::Font aFont = rLabel.get_font();
aFont.SetWeight(bBold ? WEIGHT_BOLD : WEIGHT_NORMAL);
rLabel.set_font(aFont);
}
void OTableWindow::GetFocus()
{
Window::GetFocus();
// we have to forward the focus to our listbox to enable keystrokes
if(m_xListBox)
m_xListBox->GrabFocus();
}
void OTableWindow::setActive(bool _bActive)
{
SetBoldTitle( _bActive );
if (_bActive || !m_xListBox)
return;
weld::TreeView& rTreeView = m_xListBox->get_widget();
if (rTreeView.get_selected_index() != -1)
rTreeView.unselect_all();
}
void OTableWindow::Remove()
{
// Delete the window
OJoinTableView* pTabWinCont = getTableView();
VclPtr<OTableWindow> aHoldSelf(this); // keep ourselves alive during the RemoveTabWin process
pTabWinCont->RemoveTabWin( this );
pTabWinCont->Invalidate();
}
bool OTableWindow::ExistsAConn() const
{
return getTableView()->ExistsAConn(this);
}
void OTableWindow::EnumValidFields(std::vector< OUString>& arrstrFields)
{
arrstrFields.clear();
weld::TreeView& rTreeView = m_xListBox->get_widget();
// This default implementation counts every item in the ListBox ... for any other behaviour it must be over-written
rTreeView.all_foreach([&rTreeView, &arrstrFields](weld::TreeIter& rEntry){
arrstrFields.push_back(rTreeView.get_text(rEntry));
return false;
});
}
void OTableWindow::StateChanged( StateChangedType nType )
{
Window::StateChanged( nType );
// FIXME RenderContext
if ( nType != StateChangedType::Zoom )
return;
const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
vcl::Font aFont = rStyleSettings.GetGroupFont();
if ( IsControlFont() )
aFont.Merge( GetControlFont() );
SetZoomedPointFont(*GetOutDev(), aFont);
m_xTitle->SetZoom(GetZoom());
m_xListBox->SetZoom(GetZoom());
Resize();
Invalidate();
}
Reference< XAccessible > OTableWindow::CreateAccessible()
{
return new OTableWindowAccess(this);
}
void OTableWindow::Command(const CommandEvent& rEvt)
{
switch (rEvt.GetCommand())
{
case CommandEventId::ContextMenu:
{
OJoinController& rController = getDesignView()->getController();
if(!rController.isReadOnly() && rController.isConnected())
{
Point ptWhere;
if ( rEvt.IsMouseEvent() )
ptWhere = rEvt.GetMousePosPixel();
else
{
weld::TreeView& rTreeView = m_xListBox->get_widget();
std::unique_ptr<weld::TreeIter> xCurrent = rTreeView.make_iterator();
if (rTreeView.get_cursor(xCurrent.get()))
ptWhere = rTreeView.get_row_area(*xCurrent).Center();
else
ptWhere = m_xTitle->GetPosPixel();
}
::tools::Rectangle aRect(ptWhere, Size(1, 1));
weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect);
std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, u"dbaccess/ui/jointablemenu.ui"_ustr));
std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu(u"menu"_ustr));
if (!xContextMenu->popup_at_rect(pPopupParent, aRect).isEmpty())
Remove();
}
break;
}
default:
Window::Command(rEvt);
}
}
bool OTableWindow::PreNotify(NotifyEvent& rNEvt)
{
bool bHandled = false;
switch (rNEvt.GetType())
{
case NotifyEventType::KEYINPUT:
{
if ( getDesignView()->getController().isReadOnly() )
break;
const KeyEvent* pKeyEvent = rNEvt.GetKeyEvent();
const vcl::KeyCode& rCode = pKeyEvent->GetKeyCode();
if ( rCode.IsMod1() )
{
Point aStartPoint = GetPosPixel();
if ( rCode.IsShift() )
{
aStartPoint.setX( GetSizePixel().Width() );
aStartPoint.setY( GetSizePixel().Height() );
}
switch( rCode.GetCode() )
{
case KEY_DOWN:
bHandled = true;
aStartPoint.AdjustY(m_nMoveIncrement );
break;
case KEY_UP:
bHandled = true;
aStartPoint.AdjustY(-m_nMoveIncrement );
break;
case KEY_LEFT:
bHandled = true;
aStartPoint.AdjustX(-m_nMoveIncrement );
break;
case KEY_RIGHT:
bHandled = true;
aStartPoint.AdjustX(m_nMoveIncrement );
break;
}
if ( bHandled )
{
if ( rCode.IsShift() )
{
OJoinTableView* pView = getTableView();
Point ptOld = GetPosPixel();
Size aSize = pView->getRealOutputSize();
Size aNewSize(aStartPoint.X(),aStartPoint.Y());
if ( ((ptOld.X() + aNewSize.Width()) <= aSize.Width())
&& ((ptOld.Y() + aNewSize.Height()) <= aSize.Height()) )
{
if ( aNewSize.Width() < TABWIN_WIDTH_MIN )
aNewSize.setWidth( TABWIN_WIDTH_MIN );
if ( aNewSize.Height() < TABWIN_HEIGHT_MIN )
aNewSize.setHeight( TABWIN_HEIGHT_MIN );
Size szOld = GetSizePixel();
aNewSize = Size(pView->CalcZoom(aNewSize.Width()),pView->CalcZoom(aNewSize.Height()));
SetPosSizePixel( ptOld, aNewSize );
pView->TabWinSized(this, ptOld, szOld);
Invalidate( InvalidateFlags::NoChildren );
}
}
else
{
// remember how often the user moved our window
++m_nMoveCount;
if( m_nMoveCount == 5 )
m_nMoveIncrement = 10;
else if( m_nMoveCount > 15 )
m_nMoveCount = m_nMoveIncrement = 20;
Point aOldDataPoint = GetData()->GetPosition();
Point aNewDataPoint = aStartPoint + getTableView()->GetScrollOffset();
if ( aNewDataPoint.X() > -1 && aNewDataPoint.Y() > -1 )
{
OJoinTableView* pView = getTableView();
if ( pView->isMovementAllowed(aNewDataPoint, GetData()->GetSize()) )
{
SetPosPixel(aStartPoint);
// aNewDataPoint can not be used here because SetPosPixel reset it
pView->EnsureVisible(GetData()->GetPosition(), GetData()->GetSize());
pView->TabWinMoved(this,aOldDataPoint);
Invalidate(InvalidateFlags::NoChildren);
getDesignView()->getController().setModified( true );
}
else
{
m_nMoveCount = 0; // reset our movement count
m_nMoveIncrement = 1;
}
}
else
{
m_nMoveCount = 0; // reset our movement count
m_nMoveIncrement = 1;
}
}
m_nSizingFlags = SizingFlags::NONE;
}
else
{
m_nMoveCount = 0; // reset our movement count
m_nMoveIncrement = 1;
}
}
else
{
m_nMoveCount = 0; // reset our movement count
m_nMoveIncrement = 1;
}
break;
}
case NotifyEventType::KEYUP:
{
const KeyEvent* pKeyEvent = rNEvt.GetKeyEvent();
const vcl::KeyCode& rCode = pKeyEvent->GetKeyCode();
sal_uInt16 nKeyCode = rCode.GetCode();
if ( rCode.IsMod2() && nKeyCode != KEY_UP && nKeyCode != KEY_DOWN && nKeyCode != KEY_LEFT && nKeyCode != KEY_RIGHT )
{
m_nMoveCount = 0; // reset our movement count
m_nMoveIncrement = 1;
}
break;
}
default:
break;
}
if (!bHandled)
return Window::PreNotify(rNEvt);
return true;
}
OUString OTableWindow::getTitle() const
{
return m_xTitle->GetLabel().get_label();
}
void OTableWindow::_elementInserted( const container::ContainerEvent& /*_rEvent*/ )
{
FillListBox();
}
void OTableWindow::_elementRemoved( const container::ContainerEvent& /*_rEvent*/ )
{
FillListBox();
}
void OTableWindow::_elementReplaced( const container::ContainerEvent& /*_rEvent*/ )
{
FillListBox();
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */