diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /dbaccess/source/ui/querydesign | |
parent | Initial commit. (diff) | |
download | libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.tar.xz libreoffice-940b4d1848e8c70ab7642901a68594e8016caffc.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'dbaccess/source/ui/querydesign')
50 files changed, 17209 insertions, 0 deletions
diff --git a/dbaccess/source/ui/querydesign/ConnectionLine.cxx b/dbaccess/source/ui/querydesign/ConnectionLine.cxx new file mode 100644 index 000000000..cf5af7def --- /dev/null +++ b/dbaccess/source/ui/querydesign/ConnectionLine.cxx @@ -0,0 +1,344 @@ +/* -*- 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 <ConnectionLine.hxx> +#include <ConnectionLineData.hxx> +#include <TableWindow.hxx> +#include <TableWindowListBox.hxx> +#include <TableConnection.hxx> +#include <vcl/svapp.hxx> +#include <math.h> +#include <osl/diagnose.h> +#include <vcl/lineinfo.hxx> +#include <vcl/settings.hxx> + +using namespace dbaui; +const long DESCRIPT_LINE_WIDTH = 15; +const long HIT_SENSITIVE_RADIUS = 5; + +namespace +{ + /** calcRect creates a new rectangle with the given points + @param _rBase the base point + @param _aVector the vector which will be added + */ + tools::Rectangle calcRect(const Point& _rBase,const Point& _aVector) + { + return tools::Rectangle( _rBase - _aVector, _rBase + _aVector ); + } + /** GetTextPos calculate the rectangle for the connection to be drawn + @param _pWin the table window where to draw it + @param _aConnPos the connection point + @param _aDescrLinePos the description line pos + */ + tools::Rectangle GetTextPos(const OTableWindow* _pWin, const Point& _aConnPos,const Point& _aDescrLinePos) + { + VclPtr<OTableWindowListBox> pListBox = _pWin ? _pWin->GetListBox() : nullptr; + OSL_ENSURE(_pWin && pListBox, "OConnectionLine::GetSourceTextPos : invalid call !"); + + tools::Rectangle aReturn; + if ( pListBox ) + { + const long nRowHeight = pListBox->GetEntryHeight(); + aReturn.SetTop( _aConnPos.Y() - nRowHeight ); + aReturn.SetBottom( aReturn.Top() + nRowHeight ); + if (_aDescrLinePos.X() < _aConnPos.X()) + { + aReturn.SetLeft( _aDescrLinePos.X() ); + aReturn.SetRight( aReturn.Left() + _aConnPos.X() - _aDescrLinePos.X() ); + } + else + { + aReturn.SetLeft( _aConnPos.X() ); + aReturn.SetRight( aReturn.Left() + _aDescrLinePos.X() - _aConnPos.X() ); + } + } + + return aReturn; + } + /** calcPointsYValue calculate the points Y value in relation to the listbox entry + @param _pWin the corresponding window + @param _pEntry the source or dest entry + @param _rNewConPos (in/out) the connection pos + @param _rNewDescrPos (in/out) the description pos + */ + void calcPointsYValue(const OTableWindow* _pWin,SvTreeListEntry* _pEntry,Point& _rNewConPos,Point& _rNewDescrPos) + { + const OTableWindowListBox* pListBox = _pWin->GetListBox(); + _rNewConPos.setY( _pWin->GetPosPixel().Y() ); + if ( _pEntry ) + { + const long nRowHeight = pListBox->GetEntryHeight(); + _rNewConPos.AdjustY(pListBox->GetPosPixel().Y() ); + long nEntryPos = pListBox->GetEntryPosition( _pEntry ).Y(); + + if( nEntryPos >= 0 ) + { + _rNewConPos.AdjustY(nEntryPos ); + _rNewConPos.AdjustY(static_cast<long>( 0.5 * nRowHeight ) ); + } + else + _rNewConPos.AdjustY( -static_cast<long>( 0.5 * nRowHeight ) ); + + long nListBoxBottom = _pWin->GetPosPixel().Y() + + pListBox->GetPosPixel().Y() + + pListBox->GetSizePixel().Height(); + if( _rNewConPos.Y() > nListBoxBottom ) + _rNewConPos.setY( nListBoxBottom + 2 ); + } + else + _rNewConPos.AdjustY(static_cast<sal_Int32>(pListBox->GetPosPixel().Y()*0.5) ); + + _rNewDescrPos.setY( _rNewConPos.Y() ); + } +} + +OConnectionLine::OConnectionLine( OTableConnection* _pConn, OConnectionLineDataRef const & _pLineData ) + : m_pTabConn( _pConn ) + , m_pData( _pLineData ) +{ +} + +OConnectionLine::OConnectionLine( const OConnectionLine& _rLine ) + : m_pTabConn(nullptr) +{ + m_pData = new OConnectionLineData( *_rLine.GetData() ); + *this = _rLine; +} + +OConnectionLine::~OConnectionLine() +{ +} + +OConnectionLine& OConnectionLine::operator=( const OConnectionLine& rLine ) +{ + if( &rLine != this ) + { + // as the data does not belong to me, I don't delete the old one + m_pData->CopyFrom(*rLine.GetData()); + // CopyFrom is virtual, therefore it is not a problem, if m_pData is of a type derived from OTableConnectionData + + m_pTabConn = rLine.m_pTabConn; + m_aSourceConnPos = rLine.m_aSourceConnPos; + m_aDestConnPos = rLine.m_aDestConnPos; + m_aSourceDescrLinePos = rLine.m_aSourceDescrLinePos; + m_aDestDescrLinePos = rLine.m_aDestDescrLinePos; + } + + return *this; +} + +tools::Rectangle OConnectionLine::GetBoundingRect() const +{ + // determine surrounding rectangle + tools::Rectangle aBoundingRect( Point(0,0), Point(0,0) ); + if( !IsValid() ) + return aBoundingRect; + + Point aTopLeft; + Point aBottomRight; + + if( m_aSourceDescrLinePos.Y() <= m_aDestDescrLinePos.Y() ) + { + aTopLeft.setY( m_aSourceDescrLinePos.Y() ); + aBottomRight.setY( m_aDestDescrLinePos.Y() ); + } + else + { + aTopLeft.setY( m_aDestDescrLinePos.Y() ); + aBottomRight.setY( m_aSourceDescrLinePos.Y() ); + } + + if( m_aSourceDescrLinePos.X() <= m_aDestDescrLinePos.X() ) + { + aTopLeft.setX( m_aSourceDescrLinePos.X() ); + aBottomRight.setX( m_aDestDescrLinePos.X() ); + } + else + { + aTopLeft.setX( m_aDestDescrLinePos.X() ); + aBottomRight.setX( m_aSourceDescrLinePos.X() ); + } + + const OTableWindow* pSourceWin = m_pTabConn->GetSourceWin(); + const OTableWindow* pDestWin = m_pTabConn->GetDestWin(); + // line proceeds in z-Form + if( pSourceWin == pDestWin || std::abs(m_aSourceConnPos.X() - m_aDestConnPos.X()) > std::abs(m_aSourceDescrLinePos.X() - m_aDestDescrLinePos.X()) ) + { + aTopLeft.AdjustX( -DESCRIPT_LINE_WIDTH ); + aBottomRight.AdjustX(DESCRIPT_LINE_WIDTH ); + } + + aBoundingRect = tools::Rectangle( aTopLeft-Point(2,17), aBottomRight+Point(2,2) ); + + return aBoundingRect; +} + +static void calcPointX1(const OTableWindow* _pWin,Point& _rNewConPos,Point& _rNewDescrPos) +{ + _rNewConPos.setX( _pWin->GetPosPixel().X() + _pWin->GetSizePixel().Width() ); + _rNewDescrPos.setX( _rNewConPos.X() ); + _rNewConPos.AdjustX(DESCRIPT_LINE_WIDTH ); +} + +static void calcPointX2(const OTableWindow* _pWin,Point& _rNewConPos,Point& _rNewDescrPos) +{ + _rNewConPos.setX( _pWin->GetPosPixel().X() ); + _rNewDescrPos.setX( _rNewConPos.X() ); + _rNewConPos.AdjustX( -DESCRIPT_LINE_WIDTH ); +} + +bool OConnectionLine::RecalcLine() +{ + // Windows and entries must be set + const OTableWindow* pSourceWin = m_pTabConn->GetSourceWin(); + const OTableWindow* pDestWin = m_pTabConn->GetDestWin(); + + if( !pSourceWin || !pDestWin ) + return false; + + SvTreeListEntry* pSourceEntry = pSourceWin->GetListBox()->GetEntryFromText( GetData()->GetSourceFieldName() ); + SvTreeListEntry* pDestEntry = pDestWin->GetListBox()->GetEntryFromText( GetData()->GetDestFieldName() ); + + // determine X-coordinates + Point aSourceCenter( 0, 0 ); + Point aDestCenter( 0, 0 ); + + aSourceCenter.setX( pSourceWin->GetPosPixel().X() + static_cast<long>( 0.5*pSourceWin->GetSizePixel().Width() ) ); + aDestCenter.setX( pDestWin->GetPosPixel().X() + static_cast<long>( 0.5*pDestWin->GetSizePixel().Width() ) ); + + const OTableWindow* pFirstWin = pDestWin; + const OTableWindow* pSecondWin = pSourceWin; + Point* pFirstConPos = &m_aDestConnPos; + Point* pFirstDescrPos = &m_aDestDescrLinePos; + Point* pSecondConPos = &m_aSourceConnPos; + Point* pSecondDescrPos = &m_aSourceDescrLinePos; + if( aDestCenter.X() > aSourceCenter.X() ) + { + pFirstWin = pSourceWin; + pSecondWin = pDestWin; + pFirstConPos = &m_aSourceConnPos; + pFirstDescrPos = &m_aSourceDescrLinePos; + pSecondConPos = &m_aDestConnPos; + pSecondDescrPos = &m_aDestDescrLinePos; + } + + if ( pFirstWin == pSecondWin && pSourceEntry != pDestEntry ) + calcPointX2(pFirstWin,*pFirstConPos,*pFirstDescrPos); + else + calcPointX1(pFirstWin,*pFirstConPos,*pFirstDescrPos); + calcPointX2(pSecondWin,*pSecondConPos,*pSecondDescrPos); + + // determine aSourceConnPosY + calcPointsYValue(pSourceWin,pSourceEntry,m_aSourceConnPos,m_aSourceDescrLinePos); + + // determine aDestConnPosY + calcPointsYValue(pDestWin,pDestEntry,m_aDestConnPos,m_aDestDescrLinePos); + + return true; +} + +void OConnectionLine::Draw( OutputDevice* pOutDev ) +{ + const sal_uInt16 nRectSize = 3; + + // calculate new dimension + if( !RecalcLine() ) + return; + + // draw lines + if (m_pTabConn->IsSelected()) + pOutDev->SetLineColor(Application::GetSettings().GetStyleSettings().GetHighlightColor()); + else + pOutDev->SetLineColor(Application::GetSettings().GetStyleSettings().GetWindowTextColor()); + + LineInfo aLineInfo; + if ( m_pTabConn->IsSelected() ) + aLineInfo.SetWidth(3); + tools::Polygon aPoly; + aPoly.Insert(0,m_aSourceDescrLinePos); + aPoly.Insert(1,m_aSourceConnPos); + aPoly.Insert(2,m_aDestConnPos); + aPoly.Insert(3,m_aDestDescrLinePos); + pOutDev->DrawPolyLine(aPoly,aLineInfo); + + // draw the connection rectangles + pOutDev->SetFillColor(Application::GetSettings().GetStyleSettings().GetWindowColor()); + + Point aVector(nRectSize,nRectSize); + pOutDev->DrawRect( calcRect(m_aSourceDescrLinePos,aVector) ); + pOutDev->DrawRect( calcRect( m_aDestDescrLinePos,aVector) ); +} + +bool OConnectionLine::IsValid() const +{ + return m_pData.is(); +} + +static double dist_Euklid(const Point &p1, const Point& p2,const Point& pM, Point& q) +{ + Point v(p2 - p1); + Point w(pM - p1); + double a = sqrt(static_cast<double>(v.X()*v.X() + v.Y()*v.Y())); + double l = (v.X() * w.Y() - v.Y() * w.X()) / a; + double a2 = w.X()*v.X()+w.Y()*v.Y(); + a = a2 / (a * a); + q.setX( long(p1.X() + a * v.X()) ); + q.setY( long(p1.Y() + a * v.Y()) ); + return l; +} + +bool OConnectionLine::CheckHit( const Point& rMousePos ) const +{ + /* + course of action with HitTest: + the distance is calculated according to Euklid. + */ + Point q; + double l = fabs(dist_Euklid(m_aSourceConnPos,m_aDestConnPos,rMousePos,q)); + if( l < HIT_SENSITIVE_RADIUS) + { + if(std::min(m_aSourceConnPos.X(),m_aDestConnPos.X()) <= q.X() && std::min(m_aSourceConnPos.Y(),m_aDestConnPos.Y()) <= q.Y() + && q.X() <= std::max(m_aDestConnPos.X(),m_aSourceConnPos.X()) && q.Y() <= std::max(m_aDestConnPos.Y(),m_aSourceConnPos.Y())) + return true; + } + + return false; +} + +tools::Rectangle OConnectionLine::GetSourceTextPos() const +{ + return GetTextPos(m_pTabConn->GetSourceWin(),m_aSourceConnPos,m_aSourceDescrLinePos); +} + +tools::Rectangle OConnectionLine::GetDestTextPos() const +{ + return GetTextPos(m_pTabConn->GetDestWin(),m_aDestConnPos,m_aDestDescrLinePos); +} + +Point OConnectionLine::getMidPoint() const +{ + Point aDest = m_aDestConnPos - m_aSourceConnPos; + aDest.setX( aDest.X() / 2 ); + aDest.setY( aDest.Y() / 2 ); + + return m_aSourceConnPos + aDest; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/ConnectionLineAccess.cxx b/dbaccess/source/ui/querydesign/ConnectionLineAccess.cxx new file mode 100644 index 000000000..763e53990 --- /dev/null +++ b/dbaccess/source/ui/querydesign/ConnectionLineAccess.cxx @@ -0,0 +1,187 @@ +/* -*- 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 <ConnectionLineAccess.hxx> +#include <ConnectionLine.hxx> +#include <JoinTableView.hxx> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleRelationType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <TableConnection.hxx> +#include <TableWindow.hxx> +#include <comphelper/sequence.hxx> + +namespace dbaui +{ + using namespace ::com::sun::star::accessibility; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star; + + OConnectionLineAccess::OConnectionLineAccess(OTableConnection* _pLine) + : VCLXAccessibleComponent(_pLine->GetComponentInterface().is() ? _pLine->GetWindowPeer() : nullptr) + ,m_pLine(_pLine) + { + } + void SAL_CALL OConnectionLineAccess::disposing() + { + m_pLine = nullptr; + VCLXAccessibleComponent::disposing(); + } + Any SAL_CALL OConnectionLineAccess::queryInterface( const Type& aType ) + { + Any aRet(VCLXAccessibleComponent::queryInterface( aType )); + return aRet.hasValue() ? aRet : OConnectionLineAccess_BASE::queryInterface( aType ); + } + Sequence< Type > SAL_CALL OConnectionLineAccess::getTypes( ) + { + return ::comphelper::concatSequences(VCLXAccessibleComponent::getTypes(),OConnectionLineAccess_BASE::getTypes()); + } + OUString SAL_CALL OConnectionLineAccess::getImplementationName() + { + return "org.openoffice.comp.dbu.ConnectionLineAccessibility"; + } + // XAccessibleContext + sal_Int32 SAL_CALL OConnectionLineAccess::getAccessibleChildCount( ) + { + return 0; + } + Reference< XAccessible > SAL_CALL OConnectionLineAccess::getAccessibleChild( sal_Int32 /*i*/ ) + { + return Reference< XAccessible >(); + } + sal_Int32 SAL_CALL OConnectionLineAccess::getAccessibleIndexInParent( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + sal_Int32 nIndex = -1; + if( m_pLine ) + { + // search the position of our table window in the table window map + // TODO JNA Shouldn't nIndex begin at 0? + nIndex = m_pLine->GetParent()->GetTabWinMap().size(); + const auto& rVec = m_pLine->GetParent()->getTableConnections(); + bool bFound = false; + for (auto const& elem : rVec) + { + if (elem.get() == m_pLine) + { + bFound = true; + break; + } + ++nIndex; + } + nIndex = bFound ? nIndex : -1; + } + return nIndex; + } + sal_Int16 SAL_CALL OConnectionLineAccess::getAccessibleRole( ) + { + return AccessibleRole::UNKNOWN; // ? or may be an AccessibleRole::WINDOW + } + OUString SAL_CALL OConnectionLineAccess::getAccessibleDescription( ) + { + return "Relation"; + } + Reference< XAccessibleRelationSet > SAL_CALL OConnectionLineAccess::getAccessibleRelationSet( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + return this; + } + // XAccessibleComponent + Reference< XAccessible > SAL_CALL OConnectionLineAccess::getAccessibleAtPoint( const awt::Point& /*_aPoint*/ ) + { + return Reference< XAccessible >(); + } + awt::Rectangle SAL_CALL OConnectionLineAccess::getBounds( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + tools::Rectangle aRect(m_pLine ? m_pLine->GetBoundingRect() : tools::Rectangle()); + return awt::Rectangle(aRect.getX(),aRect.getY(),aRect.getWidth(),aRect.getHeight()); + } + awt::Point SAL_CALL OConnectionLineAccess::getLocation( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + Point aPoint(m_pLine ? m_pLine->GetBoundingRect().TopLeft() : Point()); + return awt::Point(aPoint.X(),aPoint.Y()); + } + awt::Point SAL_CALL OConnectionLineAccess::getLocationOnScreen( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + Point aPoint(m_pLine ? m_pLine->GetParent()->ScreenToOutputPixel(m_pLine->GetBoundingRect().TopLeft()) : Point()); + return awt::Point(aPoint.X(),aPoint.Y()); + } + awt::Size SAL_CALL OConnectionLineAccess::getSize( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + Size aSize(m_pLine ? m_pLine->GetBoundingRect().GetSize() : Size()); + return awt::Size(aSize.Width(),aSize.Height()); + } + // XAccessibleRelationSet + sal_Int32 SAL_CALL OConnectionLineAccess::getRelationCount( ) + { + return 1; + } + AccessibleRelation SAL_CALL OConnectionLineAccess::getRelation( sal_Int32 nIndex ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if( nIndex < 0 || nIndex >= getRelationCount() ) + throw IndexOutOfBoundsException(); + + Sequence< Reference<XInterface> > aSeq(m_pLine ? 2 : 0); + if( m_pLine ) + { + aSeq[0] = m_pLine->GetSourceWin()->GetAccessible(); + aSeq[1] = m_pLine->GetDestWin()->GetAccessible(); + } + + return AccessibleRelation(AccessibleRelationType::CONTROLLED_BY,aSeq); + } + sal_Bool SAL_CALL OConnectionLineAccess::containsRelation( sal_Int16 aRelationType ) + { + return AccessibleRelationType::CONTROLLED_BY == aRelationType; + } + AccessibleRelation SAL_CALL OConnectionLineAccess::getRelationByType( sal_Int16 aRelationType ) + { + if( AccessibleRelationType::CONTROLLED_BY == aRelationType ) + return getRelation(0); + return AccessibleRelation(); + } + Reference< XAccessible > OTableConnection::CreateAccessible() + { + return new OConnectionLineAccess(this); + } + OTableConnection::~OTableConnection() + { + disposeOnce(); + } + void OTableConnection::dispose() + { + // clear vector + clearLineData(); + m_pParent.clear(); + vcl::Window::dispose(); + } + Reference< XAccessibleContext > SAL_CALL OConnectionLineAccess::getAccessibleContext( ) + { + return this; + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/ConnectionLineData.cxx b/dbaccess/source/ui/querydesign/ConnectionLineData.cxx new file mode 100644 index 000000000..db7177294 --- /dev/null +++ b/dbaccess/source/ui/querydesign/ConnectionLineData.cxx @@ -0,0 +1,76 @@ +/* -*- 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 <ConnectionLineData.hxx> + +using namespace dbaui; +OConnectionLineData::OConnectionLineData() +{ +} + +OConnectionLineData::OConnectionLineData( const OUString& rSourceFieldName, const OUString& rDestFieldName ) + :m_aSourceFieldName( rSourceFieldName ) + ,m_aDestFieldName( rDestFieldName ) +{ +} + +OConnectionLineData::OConnectionLineData( const OConnectionLineData& rConnLineData ) + : ::salhelper::SimpleReferenceObject() +{ + *this = rConnLineData; +} + +OConnectionLineData::~OConnectionLineData() +{ +} + +void OConnectionLineData::CopyFrom(const OConnectionLineData& rSource) +{ + *this = rSource; + // Here I rely on the (non-virtual) operator=, which only copies my members +} + +OConnectionLineData& OConnectionLineData::operator=( const OConnectionLineData& rConnLineData ) +{ + if (&rConnLineData == this) + return *this; + + m_aSourceFieldName = rConnLineData.GetSourceFieldName(); + m_aDestFieldName = rConnLineData.GetDestFieldName(); + + return *this; +} + +void OConnectionLineData::Reset() +{ + m_aDestFieldName.clear(); + m_aSourceFieldName.clear(); +} + +namespace dbaui +{ +bool operator==(const OConnectionLineData& lhs, const OConnectionLineData& rhs) +{ + return (lhs.m_aSourceFieldName == rhs.m_aSourceFieldName) + && (lhs.m_aDestFieldName == rhs.m_aDestFieldName); +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/JAccess.cxx b/dbaccess/source/ui/querydesign/JAccess.cxx new file mode 100644 index 000000000..8068f0fa7 --- /dev/null +++ b/dbaccess/source/ui/querydesign/JAccess.cxx @@ -0,0 +1,91 @@ +/* -*- 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 <JAccess.hxx> +#include <JoinTableView.hxx> +#include <TableWindow.hxx> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <TableConnection.hxx> +#include <o3tl/safeint.hxx> + +namespace dbaui +{ + using namespace ::com::sun::star::accessibility; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::lang; + + OJoinDesignViewAccess::OJoinDesignViewAccess(OJoinTableView* _pTableView) + :VCLXAccessibleComponent(_pTableView->GetComponentInterface().is() ? _pTableView->GetWindowPeer() : nullptr) + ,m_pTableView(_pTableView) + { + } + OUString SAL_CALL OJoinDesignViewAccess::getImplementationName() + { + return "org.openoffice.comp.dbu.JoinViewAccessibility"; + } + void OJoinDesignViewAccess::clearTableView() + { + ::osl::MutexGuard aGuard( m_aMutex ); + m_pTableView = nullptr; + } + // XAccessibleContext + sal_Int32 SAL_CALL OJoinDesignViewAccess::getAccessibleChildCount( ) + { + // TODO may be this will change to only visible windows + // this is the same assumption mt implements + ::osl::MutexGuard aGuard( m_aMutex ); + sal_Int32 nChildCount = 0; + if ( m_pTableView ) + nChildCount = m_pTableView->GetTabWinCount() + m_pTableView->getTableConnections().size(); + return nChildCount; + } + Reference< XAccessible > SAL_CALL OJoinDesignViewAccess::getAccessibleChild( sal_Int32 i ) + { + Reference< XAccessible > aRet; + ::osl::MutexGuard aGuard( m_aMutex ); + if(i < 0 || i >= getAccessibleChildCount() || !m_pTableView) + throw IndexOutOfBoundsException(); + // check if we should return a table window or a connection + sal_Int32 nTableWindowCount = m_pTableView->GetTabWinCount(); + if( i < nTableWindowCount ) + { + OJoinTableView::OTableWindowMap::const_iterator aIter = std::next(m_pTableView->GetTabWinMap().begin(), i); + aRet = aIter->second->GetAccessible(); + } + else if( o3tl::make_unsigned(i - nTableWindowCount) < m_pTableView->getTableConnections().size() ) + aRet = m_pTableView->getTableConnections()[i - nTableWindowCount]->GetAccessible(); + return aRet; + } + sal_Int16 SAL_CALL OJoinDesignViewAccess::getAccessibleRole( ) + { + return AccessibleRole::VIEW_PORT; + } + Reference< XAccessibleContext > SAL_CALL OJoinDesignViewAccess::getAccessibleContext( ) + { + return this; + } + // XInterface + IMPLEMENT_FORWARD_XINTERFACE2( OJoinDesignViewAccess, VCLXAccessibleComponent, OJoinDesignViewAccess_BASE ) + // XTypeProvider + IMPLEMENT_FORWARD_XTYPEPROVIDER2( OJoinDesignViewAccess, VCLXAccessibleComponent, OJoinDesignViewAccess_BASE ) +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/JoinController.cxx b/dbaccess/source/ui/querydesign/JoinController.cxx new file mode 100644 index 000000000..49ad53027 --- /dev/null +++ b/dbaccess/source/ui/querydesign/JoinController.cxx @@ -0,0 +1,405 @@ +/* -*- 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 <browserids.hxx> +#include <com/sun/star/sdbc/XRow.hpp> +#include <connectivity/dbexception.hxx> +#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp> +#include <JoinController.hxx> +#include <TableWindowData.hxx> +#include <TableWindow.hxx> +#include <TableConnectionData.hxx> +#include <adtabdlg.hxx> +#include <vcl/svapp.hxx> +#include <osl/mutex.hxx> +#include <osl/diagnose.h> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::frame; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::ui::dialogs; +using namespace ::dbtools; +using namespace ::comphelper; + +namespace dbaui +{ + +// AddTableDialogContext +class AddTableDialogContext : public IAddTableDialogContext +{ + OJoinController& m_rController; + +public: + explicit AddTableDialogContext( OJoinController& _rController ) + :m_rController( _rController ) + { + } + + virtual ~AddTableDialogContext() {} + + // IAddTableDialogContext + virtual css::uno::Reference< css::sdbc::XConnection > + getConnection() const override; + virtual bool allowViews() const override; + virtual bool allowQueries() const override; + virtual bool allowAddition() const override; + virtual void addTableWindow( const OUString& _rQualifiedTableName, const OUString& _rAliasName ) override; + virtual void onWindowClosing() override; + +private: + OJoinTableView* getTableView() const; +}; + +Reference< XConnection > AddTableDialogContext::getConnection() const +{ + return m_rController.getConnection(); +} + +bool AddTableDialogContext::allowViews() const +{ + return m_rController.allowViews(); +} + +bool AddTableDialogContext::allowQueries() const +{ + return m_rController.allowQueries(); +} + +bool AddTableDialogContext::allowAddition() const +{ + return m_rController.getJoinView()->getTableView()->IsAddAllowed(); +} + +void AddTableDialogContext::addTableWindow( const OUString& _rQualifiedTableName, const OUString& _rAliasName ) +{ + getTableView()->AddTabWin( _rQualifiedTableName, _rAliasName, true ); +} + +void AddTableDialogContext::onWindowClosing() +{ + if (!m_rController.getView()) + return; + m_rController.InvalidateFeature( ID_BROWSER_ADDTABLE ); + m_rController.getView()->GrabFocus(); +} + +OJoinTableView* AddTableDialogContext::getTableView() const +{ + if ( m_rController.getJoinView() ) + return m_rController.getJoinView()->getTableView(); + return nullptr; +} + +// OJoinController + +OJoinController::OJoinController(const Reference< XComponentContext >& _rM) + : OJoinController_BASE(_rM) +{ +} + +OJoinController::~OJoinController() +{ +} + +OJoinDesignView* OJoinController::getJoinView() +{ + return static_cast< OJoinDesignView* >( getView() ); +} + +void OJoinController::disposing() +{ + if (m_xAddTableDialog) + { + m_xAddTableDialog->response(RET_CLOSE); + m_xAddTableDialog.reset(); + } + + OJoinController_BASE::disposing(); + + clearView(); + + m_vTableConnectionData.clear(); + m_vTableData.clear(); +} + +void OJoinController::reconnect( bool _bUI ) +{ + OJoinController_BASE::reconnect( _bUI ); + if ( isConnected() && m_xAddTableDialog ) + m_xAddTableDialog->Update(); +} + +void OJoinController::impl_onModifyChanged() +{ + OJoinController_BASE::impl_onModifyChanged(); + InvalidateFeature( SID_RELATION_ADD_RELATION ); +} + +void OJoinController::SaveTabWinPosSize(OTableWindow const * pTabWin, long nOffsetX, long nOffsetY) +{ + // the data for the window + const TTableWindowData::value_type& pData = pTabWin->GetData(); + OSL_ENSURE(pData != nullptr, "SaveTabWinPosSize : TabWin has no data !"); + + // set Position & Size of data anew (with current window parameters) + Point aPos = pTabWin->GetPosPixel(); + aPos.AdjustX(nOffsetX ); + aPos.AdjustY(nOffsetY ); + pData->SetPosition(aPos); + pData->SetSize(pTabWin->GetSizePixel()); + +} + +FeatureState OJoinController::GetState(sal_uInt16 _nId) const +{ + FeatureState aReturn; + // (disabled automatically) + aReturn.bEnabled = true; + + switch (_nId) + { + case ID_BROWSER_EDITDOC: + aReturn.bChecked = isEditable(); + break; + case ID_BROWSER_ADDTABLE: + aReturn.bEnabled = ( getView() != nullptr ) + && const_cast< OJoinController* >( this )->getJoinView()->getTableView()->IsAddAllowed(); + aReturn.bChecked = aReturn.bEnabled && m_xAddTableDialog; + if ( aReturn.bEnabled ) + aReturn.sTitle = OAddTableDlg::getDialogTitleForContext( impl_getDialogContext() ); + break; + + default: + aReturn = OJoinController_BASE::GetState(_nId); + } + return aReturn; +} + +AddTableDialogContext& OJoinController::impl_getDialogContext() const +{ + if (!m_pDialogContext) + { + OJoinController* pNonConstThis = const_cast< OJoinController* >( this ); + pNonConstThis->m_pDialogContext.reset( new AddTableDialogContext( *pNonConstThis ) ); + } + return *m_pDialogContext; +} + +void OJoinController::Execute(sal_uInt16 _nId, const Sequence< PropertyValue >& aArgs) +{ + switch(_nId) + { + case ID_BROWSER_EDITDOC: + if(isEditable()) + { // the state should be changed to not editable + switch (saveModified()) + { + case RET_CANCEL: + // don't change anything here so return + return; + case RET_NO: + reset(); + setModified(false); // and we are not modified yet + break; + default: + break; + } + } + setEditable(!isEditable()); + getJoinView()->setReadOnly(!isEditable()); + InvalidateAll(); + return; + case ID_BROWSER_ADDTABLE: + if (m_xAddTableDialog) + { + m_xAddTableDialog->response(RET_CLOSE); + getView()->GrabFocus(); + } + else + { + runDialogAsync(); + } + break; + default: + OJoinController_BASE::Execute(_nId,aArgs); + } + InvalidateFeature(_nId); +} + +void OJoinController::runDialogAsync() +{ + assert(!m_xAddTableDialog); + m_xAddTableDialog = std::make_shared<OAddTableDlg>(getFrameWeld(), impl_getDialogContext()); + { + weld::WaitObject aWaitCursor(getFrameWeld()); + m_xAddTableDialog->Update(); + } + weld::DialogController::runAsync(m_xAddTableDialog, [this](sal_Int32 /*nResult*/){ + m_xAddTableDialog->OnClose(); + m_xAddTableDialog.reset(); + }); +} + +void OJoinController::SaveTabWinsPosSize( OJoinTableView::OTableWindowMap* pTabWinList, long nOffsetX, long nOffsetY ) +{ + // Deletion and recreation of the old implementation with the current model is not correct anymore: + // The TabWins have a pointer to their data, but they are managed by me. When I delete the old ones, the TabWins suddenly have a pointer to objects, which no longer exist. + // If the TabWins had a SetData, I could save that effort... but they don't, further I also would still have to set information anew, which actually didn't change. + // So I don't delete the TabWinDatas, but only update them. + OSL_ENSURE(m_vTableData.size() == pTabWinList->size(), + "OJoinController::SaveTabWinsPosSize : inconsistent state : should have as many TabWinDatas as TabWins !"); + + for (auto const& tabWin : *pTabWinList) + SaveTabWinPosSize(tabWin.second, nOffsetX, nOffsetY); +} + +void OJoinController::removeConnectionData(const TTableConnectionData::value_type& _pData) +{ + m_vTableConnectionData.erase( std::remove(m_vTableConnectionData.begin(),m_vTableConnectionData.end(),_pData),m_vTableConnectionData.end()); +} + +void OJoinController::describeSupportedFeatures() +{ + OJoinController_BASE::describeSupportedFeatures(); + implDescribeSupportedFeature( ".uno:Redo", ID_BROWSER_REDO, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:Save", ID_BROWSER_SAVEDOC, CommandGroup::DOCUMENT ); + implDescribeSupportedFeature( ".uno:Undo", ID_BROWSER_UNDO, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:AddTable", ID_BROWSER_ADDTABLE,CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:EditDoc", ID_BROWSER_EDITDOC, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:GetUndoStrings", SID_GETUNDOSTRINGS ); + implDescribeSupportedFeature( ".uno:GetRedoStrings", SID_GETREDOSTRINGS ); +} + +sal_Bool SAL_CALL OJoinController::suspend(sal_Bool _bSuspend) +{ + if ( getBroadcastHelper().bInDispose || getBroadcastHelper().bDisposed ) + return true; + + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + if ( getView() && getView()->IsInModalMode() ) + return false; + bool bCheck = true; + if ( _bSuspend ) + { + bCheck = saveModified() != RET_CANCEL; + if ( bCheck ) + OSingleDocumentController::suspend(_bSuspend); + } + return bCheck; +} + +void OJoinController::loadTableWindows( const ::comphelper::NamedValueCollection& i_rViewSettings ) +{ + m_vTableData.clear(); + + m_aMinimumTableViewSize = Point(); + + Sequence< PropertyValue > aWindowData; + aWindowData = i_rViewSettings.getOrDefault( "Tables", aWindowData ); + + const PropertyValue* pTablesIter = aWindowData.getConstArray(); + const PropertyValue* pTablesEnd = pTablesIter + aWindowData.getLength(); + for ( ; pTablesIter != pTablesEnd; ++pTablesIter ) + { + ::comphelper::NamedValueCollection aSingleTableData( pTablesIter->Value ); + loadTableWindow( aSingleTableData ); + } + if ( m_aMinimumTableViewSize != Point() ) + { + getJoinView()->getScrollHelper()->resetRange( m_aMinimumTableViewSize ); + } +} + +void OJoinController::loadTableWindow( const ::comphelper::NamedValueCollection& i_rTableWindowSettings ) +{ + sal_Int32 nX = -1, nY = -1, nHeight = -1, nWidth = -1; + + OUString sComposedName,sTableName,sWindowName; + bool bShowAll = false; + + sComposedName = i_rTableWindowSettings.getOrDefault( "ComposedName", sComposedName ); + sTableName = i_rTableWindowSettings.getOrDefault( "TableName", sTableName ); + sWindowName = i_rTableWindowSettings.getOrDefault( "WindowName", sWindowName ); + nY = i_rTableWindowSettings.getOrDefault( "WindowTop", nY ); + nX = i_rTableWindowSettings.getOrDefault( "WindowLeft", nX ); + nWidth = i_rTableWindowSettings.getOrDefault( "WindowWidth", nWidth ); + nHeight = i_rTableWindowSettings.getOrDefault( "WindowHeight", nHeight ); + bShowAll = i_rTableWindowSettings.getOrDefault( "ShowAll", bShowAll ); + + TTableWindowData::value_type pData = createTableWindowData(sComposedName,sTableName,sWindowName); + if ( pData ) + { + pData->SetPosition(Point(nX,nY)); + pData->SetSize( Size( nWidth, nHeight ) ); + pData->ShowAll(bShowAll); + m_vTableData.push_back(pData); + if ( m_aMinimumTableViewSize.X() < (nX+nWidth) ) + m_aMinimumTableViewSize.setX( nX+nWidth ); + if ( m_aMinimumTableViewSize.Y() < (nY+nHeight) ) + m_aMinimumTableViewSize.setY( nY+nHeight ); + } +} + +void OJoinController::saveTableWindows( ::comphelper::NamedValueCollection& o_rViewSettings ) const +{ + if ( m_vTableData.empty() ) + return; + + ::comphelper::NamedValueCollection aAllTablesData; + + sal_Int32 i = 1; + for (auto const& elem : m_vTableData) + { + ::comphelper::NamedValueCollection aWindowData; + aWindowData.put( "ComposedName", elem->GetComposedName() ); + aWindowData.put( "TableName", elem->GetTableName() ); + aWindowData.put( "WindowName", elem->GetWinName() ); + aWindowData.put( "WindowTop", static_cast<sal_Int32>(elem->GetPosition().Y()) ); + aWindowData.put( "WindowLeft", static_cast<sal_Int32>(elem->GetPosition().X()) ); + aWindowData.put( "WindowWidth", static_cast<sal_Int32>(elem->GetSize().Width()) ); + aWindowData.put( "WindowHeight", static_cast<sal_Int32>(elem->GetSize().Height()) ); + aWindowData.put( "ShowAll", elem->IsShowAll() ); + + const OUString sTableName( "Table" + OUString::number( i++ ) ); + aAllTablesData.put( sTableName, aWindowData.getPropertyValues() ); + } + + o_rViewSettings.put( "Tables", aAllTablesData.getPropertyValues() ); +} + +TTableWindowData::value_type OJoinController::createTableWindowData(const OUString& _sComposedName,const OUString& _sTableName,const OUString& _sWindowName) +{ + OJoinDesignView* pView = getJoinView(); + if( pView ) + return pView->getTableView()->createTableWindowData(_sComposedName,_sTableName,_sWindowName); + OSL_FAIL("We should never ever reach this point!"); + + return TTableWindowData::value_type(); +} + +} // namespace dbaui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/JoinDesignView.cxx b/dbaccess/source/ui/querydesign/JoinDesignView.cxx new file mode 100644 index 000000000..c0d3ea81c --- /dev/null +++ b/dbaccess/source/ui/querydesign/JoinDesignView.cxx @@ -0,0 +1,100 @@ +/* -*- 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 <JoinDesignView.hxx> +#include <JoinTableView.hxx> +#include <JoinController.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::util; + +namespace dbaui +{ + +// OJoinDesignView +OJoinDesignView::OJoinDesignView(vcl::Window* _pParent, OJoinController& _rController,const Reference< XComponentContext >& _rxContext) + :ODataView( _pParent, _rController, _rxContext ) + ,m_pTableView(nullptr) + ,m_rController( _rController ) +{ + m_pScrollWindow = VclPtr<OScrollWindowHelper>::Create(this); +} + +OJoinDesignView::~OJoinDesignView() +{ + disposeOnce(); +} + +void OJoinDesignView::dispose() +{ + m_pTableView.disposeAndClear(); + m_pScrollWindow.disposeAndClear(); + ODataView::dispose(); +} + +void OJoinDesignView::Construct() +{ + m_pScrollWindow->setTableView(m_pTableView); + m_pScrollWindow->Show(); + m_pTableView->Show(); + + SetBackground( Wallpaper( Application::GetSettings().GetStyleSettings().GetFaceColor()) ); + + ODataView::Construct(); +} + +void OJoinDesignView::initialize() +{ +} + +void OJoinDesignView::resizeDocumentView(tools::Rectangle& _rPlayground) +{ + m_pScrollWindow->SetPosSizePixel( _rPlayground.TopLeft(), _rPlayground.GetSize() ); + + // just for completeness: there is no space left, we occupied it all ... + _rPlayground.SetPos( _rPlayground.BottomRight() ); + _rPlayground.SetSize( Size( 0, 0 ) ); +} + +void OJoinDesignView::setReadOnly(bool /*_bReadOnly*/) +{ +} + +void OJoinDesignView::SaveTabWinUIConfig(OTableWindow const * pWin) +{ + OJoinController::SaveTabWinPosSize(pWin, m_pScrollWindow->GetHScrollBar().GetThumbPos(), m_pScrollWindow->GetVScrollBar().GetThumbPos()); +} + +void OJoinDesignView::KeyInput( const KeyEvent& rEvt ) +{ + if ( m_pTableView && m_pTableView->IsVisible() ) + m_pTableView->KeyInput( rEvt ); + else + ODataView::KeyInput(rEvt); +} + +} // namespace dbaui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/JoinExchange.cxx b/dbaccess/source/ui/querydesign/JoinExchange.cxx new file mode 100644 index 000000000..312c9d8e9 --- /dev/null +++ b/dbaccess/source/ui/querydesign/JoinExchange.cxx @@ -0,0 +1,129 @@ +/* -*- 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 <JoinExchange.hxx> +#include <sot/formats.hxx> +#include <comphelper/servicehelper.hxx> +#include <cppuhelper/typeprovider.hxx> + +namespace dbaui +{ + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::datatransfer; + + OJoinExchObj::OJoinExchObj(const OJoinExchangeData& jxdSource,bool _bFirstEntry) + :m_bFirstEntry(_bFirstEntry) + ,m_jxdSourceDescription(jxdSource) + ,m_pDragListener(nullptr) + { + // add available types to list + } + + OJoinExchObj::~OJoinExchObj() + { + } + + void OJoinExchObj::StartDrag( vcl::Window* _pWindow, sal_Int8 _nDragSourceActions, IDragTransferableListener* _pListener ) + { + m_pDragListener = _pListener; + TransferableHelper::StartDrag(_pWindow, _nDragSourceActions); + } + + void OJoinExchObj::DragFinished( sal_Int8 /*nDropAction*/ ) + { + if (m_pDragListener) + m_pDragListener->dragFinished(); + m_pDragListener = nullptr; + } + + bool OJoinExchObj::isFormatAvailable( const DataFlavorExVector& _rFormats ,SotClipboardFormatId _nSlotID) + { + for (auto const& format : _rFormats) + { + if ( _nSlotID == format.mnSotId ) + return true; + } + return false; + } + + OJoinExchangeData OJoinExchObj::GetSourceDescription(const Reference< XTransferable >& _rxObject) + { + OJoinExchangeData aReturn; + auto pImplementation = comphelper::getUnoTunnelImplementation<OJoinExchObj>(_rxObject); + if (pImplementation) + aReturn = pImplementation->m_jxdSourceDescription; + return aReturn; + } + + Sequence< sal_Int8 > OJoinExchObj::getUnoTunnelId() + { + static ::cppu::OImplementationId implId; + + return implId.getImplementationId(); + } + + sal_Int64 SAL_CALL OJoinExchObj::getSomething( const Sequence< sal_Int8 >& _rIdentifier ) + { + if (isUnoTunnelId<OJoinExchObj>(_rIdentifier)) + return reinterpret_cast<sal_Int64>(this); + + return 0; + } + + void OJoinExchObj::AddSupportedFormats() + { + AddFormat( SotClipboardFormatId::SBA_JOIN ); + if ( m_bFirstEntry ) + AddFormat( SotClipboardFormatId::SBA_TABID ); + } + + bool OJoinExchObj::GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& /*rDestDoc*/ ) + { + SotClipboardFormatId nFormat = SotExchange::GetFormat(rFlavor); + if ( SotClipboardFormatId::SBA_JOIN == nFormat ) + // this is a HACK + // we don't really copy our data, the instances using us have to call GetSourceDescription... + // if, one day, we have a _lot_ of time, this hack should be removed... + return true; + + return false; + } + + Any SAL_CALL OJoinExchObj::queryInterface( const Type& _rType ) + { + Any aReturn = TransferableHelper::queryInterface(_rType); + if (!aReturn.hasValue()) + aReturn = OJoinExchObj_Base::queryInterface(_rType); + return aReturn; + } + + void SAL_CALL OJoinExchObj::acquire( ) throw() + { + TransferableHelper::acquire( ); + } + + void SAL_CALL OJoinExchObj::release( ) throw() + { + TransferableHelper::release( ); + } + +} // namespace dbaui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/JoinTableView.cxx b/dbaccess/source/ui/querydesign/JoinTableView.cxx new file mode 100644 index 000000000..54ea53f0e --- /dev/null +++ b/dbaccess/source/ui/querydesign/JoinTableView.cxx @@ -0,0 +1,1566 @@ +/* -*- 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 <JoinTableView.hxx> +#include <osl/diagnose.h> +#include <JoinController.hxx> +#include <JoinDesignView.hxx> +#include <TableWindow.hxx> +#include <TableWindowListBox.hxx> +#include <TableConnection.hxx> +#include <TableConnectionData.hxx> +#include <ConnectionLine.hxx> +#include <ConnectionLineData.hxx> +#include <browserids.hxx> +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include "QueryMoveTabWinUndoAct.hxx" +#include "QuerySizeTabWinUndoAct.hxx" +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> +#include <vcl/ptrstyle.hxx> +#include <vcl/builder.hxx> +#include <TableWindowData.hxx> +#include <JAccess.hxx> +#include <com/sun/star/accessibility/XAccessible.hpp> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <cppuhelper/exc_hlp.hxx> +#include <connectivity/dbtools.hxx> +#include <tools/diagnose_ex.h> +#include <algorithm> +#include <functional> + +using namespace dbaui; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::accessibility; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; + +#define LINE_SIZE 50 +// Constants for the window layout +#define TABWIN_SPACING_X 17 +#define TABWIN_SPACING_Y 17 + +#define TABWIN_WIDTH_STD 120 +#define TABWIN_HEIGHT_STD 120 + +OScrollWindowHelper::OScrollWindowHelper( vcl::Window* pParent) : Window( pParent) + ,m_aHScrollBar( VclPtr<ScrollBar>::Create(this, WB_HSCROLL|WB_REPEAT|WB_DRAG) ) + ,m_aVScrollBar( VclPtr<ScrollBar>::Create(this, WB_VSCROLL|WB_REPEAT|WB_DRAG) ) + ,m_pCornerWindow(VclPtr<ScrollBarBox>::Create(this, WB_3DLOOK)) + ,m_pTableView(nullptr) +{ + + // ScrollBars + + GetHScrollBar().SetRange( Range(0, 1000) ); + GetVScrollBar().SetRange( Range(0, 1000) ); + + GetHScrollBar().SetLineSize( LINE_SIZE ); + GetVScrollBar().SetLineSize( LINE_SIZE ); + + GetHScrollBar().Show(); + GetVScrollBar().Show(); + m_pCornerWindow->Show(); + + // normally we should be SCROLL_PANE + SetAccessibleRole(AccessibleRole::SCROLL_PANE); +} + +OScrollWindowHelper::~OScrollWindowHelper() +{ + disposeOnce(); +} + +void OScrollWindowHelper::dispose() +{ + m_aHScrollBar.disposeAndClear(); + m_aVScrollBar.disposeAndClear(); + m_pCornerWindow.disposeAndClear(); + m_pTableView.clear(); + vcl::Window::dispose(); +} + +void OScrollWindowHelper::setTableView(OJoinTableView* _pTableView) +{ + m_pTableView = _pTableView; + // ScrollBars + GetHScrollBar().SetScrollHdl( LINK(m_pTableView, OJoinTableView, ScrollHdl) ); + GetVScrollBar().SetScrollHdl( LINK(m_pTableView, OJoinTableView, ScrollHdl) ); +} + +void OScrollWindowHelper::resetRange(const Point& _aSize) +{ + Point aPos = PixelToLogic(_aSize); + GetHScrollBar().SetRange( Range(0, aPos.X() + TABWIN_SPACING_X) ); + GetVScrollBar().SetRange( Range(0, aPos.Y() + TABWIN_SPACING_Y) ); +} + +void OScrollWindowHelper::Resize() +{ + Window::Resize(); + + Size aTotalOutputSize = GetOutputSizePixel(); + long nHScrollHeight = GetHScrollBar().GetSizePixel().Height(); + long nVScrollWidth = GetVScrollBar().GetSizePixel().Width(); + + GetHScrollBar().SetPosSizePixel( + Point( 0, aTotalOutputSize.Height()-nHScrollHeight ), + Size( aTotalOutputSize.Width()-nVScrollWidth, nHScrollHeight ) + ); + + GetVScrollBar().SetPosSizePixel( + Point( aTotalOutputSize.Width()-nVScrollWidth, 0 ), + Size( nVScrollWidth, aTotalOutputSize.Height()-nHScrollHeight ) + ); + + m_pCornerWindow->SetPosSizePixel( + Point( aTotalOutputSize.Width() - nVScrollWidth, aTotalOutputSize.Height() - nHScrollHeight), + Size( nVScrollWidth, nHScrollHeight ) + ); + + GetHScrollBar().SetPageSize( aTotalOutputSize.Width() ); + GetHScrollBar().SetVisibleSize( aTotalOutputSize.Width() ); + + GetVScrollBar().SetPageSize( aTotalOutputSize.Height() ); + GetVScrollBar().SetVisibleSize( aTotalOutputSize.Height() ); + + // adjust the ranges of the scrollbars if necessary + long lRange = GetHScrollBar().GetRange().Max() - GetHScrollBar().GetRange().Min(); + if (m_pTableView->GetScrollOffset().X() + aTotalOutputSize.Width() > lRange) + GetHScrollBar().SetRangeMax(m_pTableView->GetScrollOffset().X() + aTotalOutputSize.Width() + GetHScrollBar().GetRange().Min()); + + lRange = GetVScrollBar().GetRange().Max() - GetVScrollBar().GetRange().Min(); + if (m_pTableView->GetScrollOffset().Y() + aTotalOutputSize.Height() > lRange) + GetVScrollBar().SetRangeMax(m_pTableView->GetScrollOffset().Y() + aTotalOutputSize.Height() + GetVScrollBar().GetRange().Min()); + + m_pTableView->SetPosSizePixel(Point( 0, 0 ),Size( aTotalOutputSize.Width()-nVScrollWidth, aTotalOutputSize.Height()-nHScrollHeight )); +} + + +OJoinTableView::OJoinTableView( vcl::Window* pParent, OJoinDesignView* pView ) + :Window( pParent,WB_BORDER ) + ,DropTargetHelper(this) + ,m_aDragOffset( Point(0,0) ) + ,m_aScrollOffset( Point(0,0) ) + ,m_pDragWin( nullptr ) + ,m_pSizingWin( nullptr ) + ,m_pSelectedConn( nullptr ) + ,m_pLastFocusTabWin(nullptr) + ,m_pView( pView ) + ,m_pAccessible(nullptr) +{ + SetSizePixel( Size(1000, 1000) ); + + InitColors(); + + m_aDragScrollIdle.SetInvokeHandler(LINK(this, OJoinTableView, OnDragScrollTimer)); +} + +OJoinTableView::~OJoinTableView() +{ + disposeOnce(); +} + +void OJoinTableView::dispose() +{ + if( m_pAccessible ) + { + m_pAccessible->clearTableView(); + m_pAccessible = nullptr; + } + // delete lists + clearLayoutInformation(); + m_pDragWin.clear(); + m_pSizingWin.clear(); + m_pSelectedConn.clear(); + m_pLastFocusTabWin.clear(); + m_pView.clear(); + m_vTableConnection.clear(); + vcl::Window::dispose(); +} + +IMPL_LINK( OJoinTableView, ScrollHdl, ScrollBar*, pScrollBar, void ) +{ + // move all windows + ScrollPane( pScrollBar->GetDelta(), (pScrollBar == &GetHScrollBar()), false ); +} + +void OJoinTableView::Resize() +{ + Window::Resize(); + m_aOutputSize = GetSizePixel(); + + // tab win positions may not be up-to-date + if (m_aTableMap.empty()) + // no tab wins ... + return; + + // we have at least one table so resize it + m_aScrollOffset.setX( GetHScrollBar().GetThumbPos() ); + m_aScrollOffset.setY( GetVScrollBar().GetThumbPos() ); + + VclPtr<OTableWindow> pCheck = m_aTableMap.begin()->second; + Point aRealPos = pCheck->GetPosPixel(); + Point aAssumedPos = pCheck->GetData()->GetPosition() - GetScrollOffset(); + + if (aRealPos == aAssumedPos) + // all ok + return; + + for (auto const& elem : m_aTableMap) + { + OTableWindow* pCurrent = elem.second; + Point aPos(pCurrent->GetData()->GetPosition() - GetScrollOffset()); + pCurrent->SetPosPixel(aPos); + } +} + +sal_uLong OJoinTableView::GetTabWinCount() const +{ + return m_aTableMap.size(); +} + +bool OJoinTableView::RemoveConnection(VclPtr<OTableConnection>& rConn, bool _bDelete) +{ + VclPtr<OTableConnection> xConn(rConn); + + DeselectConn(xConn); + + // to force a redraw + xConn->InvalidateConnection(); + + m_pView->getController().removeConnectionData(xConn->GetData()); + + m_vTableConnection.erase(std::find(m_vTableConnection.begin(), m_vTableConnection.end(), xConn)); + + modified(); + if ( m_pAccessible ) + m_pAccessible->notifyAccessibleEvent( AccessibleEventId::CHILD, + makeAny(xConn->GetAccessible()), + Any()); + if (_bDelete) + xConn->disposeOnce(); + + return true; +} + +OTableWindow* OJoinTableView::GetTabWindow( const OUString& rName ) +{ + OTableWindowMap::const_iterator aIter = m_aTableMap.find(rName); + + return aIter == m_aTableMap.end() ? nullptr : aIter->second; +} + +TTableWindowData::value_type OJoinTableView::createTableWindowData(const OUString& _rComposedName + ,const OUString& _sTableName + ,const OUString& _rWinName) +{ + TTableWindowData::value_type pData( CreateImpl(_rComposedName, _sTableName,_rWinName) ); + OJoinDesignView* pParent = getDesignView(); + try + { + if ( !pData->init(pParent->getController().getConnection(),allowQueries()) ) + { + if ( pData->isValid() ) + onNoColumns_throw(); + else + pData.reset(); + } + } + catch ( const SQLException& ) + { + ::dbtools::showError( ::dbtools::SQLExceptionInfo( ::cppu::getCaughtException() ), + VCLUnoHelper::GetInterface(pParent), pParent->getController().getORB() ); + } + catch( const WrappedTargetException& e ) + { + SQLException aSql; + if ( e.TargetException >>= aSql ) + ::dbtools::showError( ::dbtools::SQLExceptionInfo( aSql ), VCLUnoHelper::GetInterface(pParent), pParent->getController().getORB() ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return pData; +} + +std::shared_ptr<OTableWindowData> OJoinTableView::CreateImpl(const OUString& _rComposedName + ,const OUString& _sTableName + ,const OUString& _rWinName) +{ + return std::make_shared<OTableWindowData>( nullptr,_rComposedName,_sTableName, _rWinName ); +} + +void OJoinTableView::AddTabWin(const OUString& _rComposedName, const OUString& rWinName, bool /*bNewTable*/) +{ + OSL_ENSURE(!_rComposedName.isEmpty(),"There must be a table name supplied!"); + + TTableWindowData::value_type pNewTabWinData(createTableWindowData( _rComposedName, rWinName,rWinName )); + + // insert new window in window list + VclPtr<OTableWindow> pNewTabWin = createWindow( pNewTabWinData ); + if ( pNewTabWin->Init() ) + { + m_pView->getController().getTableWindowData().push_back( pNewTabWinData); + // when we already have a table with this name insert the full qualified one instead + if(m_aTableMap.find(rWinName) != m_aTableMap.end()) + m_aTableMap[_rComposedName] = pNewTabWin; + else + m_aTableMap[rWinName] = pNewTabWin; + + SetDefaultTabWinPosSize( pNewTabWin ); + pNewTabWin->Show(); + + modified(); + if ( m_pAccessible ) + m_pAccessible->notifyAccessibleEvent( AccessibleEventId::CHILD, + Any(), + makeAny(pNewTabWin->GetAccessible())); + } + else + { + pNewTabWin->clearListBox(); + pNewTabWin.disposeAndClear(); + } +} + +void OJoinTableView::RemoveTabWin( OTableWindow* pTabWin ) +{ + // first delete all connections of this window to others + bool bRemove = true; + TTableWindowData::value_type pData = pTabWin->GetData(); + sal_Int32 nCount = m_vTableConnection.size(); + auto aIter = m_vTableConnection.rbegin(); + while(aIter != m_vTableConnection.rend() && bRemove) + { + VclPtr<OTableConnection>& rTabConn = *aIter; + if ( + (pData == rTabConn->GetData()->getReferencingTable()) || + (pData == rTabConn->GetData()->getReferencedTable()) + ) + { + bRemove = RemoveConnection(rTabConn, true); + aIter = m_vTableConnection.rbegin(); + } + else + ++aIter; + } + + // then delete the window itself + if ( bRemove ) + { + if ( m_pAccessible ) + m_pAccessible->notifyAccessibleEvent( AccessibleEventId::CHILD, + makeAny(pTabWin->GetAccessible()),Any() + ); + + pTabWin->Hide(); + OJoinController& rController = m_pView->getController(); + TTableWindowData::iterator aFind = std::find(rController.getTableWindowData().begin(), rController.getTableWindowData().end(), pData); + if(aFind != rController.getTableWindowData().end()) + { + rController.getTableWindowData().erase(aFind); + rController.setModified(true); + } + + if ( !m_aTableMap.erase( pTabWin->GetWinName() ) ) + m_aTableMap.erase( pTabWin->GetComposedName() ); + + if (pTabWin == m_pLastFocusTabWin) + m_pLastFocusTabWin = nullptr; + + pTabWin->clearListBox(); + pTabWin->disposeOnce(); + } + + if ( static_cast<sal_Int32>(m_vTableConnection.size()) < (nCount-1) ) // if some connections could be removed + modified(); +} + +namespace +{ + bool isScrollAllowed( OJoinTableView* _pView,long nDelta, bool bHoriz) + { + // adjust ScrollBar-Positions + ScrollBar& rBar = bHoriz ? _pView->GetHScrollBar() : _pView->GetVScrollBar() ; + + long nOldThumbPos = rBar.GetThumbPos(); + long nNewThumbPos = nOldThumbPos + nDelta; + if( nNewThumbPos < 0 ) + nNewThumbPos = 0; + else if( nNewThumbPos > rBar.GetRangeMax() ) + nNewThumbPos = rBar.GetRangeMax(); + + if ( bHoriz ) + { + if( nNewThumbPos == _pView->GetScrollOffset().X() ) + return false; + } + else if ( nNewThumbPos == _pView->GetScrollOffset().Y() ) + return false; + + return true; + } + bool getMovementImpl(OJoinTableView* _pView,const Point& _rPoint,const Size& _rSize,long& _nScrollX,long& _nScrollY) + { + _nScrollY = _nScrollX = 0; + // data about the tab win + Point aUpperLeft = _rPoint; + // normalize with respect to visibility + aUpperLeft -= _pView->GetScrollOffset(); + Point aLowerRight(aUpperLeft.X() + _rSize.Width(), aUpperLeft.Y() + _rSize.Height()); + + // data about ourself + Size aSize = _pView->getRealOutputSize(); //GetOutputSizePixel(); + + bool bVisible = true; + bool bFitsHor = (aUpperLeft.X() >= 0) && (aLowerRight.X() <= aSize.Width()); + bool bFitsVert= (aUpperLeft.Y() >= 0) && (aLowerRight.Y() <= aSize.Height()); + if (!bFitsHor || !bFitsVert) + { + if (!bFitsHor) + { + // ensure the visibility of the right border + if ( aLowerRight.X() > aSize.Width() ) + _nScrollX = aLowerRight.X() - aSize.Width() + TABWIN_SPACING_X; + + // ensure the visibility of the left border (higher priority) + if ( aUpperLeft.X() < 0 ) + _nScrollX = aUpperLeft.X() - TABWIN_SPACING_X; + } + + if (!bFitsVert) + { + // lower border + if ( aLowerRight.Y() > aSize.Height() ) + _nScrollY = aLowerRight.Y() - aSize.Height() + TABWIN_SPACING_Y; + // upper border + if ( aUpperLeft.Y() < 0 ) + _nScrollY = aUpperLeft.Y() - TABWIN_SPACING_Y; + } + + if ( _nScrollX ) // aSize.Width() > _rSize.Width() && + bVisible = isScrollAllowed(_pView,_nScrollX, true); + + if ( _nScrollY ) // aSize.Height() > _rSize.Height() && + bVisible = bVisible && isScrollAllowed(_pView,_nScrollY, false); + + if ( bVisible ) + { + sal_Int32 nHRangeMax = _pView->GetHScrollBar().GetRangeMax(); + sal_Int32 nVRangeMax = _pView->GetVScrollBar().GetRangeMax(); + + if ( aSize.Width() + _pView->GetHScrollBar().GetThumbPos() + _nScrollX > nHRangeMax ) + bVisible = false; + if ( bVisible && aSize.Height() + _pView->GetVScrollBar().GetThumbPos() + _nScrollY > nVRangeMax ) + bVisible = false; + } + } + + return bVisible; + } +} // end of ano namespace + +bool OJoinTableView::isMovementAllowed(const Point& _rPoint,const Size& _rSize) +{ + long nX,nY; + return getMovementImpl(this,_rPoint,_rSize,nX,nY); +} + +void OJoinTableView::EnsureVisible(const OTableWindow* _pWin) +{ + // data about the tab win + TTableWindowData::value_type pData = _pWin->GetData(); + EnsureVisible( pData->GetPosition() , pData->GetSize()); + Invalidate(InvalidateFlags::NoChildren); +} + +void OJoinTableView::EnsureVisible(const Point& _rPoint,const Size& _rSize) +{ + long nScrollX,nScrollY; + + if ( getMovementImpl(this,_rPoint,_rSize,nScrollX,nScrollY) ) + { + bool bVisible = true; + if (nScrollX) + bVisible = ScrollPane(nScrollX, true, true); + + if (nScrollY && bVisible) + ScrollPane(nScrollY, false, true); + } +} + +void OJoinTableView::SetDefaultTabWinPosSize( OTableWindow* pTabWin ) +{ + // determine position: + // the window is divided into lines with height TABWIN_SPACING_Y+TABWIN_HEIGHT_STD. + // Then for each line is checked, if there is space for another window. + // If there is no space, the next line is checked. + Size aOutSize = GetSizePixel(); + Point aNewPos( 0,0 ); + sal_uInt16 nRow = 0; + bool bEnd = false; + while( !bEnd ) + { + // Set new position to start of line + aNewPos.setX( TABWIN_SPACING_X ); + aNewPos.setY( (nRow+1) * TABWIN_SPACING_Y ); + + // determine rectangle for the corresponding line + tools::Rectangle aRowRect( Point(0,0), aOutSize ); + aRowRect.SetTop( nRow * ( TABWIN_SPACING_Y + TABWIN_HEIGHT_STD ) ); + aRowRect.SetBottom( (nRow+1) * ( TABWIN_SPACING_Y + TABWIN_HEIGHT_STD ) ); + + // check occupied areas of this line + for (auto const& elem : m_aTableMap) + { + OTableWindow* pOtherTabWin = elem.second; + tools::Rectangle aOtherTabWinRect( pOtherTabWin->GetPosPixel(), pOtherTabWin->GetSizePixel() ); + + if( + ( (aOtherTabWinRect.Top()>aRowRect.Top()) && (aOtherTabWinRect.Top()<aRowRect.Bottom()) ) || + ( (aOtherTabWinRect.Bottom()>aRowRect.Top()) && (aOtherTabWinRect.Bottom()<aRowRect.Bottom()) ) + ) + { + // TabWin is in the line + if( aOtherTabWinRect.Right()>aNewPos.X() ) + aNewPos.setX( aOtherTabWinRect.Right() + TABWIN_SPACING_X ); + } + } + + // Is there space left in this line? + if( (aNewPos.X()+TABWIN_WIDTH_STD)<aRowRect.Right() ) + { + aNewPos.setY( aRowRect.Top() + TABWIN_SPACING_Y ); + bEnd = true; + } + else + { + if( (aRowRect.Bottom()+aRowRect.GetHeight()) > aOutSize.Height() ) + { + // insert it in the first row + sal_Int32 nCount = m_aTableMap.size() % (nRow+1); + ++nCount; + aNewPos.setY( nCount * TABWIN_SPACING_Y + (nCount-1)*CalcZoom(TABWIN_HEIGHT_STD) ); + bEnd = true; + } + else + nRow++; + + } + } + + // determine size + Size aNewSize( CalcZoom(TABWIN_WIDTH_STD), CalcZoom(TABWIN_HEIGHT_STD) ); + + // check if the new position in inside the scrollbars ranges + Point aBottom(aNewPos); + aBottom.AdjustX(aNewSize.Width() ); + aBottom.AdjustY(aNewSize.Height() ); + + if(!GetHScrollBar().GetRange().IsInside(aBottom.X())) + GetHScrollBar().SetRange( Range(0, aBottom.X()) ); + if(!GetVScrollBar().GetRange().IsInside(aBottom.Y())) + GetVScrollBar().SetRange( Range(0, aBottom.Y()) ); + + pTabWin->SetPosSizePixel( aNewPos, aNewSize ); +} + +void OJoinTableView::DataChanged(const DataChangedEvent& rDCEvt) +{ + if (rDCEvt.GetType() == DataChangedEventType::SETTINGS) + { + // consider the worst case: the colors changed, so adjust me + InitColors(); + Invalidate(InvalidateFlags::NoChildren); + // due to the Invalidate, the connections are redrawn, so that they are also pictured in the new colors + } +} + +void OJoinTableView::InitColors() +{ + // the colors for the illustration should be the system colors + StyleSettings aSystemStyle = Application::GetSettings().GetStyleSettings(); + SetBackground(Wallpaper(aSystemStyle.GetDialogColor())); +} + +void OJoinTableView::BeginChildMove( OTableWindow* pTabWin, const Point& rMousePos ) +{ + + if (m_pView->getController().isReadOnly()) + return; + + m_pDragWin = pTabWin; + SetPointer(PointerStyle::Move); + Point aMousePos = ScreenToOutputPixel( rMousePos ); + m_aDragOffset = aMousePos - pTabWin->GetPosPixel(); + m_pDragWin->SetZOrder(nullptr, ZOrderFlags::First); + StartTracking(); +} + +void OJoinTableView::NotifyTitleClicked( OTableWindow* pTabWin, const Point& rMousePos ) +{ + DeselectConn(GetSelectedConn()); + BeginChildMove(pTabWin, rMousePos); +} + +void OJoinTableView::BeginChildSizing( OTableWindow* pTabWin, PointerStyle nPointer ) +{ + + if (m_pView->getController().isReadOnly()) + return; + + SetPointer( nPointer ); + m_pSizingWin = pTabWin; + StartTracking(); +} + +bool OJoinTableView::ScrollPane( long nDelta, bool bHoriz, bool bPaintScrollBars ) +{ + bool bRet = true; + + // adjust ScrollBar-Positions + if( bPaintScrollBars ) + { + if( bHoriz ) + { + long nOldThumbPos = GetHScrollBar().GetThumbPos(); + long nNewThumbPos = nOldThumbPos + nDelta; + if( nNewThumbPos < 0 ) + { + nNewThumbPos = 0; + bRet = false; + } + if( nNewThumbPos > GetHScrollBar().GetRange().Max() ) + { + nNewThumbPos = GetHScrollBar().GetRange().Max(); + bRet = false; + } + GetHScrollBar().SetThumbPos( nNewThumbPos ); + nDelta = GetHScrollBar().GetThumbPos() - nOldThumbPos; + } + else + { + long nOldThumbPos = GetVScrollBar().GetThumbPos(); + long nNewThumbPos = nOldThumbPos+nDelta; + if( nNewThumbPos < 0 ) + { + nNewThumbPos = 0; + bRet = false; + } + if( nNewThumbPos > GetVScrollBar().GetRange().Max() ) + { + nNewThumbPos = GetVScrollBar().GetRange().Max(); + bRet = false; + } + GetVScrollBar().SetThumbPos( nNewThumbPos ); + nDelta = GetVScrollBar().GetThumbPos() - nOldThumbPos; + } + } + + // If ScrollOffset hitting borders, no redrawing. + if( (GetHScrollBar().GetThumbPos()==m_aScrollOffset.X()) && + (GetVScrollBar().GetThumbPos()==m_aScrollOffset.Y()) ) + return false; + + // set ScrollOffset anew + if (bHoriz) + m_aScrollOffset.setX( GetHScrollBar().GetThumbPos() ); + else + m_aScrollOffset.setY( GetVScrollBar().GetThumbPos() ); + + // move all windows + OTableWindow* pTabWin; + Point aPos; + + for (auto const& elem : m_aTableMap) + { + pTabWin = elem.second; + aPos = pTabWin->GetPosPixel(); + + if( bHoriz ) + aPos.AdjustX( -nDelta ); + else aPos.AdjustY( -nDelta ); + + pTabWin->SetPosPixel( aPos ); + } + + Invalidate(); // InvalidateFlags::NoChildren + + return bRet; +} + +void OJoinTableView::Tracking( const TrackingEvent& rTEvt ) +{ + HideTracking(); + + if (rTEvt.IsTrackingEnded()) + { + if( m_pDragWin ) + { + if (m_aDragScrollIdle.IsActive()) + m_aDragScrollIdle.Stop(); + + // adjust position of child after moving + // windows are not allowed to leave display range + Point aDragWinPos = rTEvt.GetMouseEvent().GetPosPixel() - m_aDragOffset; + Size aDragWinSize = m_pDragWin->GetSizePixel(); + if( aDragWinPos.X() < 0 ) + aDragWinPos.setX( 0 ); + if( aDragWinPos.Y() < 0 ) + aDragWinPos.setY( 0 ); + if( (aDragWinPos.X() + aDragWinSize.Width()) > m_aOutputSize.Width() ) + aDragWinPos.setX( m_aOutputSize.Width() - aDragWinSize.Width() - 1 ); + if( (aDragWinPos.Y() + aDragWinSize.Height()) > m_aOutputSize.Height() ) + aDragWinPos.setY( m_aOutputSize.Height() - aDragWinSize.Height() - 1 ); + if( aDragWinPos.X() < 0 ) + aDragWinPos.setX( 0 ); + if( aDragWinPos.Y() < 0 ) + aDragWinPos.setY( 0 ); + // TODO : don't position window anew, if it is leaving range, but just expand the range + + // position window + EndTracking(); + m_pDragWin->SetZOrder(nullptr, ZOrderFlags::First); + // check, if I really moved + // (this prevents setting the modified-Flag, when there actually was no change0 + TTableWindowData::value_type pData = m_pDragWin->GetData(); + if ( ! (pData && pData->HasPosition() && (pData->GetPosition() == aDragWinPos))) + { + // old logic coordinates + Point ptOldPos = m_pDragWin->GetPosPixel() + Point(GetHScrollBar().GetThumbPos(), GetVScrollBar().GetThumbPos()); + // new positioning + m_pDragWin->SetPosPixel(aDragWinPos); + TabWinMoved(m_pDragWin, ptOldPos); + + m_pDragWin->GrabFocus(); + } + m_pDragWin = nullptr; + SetPointer(PointerStyle::Arrow); + } + // else we handle the resizing + else if( m_pSizingWin ) + { + SetPointer( PointerStyle::Arrow ); + EndTracking(); + + // old physical coordinates + + Size szOld = m_pSizingWin->GetSizePixel(); + Point ptOld = m_pSizingWin->GetPosPixel(); + Size aNewSize(CalcZoom(m_aSizingRect.GetSize().Width()),CalcZoom(m_aSizingRect.GetSize().Height())); + m_pSizingWin->SetPosSizePixel( m_aSizingRect.TopLeft(), aNewSize ); + TabWinSized(m_pSizingWin, ptOld, szOld); + + m_pSizingWin->Invalidate( m_aSizingRect ); + m_pSizingWin = nullptr; + } + } + else if (rTEvt.IsTrackingCanceled()) + { + if (m_aDragScrollIdle.IsActive()) + m_aDragScrollIdle.Stop(); + EndTracking(); + } + else + { + if( m_pDragWin ) + { + m_ptPrevDraggingPos = rTEvt.GetMouseEvent().GetPosPixel(); + // scroll at window borders + ScrollWhileDragging(); + } + + if( m_pSizingWin ) + { + Point aMousePos = rTEvt.GetMouseEvent().GetPosPixel(); + m_aSizingRect = m_pSizingWin->getSizingRect(aMousePos,m_aOutputSize); + PaintImmediately(); + ShowTracking( m_aSizingRect, ShowTrackFlags::Small | ShowTrackFlags::TrackWindow ); + } + } +} + +void OJoinTableView::ConnDoubleClicked(VclPtr<OTableConnection>& /*rConnection*/) +{ +} + +void OJoinTableView::MouseButtonDown( const MouseEvent& rEvt ) +{ + GrabFocus(); + Window::MouseButtonDown(rEvt); +} + +void OJoinTableView::MouseButtonUp( const MouseEvent& rEvt ) +{ + Window::MouseButtonUp(rEvt); + // Has a connection been selected? + if( m_vTableConnection.empty() ) + return; + + DeselectConn(GetSelectedConn()); + + for (auto & elem : m_vTableConnection) + { + if( elem->CheckHit(rEvt.GetPosPixel()) ) + { + SelectConn(elem); + + // Double-click + if( rEvt.GetClicks() == 2 ) + ConnDoubleClicked(elem); + + break; + } + } +} + +void OJoinTableView::KeyInput( const KeyEvent& rEvt ) +{ + sal_uInt16 nCode = rEvt.GetKeyCode().GetCode(); + bool bShift = rEvt.GetKeyCode().IsShift(); + bool bCtrl = rEvt.GetKeyCode().IsMod1(); + + if( !bCtrl && !bShift && (nCode==KEY_DELETE) ) + { + if (GetSelectedConn()) + RemoveConnection(GetSelectedConn(), true); + } + else + Window::KeyInput( rEvt ); +} + +void OJoinTableView::DeselectConn(OTableConnection* pConn) +{ + if (!pConn || !pConn->IsSelected()) + return; + + // deselect the corresponding entries in the ListBox of the table window + OTableWindow* pWin = pConn->GetSourceWin(); + if (pWin && pWin->GetListBox()) + pWin->GetListBox()->SelectAll(false); + + pWin = pConn->GetDestWin(); + if (pWin && pWin->GetListBox()) + pWin->GetListBox()->SelectAll(false); + + pConn->Deselect(); + m_pSelectedConn = nullptr; +} + +void OJoinTableView::SelectConn(OTableConnection* pConn) +{ + DeselectConn(GetSelectedConn()); + + pConn->Select(); + m_pSelectedConn = pConn; + GrabFocus(); // has to be called here because a table window may still be focused + + // select the concerned entries in the windows + OTableWindow* pConnSource = pConn->GetSourceWin(); + OTableWindow* pConnDest = pConn->GetDestWin(); + if (!(pConnSource && pConnDest)) + return; + + OTableWindowListBox* pSourceBox = pConnSource->GetListBox().get(); + OTableWindowListBox* pDestBox = pConnDest->GetListBox().get(); + if (!(pSourceBox && pDestBox)) + return; + + pSourceBox->SelectAll(false); + pDestBox->SelectAll(false); + + SvTreeListEntry* pFirstSourceVisible = pSourceBox->GetFirstEntryInView(); + SvTreeListEntry* pFirstDestVisible = pDestBox->GetFirstEntryInView(); + + const std::vector<std::unique_ptr<OConnectionLine>>& rLines = pConn->GetConnLineList(); + auto aIter = rLines.rbegin(); + for(;aIter != rLines.rend();++aIter) + { + if ((*aIter)->IsValid()) + { + SvTreeListEntry* pSourceEntry = pSourceBox->GetEntryFromText((*aIter)->GetData()->GetSourceFieldName()); + if (pSourceEntry) + { + pSourceBox->Select(pSourceEntry); + pSourceBox->MakeVisible(pSourceEntry); + } + + SvTreeListEntry* pDestEntry = pDestBox->GetEntryFromText((*aIter)->GetData()->GetDestFieldName()); + if (pDestEntry) + { + pDestBox->Select(pDestEntry); + pDestBox->MakeVisible(pDestEntry); + } + + } + } + + if ((pFirstSourceVisible != pSourceBox->GetFirstEntryInView()) + || (pFirstDestVisible != pDestBox->GetFirstEntryInView())) + // scrolling was done -> redraw + Invalidate(InvalidateFlags::NoChildren); +} + +void OJoinTableView::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + DrawConnections(rRenderContext, rRect); +} + +void OJoinTableView::InvalidateConnections() +{ + // draw Joins + for (auto & conn : m_vTableConnection) + conn->InvalidateConnection(); +} + +void OJoinTableView::DrawConnections(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect) +{ + // draw Joins + for(const auto& connection : m_vTableConnection) + connection->Draw(rRenderContext, rRect); + // finally redraw the selected one above all others + if (GetSelectedConn()) + GetSelectedConn()->Draw(rRenderContext, rRect); +} + +std::vector<VclPtr<OTableConnection> >::const_iterator OJoinTableView::getTableConnections(const OTableWindow* _pFromWin) const +{ + return std::find_if( m_vTableConnection.begin(), + m_vTableConnection.end(), + [_pFromWin](OTableConnection * p) { return p->isTableConnection(_pFromWin); }); +} + +sal_Int32 OJoinTableView::getConnectionCount(const OTableWindow* _pFromWin) const +{ + return std::count_if( m_vTableConnection.begin(), + m_vTableConnection.end(), + [_pFromWin](OTableConnection * p) { return p->isTableConnection(_pFromWin); }); +} + +bool OJoinTableView::ExistsAConn(const OTableWindow* pFrom) const +{ + return getTableConnections(pFrom) != m_vTableConnection.end(); +} + +void OJoinTableView::ClearAll() +{ + SetUpdateMode(false); + + HideTabWins(); + + // and the same with the Connections + for (auto & elem : m_vTableConnection) + { + RemoveConnection(elem, true); + } + m_vTableConnection.clear(); + + m_pLastFocusTabWin = nullptr; + m_pSelectedConn = nullptr; + + // scroll to the upper left + ScrollPane(-GetScrollOffset().X(), true, true); + ScrollPane(-GetScrollOffset().Y(), false, true); + Invalidate(); +} + +void OJoinTableView::ScrollWhileDragging() +{ + OSL_ENSURE(m_pDragWin != nullptr, "OJoinTableView::ScrollWhileDragging must not be called when a window is being dragged !"); + + // kill the timer + if (m_aDragScrollIdle.IsActive()) + m_aDragScrollIdle.Stop(); + + Point aDragWinPos = m_ptPrevDraggingPos - m_aDragOffset; + Size aDragWinSize = m_pDragWin->GetSizePixel(); + Point aLowerRight(aDragWinPos.X() + aDragWinSize.Width(), aDragWinPos.Y() + aDragWinSize.Height()); + + if (aDragWinPos == m_pDragWin->GetPosPixel()) + return; + + // avoid illustration errors (when scrolling with active TrackingRect) + HideTracking(); + + bool bScrolling = false; + bool bNeedScrollTimer = false; + + // scroll at window borders + // TODO : only catch, if window would disappear completely (don't, if there is still a pixel visible) + if( aDragWinPos.X() < 5 ) + { + bScrolling = ScrollPane( -LINE_SIZE, true, true ); + if( !bScrolling && (aDragWinPos.X()<0) ) + aDragWinPos.setX( 0 ); + + // do I need further (timer controlled) scrolling ? + bNeedScrollTimer = bScrolling && (aDragWinPos.X() < 5); + } + + if( aLowerRight.X() > m_aOutputSize.Width() - 5 ) + { + bScrolling = ScrollPane( LINE_SIZE, true, true ) ; + if( !bScrolling && ( aLowerRight.X() > m_aOutputSize.Width() ) ) + aDragWinPos.setX( m_aOutputSize.Width() - aDragWinSize.Width() ); + + // do I need further (timer controlled) scrolling ? + bNeedScrollTimer = bScrolling && (aLowerRight.X() > m_aOutputSize.Width() - 5); + } + + if( aDragWinPos.Y() < 5 ) + { + bScrolling = ScrollPane( -LINE_SIZE, false, true ); + if( !bScrolling && (aDragWinPos.Y()<0) ) + aDragWinPos.setY( 0 ); + + bNeedScrollTimer = bScrolling && (aDragWinPos.Y() < 5); + } + + if( aLowerRight.Y() > m_aOutputSize.Height() - 5 ) + { + bScrolling = ScrollPane( LINE_SIZE, false, true ); + if( !bScrolling && ( (aDragWinPos.Y() + aDragWinSize.Height()) > m_aOutputSize.Height() ) ) + aDragWinPos.setY( m_aOutputSize.Height() - aDragWinSize.Height() ); + + bNeedScrollTimer = bScrolling && (aLowerRight.Y() > m_aOutputSize.Height() - 5); + } + + // resetting timer, if still necessary + if (bNeedScrollTimer) + { + m_aDragScrollIdle.SetPriority( TaskPriority::HIGH_IDLE ); + m_aDragScrollIdle.Start(); + } + + // redraw DraggingRect + m_aDragRect = tools::Rectangle(m_ptPrevDraggingPos - m_aDragOffset, m_pDragWin->GetSizePixel()); + PaintImmediately(); + ShowTracking( m_aDragRect, ShowTrackFlags::Small | ShowTrackFlags::TrackWindow ); +} + +IMPL_LINK_NOARG(OJoinTableView, OnDragScrollTimer, Timer *, void) +{ + ScrollWhileDragging(); +} + +void OJoinTableView::invalidateAndModify(std::unique_ptr<SfxUndoAction> _pAction) +{ + Invalidate(InvalidateFlags::NoChildren); + m_pView->getController().addUndoActionAndInvalidate(std::move(_pAction)); +} + +void OJoinTableView::TabWinMoved(OTableWindow* ptWhich, const Point& ptOldPosition) +{ + Point ptThumbPos(GetHScrollBar().GetThumbPos(), GetVScrollBar().GetThumbPos()); + ptWhich->GetData()->SetPosition(ptWhich->GetPosPixel() + ptThumbPos); + + invalidateAndModify(std::make_unique<OJoinMoveTabWinUndoAct>(this, ptOldPosition, ptWhich)); +} + +void OJoinTableView::TabWinSized(OTableWindow* ptWhich, const Point& ptOldPosition, const Size& szOldSize) +{ + ptWhich->GetData()->SetSize(ptWhich->GetSizePixel()); + ptWhich->GetData()->SetPosition(ptWhich->GetPosPixel()); + + invalidateAndModify(std::make_unique<OJoinSizeTabWinUndoAct>(this, ptOldPosition, szOldSize, ptWhich)); +} + +bool OJoinTableView::IsAddAllowed() +{ + + // not, if Db readonly + if (m_pView->getController().isReadOnly()) + return false; + + try + { + Reference< XConnection> xConnection = m_pView->getController().getConnection(); + if(!xConnection.is()) + return false; + // not, if too many tables already + Reference < XDatabaseMetaData > xMetaData( xConnection->getMetaData() ); + + sal_Int32 nMax = xMetaData.is() ? xMetaData->getMaxTablesInSelect() : 0; + if (nMax && nMax <= static_cast<sal_Int32>(m_aTableMap.size())) + return false; + } + catch(SQLException&) + { + return false; + } + + return true; +} + +void OJoinTableView::executePopup(const Point& _aPos, VclPtr<OTableConnection>& rSelConnection) +{ + VclBuilder aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "dbaccess/ui/joinviewmenu.ui", ""); + VclPtr<PopupMenu> aContextMenu(aBuilder.get_menu("menu")); + aContextMenu->Execute(this, _aPos); + OString sIdent = aContextMenu->GetCurItemIdent(); + if (sIdent == "delete") + RemoveConnection(rSelConnection, true); + else if (sIdent == "edit") + ConnDoubleClicked(rSelConnection); // is the same as double clicked +} + +void OJoinTableView::Command(const CommandEvent& rEvt) +{ + + bool bHandled = false; + + switch (rEvt.GetCommand()) + { + case CommandEventId::ContextMenu: + { + if( m_vTableConnection.empty() ) + return; + + VclPtr<OTableConnection>& rSelConnection = GetSelectedConn(); + // when it wasn't a mouse event use the selected connection + if (!rEvt.IsMouseEvent()) + { + if (rSelConnection) + { + const std::vector<std::unique_ptr<OConnectionLine>>& rLines = rSelConnection->GetConnLineList(); + auto aIter = std::find_if(rLines.begin(), rLines.end(),std::mem_fn(&OConnectionLine::IsValid)); + if( aIter != rLines.end() ) + executePopup((*aIter)->getMidPoint(), rSelConnection); + } + } + else + { + DeselectConn(rSelConnection); + + const Point& aMousePos = rEvt.GetMousePosPixel(); + for (auto & elem : m_vTableConnection) + { + if( elem->CheckHit(aMousePos) ) + { + SelectConn(elem); + if(!getDesignView()->getController().isReadOnly() && getDesignView()->getController().isConnected()) + executePopup(rEvt.GetMousePosPixel(),elem); + break; + } + } + } + bHandled = true; + } + break; + default: break; + } + if (!bHandled) + Window::Command(rEvt); +} + +OTableConnection* OJoinTableView::GetTabConn(const OTableWindow* pLhs,const OTableWindow* pRhs,bool _bSupressCrossOrNaturalJoin) const +{ + OTableConnection* pConn = nullptr; + OSL_ENSURE(pRhs || pLhs, "OJoinTableView::GetTabConn : invalid args !"); + // only one NULL-arg allowed + + if ((!pLhs || pLhs->ExistsAConn()) && (!pRhs || pRhs->ExistsAConn())) + { + for(VclPtr<OTableConnection> const & pData : m_vTableConnection) + { + if ( ( (pData->GetSourceWin() == pLhs) + && ( (pData->GetDestWin() == pRhs) + || (nullptr == pRhs) + ) + ) + || ( (pData->GetSourceWin() == pRhs) + && ( (pData->GetDestWin() == pLhs) + || (nullptr == pLhs) + ) + ) + ) + { + if ( _bSupressCrossOrNaturalJoin ) + { + if ( supressCrossNaturalJoin(pData->GetData()) ) + continue; + } + pConn = pData; + break; + } + } + } + return pConn; +} + +bool OJoinTableView::PreNotify(NotifyEvent& rNEvt) +{ + bool bHandled = false; + switch (rNEvt.GetType()) + { + case MouseNotifyEvent::COMMAND: + { + const CommandEvent* pCommand = rNEvt.GetCommandEvent(); + if (pCommand->GetCommand() == CommandEventId::Wheel) + { + const CommandWheelData* pData = rNEvt.GetCommandEvent()->GetWheelData(); + if (pData->GetMode() == CommandWheelMode::SCROLL) + { + if (pData->GetDelta() > 0) + ScrollPane(-10 * pData->GetScrollLines(), pData->IsHorz(), true); + else + ScrollPane(10 * pData->GetScrollLines(), pData->IsHorz(), true); + bHandled = true; + } + } + } + break; + case MouseNotifyEvent::KEYINPUT: + { + if (m_aTableMap.empty()) + // no tab wins -> no conns -> no traveling + break; + + const KeyEvent* pKeyEvent = rNEvt.GetKeyEvent(); + if (!pKeyEvent->GetKeyCode().IsMod1()) + { + switch (pKeyEvent->GetKeyCode().GetCode()) + { + case KEY_TAB: + { + if (!HasChildPathFocus()) + break; + + bool bForward = !pKeyEvent->GetKeyCode().IsShift(); + // is there an active tab win ? + OTableWindowMap::const_iterator aIter = std::find_if(m_aTableMap.begin(), m_aTableMap.end(), + [](const OTableWindowMap::value_type& rEntry) { return rEntry.second && rEntry.second->HasChildPathFocus(); }); + + OTableWindow* pNextWin = nullptr; + OTableConnection* pNextConn = nullptr; + + if (aIter != m_aTableMap.end()) + { // there is a currently active tab win + // check if there is an "overflow" and we should select a conn instead of a win + if (!m_vTableConnection.empty()) + { + if ((aIter->second == m_aTableMap.rbegin()->second) && bForward) + // the last win is active and we're travelling forward -> select the first conn + pNextConn = m_vTableConnection.begin()->get(); + if ((aIter == m_aTableMap.begin()) && !bForward) + // the first win is active and we're traveling backward -> select the last conn + pNextConn = m_vTableConnection.rbegin()->get(); + } + + if (!pNextConn) + { + // no conn for any reason -> select the next or previous tab win + if(bForward) + { + if ( aIter->second == m_aTableMap.rbegin()->second ) + pNextWin = m_aTableMap.begin()->second; + else + { + ++aIter; + pNextWin = aIter->second; + } + } + else + { + if (aIter == m_aTableMap.begin()) + pNextWin = m_aTableMap.rbegin()->second; + else + { + --aIter; + pNextWin = aIter->second; + } + } + } + } + else + { // no active tab win -> travel the connections + // find the currently selected conn within the conn list + sal_Int32 i(0); + for (auto const& elem : m_vTableConnection) + { + if ( elem.get() == GetSelectedConn() ) + break; + ++i; + } + if (i == sal_Int32(m_vTableConnection.size() - 1) && bForward) + // the last conn is active and we're travelling forward -> select the first win + pNextWin = m_aTableMap.begin()->second; + if ((i == 0) && !bForward && !m_aTableMap.empty()) + // the first conn is active and we're travelling backward -> select the last win + pNextWin = m_aTableMap.rbegin()->second; + + if (pNextWin) + DeselectConn(GetSelectedConn()); + else + // no win for any reason -> select the next or previous conn + if (i < static_cast<sal_Int32>(m_vTableConnection.size())) + // there is a currently active conn + pNextConn = m_vTableConnection[(i + (bForward ? 1 : m_vTableConnection.size() - 1)) % m_vTableConnection.size()].get(); + else + { // no tab win selected, no conn selected + if (!m_vTableConnection.empty()) + pNextConn = m_vTableConnection[bForward ? 0 : m_vTableConnection.size() - 1].get(); + else if (!m_aTableMap.empty()) + { + if(bForward) + pNextWin = m_aTableMap.begin()->second; + else + pNextWin = m_aTableMap.rbegin()->second; + } + } + } + + // now select the object + if (pNextWin) + { + if (pNextWin->GetListBox()) + pNextWin->GetListBox()->GrabFocus(); + else + pNextWin->GrabFocus(); + EnsureVisible(pNextWin); + } + else if (pNextConn) + { + GrabFocus(); + // necessary : a conn may be selected even if a tab win has the focus, in this case + // the next travel would select the same conn again if we would not reset the focus ... + SelectConn(pNextConn); + } + } + break; + case KEY_RETURN: + { + if (!pKeyEvent->GetKeyCode().IsShift() && GetSelectedConn() && HasFocus()) + ConnDoubleClicked(GetSelectedConn()); + break; + } + } + } + } + break; + case MouseNotifyEvent::GETFOCUS: + { + if (m_aTableMap.empty()) + // no tab wins -> no conns -> no focus change + break; + vcl::Window* pSource = rNEvt.GetWindow(); + if (pSource) + { + vcl::Window* pSearchFor = nullptr; + if (pSource->GetParent() == this) + // it may be one of the tab wins + pSearchFor = pSource; + else if (pSource->GetParent() && (pSource->GetParent()->GetParent() == this)) + // it may be one of th list boxes of one of the tab wins + pSearchFor = pSource->GetParent(); + + if (pSearchFor) + { + for (auto const& elem : m_aTableMap) + { + if (elem.second == pSearchFor) + { + m_pLastFocusTabWin = elem.second; + break; + } + } + } + } + } + break; + default: + break; + } + + if (!bHandled) + return Window::PreNotify(rNEvt); + return true; +} + +void OJoinTableView::GrabTabWinFocus() +{ + if (m_pLastFocusTabWin && m_pLastFocusTabWin->IsVisible()) + { + if (m_pLastFocusTabWin->GetListBox()) + m_pLastFocusTabWin->GetListBox()->GrabFocus(); + else + m_pLastFocusTabWin->GrabFocus(); + } + else if (!m_aTableMap.empty() && m_aTableMap.begin()->second && m_aTableMap.begin()->second->IsVisible()) + { + VclPtr<OTableWindow> pFirstWin = m_aTableMap.begin()->second; + if (pFirstWin->GetListBox()) + pFirstWin->GetListBox()->GrabFocus(); + else + pFirstWin->GrabFocus(); + } +} + +void OJoinTableView::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(*this, aFont); + + for (auto const& elem : m_aTableMap) + { + elem.second->SetZoom(GetZoom()); + Size aSize(CalcZoom(elem.second->GetSizePixel().Width()),CalcZoom(elem.second->GetSizePixel().Height())); + elem.second->SetSizePixel(aSize); + } + Resize(); +} + +void OJoinTableView::HideTabWins() +{ + SetUpdateMode(false); + + OTableWindowMap& rTabWins = GetTabWinMap(); + + // working on a copy because the real list will be cleared in inner calls + OTableWindowMap aCopy(rTabWins); + for (auto const& elem : aCopy) + RemoveTabWin(elem.second); + + m_pView->getController().setModified(true); + + SetUpdateMode(true); + +} + +sal_Int8 OJoinTableView::AcceptDrop( const AcceptDropEvent& /*_rEvt*/ ) +{ + return DND_ACTION_NONE; +} + +sal_Int8 OJoinTableView::ExecuteDrop( const ExecuteDropEvent& /*_rEvt*/ ) +{ + return DND_ACTION_NONE; +} + +void OJoinTableView::dragFinished( ) +{ +} + +void OJoinTableView::clearLayoutInformation() +{ + m_pLastFocusTabWin = nullptr; + m_pSelectedConn = nullptr; + // delete lists + for (auto & elem : m_aTableMap) + { + if ( elem.second ) + elem.second->clearListBox(); + elem.second.disposeAndClear(); + } + + m_aTableMap.clear(); + + for (auto & elem : m_vTableConnection) + elem.disposeAndClear(); + + m_vTableConnection.clear(); +} + +void OJoinTableView::lookForUiActivities() +{ +} + +void OJoinTableView::LoseFocus() +{ + DeselectConn(GetSelectedConn()); + Window::LoseFocus(); +} + +void OJoinTableView::GetFocus() +{ + Window::GetFocus(); + if ( !m_aTableMap.empty() && !GetSelectedConn() ) + GrabTabWinFocus(); +} + +Reference< XAccessible > OJoinTableView::CreateAccessible() +{ + m_pAccessible = new OJoinDesignViewAccess(this); + return m_pAccessible; +} + +void OJoinTableView::modified() +{ + OJoinController& rController = m_pView->getController(); + rController.setModified( true ); + rController.InvalidateFeature(ID_BROWSER_ADDTABLE); + rController.InvalidateFeature(SID_RELATION_ADD_RELATION); +} + +void OJoinTableView::addConnection(OTableConnection* _pConnection,bool _bAddData) +{ + if ( _bAddData ) + { +#if OSL_DEBUG_LEVEL > 0 + TTableConnectionData& rTabConnDataList = m_pView->getController().getTableConnectionData(); + OSL_ENSURE( std::find(rTabConnDataList.begin(), rTabConnDataList.end(),_pConnection->GetData()) == rTabConnDataList.end(),"Data already in vector!"); +#endif + m_pView->getController().getTableConnectionData().push_back(_pConnection->GetData()); + } + m_vTableConnection.emplace_back(_pConnection); + _pConnection->RecalcLines(); + _pConnection->InvalidateConnection(); + + modified(); + if ( m_pAccessible ) + m_pAccessible->notifyAccessibleEvent( AccessibleEventId::CHILD, + Any(), + makeAny(_pConnection->GetAccessible())); +} + +bool OJoinTableView::allowQueries() const +{ + return true; +} + +void OJoinTableView::onNoColumns_throw() +{ + OSL_FAIL( "OTableWindow::onNoColumns_throw: cannot really handle this!" ); + throw SQLException(); +} + +bool OJoinTableView::supressCrossNaturalJoin(const TTableConnectionData::value_type& ) const +{ + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QTableConnection.cxx b/dbaccess/source/ui/querydesign/QTableConnection.cxx new file mode 100644 index 000000000..c5db155f2 --- /dev/null +++ b/dbaccess/source/ui/querydesign/QTableConnection.cxx @@ -0,0 +1,73 @@ +/* -*- 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 "QTableConnection.hxx" +#include <osl/diagnose.h> +#include <QueryTableView.hxx> + +using namespace dbaui; + +OQueryTableConnection::OQueryTableConnection(OQueryTableView* pContainer, const TTableConnectionData::value_type& pTabConnData) + : OTableConnection(pContainer, pTabConnData) + , m_bVisited(false) +{ +} + +OQueryTableConnection::OQueryTableConnection(const OQueryTableConnection& rConn) + : VclReferenceBase(), + OTableConnection( rConn ) + , m_bVisited(false) +{ + // no own members, so base class functionality is sufficient +} + +OQueryTableConnection& OQueryTableConnection::operator=(const OQueryTableConnection& rConn) +{ + if (&rConn == this) + return *this; + + OTableConnection::operator=(rConn); + // no own members ... + return *this; +} + +bool OQueryTableConnection::operator==(const OQueryTableConnection& rCompare) const +{ + OSL_ENSURE(GetData() && rCompare.GetData(), "OQueryTableConnection::operator== : one of the two participants has no data!"); + + // I don't have to compare all too much (especially not all the members) : merely the windows, which we are connected to, and the indices in the corresponding table have to match. + OQueryTableConnectionData* pMyData = static_cast<OQueryTableConnectionData*>(GetData().get()); + OQueryTableConnectionData* pCompData = static_cast<OQueryTableConnectionData*>(rCompare.GetData().get()); + + // Connections are seen as equal, if source and destination window names and source and destination field Indices match... + return ( ( (pMyData->getReferencedTable() == pCompData->getReferencedTable()) && + (pMyData->getReferencingTable() == pCompData->getReferencingTable()) && + (pMyData->GetFieldIndex(JTCS_TO) == pCompData->GetFieldIndex(JTCS_TO)) && + (pMyData->GetFieldIndex(JTCS_FROM) == pCompData->GetFieldIndex(JTCS_FROM)) + ) + || // ... or this cross matching is given + ( (pMyData->getReferencingTable() == pCompData->getReferencedTable()) && + (pMyData->getReferencedTable() == pCompData->getReferencingTable()) && + (pMyData->GetFieldIndex(JTCS_TO) == pCompData->GetFieldIndex(JTCS_FROM)) && + (pMyData->GetFieldIndex(JTCS_FROM) == pCompData->GetFieldIndex(JTCS_TO)) + ) + ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QTableConnection.hxx b/dbaccess/source/ui/querydesign/QTableConnection.hxx new file mode 100644 index 000000000..b15a26102 --- /dev/null +++ b/dbaccess/source/ui/querydesign/QTableConnection.hxx @@ -0,0 +1,48 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QTABLECONNECTION_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QTABLECONNECTION_HXX + +#include <TableConnection.hxx> +#include "QTableConnectionData.hxx" +#include <QEnumTypes.hxx> + +namespace dbaui +{ + class OQueryTableView; + class OQueryTableConnection : public OTableConnection + { + bool m_bVisited; // is true if the conn was already visited through the join algorithm + public: + OQueryTableConnection(OQueryTableView* pContainer, const TTableConnectionData::value_type& pTabConnData); + OQueryTableConnection(const OQueryTableConnection& rConn); + + OQueryTableConnection& operator=(const OQueryTableConnection& rConn); + bool operator==(const OQueryTableConnection& rCompare) const; + + OUString const & GetAliasName(EConnectionSide nWhich) const { return static_cast<OQueryTableConnectionData*>(GetData().get())->GetAliasName(nWhich); } + + bool IsVisited() const { return m_bVisited; } + void SetVisited(bool bVisited) { m_bVisited = bVisited; } + + }; +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QTABLECONNECTION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QTableConnectionData.cxx b/dbaccess/source/ui/querydesign/QTableConnectionData.cxx new file mode 100644 index 000000000..f96a5b1cd --- /dev/null +++ b/dbaccess/source/ui/querydesign/QTableConnectionData.cxx @@ -0,0 +1,113 @@ +/* -*- 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 "QTableConnectionData.hxx" +#include "QTableWindow.hxx" + +#include <osl/diagnose.h> + +using namespace dbaui; + +OQueryTableConnectionData::OQueryTableConnectionData() + : OTableConnectionData() + , m_nFromEntryIndex(0) + , m_nDestEntryIndex(0) + , m_eJoinType (INNER_JOIN) + , m_bNatural(false) +{ +} + +OQueryTableConnectionData::OQueryTableConnectionData( const OQueryTableConnectionData& rConnData ) + : OTableConnectionData( rConnData ) + , m_nFromEntryIndex(rConnData.m_nFromEntryIndex) + , m_nDestEntryIndex(rConnData.m_nDestEntryIndex) + , m_eJoinType(rConnData.m_eJoinType) + , m_bNatural(rConnData.m_bNatural) +{ +} + +OQueryTableConnectionData::OQueryTableConnectionData(const TTableWindowData::value_type& _pReferencingTable, + const TTableWindowData::value_type& _pReferencedTable) + : OTableConnectionData( _pReferencingTable,_pReferencedTable ) + , m_nFromEntryIndex(0) + , m_nDestEntryIndex(0) + , m_eJoinType (INNER_JOIN) + , m_bNatural(false) +{ +} + +OQueryTableConnectionData::~OQueryTableConnectionData() +{ +} + +void OQueryTableConnectionData::CopyFrom(const OTableConnectionData& rSource) +{ + // same as in base class, use of (non-virtual) operator= + *this = static_cast<const OQueryTableConnectionData&>(rSource); +} + +OQueryTableConnectionData& OQueryTableConnectionData::operator=(const OQueryTableConnectionData& rConnData) +{ + if (&rConnData == this) + return *this; + + OTableConnectionData::operator=(rConnData); + + m_nFromEntryIndex = rConnData.m_nFromEntryIndex; + m_nDestEntryIndex = rConnData.m_nDestEntryIndex; + + m_eJoinType = rConnData.m_eJoinType; + m_bNatural = rConnData.m_bNatural; + + return *this; +} + +OUString const & OQueryTableConnectionData::GetAliasName(EConnectionSide nWhich) const +{ + return nWhich == JTCS_FROM ? m_pReferencingTable->GetWinName() : m_pReferencedTable->GetWinName(); +} + +void OQueryTableConnectionData::InitFromDrag(const OTableFieldDescRef& rDragLeft, const OTableFieldDescRef& rDragRight) +{ + // convert Information in rDrag into parameters for the base class init + OQueryTableWindow* pSourceWin = static_cast<OQueryTableWindow*>(rDragLeft->GetTabWindow()); + OQueryTableWindow* pDestWin = static_cast<OQueryTableWindow*>(rDragRight->GetTabWindow()); + OSL_ENSURE(pSourceWin,"NO Source window found!"); + OSL_ENSURE(pDestWin,"NO Dest window found!"); + m_pReferencingTable = pSourceWin->GetData(); + m_pReferencedTable = pDestWin->GetData(); + + // set members + SetFieldIndex(JTCS_FROM, rDragLeft->GetFieldIndex()); + SetFieldIndex(JTCS_TO, rDragRight->GetFieldIndex()); + + AppendConnLine(rDragLeft->GetField(), rDragRight->GetField()); +} + +std::shared_ptr<OTableConnectionData> OQueryTableConnectionData::NewInstance() const +{ + return std::make_shared<OQueryTableConnectionData>(); +} + +bool OQueryTableConnectionData::Update() +{ + return true; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QTableConnectionData.hxx b/dbaccess/source/ui/querydesign/QTableConnectionData.hxx new file mode 100644 index 000000000..41639470f --- /dev/null +++ b/dbaccess/source/ui/querydesign/QTableConnectionData.hxx @@ -0,0 +1,69 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QTABLECONNECTIONDATA_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QTABLECONNECTIONDATA_HXX + +#include <TableConnectionData.hxx> +#include <TableFieldDescription.hxx> +#include <QEnumTypes.hxx> + +namespace dbaui +{ + class OQueryTableConnectionData final : public OTableConnectionData + { + sal_Int32 m_nFromEntryIndex; + sal_Int32 m_nDestEntryIndex; + EJoinType m_eJoinType; + bool m_bNatural; + + OQueryTableConnectionData& operator=( const OQueryTableConnectionData& rConnData ); + public: + OQueryTableConnectionData(); + OQueryTableConnectionData( const OQueryTableConnectionData& rConnData ); + OQueryTableConnectionData( const TTableWindowData::value_type& _pReferencingTable,const TTableWindowData::value_type& _pReferencedTable ); + virtual ~OQueryTableConnectionData() override; + + virtual void CopyFrom(const OTableConnectionData& rSource) override; + virtual std::shared_ptr<OTableConnectionData> NewInstance() const override; + + + /** Update create a new connection + + @return true if successful + */ + virtual bool Update() override; + + OUString const & GetAliasName(EConnectionSide nWhich) const; + + sal_Int32 GetFieldIndex(EConnectionSide nWhich) const { return nWhich==JTCS_TO ? m_nDestEntryIndex : m_nFromEntryIndex; } + void SetFieldIndex(EConnectionSide nWhich, sal_Int32 nVal) { if (nWhich==JTCS_TO) m_nDestEntryIndex=nVal; else m_nFromEntryIndex=nVal; } + + void InitFromDrag(const OTableFieldDescRef& rDragLeft, const OTableFieldDescRef& rDragRight); + + EJoinType GetJoinType() const { return m_eJoinType; }; + void SetJoinType(const EJoinType& eJT) { m_eJoinType = eJT; }; + + void setNatural(bool _bNatural) { m_bNatural = _bNatural; } + bool isNatural() const { return m_bNatural; } + }; + +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QTABLECONNECTIONDATA_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QTableWindow.cxx b/dbaccess/source/ui/querydesign/QTableWindow.cxx new file mode 100644 index 000000000..6d2916683 --- /dev/null +++ b/dbaccess/source/ui/querydesign/QTableWindow.cxx @@ -0,0 +1,176 @@ +/* -*- 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 "QTableWindow.hxx" +#include <QueryTableView.hxx> +#include <JoinController.hxx> +#include <JoinDesignView.hxx> +#include <osl/diagnose.h> +#include <helpids.h> +#include <browserids.hxx> +#include <TableWindowListBox.hxx> +#include <strings.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include "TableFieldInfo.hxx" +#include <comphelper/stl_types.hxx> +#include <vcl/treelistentry.hxx> +#include <comphelper/types.hxx> + +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::beans; +using namespace dbaui; +OQueryTableWindow::OQueryTableWindow( vcl::Window* pParent, const TTableWindowData::value_type& pTabWinData) + :OTableWindow( pParent, pTabWinData ) + ,m_nAliasNum(0) +{ + m_strInitialAlias = GetAliasName(); + + // if table name matches alias, do not pass to InitialAlias, + // as the appending of a possible token could not succeed... + if (m_strInitialAlias == pTabWinData->GetTableName()) + m_strInitialAlias.clear(); + + SetHelpId(HID_CTL_QRYDGNTAB); +} + +bool OQueryTableWindow::Init() +{ + bool bSuccess = OTableWindow::Init(); + if (!bSuccess) + return bSuccess; + + OQueryTableView* pContainer = static_cast<OQueryTableView*>(getTableView()); + + // first determine Alias + OUString sAliasName; + + TTableWindowData::value_type pWinData = GetData(); + + if (!m_strInitialAlias.isEmpty() ) + // Alias was explicitly given + sAliasName = m_strInitialAlias; + else if ( GetTable().is() ) + GetTable()->getPropertyValue( PROPERTY_NAME ) >>= sAliasName; + else + return false; + + // Alias with successive number + if (pContainer->CountTableAlias(sAliasName, m_nAliasNum)) + { + sAliasName += "_" + OUString::number(m_nAliasNum); + } + + sAliasName = sAliasName.replaceAll("\"", ""); + SetAliasName(sAliasName); + // SetAliasName passes it as WinName, hence it uses the base class + // reset the title + m_xTitle->SetText( pWinData->GetWinName() ); + m_xTitle->Show(); + + getTableView()->getDesignView()->getController().InvalidateFeature(ID_BROWSER_QUERY_EXECUTE); + return bSuccess; +} + +void* OQueryTableWindow::createUserData(const Reference< XPropertySet>& _xColumn,bool _bPrimaryKey) +{ + OTableFieldInfo* pInfo = new OTableFieldInfo(); + pInfo->SetKey(_bPrimaryKey ? TAB_PRIMARY_FIELD : TAB_NORMAL_FIELD); + if ( _xColumn.is() ) + pInfo->SetDataType(::comphelper::getINT32(_xColumn->getPropertyValue(PROPERTY_TYPE))); + return pInfo; +} + +void OQueryTableWindow::deleteUserData(void*& _pUserData) +{ + delete static_cast<OTableFieldInfo*>(_pUserData); + _pUserData = nullptr; +} + +void OQueryTableWindow::OnEntryDoubleClicked(SvTreeListEntry* pEntry) +{ + OSL_ENSURE(pEntry != nullptr, "OQueryTableWindow::OnEntryDoubleClicked : pEntry must not be NULL !"); + // you could also scan that and then return, but like this it could possibly hint to faults at the caller + + if (getTableView()->getDesignView()->getController().isReadOnly()) + return; + + OTableFieldInfo* pInf = static_cast<OTableFieldInfo*>(pEntry->GetUserData()); + OSL_ENSURE(pInf != nullptr, "OQueryTableWindow::OnEntryDoubleClicked : field doesn't have FieldInfo !"); + + // build up DragInfo + OTableFieldDescRef aInfo = new OTableFieldDesc(GetTableName(), m_xListBox->GetEntryText(pEntry)); + aInfo->SetTabWindow(this); + aInfo->SetAlias(GetAliasName()); + aInfo->SetFieldIndex(m_xListBox->GetModel()->GetAbsPos(pEntry)); + aInfo->SetDataType(pInf->GetDataType()); + + // and insert corresponding field + static_cast<OQueryTableView*>(getTableView())->InsertField(aInfo); +} + +bool OQueryTableWindow::ExistsField(const OUString& strFieldName, OTableFieldDescRef const & rInfo) +{ + OSL_ENSURE(m_xListBox != nullptr, "OQueryTableWindow::ExistsField : doesn't have css::form::ListBox !"); + OSL_ENSURE(rInfo.is(),"OQueryTableWindow::ExistsField: invalid argument for OTableFieldDescRef!"); + Reference< XConnection> xConnection = getTableView()->getDesignView()->getController().getConnection(); + bool bExists = false; + if(xConnection.is()) + { + SvTreeListEntry* pEntry = m_xListBox->First(); + try + { + Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData(); + ::comphelper::UStringMixEqual bCase(xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers()); + + while (pEntry) + { + if (bCase(strFieldName,m_xListBox->GetEntryText(pEntry))) + { + OTableFieldInfo* pInf = static_cast<OTableFieldInfo*>(pEntry->GetUserData()); + assert(pInf && "OQueryTableWindow::ExistsField : field doesn't have FieldInfo !"); + + rInfo->SetTabWindow(this); + rInfo->SetField(strFieldName); + rInfo->SetTable(GetTableName()); + rInfo->SetAlias(GetAliasName()); + rInfo->SetFieldIndex(m_xListBox->GetModel()->GetAbsPos(pEntry)); + rInfo->SetDataType(pInf->GetDataType()); + bExists = true; + break; + } + pEntry = m_xListBox->Next(pEntry); + } + } + catch(SQLException&) + { + } + } + + return bExists; +} + +bool OQueryTableWindow::ExistsAVisitedConn() const +{ + return static_cast<const OQueryTableView*>(getTableView())->ExistsAVisitedConn(this); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QTableWindow.hxx b/dbaccess/source/ui/querydesign/QTableWindow.hxx new file mode 100644 index 000000000..e64fd2e85 --- /dev/null +++ b/dbaccess/source/ui/querydesign/QTableWindow.hxx @@ -0,0 +1,78 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QTABLEWINDOW_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QTABLEWINDOW_HXX + +#include <TableWindow.hxx> +#include "QTableWindowData.hxx" +#include <TableFieldDescription.hxx> + +namespace dbaui +{ + class OQueryTableWindow : public OTableWindow + { + sal_Int32 m_nAliasNum; + OUString m_strInitialAlias; + public: + OQueryTableWindow( vcl::Window* pParent, const TTableWindowData::value_type& pTabWinData ); + + OUString const & GetAliasName() const + { + return static_cast<OQueryTableWindowData*>(GetData().get())->GetAliasName(); + } + void SetAliasName(const OUString& strNewAlias) + { + static_cast<OQueryTableWindowData*>(GetData().get())->SetAliasName(strNewAlias); + } + + // late Constructor, the base class CREATES Listbox on first call + virtual bool Init() override; + + bool ExistsField(const OUString& strFieldName, OTableFieldDescRef const & rInfo); + bool ExistsAVisitedConn() const; + + virtual OUString GetName() const override { return GetWinName(); } + + protected: + + virtual void OnEntryDoubleClicked(SvTreeListEntry* pEntry) override; + // is called from DoubleClickHdl of the ListBox + /** delete the user data with the equal type as created within createUserData + @param _pUserData + The user data store in the listbox entries. Created with a call to createUserData. + _pUserData may be <NULL/>. + */ + virtual void deleteUserData(void*& _pUserData) override; + + /** creates user information that will be append at the ListBoxentry + @param _xColumn + The corresponding column, can be <NULL/>. + @param _bPrimaryKey + <TRUE/> when the column belongs to the primary key + @return + the user data which will be append at the listbox entry, may be <NULL/> + */ + virtual void* createUserData(const css::uno::Reference< + css::beans::XPropertySet>& _xColumn, + bool _bPrimaryKey) override; + }; +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QTABLEWINDOW_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QTableWindowData.cxx b/dbaccess/source/ui/querydesign/QTableWindowData.cxx new file mode 100644 index 000000000..d8f2f2efd --- /dev/null +++ b/dbaccess/source/ui/querydesign/QTableWindowData.cxx @@ -0,0 +1,34 @@ +/* -*- 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 "QTableWindowData.hxx" + +using namespace dbaui; +using namespace ::com::sun::star::uno; + +OQueryTableWindowData::OQueryTableWindowData(const OUString& _rComposedName, const OUString& rTableName, const OUString& rTableAlias ) + :OTableWindowData(nullptr,_rComposedName, rTableName, rTableAlias) +{ +} + +OQueryTableWindowData::~OQueryTableWindowData() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QTableWindowData.hxx b/dbaccess/source/ui/querydesign/QTableWindowData.hxx new file mode 100644 index 000000000..382611eec --- /dev/null +++ b/dbaccess/source/ui/querydesign/QTableWindowData.hxx @@ -0,0 +1,40 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QTABLEWINDOWDATA_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QTABLEWINDOWDATA_HXX + +#include <TableWindowData.hxx> + + +namespace dbaui +{ + class OQueryTableWindowData : public OTableWindowData + { + public: + explicit OQueryTableWindowData(const OUString& _rComposedName, const OUString& rTableName, const OUString& rTableAlias); + virtual ~OQueryTableWindowData() override; + + OUString const & GetAliasName() const { return GetWinName(); } + void SetAliasName(const OUString& rNewAlias) { SetWinName(rNewAlias); } + }; +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QTABLEWINDOWDATA_HXX + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QueryAddTabConnUndoAction.hxx b/dbaccess/source/ui/querydesign/QueryAddTabConnUndoAction.hxx new file mode 100644 index 000000000..37e13213d --- /dev/null +++ b/dbaccess/source/ui/querydesign/QueryAddTabConnUndoAction.hxx @@ -0,0 +1,51 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYADDTABCONNUNDOACTION_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYADDTABCONNUNDOACTION_HXX + +#include "QueryTabConnUndoAction.hxx" + +namespace dbaui +{ + // OQueryAddTabConnUndoAction - Undo class for inserting a connection + + class OQueryTableView; + class OQueryAddTabConnUndoAction : public OQueryTabConnUndoAction + { + public: + explicit OQueryAddTabConnUndoAction(OQueryTableView* pOwner); + + virtual void Undo() override; + virtual void Redo() override; + }; + + // OQueryDelTabConnUndoAction - Undo class for inserting a connection + + class OQueryDelTabConnUndoAction : public OQueryTabConnUndoAction + { + public: + explicit OQueryDelTabConnUndoAction(OQueryTableView* pOwner); + + virtual void Undo() override; + virtual void Redo() override; + }; +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYADDTABCONNUNDOACTION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QueryDesignFieldUndoAct.hxx b/dbaccess/source/ui/querydesign/QueryDesignFieldUndoAct.hxx new file mode 100644 index 000000000..63f09af2e --- /dev/null +++ b/dbaccess/source/ui/querydesign/QueryDesignFieldUndoAct.hxx @@ -0,0 +1,140 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYDESIGNFIELDUNDOACT_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYDESIGNFIELDUNDOACT_HXX + +#include <GeneralUndo.hxx> +#include <strings.hrc> +#include "SelectionBrowseBox.hxx" +#include <osl/diagnose.h> + +namespace dbaui +{ + // OQueryDesignFieldUndoAct - Basisclass for undo's in the fieldlist of a query design + + class OQueryDesignFieldUndoAct : public OCommentUndoAction + { + protected: + VclPtr<OSelectionBrowseBox> pOwner; + sal_uInt16 m_nColumnPosition; + + virtual void Undo() override = 0; + virtual void Redo() override = 0; + + public: + OQueryDesignFieldUndoAct(OSelectionBrowseBox* pSelBrwBox, const char* pCommentID); + virtual ~OQueryDesignFieldUndoAct() override; + + void SetColumnPosition(sal_uInt16 _nColumnPosition) + { + m_nColumnPosition = _nColumnPosition; + OSL_ENSURE(m_nColumnPosition != BROWSER_INVALIDID,"Column position was not set add the undo action!"); + OSL_ENSURE(m_nColumnPosition < pOwner->GetColumnCount(),"Position outside the column count!"); + } + }; + + // OTabFieldCellModifiedUndoAct - undo class to change a line of the column description + + class OTabFieldCellModifiedUndoAct final : public OQueryDesignFieldUndoAct + { + OUString m_strNextCellContents; + sal_Int32 m_nCellIndex; + + public: + explicit OTabFieldCellModifiedUndoAct(OSelectionBrowseBox* pSelBrwBox) + : OQueryDesignFieldUndoAct(pSelBrwBox, STR_QUERY_UNDO_MODIFY_CELL) + ,m_nCellIndex(BROWSER_INVALIDID){ } + + void SetCellContents(const OUString& str) { m_strNextCellContents = str; } + void SetCellIndex(sal_Int32 nIndex) { m_nCellIndex = nIndex; } + + virtual void Undo() override; + virtual void Redo() override { Undo(); } + }; + + // OTabFieldSizedUndoAct - undo class to change the column width + + class OTabFieldSizedUndoAct final : public OQueryDesignFieldUndoAct + { + long m_nNextWidth; + + public: + explicit OTabFieldSizedUndoAct(OSelectionBrowseBox* pSelBrwBox) : OQueryDesignFieldUndoAct(pSelBrwBox, STR_QUERY_UNDO_SIZE_COLUMN), m_nNextWidth(0) { } + + void SetOriginalWidth(long nWidth) { m_nNextWidth = nWidth; } + + virtual void Undo() override; + virtual void Redo() override { Undo(); } + }; + + // OTabFieldUndoAct - base class for undos in the fieldlist of a query design, which are used to change complete field descriptions + + class OTabFieldUndoAct : public OQueryDesignFieldUndoAct + { + protected: + OTableFieldDescRef pDescr; // the deleted column description + + public: + OTabFieldUndoAct(OSelectionBrowseBox* pSelBrwBox, const char* pCommentID) : OQueryDesignFieldUndoAct(pSelBrwBox, pCommentID) { } + + void SetTabFieldDescr(OTableFieldDescRef const & pDescription) { pDescr = pDescription; } + }; + + // OTabFieldDelUndoAct - undo class to delete a field + + class OTabFieldDelUndoAct : public OTabFieldUndoAct + { + protected: + virtual void Undo() override { pOwner->EnterUndoMode();pOwner->InsertColumn(pDescr, m_nColumnPosition);pOwner->LeaveUndoMode(); } + virtual void Redo() override { pOwner->EnterUndoMode();pOwner->RemoveColumn(pDescr->GetColumnId());pOwner->LeaveUndoMode(); } + + public: + explicit OTabFieldDelUndoAct(OSelectionBrowseBox* pSelBrwBox) : OTabFieldUndoAct(pSelBrwBox, STR_QUERY_UNDO_TABFIELDDELETE) { } + }; + + // OTabFieldCreateUndoAct - undo class for creating a field + + class OTabFieldCreateUndoAct : public OTabFieldUndoAct + { + protected: + virtual void Undo() override { pOwner->EnterUndoMode();pOwner->RemoveColumn(pDescr->GetColumnId());pOwner->LeaveUndoMode();} + virtual void Redo() override { pOwner->EnterUndoMode();pOwner->InsertColumn(pDescr, m_nColumnPosition);pOwner->LeaveUndoMode();} + + public: + explicit OTabFieldCreateUndoAct(OSelectionBrowseBox* pSelBrwBox) : OTabFieldUndoAct(pSelBrwBox, STR_QUERY_UNDO_TABFIELDCREATE) { } + }; + + // OTabFieldMovedUndoAct - Undo class when a field was moved inside the selection + + class OTabFieldMovedUndoAct : public OTabFieldUndoAct + { + protected: + virtual void Undo() override; + virtual void Redo() override + { + Undo(); + } + + public: + explicit OTabFieldMovedUndoAct(OSelectionBrowseBox* pSelBrwBox) : OTabFieldUndoAct(pSelBrwBox, STR_QUERY_UNDO_TABFIELDMOVED) { } + }; +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYDESIGNFIELDUNDOACT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QueryDesignUndoAction.hxx b/dbaccess/source/ui/querydesign/QueryDesignUndoAction.hxx new file mode 100644 index 000000000..817e00341 --- /dev/null +++ b/dbaccess/source/ui/querydesign/QueryDesignUndoAction.hxx @@ -0,0 +1,41 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYDESIGNUNDOACTION_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYDESIGNUNDOACTION_HXX + +#include <GeneralUndo.hxx> +#include <JoinTableView.hxx> +#include <vcl/vclptr.hxx> + +namespace dbaui +{ + // OQueryDesignUndoAction - undo base class for actions in graphical query design (without field list) + class OJoinTableView; + class OQueryDesignUndoAction : public OCommentUndoAction + { + protected: + VclPtr<OJoinTableView> m_pOwner; // in this container it all happens + + public: + OQueryDesignUndoAction(OJoinTableView* pOwner, const char* pCommentID) : OCommentUndoAction(pCommentID), m_pOwner(pOwner) { } + }; +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYDESIGNUNDOACTION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QueryDesignView.cxx b/dbaccess/source/ui/querydesign/QueryDesignView.cxx new file mode 100644 index 000000000..30afb7593 --- /dev/null +++ b/dbaccess/source/ui/querydesign/QueryDesignView.cxx @@ -0,0 +1,3442 @@ +/* -*- 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 <QueryDesignView.hxx> +#include <QueryTableView.hxx> +#include "QTableWindow.hxx" +#include <querycontroller.hxx> +#include <sqlbison.hxx> +#include <vcl/split.hxx> +#include <tools/diagnose_ex.h> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <browserids.hxx> +#include "SelectionBrowseBox.hxx" +#include <strings.hrc> +#include <strings.hxx> +#include <comphelper/string.hxx> +#include <connectivity/dbtools.hxx> +#include <connectivity/dbexception.hxx> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/sdbc/ColumnValue.hpp> +#include <connectivity/PColumn.hxx> +#include "QTableConnection.hxx" +#include <ConnectionLineData.hxx> +#include "QTableConnectionData.hxx" +#include <core_resource.hxx> +#include <UITools.hxx> +#include <querycontainerwindow.hxx> +#include <unotools/localedatawrapper.hxx> +#include <unotools/syslocale.hxx> +#include <memory> +#include <set> + +using namespace ::dbaui; +using namespace ::utl; +using namespace ::connectivity; +using namespace ::dbtools; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::i18n; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; + +// here we define our functions used in the anonymous namespace to get our header file smaller +// please look at the book LargeScale C++ to know why +namespace +{ + static const char C_AND[] = " AND "; + static const char C_OR[] = " OR "; + + bool InsertJoin( const OQueryDesignView* _pView, + const ::connectivity::OSQLParseNode *pNode); + + SqlParseError InstallFields(OQueryDesignView* _pView, + const ::connectivity::OSQLParseNode* pNode, + OJoinTableView::OTableWindowMap* pTabList ); + + SqlParseError GetGroupCriteria( OQueryDesignView* _pView, + OSelectionBrowseBox* _pSelectionBrw, + const ::connectivity::OSQLParseNode* pSelectRoot ); + + SqlParseError GetHavingCriteria(OQueryDesignView* _pView, + OSelectionBrowseBox* _pSelectionBrw, + const ::connectivity::OSQLParseNode* pSelectRoot, + sal_uInt16& rLevel ); + + SqlParseError GetOrderCriteria( OQueryDesignView* _pView, + OSelectionBrowseBox* _pSelectionBrw, + const ::connectivity::OSQLParseNode* pParseRoot ); + + SqlParseError AddFunctionCondition(OQueryDesignView const * _pView, + OSelectionBrowseBox* _pSelectionBrw, + const ::connectivity::OSQLParseNode * pCondition, + const sal_uInt16 nLevel, + bool bHaving, + bool _bAddOrOnOneLine); + + OUString quoteTableAlias(bool _bQuote, const OUString& _sAliasName, const OUString& _sQuote) + { + OUString sRet; + if ( _bQuote && !_sAliasName.isEmpty() ) + { + sRet = ::dbtools::quoteName(_sQuote,_sAliasName) + "."; + } + return sRet; + } + OUString getTableRange(const OQueryDesignView* _pView,const ::connectivity::OSQLParseNode* _pTableRef) + { + Reference< XConnection> xConnection = static_cast<OQueryController&>(_pView->getController()).getConnection(); + OUString sTableRange; + if ( _pTableRef ) + { + sTableRange = ::connectivity::OSQLParseNode::getTableRange(_pTableRef); + if ( sTableRange.isEmpty() ) + _pTableRef->parseNodeToStr(sTableRange,xConnection,nullptr,false,false); + } + return sTableRange; + } + void insertConnection(const OQueryDesignView* _pView,const EJoinType& _eJoinType, const OTableFieldDescRef& _aDragLeft, const OTableFieldDescRef& _aDragRight, bool _bNatural = false) + { + OQueryTableView* pTableView = static_cast<OQueryTableView*>(_pView->getTableView()); + OQueryTableConnection* pConn = static_cast<OQueryTableConnection*>( pTableView->GetTabConn(static_cast<OTableWindow*>(_aDragLeft->GetTabWindow()),static_cast<OTableWindow*>(_aDragRight->GetTabWindow()),true)); + + if ( !pConn ) + { + auto xInfoData = std::make_shared<OQueryTableConnectionData>(); + xInfoData->InitFromDrag(_aDragLeft, _aDragRight); + xInfoData->SetJoinType(_eJoinType); + + if ( _bNatural ) + { + xInfoData->ResetConnLines(); + xInfoData->setNatural(_bNatural); + try + { + Reference<XNameAccess> xReferencedTableColumns(xInfoData->getReferencedTable()->getColumns()); + Sequence< OUString> aSeq = xInfoData->getReferencingTable()->getColumns()->getElementNames(); + const OUString* pIter = aSeq.getConstArray(); + const OUString* pEnd = pIter + aSeq.getLength(); + for(;pIter != pEnd;++pIter) + { + if ( xReferencedTableColumns->hasByName(*pIter) ) + xInfoData->AppendConnLine(*pIter,*pIter); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + ScopedVclPtrInstance< OQueryTableConnection > aInfo(pTableView, xInfoData); + // Because OQueryTableConnection never takes ownership of the data passed to it, but only remembers the pointer, + // this pointer to a local variable is not critical, as xInfoData and aInfo have the same lifetime + pTableView->NotifyTabConnection( *aInfo ); + } + else + { + OUString aSourceFieldName(_aDragLeft->GetField()); + OUString aDestFieldName(_aDragRight->GetField()); + // the connection could point on the other side + if(pConn->GetSourceWin() == _aDragRight->GetTabWindow()) + { + OUString aTmp(aSourceFieldName); + aSourceFieldName = aDestFieldName; + aDestFieldName = aTmp; + } + pConn->GetData()->AppendConnLine( aSourceFieldName,aDestFieldName); + pConn->UpdateLineList(); + // Modified-Flag + // SetModified(); + // and redraw + pConn->RecalcLines(); + // for the following Invalidate, the new Connection must first be able + // to determine its BoundingRect + pConn->InvalidateConnection(); + } + } + OUString ParseCondition( OQueryController& rController + ,const ::connectivity::OSQLParseNode* pCondition + ,const OUString& _sDecimal + ,const css::lang::Locale& _rLocale + ,sal_uInt32 _nStartIndex) + { + OUString aCondition; + Reference< XConnection> xConnection = rController.getConnection(); + if ( xConnection.is() ) + { + sal_uInt32 nCount = pCondition->count(); + for(sal_uInt32 i = _nStartIndex ; i < nCount ; ++i) + pCondition->getChild(i)->parseNodeToPredicateStr(aCondition, + xConnection, + rController.getNumberFormatter(), + _rLocale, + _sDecimal, + &rController.getParser().getContext()); + } + return aCondition; + } + SqlParseError FillOuterJoins(OQueryDesignView const * _pView, + const ::connectivity::OSQLParseNode* pTableRefList) + { + SqlParseError eErrorCode = eOk; + sal_uInt32 nCount = pTableRefList->count(); + bool bError = false; + for (sal_uInt32 i=0; !bError && i < nCount; ++i) + { + const ::connectivity::OSQLParseNode* pParseNode = pTableRefList->getChild(i); + const ::connectivity::OSQLParseNode* pJoinNode = nullptr; + + if ( SQL_ISRULE( pParseNode, qualified_join ) || SQL_ISRULE( pParseNode, joined_table ) || SQL_ISRULE( pParseNode, cross_union ) ) + pJoinNode = pParseNode; + else if( SQL_ISRULE(pParseNode,table_ref) + && pParseNode->count() == 4 ) // '{' SQL_TOKEN_OJ joined_table '}' + pJoinNode = pParseNode->getChild(2); + + if ( pJoinNode ) + { + if ( !InsertJoin(_pView,pJoinNode) ) + bError = true; + } + } + // check if error occurred + if ( bError ) + eErrorCode = eIllegalJoin; + + return eErrorCode; + } + + /** FillDragInfo fills the field description out of the table + */ + SqlParseError FillDragInfo( const OQueryDesignView* _pView, + const ::connectivity::OSQLParseNode* pColumnRef, + OTableFieldDescRef const & _rDragInfo) + { + SqlParseError eErrorCode = eOk; + + bool bErg = false; + + OUString aTableRange,aColumnName; + sal_uInt16 nCntAccount; + ::connectivity::OSQLParseTreeIterator& rParseIter = static_cast<OQueryController&>(_pView->getController()).getParseIterator(); + rParseIter.getColumnRange( pColumnRef, aColumnName, aTableRange ); + + if ( !aTableRange.isEmpty() ) + { + OQueryTableWindow* pSTW = static_cast<OQueryTableView*>(_pView->getTableView())->FindTable( aTableRange ); + bErg = (pSTW && pSTW->ExistsField( aColumnName, _rDragInfo ) ); + } + if ( !bErg ) + { + bErg = static_cast<OQueryTableView*>(_pView->getTableView())->FindTableFromField(aColumnName, _rDragInfo, nCntAccount); + if ( !bErg ) + bErg = _pView->HasFieldByAliasName(aColumnName, _rDragInfo); + } + if ( !bErg ) + { + eErrorCode = eColumnNotFound; + OUString sError(DBA_RES(STR_QRY_COLUMN_NOT_FOUND)); + sError = sError.replaceFirst("$name$",aColumnName); + _pView->getController().appendError( sError ); + + try + { + Reference<XDatabaseMetaData> xMeta = _pView->getController().getConnection()->getMetaData(); + if ( xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers() ) + _pView->getController().appendError(DBA_RES(STR_QRY_CHECK_CASESENSITIVE)); + } + catch(Exception&) + { + } + } + + return eErrorCode; + } + OUString BuildJoinCriteria( const Reference< XConnection>& _xConnection, + const OConnectionLineDataVec* pLineDataList, + const OQueryTableConnectionData* pData) + { + OUStringBuffer aCondition; + if ( _xConnection.is() ) + { + try + { + const Reference< XDatabaseMetaData > xMetaData = _xConnection->getMetaData(); + const OUString aQuote = xMetaData->getIdentifierQuoteString(); + + for (auto const& lineData : *pLineDataList) + { + if(!aCondition.isEmpty()) + aCondition.append(C_AND); + aCondition.append(quoteTableAlias(true,pData->GetAliasName(JTCS_FROM),aQuote)); + aCondition.append(::dbtools::quoteName(aQuote, lineData->GetFieldName(JTCS_FROM) )); + aCondition.append(" = "); + aCondition.append(quoteTableAlias(true,pData->GetAliasName(JTCS_TO),aQuote)); + aCondition.append(::dbtools::quoteName(aQuote, lineData->GetFieldName(JTCS_TO) )); + } + } + catch(SQLException&) + { + OSL_FAIL("Failure while building Join criteria!"); + } + } + + return aCondition.makeStringAndClear(); + } + /** JoinCycle looks for a join cycle and append it to the string + @param _xConnection the connection + @param _pEntryConn the table connection which holds the data + @param _pEntryTabTo the corresponding table window + @param _rJoin the String which will contain the resulting string + */ + void JoinCycle( const Reference< XConnection>& _xConnection, + OQueryTableConnection* _pEntryConn, + const OQueryTableWindow* _pEntryTabTo, + OUString& _rJoin ) + { + OSL_ENSURE(_pEntryConn,"TableConnection can not be null!"); + + OQueryTableConnectionData* pData = static_cast< OQueryTableConnectionData*>(_pEntryConn->GetData().get()); + if ( !(pData->GetJoinType() != INNER_JOIN && _pEntryTabTo->ExistsAVisitedConn()) ) + return; + + bool bBrace = false; + if(_rJoin.endsWith(")")) + { + bBrace = true; + _rJoin = _rJoin.replaceAt(_rJoin.getLength()-1,1,OUString(' ')); + } + _rJoin += C_AND + BuildJoinCriteria(_xConnection,&pData->GetConnLineDataList(),pData); + if(bBrace) + _rJoin += ")"; + _pEntryConn->SetVisited(true); + } + OUString BuildTable( const Reference< XConnection>& _xConnection, + const OQueryTableWindow* pEntryTab, + bool _bForce = false + ) + { + OUString aDBName(pEntryTab->GetComposedName()); + + if( _xConnection.is() ) + { + try + { + Reference< XDatabaseMetaData > xMetaData = _xConnection->getMetaData(); + + OUString sCatalog, sSchema, sTable; + ::dbtools::qualifiedNameComponents( xMetaData, aDBName, sCatalog, sSchema, sTable, ::dbtools::EComposeRule::InDataManipulation ); + OUString aTableListStr = ::dbtools::composeTableNameForSelect( _xConnection, sCatalog, sSchema, sTable ); + + OUString aQuote = xMetaData->getIdentifierQuoteString(); + if ( _bForce || isAppendTableAliasEnabled( _xConnection ) || pEntryTab->GetAliasName() != aDBName ) + { + aTableListStr += " "; + if ( generateAsBeforeTableAlias( _xConnection ) ) + aTableListStr += "AS "; + aTableListStr += ::dbtools::quoteName( aQuote, pEntryTab->GetAliasName() ); + } + aDBName = aTableListStr; + } + catch(const SQLException&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + return aDBName; + } + OUString BuildJoin( const Reference< XConnection>& _xConnection, + const OUString& rLh, + const OUString& rRh, + const OQueryTableConnectionData* pData) + { + + OUString aErg(rLh); + if ( pData->isNatural() && pData->GetJoinType() != CROSS_JOIN ) + aErg += " NATURAL "; + switch(pData->GetJoinType()) + { + case LEFT_JOIN: + aErg += " LEFT OUTER "; + break; + case RIGHT_JOIN: + aErg += " RIGHT OUTER "; + break; + case CROSS_JOIN: + OSL_ENSURE(!pData->isNatural(),"OQueryDesignView::BuildJoin: This should not happen!"); + aErg += " CROSS "; + break; + case INNER_JOIN: + OSL_ENSURE(pData->isNatural(),"OQueryDesignView::BuildJoin: This should not happen!"); + aErg += " INNER "; + break; + default: + aErg += " FULL OUTER "; + break; + } + aErg += "JOIN " + rRh; + if ( CROSS_JOIN != pData->GetJoinType() && !pData->isNatural() ) + { + aErg += " ON " + BuildJoinCriteria(_xConnection,&pData->GetConnLineDataList(),pData); + } + + return aErg; + } + OUString BuildJoin( const Reference< XConnection>& _xConnection, + const OQueryTableWindow* pLh, + const OQueryTableWindow* pRh, + const OQueryTableConnectionData* pData + ) + { + bool bForce = pData->GetJoinType() == CROSS_JOIN || pData->isNatural(); + return BuildJoin(_xConnection,BuildTable(_xConnection,pLh,bForce),BuildTable(_xConnection,pRh,bForce),pData); + } + OUString BuildJoin( const Reference< XConnection>& _xConnection, + const OUString &rLh, + const OQueryTableWindow* pRh, + const OQueryTableConnectionData* pData + ) + { + return BuildJoin(_xConnection,rLh,BuildTable(_xConnection,pRh),pData); + } + OUString BuildJoin( const Reference< XConnection>& _xConnection, + const OQueryTableWindow* pLh, + const OUString &rRh, + const OQueryTableConnectionData* pData + ) + { + // strict ANSI SQL: + // - does not support any bracketing of JOINS + // - supports nested joins only in the LEFT HAND SIDE + // In this case, we are trying to build a join with a nested join + // in the right hand side. + // So switch the direction of the join and both hand sides. + OQueryTableConnectionData data(*pData); + switch (data.GetJoinType()) + { + case LEFT_JOIN: + data.SetJoinType(RIGHT_JOIN); + break; + case RIGHT_JOIN: + data.SetJoinType(LEFT_JOIN); + break; + default: + // the other join types are symmetric, so nothing to change + break; + } + return BuildJoin(_xConnection, rRh, BuildTable(_xConnection,pLh), &data); + } + void addConnectionTableNames( const Reference< XConnection>& _xConnection, + const OQueryTableConnection* const pEntryConn, + std::set<OUString> &_rTableNames ) + { + // insert tables into table list to avoid double entries + const OQueryTableWindow* const pEntryTabFrom = static_cast<OQueryTableWindow*>(pEntryConn->GetSourceWin()); + const OQueryTableWindow* const pEntryTabTo = static_cast<OQueryTableWindow*>(pEntryConn->GetDestWin()); + _rTableNames.insert(BuildTable(_xConnection,pEntryTabFrom)); + _rTableNames.insert(BuildTable(_xConnection,pEntryTabTo)); + } + void GetNextJoin( const Reference< XConnection>& _xConnection, + OQueryTableConnection* pEntryConn, + OQueryTableWindow const * pEntryTabTo, + OUString &aJoin, + std::set<OUString> &_rTableNames) + { + OQueryTableConnectionData* pEntryConnData = static_cast<OQueryTableConnectionData*>(pEntryConn->GetData().get()); + if ( pEntryConnData->GetJoinType() == INNER_JOIN && !pEntryConnData->isNatural() ) + return; + + if(aJoin.isEmpty()) + { + addConnectionTableNames(_xConnection, pEntryConn, _rTableNames); + OQueryTableWindow* pEntryTabFrom = static_cast<OQueryTableWindow*>(pEntryConn->GetSourceWin()); + aJoin = BuildJoin(_xConnection,pEntryTabFrom,pEntryTabTo,pEntryConnData); + } + else if(pEntryTabTo == pEntryConn->GetDestWin()) + { + addConnectionTableNames(_xConnection, pEntryConn, _rTableNames); + aJoin = BuildJoin(_xConnection,aJoin,pEntryTabTo,pEntryConnData); + } + else if(pEntryTabTo == pEntryConn->GetSourceWin()) + { + addConnectionTableNames(_xConnection, pEntryConn, _rTableNames); + aJoin = BuildJoin(_xConnection,pEntryTabTo,aJoin,pEntryConnData); + } + + pEntryConn->SetVisited(true); + + // first search for the "to" window + const auto& rConnections = pEntryConn->GetParent()->getTableConnections(); + bool bFound = false; + for (auto const& connection : rConnections) + { + OQueryTableConnection* pNext = static_cast<OQueryTableConnection*>(connection.get()); + if(!pNext->IsVisited() && (pNext->GetSourceWin() == pEntryTabTo || pNext->GetDestWin() == pEntryTabTo)) + { + OQueryTableWindow* pEntryTab = pNext->GetSourceWin() == pEntryTabTo ? static_cast<OQueryTableWindow*>(pNext->GetDestWin()) : static_cast<OQueryTableWindow*>(pNext->GetSourceWin()); + // exists there a connection to a OQueryTableWindow that holds a connection that has been already visited + JoinCycle(_xConnection,pNext,pEntryTab,aJoin); + if(!pNext->IsVisited()) + GetNextJoin(_xConnection, pNext, pEntryTab, aJoin, _rTableNames); + bFound = true; + } + } + + // when nothing found look for the "from" window + if(bFound) + return; + + OQueryTableWindow* pEntryTabFrom = static_cast<OQueryTableWindow*>(pEntryConn->GetSourceWin()); + for (auto const& connection : rConnections) + { + OQueryTableConnection* pNext = static_cast<OQueryTableConnection*>(connection.get()); + if(!pNext->IsVisited() && (pNext->GetSourceWin() == pEntryTabFrom || pNext->GetDestWin() == pEntryTabFrom)) + { + OQueryTableWindow* pEntryTab = pNext->GetSourceWin() == pEntryTabFrom ? static_cast<OQueryTableWindow*>(pNext->GetDestWin()) : static_cast<OQueryTableWindow*>(pNext->GetSourceWin()); + // exists there a connection to a OQueryTableWindow that holds a connection that has been already visited + JoinCycle(_xConnection,pNext,pEntryTab,aJoin); + if(!pNext->IsVisited()) + GetNextJoin(_xConnection, pNext, pEntryTab, aJoin, _rTableNames); + } + } + } + SqlParseError InsertJoinConnection( const OQueryDesignView* _pView, + const ::connectivity::OSQLParseNode *pNode, + const EJoinType& _eJoinType, + const ::connectivity::OSQLParseNode *pLeftTable, + const ::connectivity::OSQLParseNode *pRightTable) + { + SqlParseError eErrorCode = eOk; + if (pNode->count() == 3 && // statement between brackets + SQL_ISPUNCTUATION(pNode->getChild(0),"(") && + SQL_ISPUNCTUATION(pNode->getChild(2),")")) + { + eErrorCode = InsertJoinConnection(_pView,pNode->getChild(1), _eJoinType,pLeftTable,pRightTable); + } + else if (SQL_ISRULEOR2(pNode,search_condition,boolean_term) && // AND/OR-joints: + pNode->count() == 3) + { + // only allow AND joints + if (!SQL_ISTOKEN(pNode->getChild(1),AND)) + eErrorCode = eIllegalJoinCondition; + else if ( eOk == (eErrorCode = InsertJoinConnection(_pView,pNode->getChild(0), _eJoinType,pLeftTable,pRightTable)) ) + eErrorCode = InsertJoinConnection(_pView,pNode->getChild(2), _eJoinType,pLeftTable,pRightTable); + } + else if (SQL_ISRULE(pNode,comparison_predicate)) + { + // only the comparison of columns is allowed + OSL_ENSURE(pNode->count() == 3,"OQueryDesignView::InsertJoinConnection: Error in Parse Tree"); + if (!(SQL_ISRULE(pNode->getChild(0),column_ref) && + SQL_ISRULE(pNode->getChild(2),column_ref) && + pNode->getChild(1)->getNodeType() == SQLNodeType::Equal)) + { + OUString sError(DBA_RES(STR_QRY_JOIN_COLUMN_COMPARE)); + _pView->getController().appendError( sError ); + return eIllegalJoin; + } + + OTableFieldDescRef aDragLeft = new OTableFieldDesc(); + OTableFieldDescRef aDragRight = new OTableFieldDesc(); + eErrorCode = FillDragInfo(_pView,pNode->getChild(0),aDragLeft); + if ( eOk != eErrorCode ) + return eErrorCode; + eErrorCode = FillDragInfo(_pView,pNode->getChild(2),aDragRight); + if ( eOk != eErrorCode ) + return eErrorCode; + + if ( pLeftTable ) + { + OQueryTableWindow* pLeftWindow = static_cast<OQueryTableView*>(_pView->getTableView())->FindTable( getTableRange(_pView,pLeftTable->getByRule(OSQLParseNode::table_ref) )); + if ( pLeftWindow == aDragLeft->GetTabWindow() ) + insertConnection(_pView,_eJoinType,aDragLeft,aDragRight); + else + insertConnection(_pView,_eJoinType,aDragRight,aDragLeft); + } + else + insertConnection(_pView,_eJoinType,aDragLeft,aDragRight); + } + else + eErrorCode = eIllegalJoin; + return eErrorCode; + } + bool GetInnerJoinCriteria( const OQueryDesignView* _pView, + const ::connectivity::OSQLParseNode *pCondition) + { + return InsertJoinConnection(_pView,pCondition, INNER_JOIN,nullptr,nullptr) != eOk; + } + OUString GenerateSelectList( const OQueryDesignView* _pView, + OTableFields& _rFieldList, + bool bAlias) + { + Reference< XConnection> xConnection = static_cast<OQueryController&>(_pView->getController()).getConnection(); + if ( !xConnection.is() ) + return OUString(); + + OUStringBuffer aTmpStr,aFieldListStr; + + bool bAsterisk = false; + int nVis = 0; + for (auto const& field : _rFieldList) + { + if ( field->IsVisible() ) + { + if ( field->GetField().toChar() == '*' ) + bAsterisk = true; + ++nVis; + } + } + if(nVis == 1) + bAsterisk = false; + + try + { + const Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData(); + const OUString aQuote = xMetaData->getIdentifierQuoteString(); + + OJoinTableView::OTableWindowMap& rTabList = _pView->getTableView()->GetTabWinMap(); + + for (auto const& field : _rFieldList) + { + OUString rFieldName = field->GetField(); + if ( !rFieldName.isEmpty() && field->IsVisible() ) + { + aTmpStr = ""; + const OUString rAlias = field->GetAlias(); + const OUString rFieldAlias = field->GetFieldAlias(); + + aTmpStr.append(quoteTableAlias((bAlias || bAsterisk),rAlias,aQuote)); + + // if we have a none numeric field, the table alias could be in the name + // otherwise we are not allowed to do this (e.g. 0.1 * PRICE ) + if ( !field->isOtherFunction() ) + { + // we have to look if we have alias.* here but before we have to check if the column doesn't already exist + OTableFieldDescRef aInfo = new OTableFieldDesc(); + for (auto const& table : rTabList) + { + OQueryTableWindow* pTabWin = static_cast<OQueryTableWindow*>(table.second.get()); + + if ( pTabWin->ExistsField( rFieldName, aInfo ) ) + { + rFieldName = aInfo->GetField(); + break; + } + } + if ( ( rFieldName.toChar() != '*' ) && ( rFieldName.indexOf( aQuote ) == -1 ) ) + { + OSL_ENSURE(!field->GetTable().isEmpty(),"No table field name!"); + aTmpStr.append(::dbtools::quoteName(aQuote, rFieldName)); + } + else + aTmpStr.append(rFieldName); + } + else + aTmpStr.append(rFieldName); + + if ( field->isAggregateFunction() ) + { + OSL_ENSURE(!field->GetFunction().isEmpty(),"Function name must not be empty! ;-("); + OUStringBuffer aTmpStr2( field->GetFunction()); + aTmpStr2.append("("); + aTmpStr2.append(aTmpStr.makeStringAndClear()); + aTmpStr2.append(")"); + aTmpStr = aTmpStr2; + } + + if (!rFieldAlias.isEmpty() && + (rFieldName.toChar() != '*' || + field->isNumericOrAggregateFunction() || + field->isOtherFunction())) + { + aTmpStr.append(" AS "); + aTmpStr.append(::dbtools::quoteName(aQuote, rFieldAlias)); + } + aFieldListStr.append(aTmpStr.makeStringAndClear()); + aFieldListStr.append(", "); + } + } + if(!aFieldListStr.isEmpty()) + aFieldListStr.setLength(aFieldListStr.getLength()-2); + } + catch(SQLException&) + { + OSL_FAIL("Failure while building select list!"); + } + return aFieldListStr.makeStringAndClear(); + } + bool GenerateCriterias( OQueryDesignView const * _pView, + OUStringBuffer& rRetStr, + OUStringBuffer& rHavingStr, + OTableFields& _rFieldList, + bool bMulti ) + { + Reference< XConnection> xConnection = static_cast<OQueryController&>(_pView->getController()).getConnection(); + if(!xConnection.is()) + return false; + + OUString aFieldName,aCriteria,aWhereStr,aHavingStr,aWork/*,aOrderStr*/; + // print line by line joined with AND + sal_uInt16 nMaxCriteria = 0; + for (auto const& field : _rFieldList) + { + nMaxCriteria = std::max<sal_uInt16>(nMaxCriteria,static_cast<sal_uInt16>(field->GetCriteria().size())); + } + try + { + const Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData(); + const OUString aQuote = xMetaData->getIdentifierQuoteString(); + const IParseContext& rContext = static_cast<OQueryController&>(_pView->getController()).getParser().getContext(); + // * must not contain a filter : have I already shown the correct warning ? + bool bCritsOnAsterikWarning = false; // ** TMFS ** + + for (sal_uInt16 i=0 ; i < nMaxCriteria ; i++) + { + aHavingStr.clear(); + aWhereStr.clear(); + + for (auto const& field : _rFieldList) + { + aFieldName = field->GetField(); + + if (aFieldName.isEmpty()) + continue; + aCriteria = field->GetCriteria( i ); + if ( !aCriteria.isEmpty() ) + { + // * is not allowed to contain any filter, only when used in combination an aggregate function + if ( aFieldName.toChar() == '*' && field->isNoneFunction() ) + { + // only show the messagebox the first time + if (!bCritsOnAsterikWarning) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(_pView->GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, + DBA_RES(STR_QRY_CRITERIA_ON_ASTERISK))); + xBox->run(); + } + bCritsOnAsterikWarning = true; + continue; + } + aWork = quoteTableAlias(bMulti,field->GetAlias(),aQuote); + + if ( (field->GetFunctionType() & (FKT_OTHER|FKT_NUMERIC)) || (aFieldName.toChar() == '*') ) + aWork += aFieldName; + else + aWork += ::dbtools::quoteName(aQuote, aFieldName); + + if ( field->isAggregateFunction() || field->IsGroupBy() ) + { + if (aHavingStr.isEmpty()) // no more criteria + aHavingStr += "("; // bracket + else + aHavingStr += C_AND; + + if ( field->isAggregateFunction() ) + { + OSL_ENSURE(!field->GetFunction().isEmpty(),"No function name for aggregate given!"); + aHavingStr += field->GetFunction() + "(" + aWork + ")"; // bracket + } + else + aHavingStr += aWork; + + OUString aErrorMsg; + Reference<XPropertySet> xColumn; + std::unique_ptr< ::connectivity::OSQLParseNode> pParseNode(_pView->getPredicateTreeFromEntry(field,aCriteria,aErrorMsg,xColumn)); + if (pParseNode) + { + if (bMulti && !(field->isOtherFunction() || (aFieldName.toChar() == '*'))) + pParseNode->replaceNodeValue(field->GetAlias(),aFieldName); + OUString sHavingStr = aHavingStr; + + sal_uInt32 nCount = pParseNode->count(); + for( sal_uInt32 node = 1 ; node < nCount ; ++node) + pParseNode->getChild(node)->parseNodeToStr( sHavingStr, + xConnection, + &rContext, + false, + !field->isOtherFunction()); + aHavingStr = sHavingStr; + } + else + aHavingStr += aCriteria; + } + else + { + if ( aWhereStr.isEmpty() ) // no more criteria + aWhereStr += "("; // bracket + else + aWhereStr += C_AND; + + aWhereStr += " "; + // aCriteria could have some German numbers so I have to be sure here + OUString aErrorMsg; + Reference<XPropertySet> xColumn; + std::unique_ptr< ::connectivity::OSQLParseNode> pParseNode( _pView->getPredicateTreeFromEntry(field,aCriteria,aErrorMsg,xColumn)); + if (pParseNode) + { + if (bMulti && !(field->isOtherFunction() || (aFieldName.toChar() == '*'))) + pParseNode->replaceNodeValue(field->GetAlias(),aFieldName); + OUString aWhere = aWhereStr; + pParseNode->parseNodeToStr( aWhere, + xConnection, + &rContext, + false, + !field->isOtherFunction() ); + aWhereStr = aWhere; + } + else + { + aWhereStr += aWork + "=" + aCriteria; + } + } + } + // only once for each field + else if ( !i && field->isCondition() ) + { + if (aWhereStr.isEmpty()) // no more criteria + aWhereStr += "("; // bracket + else + aWhereStr += C_AND; + aWhereStr += field->GetField(); + } + } + if (!aWhereStr.isEmpty()) + { + aWhereStr += ")"; // close bracket for the AND branch + if (!rRetStr.isEmpty()) // are there conditions on the field? + rRetStr.append(C_OR); + else // open bracket for the OR branch + rRetStr.append('('); + rRetStr.append(aWhereStr); + } + if (!aHavingStr.isEmpty()) + { + aHavingStr += ")"; // close bracket for the AND branch + if (!rHavingStr.isEmpty()) // are there conditions on the field? + rHavingStr.append(C_OR); + else // Open bracket for the OR branch + rHavingStr.append('('); + rHavingStr.append(aHavingStr); + } + } + + if (!rRetStr.isEmpty()) + rRetStr.append(')'); // close bracket for the OR branch + if (!rHavingStr.isEmpty()) + rHavingStr.append(')'); // close bracket for the OR branch + } + catch(SQLException&) + { + OSL_FAIL("Failure while building where clause!"); + } + return true; + } + SqlParseError GenerateOrder( OQueryDesignView const * _pView, + OTableFields& _rFieldList, + bool bMulti, + OUString& _rsRet) + { + const OQueryController& rController = static_cast<OQueryController&>(_pView->getController()); + const Reference< XConnection>& xConnection = rController.getConnection(); + if ( !xConnection.is() ) + return eNoConnection; + + SqlParseError eErrorCode = eOk; + + OUString aColumnName; + OUString aWorkStr; + try + { + const bool bColumnAliasInOrderBy = rController.getSdbMetaData().supportsColumnAliasInOrderBy(); + Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData(); + OUString aQuote = xMetaData->getIdentifierQuoteString(); + // * must not contain filter - have I already shown the warning? + bool bCritsOnAsterikWarning = false; // ** TMFS ** + for (auto const& field : _rFieldList) + { + EOrderDir eOrder = field->GetOrderDir(); + // only create a sort expression when the table name and the sort criteria are defined + // otherwise they will be built in GenerateCriteria + if ( eOrder != ORDER_NONE ) + { + aColumnName = field->GetField(); + if(aColumnName.toChar() == '*') + { + // only show the MessageBox the first time + if (!bCritsOnAsterikWarning) + { + std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(_pView->GetFrameWeld(), + VclMessageType::Warning, VclButtonsType::Ok, + DBA_RES(STR_QRY_ORDERBY_ON_ASTERISK))); + xBox->run(); + } + bCritsOnAsterikWarning = true; + continue; + } + + if ( bColumnAliasInOrderBy && !field->GetFieldAlias().isEmpty() ) + { + aWorkStr += ::dbtools::quoteName(aQuote, field->GetFieldAlias()); + } + else if ( field->isNumericOrAggregateFunction() ) + { + OSL_ENSURE(!field->GetFunction().isEmpty(),"Function name cannot be empty! ;-("); + aWorkStr += field->GetFunction() + "(" + + quoteTableAlias( + bMulti, field->GetAlias(), aQuote); + // only quote column name when we don't have a numeric + if ( field->isNumeric() ) + aWorkStr += aColumnName; + else + aWorkStr += ::dbtools::quoteName(aQuote, aColumnName); + + aWorkStr += ")"; + } + else if ( field->isOtherFunction() ) + { + aWorkStr += aColumnName; + } + else + { + aWorkStr += quoteTableAlias(bMulti,field->GetAlias(),aQuote) + ::dbtools::quoteName(aQuote, aColumnName); + } + aWorkStr += " " + OUString( ";ASC;DESC" ).getToken( static_cast<sal_uInt16>(eOrder), ';' ) + ","; + } + } + + { + OUString sTemp(comphelper::string::stripEnd(aWorkStr, ',')); + aWorkStr = sTemp; + } + + if ( !aWorkStr.isEmpty() ) + { + const sal_Int32 nMaxOrder = xMetaData->getMaxColumnsInOrderBy(); + if ( nMaxOrder && nMaxOrder < comphelper::string::getTokenCount(aWorkStr, ',') ) + eErrorCode = eStatementTooLong; + else + { + _rsRet = " ORDER BY " + aWorkStr; + } + } + } + catch(SQLException&) + { + OSL_FAIL("Failure while building group by!"); + } + + return eErrorCode; + } + + void GenerateInnerJoinCriterias(const Reference< XConnection>& _xConnection, + OUString& _rJoinCrit, + const std::vector<VclPtr<OTableConnection> >& _rConnList) + { + for (auto const& connection : _rConnList) + { + const OQueryTableConnection* pEntryConn = static_cast<const OQueryTableConnection*>(connection.get()); + OQueryTableConnectionData* pEntryConnData = static_cast<OQueryTableConnectionData*>(pEntryConn->GetData().get()); + if ( pEntryConnData->GetJoinType() == INNER_JOIN && !pEntryConnData->isNatural() ) + { + if(!_rJoinCrit.isEmpty()) + _rJoinCrit += C_AND; + _rJoinCrit += BuildJoinCriteria(_xConnection,&pEntryConnData->GetConnLineDataList(),pEntryConnData); + } + } + } + void searchAndAppendName(const Reference< XConnection>& _xConnection, + const OQueryTableWindow* _pTableWindow, + std::set<OUString>& _rTableNames, + OUString& _rsTableListStr + ) + { + OUString sTabName(BuildTable(_xConnection,_pTableWindow)); + + if(_rTableNames.insert(sTabName).second) + { + _rsTableListStr += sTabName + ","; + } + } + OUString GenerateFromClause( const Reference< XConnection>& _xConnection, + const OQueryTableView::OTableWindowMap* pTabList, + const std::vector<VclPtr<OTableConnection> >& rConnList + ) + { + + OUString aTableListStr; + // used to avoid putting a table twice in FROM clause + std::set<OUString> aTableNames; + + // generate outer join clause in from + if(!rConnList.empty()) + { + std::map<OTableWindow*,sal_Int32> aConnectionCount; + auto aEnd = rConnList.end(); + for (auto const& connection : rConnList) + { + static_cast<OQueryTableConnection*>(connection.get())->SetVisited(false); + ++aConnectionCount[connection->GetSourceWin()]; + ++aConnectionCount[connection->GetDestWin()]; + } + std::multimap<sal_Int32 , OTableWindow*> aMulti; + for (auto const& elem : aConnectionCount) + { + aMulti.emplace(elem.second,elem.first); + } + + const bool bUseEscape = ::dbtools::getBooleanDataSourceSetting( _xConnection, PROPERTY_OUTERJOINESCAPE ); + std::multimap<sal_Int32 , OTableWindow*>::const_reverse_iterator aRIter = aMulti.rbegin(); + std::multimap<sal_Int32 , OTableWindow*>::const_reverse_iterator aREnd = aMulti.rend(); + for(;aRIter != aREnd;++aRIter) + { + auto aConIter = aRIter->second->getTableView()->getTableConnections(aRIter->second); + for(;aConIter != aEnd;++aConIter) + { + OQueryTableConnection* pEntryConn = static_cast<OQueryTableConnection*>((*aConIter).get()); + if(!pEntryConn->IsVisited() && pEntryConn->GetSourceWin() == aRIter->second ) + { + OUString aJoin; + GetNextJoin(_xConnection, + pEntryConn, + static_cast<OQueryTableWindow*>(pEntryConn->GetDestWin()), + aJoin, + aTableNames); + + if(!aJoin.isEmpty()) + { + OUString aStr; + switch(static_cast<OQueryTableConnectionData*>(pEntryConn->GetData().get())->GetJoinType()) + { + case LEFT_JOIN: + case RIGHT_JOIN: + case FULL_JOIN: + { + // create outer join + if ( bUseEscape ) + aStr += "{ oj "; + aStr += aJoin; + if ( bUseEscape ) + aStr += " }"; + } + break; + default: + aStr += aJoin; + break; + } + aStr += ","; + aTableListStr += aStr; + } + } + } + } + + // and now all inner joins + // these are implemented as + // "FROM tbl1, tbl2 WHERE tbl1.col1=tlb2.col2" + // rather than + // "FROM tbl1 INNER JOIN tbl2 ON tbl1.col1=tlb2.col2" + for (auto const& connection : rConnList) + { + OQueryTableConnection* pEntryConn = static_cast<OQueryTableConnection*>(connection.get()); + if(!pEntryConn->IsVisited()) + { + searchAndAppendName(_xConnection, + static_cast<OQueryTableWindow*>(pEntryConn->GetSourceWin()), + aTableNames, + aTableListStr); + + searchAndAppendName(_xConnection, + static_cast<OQueryTableWindow*>(pEntryConn->GetDestWin()), + aTableNames, + aTableListStr); + } + } + } + // all tables that haven't a connection to anyone + for (auto const& table : *pTabList) + { + const OQueryTableWindow* pEntryTab = static_cast<const OQueryTableWindow*>(table.second.get()); + if(!pEntryTab->ExistsAConn()) + { + aTableListStr += BuildTable(_xConnection,pEntryTab) + ","; + } + } + + if(!aTableListStr.isEmpty()) + aTableListStr = aTableListStr.replaceAt(aTableListStr.getLength()-1,1, OUString() ); + return aTableListStr; + } + OUString GenerateGroupBy(const OQueryDesignView* _pView,OTableFields& _rFieldList, bool bMulti ) + { + OQueryController& rController = static_cast<OQueryController&>(_pView->getController()); + const Reference< XConnection> xConnection = rController.getConnection(); + if(!xConnection.is()) + return OUString(); + + std::map< OUString,bool> aGroupByNames; + + OUString aGroupByStr; + try + { + const Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData(); + const OUString aQuote = xMetaData->getIdentifierQuoteString(); + + for (auto const& field : _rFieldList) + { + if ( field->IsGroupBy() ) + { + OSL_ENSURE(!field->GetField().isEmpty(),"No Field Name available!;-("); + OUString sGroupByPart = quoteTableAlias(bMulti,field->GetAlias(),aQuote); + + // only quote the field name when it isn't calculated + if ( field->isNoneFunction() ) + { + sGroupByPart += ::dbtools::quoteName(aQuote, field->GetField()); + } + else + { + OUString aTmp = field->GetField(); + OUString aErrorMsg; + Reference<XPropertySet> xColumn; + std::unique_ptr< ::connectivity::OSQLParseNode> pParseNode(_pView->getPredicateTreeFromEntry(field,aTmp,aErrorMsg,xColumn)); + if (pParseNode) + { + OUString sGroupBy; + pParseNode->getChild(0)->parseNodeToStr( sGroupBy, + xConnection, + &rController.getParser().getContext(), + false, + !field->isOtherFunction()); + sGroupByPart += sGroupBy; + } + else + sGroupByPart += field->GetField(); + } + if ( aGroupByNames.find(sGroupByPart) == aGroupByNames.end() ) + { + aGroupByNames.emplace(sGroupByPart,true); + aGroupByStr += sGroupByPart + ","; + } + } + } + if ( !aGroupByStr.isEmpty() ) + { + aGroupByStr = aGroupByStr.replaceAt(aGroupByStr.getLength()-1,1, OUString(' ') ); + OUString aGroupByStr2 = " GROUP BY " + aGroupByStr; + aGroupByStr = aGroupByStr2; + } + } + catch(SQLException&) + { + OSL_FAIL("Failure while building group by!"); + } + return aGroupByStr; + } + SqlParseError GetORCriteria(OQueryDesignView* _pView, + OSelectionBrowseBox* _pSelectionBrw, + const ::connectivity::OSQLParseNode * pCondition, + sal_uInt16& nLevel , + bool bHaving = false, + bool bAddOrOnOneLine = false); + SqlParseError GetSelectionCriteria( OQueryDesignView* _pView, + OSelectionBrowseBox* _pSelectionBrw, + const ::connectivity::OSQLParseNode* pNode, + sal_uInt16& rLevel ) + { + if (!pNode || !SQL_ISRULE(pNode, select_statement)) + return eNoSelectStatement; + + // nyi: more checking for the correct structure! + pNode = pNode->getChild(3)->getChild(1); + // no where clause found + if (!pNode || pNode->isLeaf()) + return eOk; + + // Next free sentence... + SqlParseError eErrorCode = eOk; + ::connectivity::OSQLParseNode * pCondition = pNode->getChild(1); + if ( pCondition ) // no where clause + { + // now we have to check the other conditions + // first make the logical easier + ::connectivity::OSQLParseNode::negateSearchCondition(pCondition); + ::connectivity::OSQLParseNode *pNodeTmp = pNode->getChild(1); + + ::connectivity::OSQLParseNode::disjunctiveNormalForm(pNodeTmp); + pNodeTmp = pNode->getChild(1); + ::connectivity::OSQLParseNode::absorptions(pNodeTmp); + pNodeTmp = pNode->getChild(1); + // compress sort the criteria @see https://bz.apache.org/ooo/show_bug.cgi?id=24079 + OSQLParseNode::compress(pNodeTmp); + pNodeTmp = pNode->getChild(1); + + // first extract the inner joins conditions + GetInnerJoinCriteria(_pView,pNodeTmp); + // now simplify again, join are checked in ComparisonPredicate + ::connectivity::OSQLParseNode::absorptions(pNodeTmp); + pNodeTmp = pNode->getChild(1); + + // it could happen that pCondition is not more valid + eErrorCode = GetORCriteria(_pView,_pSelectionBrw,pNodeTmp, rLevel); + } + return eErrorCode; + } + SqlParseError GetANDCriteria( OQueryDesignView* _pView, + OSelectionBrowseBox* _pSelectionBrw, + const ::connectivity::OSQLParseNode * pCondition, + sal_uInt16& nLevel, + bool bHaving, + bool bAddOrOnOneLine); + SqlParseError ComparisonPredicate(OQueryDesignView const * _pView, + OSelectionBrowseBox* _pSelectionBrw, + const ::connectivity::OSQLParseNode * pCondition, + const sal_uInt16 nLevel, + bool bHaving, + bool bAddOrOnOneLine); + SqlParseError GetORCriteria(OQueryDesignView* _pView, + OSelectionBrowseBox* _pSelectionBrw, + const ::connectivity::OSQLParseNode * pCondition, + sal_uInt16& nLevel , + bool bHaving, + bool bAddOrOnOneLine) + { + SqlParseError eErrorCode = eOk; + + // round brackets around the printout + if (pCondition->count() == 3 && + SQL_ISPUNCTUATION(pCondition->getChild(0),"(") && + SQL_ISPUNCTUATION(pCondition->getChild(2),")")) + { + eErrorCode = GetORCriteria(_pView,_pSelectionBrw,pCondition->getChild(1),nLevel,bHaving,bAddOrOnOneLine); + } + // OR condition + // a searchcondition can only look like this: search_condition SQL_TOKEN_OR boolean_term + else if (SQL_ISRULE(pCondition,search_condition)) + { + for (int i = 0; i < 3 && eErrorCode == eOk ; i+=2) + { + const ::connectivity::OSQLParseNode* pChild = pCondition->getChild(i); + if ( SQL_ISRULE(pChild,search_condition) ) + eErrorCode = GetORCriteria(_pView,_pSelectionBrw,pChild,nLevel,bHaving,bAddOrOnOneLine); + else + { + eErrorCode = GetANDCriteria(_pView,_pSelectionBrw,pChild, nLevel,bHaving, i != 0 && bAddOrOnOneLine); + if ( !bAddOrOnOneLine) + nLevel++; + } + } + } + else + eErrorCode = GetANDCriteria( _pView,_pSelectionBrw,pCondition, nLevel, bHaving,bAddOrOnOneLine ); + + return eErrorCode; + } + bool CheckOrCriteria(const ::connectivity::OSQLParseNode* _pCondition,::connectivity::OSQLParseNode* _pFirstColumnRef) + { + bool bRet = true; + ::connectivity::OSQLParseNode* pFirstColumnRef = _pFirstColumnRef; + for (size_t i = 0; bRet && i < _pCondition->count(); ++i) + { + const ::connectivity::OSQLParseNode* pChild = _pCondition->getChild(i); + if ( pChild->isToken() ) + continue; + else if ( SQL_ISRULE(pChild,search_condition) ) + bRet = CheckOrCriteria(pChild,pFirstColumnRef); + else + { + // this is a simple way to test columns are the same, may be we have to adjust this algo a little bit in future. :-) + ::connectivity::OSQLParseNode* pSecondColumnRef = pChild->getByRule(::connectivity::OSQLParseNode::column_ref); + if ( pFirstColumnRef && pSecondColumnRef ) + bRet = *pFirstColumnRef == *pSecondColumnRef; + else if ( !pFirstColumnRef ) + pFirstColumnRef = pSecondColumnRef; + } + } + return bRet; + } + SqlParseError GetANDCriteria( OQueryDesignView* _pView, + OSelectionBrowseBox* _pSelectionBrw, + const ::connectivity::OSQLParseNode * pCondition, + sal_uInt16& nLevel, + bool bHaving, + bool bAddOrOnOneLine) + { + const css::lang::Locale aLocale = _pView->getLocale(); + const OUString sDecimal = _pView->getDecimalSeparator(); + + // I will need a cast pointer to my css::sdbcx::Container + OQueryController& rController = static_cast<OQueryController&>(_pView->getController()); + SqlParseError eErrorCode = eOk; + + // round brackets + if (SQL_ISRULE(pCondition,boolean_primary)) + { + // check if we have to put the or criteria on one line. + const ::connectivity::OSQLParseNode* pSearchCondition = pCondition->getChild(1); + bool bMustAddOrOnOneLine = CheckOrCriteria(pSearchCondition,nullptr); + if ( SQL_ISRULE( pSearchCondition, search_condition) ) // we have a or + { + _pSelectionBrw->DuplicateConditionLevel( nLevel); + eErrorCode = GetORCriteria(_pView,_pSelectionBrw,pSearchCondition->getChild(0), nLevel,bHaving,bMustAddOrOnOneLine ); + if ( eErrorCode == eOk ) + { + ++nLevel; + eErrorCode = GetORCriteria(_pView,_pSelectionBrw,pSearchCondition->getChild(2), nLevel,bHaving,bMustAddOrOnOneLine ); + } + } + else + eErrorCode = GetORCriteria(_pView,_pSelectionBrw,pSearchCondition, nLevel,bHaving,bMustAddOrOnOneLine ); + } + // The first element is (again) an AND condition + else if ( SQL_ISRULE(pCondition,boolean_term) ) + { + OSL_ENSURE(pCondition->count() == 3,"Illegal definition of boolean_term"); + eErrorCode = GetANDCriteria(_pView,_pSelectionBrw,pCondition->getChild(0), nLevel,bHaving,bAddOrOnOneLine ); + if ( eErrorCode == eOk ) + eErrorCode = GetANDCriteria(_pView,_pSelectionBrw,pCondition->getChild(2), nLevel,bHaving,bAddOrOnOneLine ); + } + else if (SQL_ISRULE( pCondition, comparison_predicate)) + { + eErrorCode = ComparisonPredicate(_pView,_pSelectionBrw,pCondition,nLevel,bHaving,bAddOrOnOneLine); + } + else if( SQL_ISRULE(pCondition,like_predicate) ) + { + const ::connectivity::OSQLParseNode* pValueExp = pCondition->getChild(0); + if (SQL_ISRULE(pValueExp, column_ref ) ) + { + OUString aCondition; + Reference< XConnection> xConnection = rController.getConnection(); + if ( xConnection.is() ) + { + OUString aColumnName; + // the international doesn't matter I have a string + pCondition->parseNodeToPredicateStr(aCondition, + xConnection, + rController.getNumberFormatter(), + aLocale, + sDecimal, + &rController.getParser().getContext()); + + pValueExp->parseNodeToPredicateStr( aColumnName, + xConnection, + rController.getNumberFormatter(), + aLocale, + sDecimal, + &rController.getParser().getContext()); + + // don't display the column name + aCondition = aCondition.copy(aColumnName.getLength()); + aCondition = aCondition.trim(); + } + + OTableFieldDescRef aDragLeft = new OTableFieldDesc(); + if ( eOk == ( eErrorCode = FillDragInfo(_pView,pValueExp,aDragLeft) )) + { + if ( bHaving ) + aDragLeft->SetGroupBy(true); + _pSelectionBrw->AddCondition(aDragLeft, aCondition, nLevel,bAddOrOnOneLine); + } + } + else if(SQL_ISRULEOR3(pValueExp, general_set_fct, set_fct_spec, position_exp) || + SQL_ISRULEOR3(pValueExp, extract_exp, fold, char_substring_fct) || + SQL_ISRULEOR2(pValueExp, length_exp, char_value_fct)) + { + AddFunctionCondition( _pView, + _pSelectionBrw, + pCondition, + nLevel, + bHaving, + bAddOrOnOneLine); + } + else + { + eErrorCode = eNoColumnInLike; + OUString sError(DBA_RES(STR_QRY_LIKE_LEFT_NO_COLUMN)); + _pView->getController().appendError( sError ); + } + } + else if( SQL_ISRULEOR2(pCondition,test_for_null,in_predicate) + || SQL_ISRULEOR2(pCondition,all_or_any_predicate,between_predicate)) + { + if ( SQL_ISRULEOR2(pCondition->getChild(0), set_fct_spec , general_set_fct ) ) + { + AddFunctionCondition( _pView, + _pSelectionBrw, + pCondition, + nLevel, + bHaving, + bAddOrOnOneLine); + } + else if ( SQL_ISRULE(pCondition->getChild(0), column_ref ) ) + { + // parse condition + OUString sCondition = ParseCondition(rController,pCondition,sDecimal,aLocale,1); + OTableFieldDescRef aDragLeft = new OTableFieldDesc(); + if ( eOk == ( eErrorCode = FillDragInfo(_pView,pCondition->getChild(0),aDragLeft)) ) + { + if ( bHaving ) + aDragLeft->SetGroupBy(true); + _pSelectionBrw->AddCondition(aDragLeft, sCondition, nLevel,bAddOrOnOneLine); + } + } + else + { + // Parse the function condition + OUString sCondition = ParseCondition(rController,pCondition,sDecimal,aLocale,1); + Reference< XConnection> xConnection = rController.getConnection(); + // the international doesn't matter I have a string + OUString sName; + pCondition->getChild(0)->parseNodeToPredicateStr(sName, + xConnection, + rController.getNumberFormatter(), + aLocale, + sDecimal, + &rController.getParser().getContext()); + + OTableFieldDescRef aDragLeft = new OTableFieldDesc(); + aDragLeft->SetField(sName); + aDragLeft->SetFunctionType(FKT_OTHER); + + if ( bHaving ) + aDragLeft->SetGroupBy(true); + _pSelectionBrw->AddCondition(aDragLeft, sCondition, nLevel,bAddOrOnOneLine); + } + } + else if( SQL_ISRULEOR2(pCondition,existence_test,unique_test) ) + { + // Parse the function condition + OUString aCondition = ParseCondition(rController,pCondition,sDecimal,aLocale,0); + + OTableFieldDescRef aDragLeft = new OTableFieldDesc(); + aDragLeft->SetField(aCondition); + aDragLeft->SetFunctionType(FKT_CONDITION); + + eErrorCode = _pSelectionBrw->InsertField(aDragLeft,BROWSER_INVALIDID,false).is() ? eOk : eTooManyColumns; + } + else //! TODO not supported yet + eErrorCode = eStatementTooComplex; + // Pass on the error code + return eErrorCode; + } + SqlParseError AddFunctionCondition(OQueryDesignView const * _pView, + OSelectionBrowseBox* _pSelectionBrw, + const ::connectivity::OSQLParseNode * pCondition, + const sal_uInt16 nLevel, + bool bHaving, + bool bAddOrOnOneLine) + { + SqlParseError eErrorCode = eOk; + OQueryController& rController = static_cast<OQueryController&>(_pView->getController()); + + OSQLParseNode* pFunction = pCondition->getChild(0); + + OSL_ENSURE(SQL_ISRULEOR3(pFunction, general_set_fct, set_fct_spec, position_exp) || + SQL_ISRULEOR3(pFunction, extract_exp, fold, char_substring_fct) || + SQL_ISRULEOR2(pFunction,length_exp,char_value_fct), + "Illegal call!"); + + Reference< XConnection> xConnection = rController.getConnection(); + if(xConnection.is()) + { + OUString aCondition; + OUString aColumnName; + OTableFieldDescRef aDragLeft = new OTableFieldDesc(); + pCondition->parseNodeToPredicateStr(aCondition, + xConnection, + rController.getNumberFormatter(), + _pView->getLocale(), + _pView->getDecimalSeparator(), + &rController.getParser().getContext()); + + pFunction->parseNodeToStr( aColumnName, + xConnection, + &rController.getParser().getContext(), + true); // quote is to true because we need quoted elements inside the function + // don't display the column name + aCondition = aCondition.copy(aColumnName.getLength()); + aCondition = aCondition.trim(); + if ( aCondition.startsWith("=") ) // ignore the equal sign + aCondition = aCondition.copy(1); + + if ( SQL_ISRULE(pFunction, general_set_fct ) ) + { + sal_Int32 nFunctionType = FKT_AGGREGATE; + OSQLParseNode* pParamNode = pFunction->getChild(pFunction->count()-2); + if ( pParamNode && pParamNode->getTokenValue().toChar() == '*' ) + { + OJoinTableView::OTableWindowMap& rTabList = _pView->getTableView()->GetTabWinMap(); + for (auto const& table : rTabList) + { + OQueryTableWindow* pTabWin = static_cast<OQueryTableWindow*>(table.second.get()); + if (pTabWin->ExistsField( "*", aDragLeft )) + { + aDragLeft->SetAlias(OUString()); + aDragLeft->SetTable(OUString()); + break; + } + } + } + else if (pParamNode) + { + eErrorCode = FillDragInfo(_pView,pParamNode,aDragLeft); + if ( eOk != eErrorCode && SQL_ISRULE(pParamNode,num_value_exp)) + { + OUString sParameterValue; + pParamNode->parseNodeToStr( sParameterValue, + xConnection, + &rController.getParser().getContext()); + nFunctionType |= FKT_NUMERIC; + aDragLeft->SetField(sParameterValue); + eErrorCode = eOk; + } + } + aDragLeft->SetFunctionType(nFunctionType); + if ( bHaving ) + aDragLeft->SetGroupBy(true); + aDragLeft->SetFunction(aColumnName.getToken(0, '(')); + } + else + { + // for an unknown function we write the whole text in the field + aDragLeft->SetField(aColumnName); + if(bHaving) + aDragLeft->SetGroupBy(true); + aDragLeft->SetFunctionType(FKT_OTHER|FKT_NUMERIC); + } + _pSelectionBrw->AddCondition(aDragLeft, aCondition, nLevel,bAddOrOnOneLine); + } + + return eErrorCode; + } + SqlParseError ComparisonPredicate(OQueryDesignView const * _pView, + OSelectionBrowseBox* _pSelectionBrw, + const ::connectivity::OSQLParseNode * pCondition, + const sal_uInt16 nLevel, + bool bHaving + ,bool bAddOrOnOneLine) + { + SqlParseError eErrorCode = eOk; + OQueryController& rController = static_cast<OQueryController&>(_pView->getController()); + + OSL_ENSURE(SQL_ISRULE( pCondition, comparison_predicate),"ComparisonPredicate: pCondition is not a Comparison Predicate"); + if ( SQL_ISRULE(pCondition->getChild(0), column_ref ) + || SQL_ISRULE(pCondition->getChild(pCondition->count()-1), column_ref) ) + { + OUString aCondition; + OTableFieldDescRef aDragLeft = new OTableFieldDesc(); + + if ( SQL_ISRULE(pCondition->getChild(0), column_ref ) && SQL_ISRULE(pCondition->getChild(pCondition->count()-1), column_ref ) ) + { + OTableFieldDescRef aDragRight = new OTableFieldDesc(); + eErrorCode = FillDragInfo(_pView,pCondition->getChild(0),aDragLeft); + if (eOk != eErrorCode) + return eErrorCode; + eErrorCode = FillDragInfo(_pView,pCondition->getChild(2),aDragRight); + if (eOk != eErrorCode) + return eErrorCode; + + OQueryTableConnection* pConn = static_cast<OQueryTableConnection*>( + _pView->getTableView()->GetTabConn(static_cast<OQueryTableWindow*>(aDragLeft->GetTabWindow()), + static_cast<OQueryTableWindow*>(aDragRight->GetTabWindow()), + true)); + if ( pConn ) + { + OConnectionLineDataVec& rLineDataList = pConn->GetData()->GetConnLineDataList(); + for (auto const& lineData : rLineDataList) + { + if(lineData->GetSourceFieldName() == aDragLeft->GetField() || + lineData->GetDestFieldName() == aDragLeft->GetField() ) + return eOk; + } + } + } + + sal_uInt32 nPos = 0; + if(SQL_ISRULE(pCondition->getChild(0), column_ref )) + { + nPos = 0; + sal_uInt32 i=1; + + // don't display the equal + if (pCondition->getChild(i)->getNodeType() == SQLNodeType::Equal) + i++; + + // parse the condition + aCondition = ParseCondition(rController + ,pCondition + ,_pView->getDecimalSeparator() + ,_pView->getLocale() + ,i); + } + else if( SQL_ISRULE(pCondition->getChild(pCondition->count()-1), column_ref ) ) + { + nPos = pCondition->count()-1; + + sal_Int32 i = static_cast<sal_Int32>(pCondition->count() - 2); + switch (pCondition->getChild(i)->getNodeType()) + { + case SQLNodeType::Equal: + // don't display the equal + i--; + break; + case SQLNodeType::Less: + // take the opposite as we change the order + i--; + aCondition += ">"; + break; + case SQLNodeType::LessEq: + // take the opposite as we change the order + i--; + aCondition += ">="; + break; + case SQLNodeType::Great: + // take the opposite as we change the order + i--; + aCondition += "<"; + break; + case SQLNodeType::GreatEq: + // take the opposite as we change the order + i--; + aCondition += "<="; + break; + default: + break; + } + + // go backward + Reference< XConnection> xConnection = rController.getConnection(); + if(xConnection.is()) + { + for (; i >= 0; i--) + pCondition->getChild(i)->parseNodeToPredicateStr(aCondition, + xConnection, + rController.getNumberFormatter(), + _pView->getLocale(), + _pView->getDecimalSeparator(), + &rController.getParser().getContext()); + } + } + // else ??? + + if( eOk == ( eErrorCode = FillDragInfo(_pView,pCondition->getChild(nPos),aDragLeft))) + { + if(bHaving) + aDragLeft->SetGroupBy(true); + _pSelectionBrw->AddCondition(aDragLeft, aCondition, nLevel,bAddOrOnOneLine); + } + } + else if( SQL_ISRULEOR2(pCondition->getChild(0), set_fct_spec , general_set_fct ) ) + { + AddFunctionCondition( _pView, + _pSelectionBrw, + pCondition, + nLevel, + bHaving, + bAddOrOnOneLine); + } + else // it can only be an Expr + { + OUString aName,aCondition; + + // Field name + Reference< XConnection> xConnection = rController.getConnection(); + if(xConnection.is()) + { + ::connectivity::OSQLParseNode *pLhs = pCondition->getChild(0); + ::connectivity::OSQLParseNode *pRhs = pCondition->getChild(2); + pLhs->parseNodeToStr(aName, + xConnection, + &rController.getParser().getContext(), + true); + // Criteria + aCondition = pCondition->getChild(1)->getTokenValue(); + pRhs->parseNodeToPredicateStr(aCondition, + xConnection, + rController.getNumberFormatter(), + _pView->getLocale(), + _pView->getDecimalSeparator(), + &rController.getParser().getContext()); + } + + OTableFieldDescRef aDragLeft = new OTableFieldDesc(); + aDragLeft->SetField(aName); + aDragLeft->SetFunctionType(FKT_OTHER|FKT_NUMERIC); + // and add it on + _pSelectionBrw->AddCondition(aDragLeft, aCondition, nLevel,bAddOrOnOneLine); + } + return eErrorCode; + } + + OQueryTableWindow* lcl_findColumnInTables( const OUString& _rColumName, const OJoinTableView::OTableWindowMap& _rTabList, OTableFieldDescRef const & _rInfo ) + { + for (auto const& table : _rTabList) + { + OQueryTableWindow* pTabWin = static_cast< OQueryTableWindow* >( table.second.get() ); + if ( pTabWin && pTabWin->ExistsField( _rColumName, _rInfo ) ) + return pTabWin; + } + return nullptr; + } + + void InsertColumnRef(const OQueryDesignView* _pView, + const ::connectivity::OSQLParseNode * pColumnRef, + OUString& aColumnName, + const OUString& aColumnAlias, + OUString& aTableRange, + OTableFieldDescRef const & _raInfo, + OJoinTableView::OTableWindowMap const * pTabList) + { + + // Put the table names together + ::connectivity::OSQLParseTreeIterator& rParseIter = static_cast<OQueryController&>(_pView->getController()).getParseIterator(); + rParseIter.getColumnRange( pColumnRef, aColumnName, aTableRange ); + + bool bFound(false); + OSL_ENSURE(!aColumnName.isEmpty(),"Column name must not be empty"); + if (aTableRange.isEmpty()) + { + // SELECT column, ... + bFound = nullptr != lcl_findColumnInTables( aColumnName, *pTabList, _raInfo ); + if ( bFound && ( aColumnName.toChar() != '*' ) ) + _raInfo->SetFieldAlias(aColumnAlias); + } + else + { + // SELECT range.column, ... + OQueryTableWindow* pTabWin = static_cast<OQueryTableView*>(_pView->getTableView())->FindTable(aTableRange); + + if (pTabWin && pTabWin->ExistsField(aColumnName, _raInfo)) + { + if(aColumnName.toChar() != '*') + _raInfo->SetFieldAlias(aColumnAlias); + bFound = true; + } + } + if (!bFound) + { + _raInfo->SetTable(OUString()); + _raInfo->SetAlias(OUString()); + _raInfo->SetField(aColumnName); + _raInfo->SetFieldAlias(aColumnAlias); // nyi : here it continues Expr_1, Expr_2 ... + _raInfo->SetFunctionType(FKT_OTHER); + } + } + bool checkJoinConditions( const OQueryDesignView* _pView, + const ::connectivity::OSQLParseNode* _pNode ) + { + const ::connectivity::OSQLParseNode* pJoinNode = nullptr; + bool bRet = true; + if (SQL_ISRULE(_pNode,qualified_join)) + pJoinNode = _pNode; + else if (SQL_ISRULE(_pNode,table_ref) + && _pNode->count() == 3 + && SQL_ISPUNCTUATION(_pNode->getChild(0),"(") + && SQL_ISPUNCTUATION(_pNode->getChild(2),")") ) // '(' joined_table ')' + pJoinNode = _pNode->getChild(1); + else if (! ( SQL_ISRULE(_pNode, table_ref) && _pNode->count() == 2) ) // table_node table_primary_as_range_column + bRet = false; + + if (pJoinNode && !InsertJoin(_pView,pJoinNode)) + bRet = false; + return bRet; + } + bool InsertJoin(const OQueryDesignView* _pView, + const ::connectivity::OSQLParseNode *pNode) + { + OSL_ENSURE( SQL_ISRULE( pNode, qualified_join ) || SQL_ISRULE( pNode, joined_table ) || SQL_ISRULE( pNode, cross_union ), + "OQueryDesignView::InsertJoin: Error in the Parse Tree"); + + if (SQL_ISRULE(pNode,joined_table)) + return InsertJoin(_pView,pNode->getChild(1)); + + // first check the left and right side + const ::connectivity::OSQLParseNode* pRightTableRef = pNode->getChild(3); // table_ref + if ( SQL_ISRULE(pNode, qualified_join) && SQL_ISTOKEN(pNode->getChild(1),NATURAL) ) + pRightTableRef = pNode->getChild(4); // table_ref + + if ( !checkJoinConditions(_pView,pNode->getChild(0)) || !checkJoinConditions(_pView,pRightTableRef)) + return false; + + // named column join may be implemented later + // SQL_ISRULE(pNode->getChild(4),named_columns_join) + EJoinType eJoinType = INNER_JOIN; + bool bNatural = false; + if ( SQL_ISRULE(pNode, qualified_join) ) + { + ::connectivity::OSQLParseNode* pJoinType = pNode->getChild(1); // join_type + if ( SQL_ISTOKEN(pJoinType,NATURAL) ) + { + bNatural = true; + pJoinType = pNode->getChild(2); + } + + if (SQL_ISRULE(pJoinType,join_type) && (!pJoinType->count() || SQL_ISTOKEN(pJoinType->getChild(0),INNER))) + { + eJoinType = INNER_JOIN; + } + else + { + if (SQL_ISRULE(pJoinType,join_type)) // one level deeper + pJoinType = pJoinType->getChild(0); + + if (SQL_ISTOKEN(pJoinType->getChild(0),LEFT)) + eJoinType = LEFT_JOIN; + else if(SQL_ISTOKEN(pJoinType->getChild(0),RIGHT)) + eJoinType = RIGHT_JOIN; + else + eJoinType = FULL_JOIN; + } + if ( SQL_ISRULE(pNode->getChild(4),join_condition) ) + { + if ( InsertJoinConnection(_pView,pNode->getChild(4)->getChild(1), eJoinType,pNode->getChild(0),pRightTableRef) != eOk ) + return false; + } + } + else if ( SQL_ISRULE(pNode, cross_union) ) + { + eJoinType = CROSS_JOIN; + pRightTableRef = pNode->getChild(pNode->count() - 1); + } + else + return false; + + if ( eJoinType == CROSS_JOIN || bNatural ) + { + + OQueryTableWindow* pLeftWindow = static_cast<OQueryTableView*>(_pView->getTableView())->FindTable( getTableRange(_pView,pNode->getChild(0)) ); + OQueryTableWindow* pRightWindow = static_cast<OQueryTableView*>(_pView->getTableView())->FindTable( getTableRange(_pView,pRightTableRef) ); + OSL_ENSURE(pLeftWindow && pRightWindow,"Table Windows could not be found!"); + if ( !pLeftWindow || !pRightWindow ) + return false; + + OTableFieldDescRef aDragLeft = new OTableFieldDesc(); + aDragLeft->SetTabWindow(pLeftWindow); + aDragLeft->SetTable(pLeftWindow->GetTableName()); + aDragLeft->SetAlias(pLeftWindow->GetAliasName()); + + OTableFieldDescRef aDragRight = new OTableFieldDesc(); + aDragRight->SetTabWindow(pRightWindow); + aDragRight->SetTable(pRightWindow->GetTableName()); + aDragRight->SetAlias(pRightWindow->GetAliasName()); + + insertConnection(_pView,eJoinType,aDragLeft,aDragRight,bNatural); + } + + return true; + } + void insertUnUsedFields(OQueryDesignView const * _pView,OSelectionBrowseBox* _pSelectionBrw) + { + // now we have to insert the fields which aren't in the statement + OQueryController& rController = static_cast<OQueryController&>(_pView->getController()); + OTableFields& rUnUsedFields = rController.getUnUsedFields(); + for (auto & unusedField : rUnUsedFields) + if(_pSelectionBrw->InsertField(unusedField,BROWSER_INVALIDID,false,false).is()) + unusedField = nullptr; + OTableFields().swap( rUnUsedFields ); + } + + SqlParseError InitFromParseNodeImpl(OQueryDesignView* _pView,OSelectionBrowseBox* _pSelectionBrw) + { + SqlParseError eErrorCode = eOk; + + OQueryController& rController = static_cast<OQueryController&>(_pView->getController()); + + _pSelectionBrw->PreFill(); + _pSelectionBrw->SetReadOnly(rController.isReadOnly()); + _pSelectionBrw->Fill(); + + ::connectivity::OSQLParseTreeIterator& aIterator = rController.getParseIterator(); + const ::connectivity::OSQLParseNode* pParseTree = aIterator.getParseTree(); + + do + { + if ( !pParseTree ) + { + // now we have to insert the fields which aren't in the statement + insertUnUsedFields(_pView,_pSelectionBrw); + break; + } + + if ( !rController.isEscapeProcessing() ) // not allowed in this mode + { + eErrorCode = eNativeMode; + break; + } + + if ( !( SQL_ISRULE( pParseTree, select_statement ) ) ) + { + eErrorCode = eNoSelectStatement; + break; + } + + const OSQLParseNode* pTableExp = pParseTree->getChild(3); + if ( pTableExp->getChild(7)->count() > 0 || pTableExp->getChild(8)->count() > 0) + { + eErrorCode = eStatementTooComplex; + break; + } + + Reference< XConnection> xConnection = rController.getConnection(); + if ( !xConnection.is() ) + { + OSL_FAIL( "InitFromParseNodeImpl: no connection? no connection!" ); + break; + } + + const OSQLTables& aMap = aIterator.getTables(); + ::comphelper::UStringMixLess aTmp(aMap.key_comp()); + ::comphelper::UStringMixEqual aKeyComp( aTmp.isCaseSensitive() ); + + Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData(); + try + { + sal_Int32 nMax = xMetaData->getMaxTablesInSelect(); + if ( nMax && nMax < static_cast<sal_Int32>(aMap.size()) ) + { + eErrorCode = eTooManyTables; + break; + } + + OUString sComposedName; + OUString sAlias; + + OQueryTableView* pTableView = static_cast<OQueryTableView*>(_pView->getTableView()); + pTableView->clearLayoutInformation(); + for (auto const& elem : aMap) + { + OSQLTable xTable = elem.second; + Reference< XPropertySet > xTableProps( xTable, UNO_QUERY_THROW ); + + sAlias = elem.first; + + // check whether this is a query + Reference< XPropertySetInfo > xPSI = xTableProps->getPropertySetInfo(); + bool bIsQuery = xPSI.is() && xPSI->hasPropertyByName( PROPERTY_COMMAND ); + + if ( bIsQuery ) + OSL_VERIFY( xTableProps->getPropertyValue( PROPERTY_NAME ) >>= sComposedName ); + else + { + sComposedName = ::dbtools::composeTableName( xMetaData, xTableProps, ::dbtools::EComposeRule::InDataManipulation, false ); + + // if the alias is the complete (composed) table, then shorten it + if ( aKeyComp( sComposedName, elem.first ) ) + { + OUString sCatalog, sSchema, sTable; + ::dbtools::qualifiedNameComponents( xMetaData, sComposedName, sCatalog, sSchema, sTable, ::dbtools::EComposeRule::InDataManipulation ); + sAlias = sTable; + } + } + + // find the existent window for this alias + OQueryTableWindow* pExistentWin = pTableView->FindTable( sAlias ); + if ( !pExistentWin ) + { + pTableView->AddTabWin( sComposedName, sAlias ); // don't create data here + } + else + { + // there already exists a window for this alias... + if ( !aKeyComp( pExistentWin->GetData()->GetComposedName(), sComposedName ) ) + // ... but for another complete table name -> new window + pTableView->AddTabWin(sComposedName, sAlias); + } + } + + // now delete the data for which we haven't any tablewindow + OJoinTableView::OTableWindowMap aTableMap(pTableView->GetTabWinMap()); + for (auto const& table : aTableMap) + { + if(aMap.find(table.second->GetComposedName()) == aMap.end() && + aMap.find(table.first) == aMap.end()) + pTableView->RemoveTabWin(table.second); + } + + if ( eOk == (eErrorCode = FillOuterJoins(_pView,pTableExp->getChild(0)->getChild(1))) ) + { + // check if we have a distinct statement + if(SQL_ISTOKEN(pParseTree->getChild(1),DISTINCT)) + { + rController.setDistinct(true); + rController.InvalidateFeature(SID_QUERY_DISTINCT_VALUES); + } + else + { + rController.setDistinct(false); + } + + ///check if query has a limit + if( pTableExp->getChild(6)->count() >= 2 && pTableExp->getChild(6)->getChild(1) ) + { + rController.setLimit( + pTableExp->getChild(6)->getChild(1)->getTokenValue().toInt64() ); + } + else + { + rController.setLimit(-1); + } + + if ( (eErrorCode = InstallFields(_pView, pParseTree, &pTableView->GetTabWinMap())) == eOk ) + { + // GetSelectionCriteria must be called before GetHavingCriteria + sal_uInt16 nLevel=0; + + if ( eOk == (eErrorCode = GetSelectionCriteria(_pView,_pSelectionBrw,pParseTree,nLevel)) ) + { + if ( eOk == (eErrorCode = GetGroupCriteria(_pView,_pSelectionBrw,pParseTree)) ) + { + if ( eOk == (eErrorCode = GetHavingCriteria(_pView,_pSelectionBrw,pParseTree,nLevel)) ) + { + if ( eOk == (eErrorCode = GetOrderCriteria(_pView,_pSelectionBrw,pParseTree)) ) + insertUnUsedFields(_pView,_pSelectionBrw); + } + } + } + } + } + } + catch(SQLException&) + { + OSL_FAIL("getMaxTablesInSelect!"); + } + } + while ( false ); + + // New Undo-Actions were created in the Manager by the regeneration + rController.ClearUndoManager(); + _pSelectionBrw->Invalidate(); + return eErrorCode; + } + /** fillSelectSubList + @return + <TRUE/> when columns could be inserted otherwise <FALSE/> + */ + SqlParseError fillSelectSubList( OQueryDesignView* _pView, + OJoinTableView::OTableWindowMap* _pTabList) + { + SqlParseError eErrorCode = eOk; + bool bFirstField = true; + for (auto const& table : *_pTabList) + { + OQueryTableWindow* pTabWin = static_cast<OQueryTableWindow*>(table.second.get()); + OTableFieldDescRef aInfo = new OTableFieldDesc(); + if (pTabWin->ExistsField( "*", aInfo )) + { + eErrorCode = _pView->InsertField(aInfo, bFirstField); + bFirstField = false; + if (eErrorCode != eOk) + break; + } + } + return eErrorCode; + } + SqlParseError InstallFields(OQueryDesignView* _pView, + const ::connectivity::OSQLParseNode* pNode, + OJoinTableView::OTableWindowMap* pTabList ) + { + if( pNode==nullptr || !SQL_ISRULE(pNode,select_statement)) + return eNoSelectStatement; + + ::connectivity::OSQLParseNode* pParseTree = pNode->getChild(2); // selection + bool bFirstField = true; // When initializing, the first field must be reactivated + + SqlParseError eErrorCode = eOk; + + if ( pParseTree->isRule() && SQL_ISPUNCTUATION(pParseTree->getChild(0),"*") ) + { + // SELECT * ... + eErrorCode = fillSelectSubList(_pView,pTabList); + } + else if (SQL_ISRULE(pParseTree,scalar_exp_commalist) ) + { + // SELECT column, ... + OQueryController& rController = static_cast<OQueryController&>(_pView->getController()); + Reference< XConnection> xConnection = rController.getConnection(); + + OUString aColumnName,aTableRange; + for (size_t i = 0; i < pParseTree->count() && eOk == eErrorCode ; ++i) + { + ::connectivity::OSQLParseNode * pColumnRef = pParseTree->getChild(i); + + do { + + if ( SQL_ISRULE(pColumnRef,select_sublist) ) + { + eErrorCode = fillSelectSubList(_pView,pTabList); + break; + } + + if ( SQL_ISRULE(pColumnRef,derived_column) ) + { + OUString aColumnAlias(connectivity::OSQLParseTreeIterator::getColumnAlias(pColumnRef)); // might be empty + pColumnRef = pColumnRef->getChild(0); + OTableFieldDescRef aInfo = new OTableFieldDesc(); + + if ( pColumnRef->getKnownRuleID() != OSQLParseNode::subquery && + pColumnRef->count() == 3 && + SQL_ISPUNCTUATION(pColumnRef->getChild(0),"(") && + SQL_ISPUNCTUATION(pColumnRef->getChild(2),")") + ) + pColumnRef = pColumnRef->getChild(1); + + if (SQL_ISRULE(pColumnRef,column_ref)) + { + InsertColumnRef(_pView,pColumnRef,aColumnName,aColumnAlias,aTableRange,aInfo,pTabList); + eErrorCode = _pView->InsertField(aInfo, bFirstField); + bFirstField = false; + } + else if(SQL_ISRULEOR3(pColumnRef, general_set_fct, set_fct_spec, position_exp) || + SQL_ISRULEOR3(pColumnRef, extract_exp, fold, char_substring_fct) || + SQL_ISRULEOR2(pColumnRef,length_exp,char_value_fct)) + { + OUString aColumns; + pColumnRef->parseNodeToPredicateStr(aColumns, + xConnection, + rController.getNumberFormatter(), + _pView->getLocale(), + _pView->getDecimalSeparator(), + &rController.getParser().getContext()); + + sal_Int32 nFunctionType = FKT_NONE; + ::connectivity::OSQLParseNode* pParamRef = nullptr; + sal_Int32 nColumnRefPos = pColumnRef->count() - 2; + if ( nColumnRefPos >= 0 && o3tl::make_unsigned(nColumnRefPos) < pColumnRef->count() ) + pParamRef = pColumnRef->getChild(nColumnRefPos); + + if ( SQL_ISRULE(pColumnRef,general_set_fct) + && pParamRef && SQL_ISRULE(pParamRef,column_ref) ) + { + // Check the parameters for Column references + InsertColumnRef(_pView,pParamRef,aColumnName,aColumnAlias,aTableRange,aInfo,pTabList); + } + else if ( SQL_ISRULE(pColumnRef,general_set_fct) ) + { + if ( pParamRef && pParamRef->getTokenValue().toChar() == '*' ) + { + for (auto const& table : *pTabList) + { + OQueryTableWindow* pTabWin = static_cast<OQueryTableWindow*>(table.second.get()); + if (pTabWin->ExistsField( "*", aInfo )) + { + aInfo->SetAlias(OUString()); + aInfo->SetTable(OUString()); + break; + } + } + } + else + { + OUString sFieldName = aColumns; + if ( pParamRef ) + { // we got an aggregate function but without column name inside + // so we set the whole argument of the function as field name + nFunctionType |= FKT_NUMERIC; + sFieldName.clear(); + pParamRef->parseNodeToStr( sFieldName, + xConnection, + &rController.getParser().getContext(), + true); // quote is to true because we need quoted elements inside the function + } + aInfo->SetDataType(DataType::DOUBLE); + aInfo->SetFieldType(TAB_NORMAL_FIELD); + aInfo->SetField(sFieldName); + } + aInfo->SetTabWindow(nullptr); + aInfo->SetFieldAlias(aColumnAlias); + } + else + { + _pView->fillFunctionInfo(pColumnRef,aColumns,aInfo); + aInfo->SetFieldAlias(aColumnAlias); + } + + if ( SQL_ISRULE(pColumnRef,general_set_fct) ) + { + aInfo->SetFunctionType(nFunctionType|FKT_AGGREGATE); + aInfo->SetFunction(comphelper::string::stripEnd(aColumns.getToken(0,'('), ' ')); + } + else + aInfo->SetFunctionType(nFunctionType|FKT_OTHER); + + eErrorCode = _pView->InsertField(aInfo, bFirstField); + bFirstField = false; + } + else + { + OUString aColumns; + pColumnRef->parseNodeToStr( aColumns, + xConnection, + &rController.getParser().getContext(), + true); // quote is to true because we need quoted elements inside the function + + aInfo->SetTabWindow( nullptr ); + + // since we support queries in queries, the thingie might belong to an existing "table" + OQueryTableWindow* pExistingTable = lcl_findColumnInTables( aColumns, *pTabList, aInfo ); + if ( pExistingTable ) + { + aInfo->SetTabWindow( pExistingTable ); + aInfo->SetTable( pExistingTable->GetTableName() ); + aInfo->SetAlias( pExistingTable->GetAliasName() ); + } + + aInfo->SetDataType(DataType::DOUBLE); + aInfo->SetFieldType(TAB_NORMAL_FIELD); + aInfo->SetField(aColumns); + aInfo->SetFieldAlias(aColumnAlias); + aInfo->SetFunctionType(FKT_NUMERIC | FKT_OTHER); + + eErrorCode = _pView->InsertField(aInfo, bFirstField); + bFirstField = false; + } + + break; + } + + OSL_FAIL( "InstallFields: don't know how to interpret this parse node!" ); + + } while ( false ); + } + } + else + eErrorCode = eStatementTooComplex; + + return eErrorCode; + } + SqlParseError GetOrderCriteria( OQueryDesignView* _pView, + OSelectionBrowseBox* _pSelectionBrw, + const ::connectivity::OSQLParseNode* pParseRoot ) + { + SqlParseError eErrorCode = eOk; + if (!pParseRoot->getChild(3)->getChild(ORDER_BY_CHILD_POS)->isLeaf()) + { + ::connectivity::OSQLParseNode* pNode = pParseRoot->getChild(3)->getChild(ORDER_BY_CHILD_POS)->getChild(2); + ::connectivity::OSQLParseNode* pParamRef = nullptr; + + OQueryController& rController = static_cast<OQueryController&>(_pView->getController()); + EOrderDir eOrderDir; + for( size_t i=0 ; i<pNode->count() ; i++ ) + { + OTableFieldDescRef aDragLeft = new OTableFieldDesc(); + eOrderDir = ORDER_ASC; + ::connectivity::OSQLParseNode* pChild = pNode->getChild( i ); + + if (SQL_ISTOKEN( pChild->getChild(1), DESC ) ) + eOrderDir = ORDER_DESC; + + ::connectivity::OSQLParseNode* pArgument = pChild->getChild(0); + + if(SQL_ISRULE(pArgument,column_ref)) + { + if( eOk == FillDragInfo(_pView,pArgument,aDragLeft)) + _pSelectionBrw->AddOrder( aDragLeft, eOrderDir, i); + else // it could be an alias name for a field + { + OUString aTableRange,aColumnName; + ::connectivity::OSQLParseTreeIterator& rParseIter = rController.getParseIterator(); + rParseIter.getColumnRange( pArgument, aColumnName, aTableRange ); + + OTableFields& aList = rController.getTableFieldDesc(); + for (auto const& elem : aList) + { + if(elem.is() && elem->GetFieldAlias() == aColumnName) + elem->SetOrderDir( eOrderDir ); + } + } + } + else if(SQL_ISRULE(pArgument, general_set_fct ) && + SQL_ISRULE(pParamRef = pArgument->getChild(pArgument->count()-2),column_ref) && + eOk == FillDragInfo(_pView,pParamRef,aDragLeft)) + _pSelectionBrw->AddOrder( aDragLeft, eOrderDir, i ); + else if( SQL_ISRULE(pArgument, set_fct_spec ) ) + { + + Reference< XConnection> xConnection = rController.getConnection(); + if(xConnection.is()) + { + OUString sCondition; + pArgument->parseNodeToPredicateStr(sCondition, + xConnection, + rController.getNumberFormatter(), + _pView->getLocale(), + _pView->getDecimalSeparator(), + &rController.getParser().getContext()); + _pView->fillFunctionInfo(pArgument,sCondition,aDragLeft); + aDragLeft->SetFunctionType(FKT_OTHER); + aDragLeft->SetOrderDir(eOrderDir); + aDragLeft->SetVisible(false); + _pSelectionBrw->AddOrder( aDragLeft, eOrderDir, i ); + } + else + eErrorCode = eColumnNotFound; + } + else + eErrorCode = eColumnNotFound; + } + } + return eErrorCode; + } + SqlParseError GetHavingCriteria( OQueryDesignView* _pView, + OSelectionBrowseBox* _pSelectionBrw, + const ::connectivity::OSQLParseNode* pSelectRoot, + sal_uInt16& rLevel ) + { + SqlParseError eErrorCode = eOk; + if (!pSelectRoot->getChild(3)->getChild(3)->isLeaf()) + eErrorCode = GetORCriteria(_pView,_pSelectionBrw,pSelectRoot->getChild(3)->getChild(3)->getChild(1),rLevel, true); + return eErrorCode; + } + SqlParseError GetGroupCriteria( OQueryDesignView* _pView, + OSelectionBrowseBox* _pSelectionBrw, + const ::connectivity::OSQLParseNode* pSelectRoot ) + { + SqlParseError eErrorCode = eOk; + if (!pSelectRoot->getChild(3)->getChild(2)->isLeaf()) // opt_group_by_clause + { + OQueryController& rController = static_cast<OQueryController&>(_pView->getController()); + ::connectivity::OSQLParseNode* pGroupBy = pSelectRoot->getChild(3)->getChild(2)->getChild(2); + + for( size_t i=0 ; i < pGroupBy->count() && eOk == eErrorCode; ++i ) + { + OTableFieldDescRef aDragInfo = new OTableFieldDesc(); + ::connectivity::OSQLParseNode* pParamRef = nullptr; + ::connectivity::OSQLParseNode* pArgument = pGroupBy->getChild( i ); + if(SQL_ISRULE(pArgument,column_ref)) + { + if ( eOk == (eErrorCode = FillDragInfo(_pView,pArgument,aDragInfo)) ) + { + aDragInfo->SetGroupBy(true); + _pSelectionBrw->AddGroupBy(aDragInfo); + } + } + else if(SQL_ISRULE(pArgument, general_set_fct ) && + SQL_ISRULE(pParamRef = pArgument->getChild(pArgument->count()-2),column_ref) && + eOk == FillDragInfo(_pView,pParamRef,aDragInfo)) + { + aDragInfo->SetGroupBy(true); + _pSelectionBrw->AddGroupBy( aDragInfo ); + } + else if( SQL_ISRULE(pArgument, set_fct_spec ) ) + { + Reference< XConnection> xConnection = rController.getConnection(); + if(xConnection.is()) + { + OUString sGroupByExpression; + pArgument->parseNodeToStr( sGroupByExpression, + xConnection, + &rController.getParser().getContext(), + true); // quote is to true because we need quoted elements inside the function + _pView->fillFunctionInfo(pArgument,sGroupByExpression,aDragInfo); + aDragInfo->SetFunctionType(FKT_OTHER); + aDragInfo->SetGroupBy(true); + aDragInfo->SetVisible(false); + _pSelectionBrw->AddGroupBy( aDragInfo ); + } + else + eErrorCode = eColumnNotFound; + } + } + } + return eErrorCode; + } + + OUString getParseErrorMessage( SqlParseError _eErrorCode ) + { + const char* pResId; + switch (_eErrorCode) + { + case eIllegalJoin: + pResId = STR_QRY_ILLEGAL_JOIN; + break; + case eStatementTooLong: + pResId = STR_QRY_TOO_LONG_STATEMENT; + break; + case eNoConnection: + pResId = STR_QRY_SYNTAX; + break; + case eNoSelectStatement: + pResId = STR_QRY_NOSELECT; + break; + case eNoColumnInLike: + pResId = STR_QRY_SYNTAX; + break; + case eColumnNotFound: + pResId = STR_QRY_SYNTAX; + break; + case eNativeMode: + pResId = STR_QRY_NATIVE; + break; + case eTooManyTables: + pResId = STR_QRY_TOO_MANY_TABLES; + break; + case eTooManyColumns: + pResId = STR_QRY_TOO_MANY_COLUMNS; + break; + case eStatementTooComplex: + pResId = STR_QRY_TOOCOMPLEX; + break; + default: + pResId = STR_QRY_SYNTAX; + break; + } + return DBA_RES(pResId); + } +} + +// end of anonymous namespace + +OQueryDesignView::OQueryDesignView( OQueryContainerWindow* _pParent, + OQueryController& _rController, + const Reference< XComponentContext >& _rxContext) + :OJoinDesignView( _pParent, _rController, _rxContext ) + ,m_aSplitter( VclPtr<Splitter>::Create(this) ) + ,m_eChildFocus(NONE) + ,m_bInSplitHandler( false ) +{ + + try + { + SvtSysLocale aSysLocale; + m_aLocale = aSysLocale.GetLanguageTag().getLocale(); + m_sDecimalSep = aSysLocale.GetLocaleData().getNumDecimalSep(); + } + catch(Exception&) + { + } + + m_pSelectionBox = VclPtr<OSelectionBrowseBox>::Create(this); + + setNoneVisbleRow(static_cast<OQueryController&>(getController()).getVisibleRows()); + m_pSelectionBox->Show(); + // setup Splitter + m_aSplitter->SetSplitHdl(LINK(this, OQueryDesignView,SplitHdl)); + m_aSplitter->Show(); + +} + +OQueryDesignView::~OQueryDesignView() +{ + disposeOnce(); +} + +void OQueryDesignView::dispose() +{ + if ( m_pTableView ) + ::dbaui::notifySystemWindow(this,m_pTableView,::comphelper::mem_fun(&TaskPaneList::RemoveWindow)); + m_pSelectionBox.disposeAndClear(); + m_aSplitter.disposeAndClear(); + OJoinDesignView::dispose(); +} + +IMPL_LINK_NOARG( OQueryDesignView, SplitHdl, Splitter*, void ) +{ + if (!getController().isReadOnly()) + { + m_bInSplitHandler = true; + m_aSplitter->SetPosPixel( Point( m_aSplitter->GetPosPixel().X(),m_aSplitter->GetSplitPosPixel() ) ); + static_cast<OQueryController&>(getController()).setSplitPos(m_aSplitter->GetSplitPosPixel()); + static_cast<OQueryController&>(getController()).setModified( true ); + Resize(); + m_bInSplitHandler = true; + } +} + +void OQueryDesignView::Construct() +{ + m_pTableView = VclPtr<OQueryTableView>::Create(m_pScrollWindow,this); + ::dbaui::notifySystemWindow(this,m_pTableView,::comphelper::mem_fun(&TaskPaneList::AddWindow)); + OJoinDesignView::Construct(); +} + +void OQueryDesignView::initialize() +{ + if(static_cast<OQueryController&>(getController()).getSplitPos() != -1) + { + m_aSplitter->SetPosPixel( Point( m_aSplitter->GetPosPixel().X(),static_cast<OQueryController&>(getController()).getSplitPos() ) ); + m_aSplitter->SetSplitPosPixel(static_cast<OQueryController&>(getController()).getSplitPos()); + } + m_pSelectionBox->initialize(); + reset(); +} + +void OQueryDesignView::resizeDocumentView(tools::Rectangle& _rPlayground) +{ + Point aPlaygroundPos( _rPlayground.TopLeft() ); + Size aPlaygroundSize( _rPlayground.GetSize() ); + + // calc the split pos, and forward it to the controller + sal_Int32 nSplitPos = static_cast<OQueryController&>(getController()).getSplitPos(); + if ( 0 != aPlaygroundSize.Height() ) + { + if ( ( -1 == nSplitPos ) + || ( nSplitPos >= aPlaygroundSize.Height() ) + ) + { + // let the selection browse box determine an optimal size + Size aSelectionBoxSize = m_pSelectionBox->CalcOptimalSize( aPlaygroundSize ); + nSplitPos = aPlaygroundSize.Height() - aSelectionBoxSize.Height() - m_aSplitter->GetSizePixel().Height(); + // still an invalid size? + if ( nSplitPos == -1 || nSplitPos >= aPlaygroundSize.Height() ) + nSplitPos = sal_Int32(aPlaygroundSize.Height()*0.6); + + static_cast<OQueryController&>(getController()).setSplitPos(nSplitPos); + } + + if ( !m_bInSplitHandler ) + { // the resize is triggered by something else than the split handler + // our main focus is to try to preserve the size of the selectionbrowse box + Size aSelBoxSize = m_pSelectionBox->GetSizePixel(); + if ( aSelBoxSize.Height() ) + { + // keep the size of the sel box constant + nSplitPos = aPlaygroundSize.Height() - m_aSplitter->GetSizePixel().Height() - aSelBoxSize.Height(); + + // and if the box is smaller than the optimal size, try to do something about it + Size aSelBoxOptSize = m_pSelectionBox->CalcOptimalSize( aPlaygroundSize ); + if ( aSelBoxOptSize.Height() > aSelBoxSize.Height() ) + { + nSplitPos = aPlaygroundSize.Height() - m_aSplitter->GetSizePixel().Height() - aSelBoxOptSize.Height(); + } + + static_cast< OQueryController& >(getController()).setSplitPos( nSplitPos ); + } + } + } + + // normalize the split pos + Point aSplitPos( _rPlayground.Left(), nSplitPos ); + Size aSplitSize( _rPlayground.GetSize().Width(), m_aSplitter->GetSizePixel().Height() ); + + if( ( aSplitPos.Y() + aSplitSize.Height() ) > ( aPlaygroundSize.Height() )) + aSplitPos.setY( aPlaygroundSize.Height() - aSplitSize.Height() ); + + if( aSplitPos.Y() <= aPlaygroundPos.Y() ) + aSplitPos.setY( aPlaygroundPos.Y() + sal_Int32(aPlaygroundSize.Height() * 0.2) ); + + // position the table + Size aTableViewSize(aPlaygroundSize.Width(), aSplitPos.Y() - aPlaygroundPos.Y()); + m_pScrollWindow->SetPosSizePixel(aPlaygroundPos, aTableViewSize); + + // position the selection browse box + Point aPos( aPlaygroundPos.X(), aSplitPos.Y() + aSplitSize.Height() ); + m_pSelectionBox->SetPosSizePixel( aPos, Size( aPlaygroundSize.Width(), aPlaygroundSize.Height() - aSplitSize.Height() - aTableViewSize.Height() )); + + // set the size of the splitter + m_aSplitter->SetPosSizePixel( aSplitPos, aSplitSize ); + m_aSplitter->SetDragRectPixel( _rPlayground ); + + // just for completeness: there is no space left, we occupied it all ... + _rPlayground.SetPos( _rPlayground.BottomRight() ); + _rPlayground.SetSize( Size( 0, 0 ) ); +} + +void OQueryDesignView::setReadOnly(bool _bReadOnly) +{ + m_pSelectionBox->SetReadOnly(_bReadOnly); +} + +void OQueryDesignView::clear() +{ + m_pSelectionBox->ClearAll(); // clear the whole selection + m_pTableView->ClearAll(); +} + +void OQueryDesignView::copy() +{ + if( m_eChildFocus == SELECTION) + m_pSelectionBox->copy(); +} + +bool OQueryDesignView::isCutAllowed() const +{ + bool bAllowed = false; + if ( SELECTION == m_eChildFocus ) + bAllowed = m_pSelectionBox->isCutAllowed(); + return bAllowed; +} + +bool OQueryDesignView::isPasteAllowed() const +{ + bool bAllowed = false; + if ( SELECTION == m_eChildFocus ) + bAllowed = m_pSelectionBox->isPasteAllowed(); + return bAllowed; +} + +bool OQueryDesignView::isCopyAllowed() const +{ + bool bAllowed = false; + if ( SELECTION == m_eChildFocus ) + bAllowed = m_pSelectionBox->isCopyAllowed(); + return bAllowed; +} + +void OQueryDesignView::stopTimer() +{ + m_pSelectionBox->stopTimer(); +} + +void OQueryDesignView::startTimer() +{ + m_pSelectionBox->startTimer(); +} + +void OQueryDesignView::cut() +{ + if( m_eChildFocus == SELECTION) + { + m_pSelectionBox->cut(); + static_cast<OQueryController&>(getController()).setModified(true); + } +} + +void OQueryDesignView::paste() +{ + if( m_eChildFocus == SELECTION) + { + m_pSelectionBox->paste(); + static_cast<OQueryController&>(getController()).setModified(true); + } +} + +void OQueryDesignView::TableDeleted(const OUString& rAliasName) +{ + // message that the table was removed from the window + m_pSelectionBox->DeleteFields( rAliasName ); + static_cast<OQueryController&>(getController()).InvalidateFeature(ID_BROWSER_ADDTABLE); // inform the view again +} + +bool OQueryDesignView::HasFieldByAliasName(const OUString& rFieldName, OTableFieldDescRef const & rInfo) const +{ + return m_pSelectionBox->HasFieldByAliasName( rFieldName, rInfo); +} + +SqlParseError OQueryDesignView::InsertField( const OTableFieldDescRef& rInfo, bool bActivate) +{ + return m_pSelectionBox->InsertField( rInfo, BROWSER_INVALIDID, true/*bVis*/, bActivate ).is() ? eOk : eTooManyColumns; +} + +sal_Int32 OQueryDesignView::getColWidth(sal_uInt16 _nColPos) const +{ + static sal_Int32 s_nDefaultWidth = GetTextWidth("0") * 15; + sal_Int32 nWidth = static_cast<OQueryController&>(getController()).getColWidth(_nColPos); + if ( !nWidth ) + nWidth = s_nDefaultWidth; + return nWidth; +} + +void OQueryDesignView::fillValidFields(const OUString& sAliasName, weld::ComboBox& rFieldList) +{ + rFieldList.clear(); + + bool bAllTables = sAliasName.isEmpty(); + + OJoinTableView::OTableWindowMap& rTabWins = m_pTableView->GetTabWinMap(); + OUString strCurrentPrefix; + std::vector< OUString> aFields; + for (auto const& tabWin : rTabWins) + { + OQueryTableWindow* pCurrentWin = static_cast<OQueryTableWindow*>(tabWin.second.get()); + if (bAllTables || (pCurrentWin->GetAliasName() == sAliasName)) + { + strCurrentPrefix = pCurrentWin->GetAliasName() + "."; + + pCurrentWin->EnumValidFields(aFields); + + for (auto const& field : aFields) + { + if (bAllTables || field.toChar() == '*') + rFieldList.append_text(strCurrentPrefix + field); + else + rFieldList.append_text(field); + } + + if (!bAllTables) + // this means that I came into this block because the table name was exactly what I was looking for so I can end here + // (and I prevent that fields get added more than once, if a table is repeated in TabWin) + break; + } + } +} + +bool OQueryDesignView::PreNotify(NotifyEvent& rNEvt) +{ + if (rNEvt.GetType() == MouseNotifyEvent::GETFOCUS) + { + if ( m_pSelectionBox && m_pSelectionBox->HasChildPathFocus() ) + m_eChildFocus = SELECTION; + else + m_eChildFocus = TABLEVIEW; + } + + return OJoinDesignView::PreNotify(rNEvt); +} + +// check if the statement is correct when not returning false +bool OQueryDesignView::checkStatement() +{ + bool bRet = true; + if ( m_pSelectionBox ) + bRet = m_pSelectionBox->Save(); // an error occurred so we return no + return bRet; +} + +OUString OQueryDesignView::getStatement() +{ + OQueryController& rController = static_cast<OQueryController&>(getController()); + m_rController.clearError(); + // used for fields which aren't any longer in the statement + OTableFields& rUnUsedFields = rController.getUnUsedFields(); + OTableFields().swap( rUnUsedFields ); + + // create the select columns + sal_uInt32 nFieldcount = 0; + OTableFields& rFieldList = rController.getTableFieldDesc(); + for (auto const& field : rFieldList) + { + if (!field->GetField().isEmpty() && field->IsVisible() ) + ++nFieldcount; + else if (!field->GetField().isEmpty() && + !field->HasCriteria() && + field->isNoneFunction() && + field->GetOrderDir() == ORDER_NONE && + !field->IsGroupBy() && + field->GetFunction().isEmpty() ) + rUnUsedFields.push_back(field); + } + if ( !nFieldcount ) // no visible fields so return + { + rUnUsedFields = rFieldList; + return OUString(); + } + + OQueryTableView::OTableWindowMap& rTabList = m_pTableView->GetTabWinMap(); + sal_uInt32 nTabcount = rTabList.size(); + + OUString aFieldListStr(GenerateSelectList(this,rFieldList,nTabcount>1)); + if( aFieldListStr.isEmpty() ) + return OUString(); + + // Exception handling, if no fields have been passed we should not + // change the tab page + // TabBarSelectHdl will query the SQL-OUString for STATEMENT_NOFIELDS + // and trigger an error message + // ----------------- Build table list ---------------------- + + const auto& rConnList = m_pTableView->getTableConnections(); + Reference< XConnection> xConnection = rController.getConnection(); + OUString aTableListStr(GenerateFromClause(xConnection,&rTabList,rConnList)); + OSL_ENSURE(!aTableListStr.isEmpty(), "OQueryDesignView::getStatement() : unexpected : have Fields, but no Tables !"); + // if fields exist now, these only can be created by inserting from an already existing table; if on the other hand + // a table is deleted, also the belonging fields will be deleted -> therefore it CANNOT occur that fields + // exist but no tables exist (and aFieldListStr has its length, I secure this above) + OUStringBuffer aHavingStr,aCriteriaListStr; + + // ----------------- build the criteria ---------------------- + if (!GenerateCriterias(this,aCriteriaListStr,aHavingStr,rFieldList, nTabcount > 1)) + return OUString(); + + OUString aJoinCrit; + GenerateInnerJoinCriterias(xConnection,aJoinCrit,rConnList); + if(!aJoinCrit.isEmpty()) + { + OUString aTmp = "( " + aJoinCrit + " )"; + if(!aCriteriaListStr.isEmpty()) + { + aTmp += C_AND + aCriteriaListStr.makeStringAndClear(); + } + aCriteriaListStr = aTmp; + } + // ----------------- construct statement ---------------------- + OUStringBuffer aSqlCmd("SELECT "); + if(rController.isDistinct()) + aSqlCmd.append(" DISTINCT "); + aSqlCmd.append(aFieldListStr); + aSqlCmd.append(" FROM "); + aSqlCmd.append(aTableListStr); + + if (!aCriteriaListStr.isEmpty()) + { + aSqlCmd.append(" WHERE "); + aSqlCmd.append(aCriteriaListStr.makeStringAndClear()); + } + Reference<XDatabaseMetaData> xMeta; + if ( xConnection.is() ) + xMeta = xConnection->getMetaData(); + bool bUseAlias = nTabcount > 1; + if ( xMeta.is() ) + bUseAlias = bUseAlias || !xMeta->supportsGroupByUnrelated(); + + aSqlCmd.append(GenerateGroupBy(this,rFieldList,bUseAlias)); + // ----------------- construct GroupBy and attach ------------ + if(!aHavingStr.isEmpty()) + { + aSqlCmd.append(" HAVING "); + aSqlCmd.append(aHavingStr.makeStringAndClear()); + } + // ----------------- construct sorting and attach ------------ + OUString sOrder; + SqlParseError eErrorCode = eOk; + if ( (eErrorCode = GenerateOrder(this,rFieldList,nTabcount > 1,sOrder)) == eOk) + aSqlCmd.append(sOrder); + else + { + if ( !m_rController.hasError() ) + m_rController.appendError( getParseErrorMessage( eErrorCode ) ); + + m_rController.displayError(); + } + // --------------------- Limit Clause ------------------- + { + const sal_Int64 nLimit = rController.getLimit(); + if( nLimit != -1 ) + { + aSqlCmd.append( " LIMIT " ).append( OUString::number(nLimit) ); + } + } + + OUString sSQL = aSqlCmd.makeStringAndClear(); + if ( xConnection.is() ) + { + ::connectivity::OSQLParser& rParser( rController.getParser() ); + OUString sErrorMessage; + std::unique_ptr<OSQLParseNode> pParseNode( rParser.parseTree( sErrorMessage, sSQL, true ) ); + if (pParseNode) + { + OSQLParseNode* pNode = pParseNode->getChild(3)->getChild(1); + if ( pNode->count() > 1 ) + { + ::connectivity::OSQLParseNode * pCondition = pNode->getChild(1); + if ( pCondition ) // no where clause + { + OSQLParseNode::compress(pCondition); + OUString sTemp; + pParseNode->parseNodeToStr(sTemp,xConnection); + sSQL = sTemp; + } + } + } + } + return sSQL; +} + +void OQueryDesignView::setSlotEnabled(sal_Int32 _nSlotId, bool _bEnable) +{ + sal_uInt16 nRow; + switch (_nSlotId) + { + case SID_QUERY_VIEW_FUNCTIONS: + nRow = BROW_FUNCTION_ROW; + break; + case SID_QUERY_VIEW_TABLES: + nRow = BROW_TABLE_ROW; + break; + case SID_QUERY_VIEW_ALIASES: + nRow = BROW_COLUMNALIAS_ROW; + break; + default: + // ???????????? + nRow = 0; + break; + } + m_pSelectionBox->SetRowVisible(nRow,_bEnable); + m_pSelectionBox->Invalidate(); +} + +bool OQueryDesignView::isSlotEnabled(sal_Int32 _nSlotId) +{ + sal_uInt16 nRow; + switch (_nSlotId) + { + case SID_QUERY_VIEW_FUNCTIONS: + nRow = BROW_FUNCTION_ROW; + break; + case SID_QUERY_VIEW_TABLES: + nRow = BROW_TABLE_ROW; + break; + case SID_QUERY_VIEW_ALIASES: + nRow = BROW_COLUMNALIAS_ROW; + break; + default: + // ????????? + nRow = 0; + break; + } + return m_pSelectionBox->IsRowVisible(nRow); +} + +void OQueryDesignView::SaveUIConfig() +{ + OQueryController& rCtrl = static_cast<OQueryController&>(getController()); + rCtrl.SaveTabWinsPosSize( &m_pTableView->GetTabWinMap(), m_pScrollWindow->GetHScrollBar().GetThumbPos(), m_pScrollWindow->GetVScrollBar().GetThumbPos() ); + rCtrl.setVisibleRows( m_pSelectionBox->GetNoneVisibleRows() ); + if ( m_aSplitter->GetSplitPosPixel() != 0 ) + rCtrl.setSplitPos( m_aSplitter->GetSplitPosPixel() ); +} + +std::unique_ptr<OSQLParseNode> OQueryDesignView::getPredicateTreeFromEntry(const OTableFieldDescRef& pEntry, + const OUString& _sCriteria, + OUString& _rsErrorMessage, + Reference<XPropertySet>& _rxColumn) const +{ + OSL_ENSURE(pEntry.is(),"Entry is null!"); + if(!pEntry.is()) + return nullptr; + Reference< XConnection> xConnection = static_cast<OQueryController&>(getController()).getConnection(); + if(!xConnection.is()) + return nullptr; + + ::connectivity::OSQLParser& rParser( static_cast<OQueryController&>(getController()).getParser() ); + OQueryTableWindow* pWin = static_cast<OQueryTableWindow*>(pEntry->GetTabWindow()); + + // special handling for functions + if ( pEntry->GetFunctionType() & (FKT_OTHER | FKT_AGGREGATE | FKT_NUMERIC) ) + { + // we have a function here so we have to distinguish the type of return value + OUString sFunction; + if ( pEntry->isNumericOrAggregateFunction() ) + sFunction = pEntry->GetFunction().getToken(0, '('); + + if ( sFunction.isEmpty() ) + sFunction = pEntry->GetField().getToken(0, '('); + + sal_Int32 nType = ::connectivity::OSQLParser::getFunctionReturnType(sFunction,&rParser.getContext()); + if ( nType == DataType::OTHER || (sFunction.isEmpty() && pEntry->isNumericOrAggregateFunction()) ) + { + // first try the international version + OUString sSql = "SELECT * FROM x WHERE " + pEntry->GetField() + _sCriteria; + std::unique_ptr<OSQLParseNode> pParseNode( rParser.parseTree( _rsErrorMessage, sSql, true ) ); + nType = DataType::DOUBLE; + if (pParseNode) + { + OSQLParseNode* pColumnRef = pParseNode->getByRule(OSQLParseNode::column_ref); + if ( pColumnRef ) + { + OTableFieldDescRef aField = new OTableFieldDesc(); + if ( eOk == FillDragInfo(this,pColumnRef,aField) ) + { + nType = aField->GetDataType(); + } + } + } + } + + Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData(); + parse::OParseColumn* pColumn = new parse::OParseColumn( pEntry->GetField(), + OUString(), + OUString(), + OUString(), + ColumnValue::NULLABLE_UNKNOWN, + 0, + 0, + nType, + false, + false, + xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers(), + OUString(), + OUString(), + OUString()); + _rxColumn = pColumn; + pColumn->setFunction(true); + pColumn->setRealName(pEntry->GetField()); + } + else + { + if (pWin) + { + Reference<XNameAccess> xColumns = pWin->GetOriginalColumns(); + if (xColumns.is() && xColumns->hasByName(pEntry->GetField())) + xColumns->getByName(pEntry->GetField()) >>= _rxColumn; + } + } + + // _rxColumn, if it is a "lookup" column, not a computed column, + // is guaranteed to be the column taken from the *source* of the column, + // that is either a table or another query. + // _rxColumn is *not* taken from the columns of the query we are constructing + // (and rightfully so, since it may not be part of these columns; SELECT A FROM t WHERE B = foo) + // If it is a computed column, we just constructed it above, with same Name and RealName. + // In all cases, we should use the "external" name of the column, not the "RealName"; + // the latter is the name that the column had in the source of the source query. + // An example: we are designing "SELECT A, B FROM q WHERE C='foo'" + // q itself is query "SELECT aye AS A, bee as B, cee as C FROM t" + // We are currently treating the entry "C='foo'" + // Then _rxColumn has Name "C" and RealName "cee". We should *obviously* use "C", not "cee". + std::unique_ptr<OSQLParseNode> pParseNode = rParser.predicateTree( _rsErrorMessage, + _sCriteria, + static_cast<OQueryController&>(getController()).getNumberFormatter(), + _rxColumn, + false); + return pParseNode; +} + +void OQueryDesignView::GetFocus() +{ + OJoinDesignView::GetFocus(); + if ( m_pSelectionBox && !m_pSelectionBox->HasChildPathFocus() ) + { + // first we have to deactivate the current cell to refill when necessary + m_pSelectionBox->DeactivateCell(); + m_pSelectionBox->ActivateCell(m_pSelectionBox->GetCurRow(), m_pSelectionBox->GetCurColumnId()); + m_pSelectionBox->GrabFocus(); + } +} + +void OQueryDesignView::reset() +{ + m_pTableView->ClearAll(); + m_pTableView->ReSync(); +} + +void OQueryDesignView::setNoneVisbleRow(sal_Int32 _nRows) +{ + m_pSelectionBox->SetNoneVisbleRow(_nRows); +} + +void OQueryDesignView::initByFieldDescriptions( const Sequence< PropertyValue >& i_rFieldDescriptions ) +{ + OQueryController& rController = static_cast< OQueryController& >( getController() ); + + m_pSelectionBox->PreFill(); + m_pSelectionBox->SetReadOnly( rController.isReadOnly() ); + m_pSelectionBox->Fill(); + + for ( auto const & field : i_rFieldDescriptions ) + { + ::rtl::Reference< OTableFieldDesc > pField( new OTableFieldDesc() ); + pField->Load( field, true ); + InsertField( pField, false ); + } + + rController.ClearUndoManager(); + m_pSelectionBox->Invalidate(); +} + +bool OQueryDesignView::initByParseIterator( ::dbtools::SQLExceptionInfo* _pErrorInfo ) +{ + SqlParseError eErrorCode = eNativeMode; + m_rController.clearError(); + + try + { + eErrorCode = InitFromParseNodeImpl( this, m_pSelectionBox ); + + if ( eErrorCode != eOk ) + { + if ( !m_rController.hasError() ) + m_rController.appendError( getParseErrorMessage( eErrorCode ) ); + + if ( _pErrorInfo ) + { + *_pErrorInfo = m_rController.getError(); + } + else + { + m_rController.displayError(); + } + } + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + return eErrorCode == eOk; +} + +// Utility function for fillFunctionInfo +namespace { + sal_Int32 char_datatype(const::connectivity::OSQLParseNode* pDataType, const unsigned int offset) { + int cnt = pDataType->count() - offset; + if ( cnt < 0 ) + { + OSL_FAIL("internal error in decoding character datatype specification"); + return DataType::VARCHAR; + } + else if ( cnt == 0 ) + { + if ( offset == 0 ) + { + // The datatype is the node itself + if ( SQL_ISTOKENOR2 (pDataType, CHARACTER, CHAR) ) + return DataType::CHAR; + else if ( SQL_ISTOKEN (pDataType, VARCHAR) ) + return DataType::VARCHAR; + else if ( SQL_ISTOKEN (pDataType, CLOB) ) + return DataType::CLOB; + else + { + OSL_FAIL("unknown/unexpected token in decoding character datatype specification"); + return DataType::VARCHAR; + } + } + else + { + // No child left to read! + OSL_FAIL("incomplete datatype in decoding character datatype specification"); + return DataType::VARCHAR; + } + } + + if ( SQL_ISTOKEN(pDataType->getChild(offset), NATIONAL) ) + return char_datatype(pDataType, offset+1); + else if ( SQL_ISTOKENOR3(pDataType->getChild(offset), CHARACTER, CHAR, NCHAR) ) + { + if ( cnt > 2 && SQL_ISTOKEN(pDataType->getChild(offset+1), LARGE) && SQL_ISTOKEN(pDataType->getChild(offset+2), OBJECT) ) + return DataType::CLOB; + else if ( cnt > 1 && SQL_ISTOKEN(pDataType->getChild(offset+1), VARYING) ) + return DataType::VARCHAR; + else + return DataType::CHAR; + } + else if ( SQL_ISTOKEN (pDataType->getChild(offset), VARCHAR) ) + return DataType::VARCHAR; + else if ( SQL_ISTOKENOR2 (pDataType->getChild(offset), CLOB, NCLOB) ) + return DataType::CLOB; + + OSL_FAIL("unrecognised character datatype"); + return DataType::VARCHAR; + } +} + +// Try to guess the type of an expression in simple cases. +// Originally meant to be called only on a function call (hence the misnomer), +// but now tries to do the best it can also in other cases. +// Don't completely rely on fillFunctionInfo, +// it won't look at the function's arguments to find the return type +// (in particular, in the case of general_set_fct, +// the return type is the type of the argument; +// if that is (as is typical) a column reference, +// it is the type of the column). +// TODO: There is similar "guess the expression's type" code in several places: +// SelectionBrowseBox.cxx: OSelectionBrowseBox::saveField +// QueryDesignView.cxx: InstallFields, GetOrderCriteria, GetGroupCriteria +// If possible, they should be factorised into this function +// (which should then be renamed...) + +void OQueryDesignView::fillFunctionInfo( const ::connectivity::OSQLParseNode* pNode + ,const OUString& sFunctionTerm + ,OTableFieldDescRef& aInfo) +{ + // get the type of the expression, as far as easily possible + OQueryController& rController = static_cast<OQueryController&>(getController()); + sal_Int32 nDataType = DataType::DOUBLE; + switch(pNode->getNodeType()) + { + case SQLNodeType::Concat: + case SQLNodeType::String: + nDataType = DataType::VARCHAR; + break; + case SQLNodeType::IntNum: + nDataType = DataType::INTEGER; + break; + case SQLNodeType::ApproxNum: + nDataType = DataType::DOUBLE; + break; + case SQLNodeType::AccessDate: + nDataType = DataType::TIMESTAMP; + break; + case SQLNodeType::Equal: + case SQLNodeType::Less: + case SQLNodeType::Great: + case SQLNodeType::LessEq: + case SQLNodeType::GreatEq: + case SQLNodeType::NotEqual: + nDataType = DataType::BOOLEAN; + break; + case SQLNodeType::Name: + case SQLNodeType::ListRule: + case SQLNodeType::CommaListRule: + case SQLNodeType::Keyword: + case SQLNodeType::Punctuation: + OSL_FAIL("Unexpected SQL Node Type"); + break; + case SQLNodeType::Rule: + switch(pNode->getKnownRuleID()) + { + case OSQLParseNode::select_statement: + case OSQLParseNode::table_exp: + case OSQLParseNode::table_ref_commalist: + case OSQLParseNode::table_ref: + case OSQLParseNode::catalog_name: + case OSQLParseNode::schema_name: + case OSQLParseNode::table_name: + case OSQLParseNode::opt_column_commalist: + case OSQLParseNode::column_commalist: + case OSQLParseNode::column_ref_commalist: + case OSQLParseNode::column_ref: + case OSQLParseNode::opt_order_by_clause: + case OSQLParseNode::ordering_spec_commalist: + case OSQLParseNode::ordering_spec: + case OSQLParseNode::opt_asc_desc: + case OSQLParseNode::where_clause: + case OSQLParseNode::opt_where_clause: + case OSQLParseNode::opt_escape: + case OSQLParseNode::scalar_exp_commalist: + case OSQLParseNode::scalar_exp: // Seems to never be generated? + case OSQLParseNode::parameter_ref: + case OSQLParseNode::parameter: + case OSQLParseNode::range_variable: + case OSQLParseNode::delete_statement_positioned: + case OSQLParseNode::delete_statement_searched: + case OSQLParseNode::update_statement_positioned: + case OSQLParseNode::update_statement_searched: + case OSQLParseNode::assignment_commalist: + case OSQLParseNode::assignment: + case OSQLParseNode::insert_statement: + case OSQLParseNode::insert_atom_commalist: + case OSQLParseNode::insert_atom: + case OSQLParseNode::from_clause: + case OSQLParseNode::qualified_join: + case OSQLParseNode::cross_union: + case OSQLParseNode::select_sublist: + case OSQLParseNode::join_type: + case OSQLParseNode::named_columns_join: + case OSQLParseNode::joined_table: + case OSQLParseNode::sql_not: + case OSQLParseNode::manipulative_statement: + case OSQLParseNode::value_exp_commalist: + case OSQLParseNode::union_statement: + case OSQLParseNode::outer_join_type: + case OSQLParseNode::selection: + case OSQLParseNode::base_table_def: + case OSQLParseNode::base_table_element_commalist: + case OSQLParseNode::data_type: + case OSQLParseNode::column_def: + case OSQLParseNode::table_node: + case OSQLParseNode::as_clause: + case OSQLParseNode::opt_as: + case OSQLParseNode::op_column_commalist: + case OSQLParseNode::table_primary_as_range_column: + case OSQLParseNode::character_string_type: + case OSQLParseNode::comparison: + OSL_FAIL("Unexpected SQL RuleID"); + break; + case OSQLParseNode::column: + case OSQLParseNode::column_val: + OSL_FAIL("Cannot guess column type"); + break; + case OSQLParseNode::values_or_query_spec: + OSL_FAIL("Cannot guess VALUES type"); + break; + case OSQLParseNode::derived_column: + OSL_FAIL("Cannot guess computed column type"); + break; + case OSQLParseNode::subquery: + OSL_FAIL("Cannot guess subquery return type"); + break; + case OSQLParseNode::search_condition: + case OSQLParseNode::comparison_predicate: + case OSQLParseNode::between_predicate: + case OSQLParseNode::like_predicate: + case OSQLParseNode::test_for_null: + case OSQLParseNode::boolean_term: + case OSQLParseNode::boolean_primary: + case OSQLParseNode::in_predicate: + case OSQLParseNode::existence_test: + case OSQLParseNode::unique_test: + case OSQLParseNode::all_or_any_predicate: + case OSQLParseNode::join_condition: + case OSQLParseNode::boolean_factor: + case OSQLParseNode::comparison_predicate_part_2: + case OSQLParseNode::parenthesized_boolean_value_expression: + case OSQLParseNode::other_like_predicate_part_2: + case OSQLParseNode::between_predicate_part_2: + nDataType = DataType::BOOLEAN; + break; + case OSQLParseNode::num_value_exp: + case OSQLParseNode::extract_exp: + case OSQLParseNode::term: + case OSQLParseNode::factor: + // Might by an integer or a float; take the most generic + nDataType = DataType::DOUBLE; + break; + case OSQLParseNode::value_exp_primary: + case OSQLParseNode::value_exp: + case OSQLParseNode::odbc_call_spec: + // Really, we don't know. Let the default. + break; + case OSQLParseNode::position_exp: + case OSQLParseNode::length_exp: + nDataType = DataType::INTEGER; + break; + case OSQLParseNode::char_value_exp: + case OSQLParseNode::char_value_fct: + case OSQLParseNode::fold: + case OSQLParseNode::char_substring_fct: + case OSQLParseNode::char_factor: + case OSQLParseNode::concatenation: + nDataType = DataType::VARCHAR; + break; + case OSQLParseNode::datetime_primary: + nDataType = DataType::TIMESTAMP; + break; + case OSQLParseNode::bit_value_fct: + nDataType = DataType::BINARY; + break; + case OSQLParseNode::general_set_fct: // May depend on argument; ignore that for now + case OSQLParseNode::set_fct_spec: + { + if (pNode->count() == 0) + { + // This is not a function call, no sense to continue with a function return type lookup + OSL_FAIL("Got leaf SQL node where non-leaf expected"); + break; + } + const OSQLParseNode* pFunctionName = pNode->getChild(0); + if ( SQL_ISPUNCTUATION(pFunctionName,"{") ) + { + if ( pNode->count() == 3 ) + return fillFunctionInfo( pNode->getChild(1), sFunctionTerm, aInfo ); + else + OSL_FAIL("ODBC escape not in recognised form"); + break; + } + else + { + if ( SQL_ISRULEOR2(pNode,length_exp,char_value_fct) ) + pFunctionName = pFunctionName->getChild(0); + + OUString sFunctionName = pFunctionName->getTokenValue(); + if ( sFunctionName.isEmpty() ) + sFunctionName = OStringToOUString(OSQLParser::TokenIDToStr(pFunctionName->getTokenID()),RTL_TEXTENCODING_UTF8); + + nDataType = OSQLParser::getFunctionReturnType( + sFunctionName + ,&rController.getParser().getContext()); + } + break; + } + case OSQLParseNode::odbc_fct_spec: + { + if (pNode->count() != 2) + { + OSL_FAIL("interior of ODBC escape not in recognised shape"); + break; + } + + const OSQLParseNode* const pEscapeType = pNode->getChild(0); + if (SQL_ISTOKEN(pEscapeType, TS)) + nDataType = DataType::TIMESTAMP; + else if (SQL_ISTOKEN(pEscapeType, D)) + nDataType = DataType::DATE; + else if (SQL_ISTOKEN(pEscapeType, T)) + nDataType = DataType::TIME; + else if (SQL_ISTOKEN(pEscapeType, FN)) + return fillFunctionInfo( pNode->getChild(1), sFunctionTerm, aInfo ); + else + OSL_FAIL("Unknown ODBC escape"); + break; + } + case OSQLParseNode::cast_spec: + { + if ( pNode->count() != 6 || !SQL_ISTOKEN(pNode->getChild(3), AS) ) + { + OSL_FAIL("CAST not in recognised shape"); + break; + } + const OSQLParseNode *pCastTarget = pNode->getChild(4); + if ( SQL_ISTOKENOR2(pCastTarget, INTEGER, INT) ) + nDataType = DataType::INTEGER; + else if ( SQL_ISTOKEN(pCastTarget, SMALLINT) ) + nDataType = DataType::SMALLINT; + else if ( SQL_ISTOKEN(pCastTarget, BIGINT) ) + nDataType = DataType::BIGINT; + else if ( SQL_ISTOKEN(pCastTarget, FLOAT) ) + nDataType = DataType::FLOAT; + else if ( SQL_ISTOKEN(pCastTarget, REAL) ) + nDataType = DataType::REAL; + else if ( SQL_ISTOKEN(pCastTarget, DOUBLE) ) + nDataType = DataType::DOUBLE; + else if ( SQL_ISTOKEN(pCastTarget, BOOLEAN) ) + nDataType = DataType::BOOLEAN; + else if ( SQL_ISTOKEN(pCastTarget, DATE) ) + nDataType = DataType::DATE; + else if ( pCastTarget->count() > 0 ) + { + const OSQLParseNode *pDataType = pCastTarget->getChild(0); + while (pDataType->count() > 0) + { + pCastTarget = pDataType; + pDataType = pDataType->getChild(0); + } + if ( SQL_ISTOKEN (pDataType, TIME) ) + nDataType = DataType::TIME; + else if ( SQL_ISTOKEN (pDataType, TIMESTAMP) ) + nDataType = DataType::TIMESTAMP; + else if ( SQL_ISTOKENOR3 (pDataType, CHARACTER, CHAR, NCHAR) ) + nDataType = char_datatype(pCastTarget, 0); + else if ( SQL_ISTOKEN (pDataType, VARCHAR) ) + nDataType = DataType::VARCHAR; + else if ( SQL_ISTOKEN (pDataType, CLOB) ) + nDataType = DataType::CLOB; + else if ( SQL_ISTOKEN (pDataType, NATIONAL) ) + nDataType = char_datatype(pCastTarget, 1); + else if ( SQL_ISTOKEN (pDataType, BINARY) ) + { + if ( pCastTarget->count() > 2 && SQL_ISTOKEN(pCastTarget->getChild(1), LARGE) && SQL_ISTOKEN(pCastTarget->getChild(2), OBJECT) ) + nDataType = DataType::BLOB; + else if ( pCastTarget->count() > 1 && SQL_ISTOKEN(pCastTarget->getChild(1), VARYING) ) + nDataType = DataType::VARBINARY; + else + nDataType = DataType::BINARY; + } + else if ( SQL_ISTOKEN (pDataType, VARBINARY) ) + nDataType = DataType::VARBINARY; + else if ( SQL_ISTOKEN (pDataType, BLOB) ) + nDataType = DataType::BLOB; + else if ( SQL_ISTOKEN (pDataType, NUMERIC) ) + nDataType = DataType::NUMERIC; + else if ( SQL_ISTOKENOR2 (pDataType, DECIMAL, DEC) ) + nDataType = DataType::DECIMAL; + else if ( SQL_ISTOKEN (pDataType, FLOAT) ) + nDataType = DataType::FLOAT; + else if ( SQL_ISTOKEN (pDataType, DOUBLE) ) + nDataType = DataType::DOUBLE; + else if ( SQL_ISTOKEN (pDataType, INTERVAL) ) + // Not in DataType published constant (because not in JDBC...) + nDataType = DataType::VARCHAR; + else + OSL_FAIL("Failed to decode CAST target"); + } + else + OSL_FAIL("Could not decipher CAST target"); + break; + } + default: + OSL_FAIL("Unknown SQL RuleID"); + break; + } + break; + default: + OSL_FAIL("Unknown SQL Node Type"); + break; + } + + aInfo->SetDataType(nDataType); + aInfo->SetFieldType(TAB_NORMAL_FIELD); + aInfo->SetField(sFunctionTerm); + aInfo->SetTabWindow(nullptr); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QueryMoveTabWinUndoAct.cxx b/dbaccess/source/ui/querydesign/QueryMoveTabWinUndoAct.cxx new file mode 100644 index 000000000..1e7ca6bb9 --- /dev/null +++ b/dbaccess/source/ui/querydesign/QueryMoveTabWinUndoAct.cxx @@ -0,0 +1,39 @@ +/* -*- 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 "QueryMoveTabWinUndoAct.hxx" +#include <JoinTableView.hxx> + +using namespace dbaui; +void OJoinMoveTabWinUndoAct::TogglePosition() +{ + Point ptFrameScrollPos(m_pOwner->GetHScrollBar().GetThumbPos(), m_pOwner->GetVScrollBar().GetThumbPos()); + Point ptNext = m_pTabWin->GetPosPixel() + ptFrameScrollPos; + + m_pTabWin->SetPosPixel(m_ptNextPosition - ptFrameScrollPos); + + // it looks like ptFrameScrollPos is meaningless, then I subtract it here and add it to ptNext, and + // next time I subtract again... + // Albeit ptFrameScrollPos could have changed next time... + m_pOwner->EnsureVisible(m_pTabWin); + + m_ptNextPosition = ptNext; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QueryMoveTabWinUndoAct.hxx b/dbaccess/source/ui/querydesign/QueryMoveTabWinUndoAct.hxx new file mode 100644 index 000000000..38cca8bc6 --- /dev/null +++ b/dbaccess/source/ui/querydesign/QueryMoveTabWinUndoAct.hxx @@ -0,0 +1,56 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYMOVETABWINUNDOACT_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYMOVETABWINUNDOACT_HXX + +#include "QueryDesignUndoAction.hxx" +#include <strings.hrc> +#include <JoinTableView.hxx> +#include <TableWindow.hxx> +#include <tools/gen.hxx> + +namespace dbaui +{ + + // OQueryMoveTabWinUndoAct - Undo class for moving a TabWin + class OTableWindow; + class OJoinMoveTabWinUndoAct final : public OQueryDesignUndoAction + { + Point m_ptNextPosition; + VclPtr<OTableWindow> m_pTabWin; + + void TogglePosition(); + + public: + OJoinMoveTabWinUndoAct(OJoinTableView* pOwner, const Point& ptOriginalPosition, OTableWindow* pTabWin); + + virtual void Undo() override { TogglePosition(); } + virtual void Redo() override { TogglePosition(); } + }; + + inline OJoinMoveTabWinUndoAct::OJoinMoveTabWinUndoAct(OJoinTableView* pOwner, const Point& ptOriginalPosition, OTableWindow* pTabWin) + :OQueryDesignUndoAction(pOwner, STR_QUERY_UNDO_MOVETABWIN) + ,m_ptNextPosition(ptOriginalPosition) + ,m_pTabWin(pTabWin) + { + } +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYMOVETABWINUNDOACT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QuerySizeTabWinUndoAct.hxx b/dbaccess/source/ui/querydesign/QuerySizeTabWinUndoAct.hxx new file mode 100644 index 000000000..a36eb5eb5 --- /dev/null +++ b/dbaccess/source/ui/querydesign/QuerySizeTabWinUndoAct.hxx @@ -0,0 +1,73 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYSIZETABWINUNDOACT_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYSIZETABWINUNDOACT_HXX + +#include "QueryDesignUndoAction.hxx" +#include <strings.hrc> +#include <TableWindow.hxx> + +namespace dbaui +{ + + // OQuerySizeTabWinUndoAct - undo class to change size of TabWins + class OTableWindow; + class OJoinSizeTabWinUndoAct final : public OQueryDesignUndoAction + { + Point m_ptNextPosition; + Size m_szNextSize; + VclPtr<OTableWindow> m_pTabWin; + + inline void ToggleSizePosition(); + + public: + OJoinSizeTabWinUndoAct(OJoinTableView* pOwner, const Point& ptOriginalPos, const Size& szOriginalSize, OTableWindow* pTabWin); + // Boundary condition: while retrieving size/position scrolling is not allowed, meaning the position + // here returns physical and not logical coordinates + // (in contrary to QueryMoveTabWinUndoAct) + + virtual void Undo() override { ToggleSizePosition(); } + virtual void Redo() override { ToggleSizePosition(); } + }; + + inline OJoinSizeTabWinUndoAct::OJoinSizeTabWinUndoAct(OJoinTableView* pOwner, const Point& ptOriginalPos, const Size& szOriginalSize, OTableWindow* pTabWin) + :OQueryDesignUndoAction(pOwner, STR_QUERY_UNDO_SIZETABWIN) + ,m_ptNextPosition(ptOriginalPos) + ,m_szNextSize(szOriginalSize) + ,m_pTabWin(pTabWin) + { + } + + inline void OJoinSizeTabWinUndoAct::ToggleSizePosition() + { + Point ptNext = m_pTabWin->GetPosPixel(); + Size szNext = m_pTabWin->GetSizePixel(); + + m_pOwner->Invalidate(InvalidateFlags::NoChildren); + m_pTabWin->SetPosSizePixel(m_ptNextPosition, m_szNextSize); + m_pOwner->Invalidate(InvalidateFlags::NoChildren); + + m_ptNextPosition = ptNext; + m_szNextSize = szNext; + } +} + +#endif // INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYSIZETABWINUNDOACT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QueryTabConnUndoAction.cxx b/dbaccess/source/ui/querydesign/QueryTabConnUndoAction.cxx new file mode 100644 index 000000000..b209a62ce --- /dev/null +++ b/dbaccess/source/ui/querydesign/QueryTabConnUndoAction.cxx @@ -0,0 +1,121 @@ +/* -*- 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 "QueryTabConnUndoAction.hxx" +#include "QTableConnection.hxx" +#include <QueryTableView.hxx> +#include "QueryAddTabConnUndoAction.hxx" +#include "QueryTabWinShowUndoAct.hxx" +#include <strings.hrc> + +using namespace dbaui; + +OQueryTabConnUndoAction::~OQueryTabConnUndoAction() +{ + if (m_bOwnerOfConn) + { // I have the connection -> delete + m_pOwner->DeselectConn(m_pConnection); + m_pConnection.disposeAndClear(); + } +} + +OQueryTabConnUndoAction::OQueryTabConnUndoAction(OQueryTableView* pOwner, const char* pCommentID) + :OQueryDesignUndoAction(pOwner, pCommentID) + ,m_pConnection(nullptr) + ,m_bOwnerOfConn(false) +{ +} + +OQueryAddTabConnUndoAction::OQueryAddTabConnUndoAction(OQueryTableView* pOwner) + : OQueryTabConnUndoAction(pOwner, STR_QUERY_UNDO_INSERTCONNECTION) +{ +} + +void OQueryAddTabConnUndoAction::Undo() +{ + static_cast<OQueryTableView*>(m_pOwner.get())->DropConnection(m_pConnection); + SetOwnership(true); +} + +void OQueryAddTabConnUndoAction::Redo() +{ + static_cast<OQueryTableView*>(m_pOwner.get())->GetConnection(m_pConnection); + SetOwnership(false); +} + +OQueryDelTabConnUndoAction::OQueryDelTabConnUndoAction(OQueryTableView* pOwner) + : OQueryTabConnUndoAction(pOwner, STR_QUERY_UNDO_REMOVECONNECTION) +{ +} + +void OQueryDelTabConnUndoAction::Undo() +{ + static_cast<OQueryTableView*>(m_pOwner.get())->GetConnection(m_pConnection); + SetOwnership(false); +} + +void OQueryDelTabConnUndoAction::Redo() +{ + static_cast<OQueryTableView*>(m_pOwner.get())->DropConnection(m_pConnection); + SetOwnership(true); +} + +OQueryTabWinShowUndoAct::OQueryTabWinShowUndoAct(OQueryTableView* pOwner) + : OQueryTabWinUndoAct(pOwner, STR_QUERY_UNDO_TABWINSHOW) +{ +} + +OQueryTabWinShowUndoAct::~OQueryTabWinShowUndoAct() +{ +} + +void OQueryTabWinShowUndoAct::Undo() +{ + static_cast<OQueryTableView*>(m_pOwner.get())->HideTabWin(m_pTabWin, this); + SetOwnership(true); +} + +void OQueryTabWinShowUndoAct::Redo() +{ + static_cast<OQueryTableView*>(m_pOwner.get())->ShowTabWin(m_pTabWin, this,true); + SetOwnership(false); +} + +OQueryTabWinDelUndoAct::OQueryTabWinDelUndoAct(OQueryTableView* pOwner) + : OQueryTabWinUndoAct(pOwner, STR_QUERY_UNDO_TABWINDELETE) +{ +} + +OQueryTabWinDelUndoAct::~OQueryTabWinDelUndoAct() +{ +} + +void OQueryTabWinDelUndoAct::Undo() +{ + static_cast<OQueryTableView*>(m_pOwner.get())->ShowTabWin( m_pTabWin, this,true ); + SetOwnership(false); +} + +void OQueryTabWinDelUndoAct::Redo() +{ + static_cast<OQueryTableView*>(m_pOwner.get())->HideTabWin( m_pTabWin, this ); + SetOwnership(true); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QueryTabConnUndoAction.hxx b/dbaccess/source/ui/querydesign/QueryTabConnUndoAction.hxx new file mode 100644 index 000000000..36b1aae98 --- /dev/null +++ b/dbaccess/source/ui/querydesign/QueryTabConnUndoAction.hxx @@ -0,0 +1,52 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYTABCONNUNDOACTION_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYTABCONNUNDOACTION_HXX + +#include "QueryDesignUndoAction.hxx" +#include "QTableConnection.hxx" +#include <JoinTableView.hxx> + +namespace dbaui +{ + class OQueryTableConnection; + class OQueryTableView; + class OQueryTabConnUndoAction : public OQueryDesignUndoAction + { + protected: + VclPtr<OQueryTableConnection> m_pConnection; + bool m_bOwnerOfConn; + // am I the only owner of the connection? (changes with every redo and undo) + + public: + OQueryTabConnUndoAction(OQueryTableView* pOwner, const char* pCommentID); + virtual ~OQueryTabConnUndoAction() override; + + virtual void Undo() override = 0; + virtual void Redo() override = 0; + + void SetConnection(OQueryTableConnection* pConn) { m_pConnection = pConn; } + // now SetOwnership please + void SetOwnership(bool bTakeIt) { m_bOwnerOfConn = bTakeIt; } + }; +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYTABCONNUNDOACTION_HXX + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QueryTabWinShowUndoAct.hxx b/dbaccess/source/ui/querydesign/QueryTabWinShowUndoAct.hxx new file mode 100644 index 000000000..748ca7c38 --- /dev/null +++ b/dbaccess/source/ui/querydesign/QueryTabWinShowUndoAct.hxx @@ -0,0 +1,53 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYTABWINSHOWUNDOACT_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYTABWINSHOWUNDOACT_HXX + +#include "QueryTabWinUndoAct.hxx" + +namespace dbaui +{ + // OQueryTabWinShowUndoAct - undo class to show a TabWins + + class OQueryTabWinShowUndoAct : public OQueryTabWinUndoAct + { + public: + explicit OQueryTabWinShowUndoAct(OQueryTableView* pOwner); + virtual ~OQueryTabWinShowUndoAct() override; + + virtual void Undo() override; + virtual void Redo() override; + }; + + // OQueryTabWinDelUndoAct - undo class to delete a TabWins + + class OQueryTabWinDelUndoAct : public OQueryTabWinUndoAct + { + public: + explicit OQueryTabWinDelUndoAct(OQueryTableView* pOwner); + virtual ~OQueryTabWinDelUndoAct() override; + + virtual void Undo() override; + virtual void Redo() override; + }; +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYTABWINSHOWUNDOACT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QueryTabWinUndoAct.cxx b/dbaccess/source/ui/querydesign/QueryTabWinUndoAct.cxx new file mode 100644 index 000000000..6242c94fc --- /dev/null +++ b/dbaccess/source/ui/querydesign/QueryTabWinUndoAct.cxx @@ -0,0 +1,113 @@ +/* -*- 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 "QueryTabWinUndoAct.hxx" +#include <osl/diagnose.h> +#include "QTableWindow.hxx" +#include <TableConnection.hxx> +#include "QueryDesignFieldUndoAct.hxx" +#include <QueryTableView.hxx> + +using namespace dbaui; +OQueryDesignFieldUndoAct::OQueryDesignFieldUndoAct(OSelectionBrowseBox* pSelBrwBox, const char* pCommentID) + : OCommentUndoAction(pCommentID) + , pOwner(pSelBrwBox) + , m_nColumnPosition(BROWSER_INVALIDID) +{ +} + +OQueryDesignFieldUndoAct::~OQueryDesignFieldUndoAct() +{ + pOwner = nullptr; +} + +OQueryTabWinUndoAct::OQueryTabWinUndoAct(OQueryTableView* pOwner, const char* pCommentID) + : OQueryDesignUndoAction(pOwner, pCommentID) + , m_pTabWin(nullptr) + , m_bOwnerOfObjects(false) +{ +} + +OQueryTabWinUndoAct::~OQueryTabWinUndoAct() +{ + if (!m_bOwnerOfObjects) + return; + + // I should take care to delete the window if I am the only owner + OSL_ENSURE(m_pTabWin != nullptr, "OQueryTabWinUndoAct::~OQueryTabWinUndoAct() : m_pTabWin must not be NULL"); + OSL_ENSURE(!m_pTabWin->IsVisible(), "OQueryTabWinUndoAct::~OQueryTabWinUndoAct() : *m_pTabWin must not be visible"); + + if ( m_pTabWin ) + m_pTabWin->clearListBox(); + m_pTabWin.disposeAndClear(); + + // and of course the corresponding connections + for (auto & connection : m_vTableConnection) + { + m_pOwner->DeselectConn(connection); + connection.disposeAndClear(); + } + m_vTableConnection.clear(); +} + +void OTabFieldCellModifiedUndoAct::Undo() +{ + pOwner->EnterUndoMode(); + OSL_ENSURE(m_nColumnPosition != BROWSER_INVALIDID,"Column position was not set add the undo action!"); + OSL_ENSURE(m_nColumnPosition < pOwner->GetColumnCount(),"Position outside the column count!"); + if ( m_nColumnPosition != BROWSER_INVALIDID ) + { + sal_uInt16 nColumnId = pOwner->GetColumnId(m_nColumnPosition); + OUString strNext = pOwner->GetCellContents(m_nCellIndex, nColumnId); + pOwner->SetCellContents(m_nCellIndex, nColumnId, m_strNextCellContents); + m_strNextCellContents = strNext; + } + pOwner->LeaveUndoMode(); +} + +void OTabFieldSizedUndoAct::Undo() +{ + pOwner->EnterUndoMode(); + OSL_ENSURE(m_nColumnPosition != BROWSER_INVALIDID,"Column position was not set add the undo action!"); + if ( m_nColumnPosition != BROWSER_INVALIDID ) + { + sal_uInt16 nColumnId = pOwner->GetColumnId(m_nColumnPosition); + long nNextWidth = pOwner->GetColumnWidth(nColumnId); + pOwner->SetColWidth(nColumnId, m_nNextWidth); + m_nNextWidth = nNextWidth; + } + pOwner->LeaveUndoMode(); +} + +void OTabFieldMovedUndoAct::Undo() +{ + pOwner->EnterUndoMode(); + OSL_ENSURE(m_nColumnPosition != BROWSER_INVALIDID,"Column position was not set add the undo action!"); + if ( m_nColumnPosition != BROWSER_INVALIDID ) + { + sal_uInt16 nId = pDescr->GetColumnId(); + sal_uInt16 nOldPos = pOwner->GetColumnPos(nId); + pOwner->SetColumnPos(nId,m_nColumnPosition); + pOwner->ColumnMoved(nId,false); + m_nColumnPosition = nOldPos; + } + pOwner->LeaveUndoMode(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QueryTabWinUndoAct.hxx b/dbaccess/source/ui/querydesign/QueryTabWinUndoAct.hxx new file mode 100644 index 000000000..008c76260 --- /dev/null +++ b/dbaccess/source/ui/querydesign/QueryTabWinUndoAct.hxx @@ -0,0 +1,63 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYTABWINUNDOACT_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYTABWINUNDOACT_HXX + +#include "QueryDesignUndoAction.hxx" +#include "QTableWindow.hxx" +#include <TableConnection.hxx> +#include <vector> + +namespace dbaui +{ + // OQueryTabWinUndoAct - undo base class for all which is concerned with insert/remove TabWins + + class OQueryTableWindow; + class OTableConnection; + class OQueryTableView; + class OQueryTabWinUndoAct : public OQueryDesignUndoAction + { + protected: + std::vector<VclPtr<OTableConnection> > m_vTableConnection; + VclPtr<OQueryTableWindow> m_pTabWin; + bool m_bOwnerOfObjects; + // am I the only owner of the managed objects? (changes with every redo or undo) + + public: + OQueryTabWinUndoAct(OQueryTableView* pOwner, const char* pCommentID); + virtual ~OQueryTabWinUndoAct() override; + + void SetOwnership(bool bTakeIt) { m_bOwnerOfObjects = bTakeIt; } + + virtual void Undo() override = 0; + virtual void Redo() override = 0; + + // access to the TabWin + void SetTabWin(OQueryTableWindow* pTW) { m_pTabWin = pTW; } + // now SetOwnership should be invoked + + std::vector<VclPtr<OTableConnection> >& GetTabConnList() { return m_vTableConnection; } + + void InsertConnection( OTableConnection* pConnection ) { m_vTableConnection.push_back(pConnection); } + }; + +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYTABWINUNDOACT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QueryTableView.cxx b/dbaccess/source/ui/querydesign/QueryTableView.cxx new file mode 100644 index 000000000..1847cfe7b --- /dev/null +++ b/dbaccess/source/ui/querydesign/QueryTableView.cxx @@ -0,0 +1,887 @@ +/* -*- 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 <QueryTableView.hxx> +#include <TableFieldDescription.hxx> +#include <tools/diagnose_ex.h> +#include <osl/diagnose.h> +#include <helpids.h> +#include "QTableWindow.hxx" +#include "QTableConnection.hxx" +#include "QTableConnectionData.hxx" +#include <QueryDesignView.hxx> +#include "QueryAddTabConnUndoAction.hxx" +#include "QueryTabWinShowUndoAct.hxx" +#include <browserids.hxx> +#include <com/sun/star/sdbc/XConnection.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/accessibility/AccessibleEventId.hpp> +#include <JAccess.hxx> +#include <com/sun/star/sdbcx/KeyType.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <connectivity/dbtools.hxx> +#include <comphelper/sequence.hxx> +#include "querydlg.hxx" +#include <core_resource.hxx> +#include <strings.hrc> +#include <strings.hxx> + +using namespace dbaui; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::accessibility; + +namespace +{ + /** appends a new TabAdd Undo action at controller + @param _pView the view which we use + @param _pUndoAction the undo action which should be added + @param _pConnection the connection for which the undo action should be appended + @param _bOwner is the undo action the owner + */ + void addUndoAction( OQueryTableView const * _pView, + std::unique_ptr<OQueryTabConnUndoAction> _pUndoAction, + OQueryTableConnection* _pConnection, + bool _bOwner = false) + { + _pUndoAction->SetOwnership(_bOwner); + _pUndoAction->SetConnection(_pConnection); + _pView->getDesignView()->getController().addUndoActionAndInvalidate(std::move(_pUndoAction)); + } + /** openJoinDialog opens the join dialog with this connection data + @param _pView the view which we use + @param _pConnectionData the connection data + + @return true when OK was pressed otherwise false + */ + bool openJoinDialog(OQueryTableView* _pView,const TTableConnectionData::value_type& _pConnectionData,bool _bSelectableTables) + { + OQueryTableConnectionData* pData = static_cast< OQueryTableConnectionData*>(_pConnectionData.get()); + + DlgQryJoin aDlg(_pView,_pConnectionData,&_pView->GetTabWinMap(),_pView->getDesignView()->getController().getConnection(),_bSelectableTables); + bool bOk = aDlg.run() == RET_OK; + if( bOk ) + { + pData->SetJoinType(aDlg.GetJoinType()); + _pView->getDesignView()->getController().setModified(true); + } + + return bOk; + } + /** connectionModified adds an undo action for the modified connection and forces a redraw + @param _pView the view which we use + @param _pConnection the connection which was modified + @param _bAddUndo true when an undo action should be appended + */ + void connectionModified(OQueryTableView* _pView, + OTableConnection* _pConnection, + bool _bAddUndo) + { + OSL_ENSURE(_pConnection,"Invalid connection!"); + _pConnection->UpdateLineList(); + + // add an undo action + if ( _bAddUndo ) + addUndoAction( _pView, + std::make_unique<OQueryAddTabConnUndoAction>(_pView), + static_cast< OQueryTableConnection*>(_pConnection)); + // redraw + _pConnection->RecalcLines(); + // force an invalidation of the bounding rectangle + _pConnection->InvalidateConnection(); + + _pView->Invalidate(InvalidateFlags::NoChildren); + } + void addConnections(OQueryTableView* _pView, + const OQueryTableWindow& _rSource, + const OQueryTableWindow& _rDest, + const Reference<XNameAccess>& _rxSourceForeignKeyColumns) + { + if ( _rSource.GetData()->isQuery() || _rDest.GetData()->isQuery() ) + // nothing to do if one of both denotes a query + return; + + // we found a table in our view where we can insert some connections + // the key columns have a property called RelatedColumn + // build OQueryTableConnectionData + auto xNewConnData = std::make_shared<OQueryTableConnectionData>( _rSource.GetData(), _rDest.GetData() ); + + OUString sRelatedColumn; + + // iterate through all foreignkey columns to create the connections + const Sequence<OUString> aKeyCols = _rxSourceForeignKeyColumns->getElementNames(); + for(const OUString& rElement : aKeyCols) + { + Reference<XPropertySet> xColumn; + if ( !( _rxSourceForeignKeyColumns->getByName(rElement) >>= xColumn ) ) + { + OSL_FAIL( "addConnections: invalid foreign key column!" ); + continue; + } + + xColumn->getPropertyValue(PROPERTY_RELATEDCOLUMN) >>= sRelatedColumn; + + { + sal_Int32 nFindIndex = ::comphelper::findValue(_rSource.GetOriginalColumns()->getElementNames(),rElement); + if(nFindIndex != -1) + xNewConnData->SetFieldIndex(JTCS_FROM,nFindIndex+1); + else + OSL_FAIL("Column not found!"); + } + // get the position inside the table + Reference<XNameAccess> xRefColumns = _rDest.GetOriginalColumns(); + if(xRefColumns.is()) + { + sal_Int32 nFindIndex = ::comphelper::findValue(xRefColumns->getElementNames(),sRelatedColumn); + if(nFindIndex != -1) + xNewConnData->SetFieldIndex(JTCS_TO,nFindIndex+1); + else + OSL_FAIL("Column not found!"); + } + xNewConnData->AppendConnLine(rElement,sRelatedColumn); + + // now add the Conn itself + ScopedVclPtrInstance< OQueryTableConnection > aNewConn(_pView, xNewConnData); + // referring to the local variable is not important, as NotifyQueryTabConn creates a new copy + // to add me (if not existent) + _pView->NotifyTabConnection(*aNewConn, false); + // don't create an Undo-Action for the new connection : the connection is + // covered by the Undo-Action for the tabwin, as the "Undo the insert" will + // automatically remove all connections adjacent to the win. + // (Because of this automatism we would have an ownership ambiguity for + // the connection data if we would insert the conn-Undo-Action) + } + } +} + +OQueryTableView::OQueryTableView( vcl::Window* pParent,OQueryDesignView* pView) + : OJoinTableView( pParent,pView) +{ + SetHelpId(HID_CTL_QRYDGNTAB); +} + +sal_Int32 OQueryTableView::CountTableAlias(const OUString& rName, sal_Int32& rMax) +{ + sal_Int32 nRet = 0; + + OTableWindowMap::const_iterator aIter = GetTabWinMap().find(rName); + while(aIter != GetTabWinMap().end()) + { + OUString aNewName = rName + "_" + OUString::number(++nRet); + aIter = GetTabWinMap().find(aNewName); + } + + rMax = nRet; + + return nRet; +} + +void OQueryTableView::ReSync() +{ + TTableWindowData& rTabWinDataList = m_pView->getController().getTableWindowData(); + OSL_ENSURE((getTableConnections().empty()) && (GetTabWinMap().empty()), + "before calling OQueryTableView::ReSync() please call ClearAll !"); + + // I need a collection of all window names that cannot be created so that I do not initialize connections for them. + std::vector<OUString> arrInvalidTables; + + TTableWindowData::const_reverse_iterator aIter = rTabWinDataList.rbegin(); + // Create the window and add it + + for(;aIter != rTabWinDataList.rend();++aIter) + { + OQueryTableWindowData* pData = static_cast<OQueryTableWindowData*>(aIter->get()); + VclPtr<OTableWindow> pTabWin = createWindow(*aIter); + + // I don't use ShowTabWin as this adds the window data to the list of documents. + // This would be bad as I am getting them from there. + // Instead, I do it step by step + if (!pTabWin->Init()) + { + // The initialisation has gone wrong, this TabWin is not available, so + // I must clean up the data and the document + pTabWin->clearListBox(); + pTabWin.disposeAndClear(); + arrInvalidTables.push_back(pData->GetAliasName()); + + rTabWinDataList.erase( std::remove(rTabWinDataList.begin(), rTabWinDataList.end(), *aIter), rTabWinDataList.end()); + continue; + } + + GetTabWinMap()[pData->GetAliasName()] = pTabWin; // add at the beginning as I am going backwards through the DataList + // Use the default if there is no position or size + if (!pData->HasPosition() && !pData->HasSize()) + SetDefaultTabWinPosSize(pTabWin); + + pTabWin->Show(); + } + + // Add the connections + TTableConnectionData& rTabConnDataList = m_pView->getController().getTableConnectionData(); + TTableConnectionData::const_reverse_iterator aConIter = rTabConnDataList.rbegin(); + + for(;aConIter != rTabConnDataList.rend();++aConIter) + { + OQueryTableConnectionData* pTabConnData = static_cast<OQueryTableConnectionData*>(aConIter->get()); + + // do both tables for the connection exist ? + OUString strTabExistenceTest = pTabConnData->getReferencingTable()->GetWinName(); + bool bInvalid = std::find(arrInvalidTables.begin(),arrInvalidTables.end(),strTabExistenceTest) != arrInvalidTables.end(); + strTabExistenceTest = pTabConnData->getReferencedTable()->GetWinName(); + bInvalid = bInvalid && std::find(arrInvalidTables.begin(),arrInvalidTables.end(),strTabExistenceTest) != arrInvalidTables.end(); + + if (bInvalid) + { + // no -> bad luck, no connection + rTabConnDataList.erase( std::remove(rTabConnDataList.begin(), rTabConnDataList.end(), *aConIter), rTabConnDataList.end()); + continue; + } + + // adds a new connection to join view and notifies our accessible and invalidates the controller + addConnection(VclPtr<OQueryTableConnection>::Create(this, *aConIter)); + } +} + +void OQueryTableView::ClearAll() +{ + OJoinTableView::ClearAll(); + + SetUpdateMode(true); + m_pView->getController().setModified(true); +} + +VclPtr<OTableWindow> OQueryTableView::createWindow(const TTableWindowData::value_type& _pData) +{ + return VclPtr<OQueryTableWindow>::Create(this,_pData); +} + +void OQueryTableView::NotifyTabConnection(const OQueryTableConnection& rNewConn, bool _bCreateUndoAction) +{ + // let's first check if I have the connection already + OQueryTableConnection* pTabConn = nullptr; + const auto& rConnections = getTableConnections(); + auto aEnd = rConnections.end(); + auto aIter = std::find( rConnections.begin(), + aEnd, + VclPtr<OTableConnection>(const_cast<OTableConnection*>(static_cast<const OTableConnection*>(&rNewConn))) + ); + if(aIter == aEnd) + { + for (auto const& connection : rConnections) + { + if(*static_cast<OQueryTableConnection*>(connection.get()) == rNewConn) + { + pTabConn = static_cast<OQueryTableConnection*>(connection.get()); + break; + } + } + } + else + pTabConn = static_cast<OQueryTableConnection*>((*aIter).get()); + + // no -> insert + if (pTabConn == nullptr) + { + // the new data ... + auto pNewData = std::static_pointer_cast<OQueryTableConnectionData>(rNewConn.GetData()->NewInstance()); + pNewData->CopyFrom(*rNewConn.GetData()); + VclPtrInstance<OQueryTableConnection> pNewConn(this, pNewData); + GetConnection(pNewConn); + + connectionModified(this,pNewConn,_bCreateUndoAction); + } +} + +std::shared_ptr<OTableWindowData> OQueryTableView::CreateImpl(const OUString& _rComposedName + ,const OUString& _sTableName + ,const OUString& _rWinName) +{ + return std::make_shared<OQueryTableWindowData>( _rComposedName, _sTableName,_rWinName ); +} + +void OQueryTableView::AddTabWin(const OUString& _rTableName, const OUString& _rAliasName, bool bNewTable) +{ + // this method has been inherited from the base class, linking back to the parent and which constructs + // an Alias and which passes on to my other AddTabWin + + // pity _rTableName is fully qualified, OQueryDesignView expects a string which only + // contains schema and tables but no catalog. + Reference< XConnection> xConnection = m_pView->getController().getConnection(); + if(!xConnection.is()) + return; + try + { + Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData(); + OUString sCatalog, sSchema, sTable; + ::dbtools::qualifiedNameComponents(xMetaData, + _rTableName, + sCatalog, + sSchema, + sTable, + ::dbtools::EComposeRule::InDataManipulation); + OUString sRealName(sSchema); + if (!sRealName.isEmpty()) + sRealName += "."; + sRealName += sTable; + + AddTabWin(_rTableName, sRealName, _rAliasName, bNewTable); + } + catch(SQLException&) + { + OSL_FAIL("qualifiedNameComponents"); + } +} + +// find the table which has a foreign key with this referencedTable name +static Reference<XPropertySet> getKeyReferencedTo(const Reference<XIndexAccess>& _rxKeys,const OUString& _rReferencedTable) +{ + if(!_rxKeys.is()) + return Reference<XPropertySet>(); + + // search the one and only primary key + const sal_Int32 nCount = _rxKeys->getCount(); + for(sal_Int32 i=0;i<nCount ;++i) + { + Reference<XPropertySet> xKey(_rxKeys->getByIndex(i),UNO_QUERY); + if(xKey.is()) + { + sal_Int32 nKeyType = 0; + xKey->getPropertyValue(PROPERTY_TYPE) >>= nKeyType; + if(KeyType::FOREIGN == nKeyType) + { + OUString sReferencedTable; + xKey->getPropertyValue(PROPERTY_REFERENCEDTABLE) >>= sReferencedTable; + // TODO check case + if(sReferencedTable == _rReferencedTable) + return xKey; + } + } + } + return Reference<XPropertySet>(); +} + +void OQueryTableView::AddTabWin(const OUString& _rComposedName, const OUString& _rTableName, const OUString& strAlias, bool bNewTable) +{ + OSL_ENSURE(!_rTableName.isEmpty() || !strAlias.isEmpty(), "OQueryTableView::AddTabWin : no tables or aliases !"); + // If the table is not set, then it is a dummy window, but at least the alias must be set + + // build a new data structure + // first check if this already has its data + bool bAppend = bNewTable; + TTableWindowData::value_type pNewTabWinData; + TTableWindowData& rWindowData = getDesignView()->getController().getTableWindowData(); + bool bFoundElem = false; + for (auto const& elem : rWindowData) + { + pNewTabWinData = elem; + if (pNewTabWinData && pNewTabWinData->GetWinName() == strAlias && pNewTabWinData->GetComposedName() == _rComposedName && pNewTabWinData->GetTableName() == _rTableName) + { + bFoundElem = true; + break; + } + } + if ( !bAppend ) + bAppend = !bFoundElem; + if ( bAppend ) + pNewTabWinData = createTableWindowData(_rComposedName, _rTableName, strAlias); + // I do not need to add TabWinData to the DocShell list, ShowTabWin does that. + + // Create a new window + VclPtr<OQueryTableWindow> pNewTabWin = static_cast<OQueryTableWindow*>(createWindow(pNewTabWinData).get()); + // No need to initialize, as that happens in ShowTabWin + + // New UndoAction + std::unique_ptr<OQueryTabWinShowUndoAct> pUndoAction(new OQueryTabWinShowUndoAct(this)); + pUndoAction->SetTabWin(pNewTabWin); // Window + bool bSuccess = ShowTabWin(pNewTabWin, pUndoAction.get(), bAppend); + if(!bSuccess) + { + // reset table window + pUndoAction->SetTabWin(nullptr); + pUndoAction->SetOwnership(false); + return; + } + + // Show the relations between the individual tables + OTableWindowMap& rTabWins = GetTabWinMap(); + if(bNewTable && !rTabWins.empty() && !_rTableName.isEmpty()) + { + modified(); + if ( m_pAccessible ) + m_pAccessible->notifyAccessibleEvent( AccessibleEventId::CHILD, + Any(), + makeAny(pNewTabWin->GetAccessible()) + ); + + do { + + if ( pNewTabWin->GetData()->isQuery() ) + break; + + try + { + // find relations between the table and the tables already inserted + Reference< XIndexAccess> xKeyIndex = pNewTabWin->GetData()->getKeys(); + if ( !xKeyIndex.is() ) + break; + + Reference<XNameAccess> xFKeyColumns; + OUString aReferencedTable; + Reference<XColumnsSupplier> xColumnsSupplier; + + const sal_Int32 nKeyCount = xKeyIndex->getCount(); + for ( sal_Int32 i=0; i<nKeyCount ; ++i ) + { + Reference< XPropertySet > xProp( xKeyIndex->getByIndex(i), UNO_QUERY_THROW ); + xColumnsSupplier.set( xProp, UNO_QUERY_THROW ); + xFKeyColumns.set( xColumnsSupplier->getColumns(), UNO_SET_THROW ); + + sal_Int32 nKeyType = 0; + xProp->getPropertyValue(PROPERTY_TYPE) >>= nKeyType; + + switch ( nKeyType ) + { + case KeyType::FOREIGN: + { // our new table has a foreign key + // so look if the referenced table is already in our list + xProp->getPropertyValue(PROPERTY_REFERENCEDTABLE) >>= aReferencedTable; + OSL_ENSURE(!aReferencedTable.isEmpty(),"Foreign key without referencedTableName"); + + OTableWindowMap::const_iterator aIter = rTabWins.find(aReferencedTable); + OTableWindowMap::const_iterator aEnd = rTabWins.end(); + if(aIter == aEnd) + { + for(aIter = rTabWins.begin();aIter != aEnd;++aIter) + { + OQueryTableWindow* pTabWinTmp = static_cast<OQueryTableWindow*>(aIter->second.get()); + OSL_ENSURE( pTabWinTmp,"TableWindow is null!" ); + if ( pTabWinTmp != pNewTabWin && pTabWinTmp->GetComposedName() == aReferencedTable ) + break; + } + } + if ( aIter != aEnd && pNewTabWin.get() != aIter->second.get() ) + addConnections( this, *pNewTabWin, *static_cast<OQueryTableWindow*>(aIter->second.get()), xFKeyColumns ); + } + break; + + case KeyType::PRIMARY: + { + // we have a primary key so look in our list if there exists a key which this is referred to + for (auto const& tabWin : rTabWins) + { + OQueryTableWindow* pTabWinTmp = static_cast<OQueryTableWindow*>(tabWin.second.get()); + if ( pTabWinTmp == pNewTabWin ) + continue; + + if ( pTabWinTmp->GetData()->isQuery() ) + continue; + + OSL_ENSURE(pTabWinTmp,"TableWindow is null!"); + Reference< XPropertySet > xFKKey = getKeyReferencedTo( pTabWinTmp->GetData()->getKeys(), pNewTabWin->GetComposedName() ); + if ( !xFKKey.is() ) + continue; + + Reference<XColumnsSupplier> xFKColumnsSupplier( xFKKey, UNO_QUERY_THROW ); + Reference< XNameAccess > xTColumns( xFKColumnsSupplier->getColumns(), UNO_SET_THROW ); + addConnections( this, *pTabWinTmp, *pNewTabWin, xTColumns ); + } + } + break; + } + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + } while ( false ); + } + + // My parent needs to be informed about the delete + m_pView->getController().addUndoActionAndInvalidate( std::move(pUndoAction) ); +} + +void OQueryTableView::AddConnection(const OJoinExchangeData& jxdSource, const OJoinExchangeData& jxdDest) +{ + OQueryTableWindow* pSourceWin = static_cast< OQueryTableWindow*>(jxdSource.pListBox->GetTabWin()); + OQueryTableWindow* pDestWin = static_cast< OQueryTableWindow*>(jxdDest.pListBox->GetTabWin()); + + OUString aSourceFieldName, aDestFieldName; + aSourceFieldName = jxdSource.pListBox->GetEntryText(jxdSource.pEntry); + aDestFieldName = jxdDest.pListBox->GetEntryText(jxdDest.pEntry); + + OTableConnection* pConn = GetTabConn(pSourceWin,pDestWin,true); + if ( !pConn ) + { + // new data object + auto xNewConnectionData = std::make_shared<OQueryTableConnectionData>(pSourceWin->GetData(), pDestWin->GetData()); + + sal_uInt32 nSourceFieldIndex, nDestFieldIndex; + + // Get name/position of both affected fields ... + // Source + nSourceFieldIndex = jxdSource.pListBox->GetModel()->GetAbsPos(jxdSource.pEntry); + // Dest + nDestFieldIndex = jxdDest.pListBox->GetModel()->GetAbsPos(jxdDest.pEntry); + + // ... and set them + xNewConnectionData->SetFieldIndex(JTCS_FROM, nSourceFieldIndex); + xNewConnectionData->SetFieldIndex(JTCS_TO, nDestFieldIndex); + + xNewConnectionData->AppendConnLine( aSourceFieldName,aDestFieldName ); + + ScopedVclPtrInstance< OQueryTableConnection > aNewConnection(this, xNewConnectionData); + NotifyTabConnection(*aNewConnection); + // As usual with NotifyTabConnection, using a local variable is fine because a copy is made + } + else + { + // the connection could point on the other side + if(pConn->GetSourceWin() == pDestWin) + { + OUString aTmp(aSourceFieldName); + aSourceFieldName = aDestFieldName; + aDestFieldName = aTmp; + } + + pConn->GetData()->AppendConnLine( aSourceFieldName,aDestFieldName ); + + connectionModified(this,pConn,false); + } +} + +void OQueryTableView::ConnDoubleClicked(VclPtr<OTableConnection>& rConnection) +{ + if (openJoinDialog(this, rConnection->GetData(), false)) + { + connectionModified(this, rConnection, false); + SelectConn(rConnection); + } +} + +void OQueryTableView::createNewConnection() +{ + TTableConnectionData::value_type pData = std::make_shared<OQueryTableConnectionData>(); + if( !openJoinDialog(this,pData,true) ) + return; + + OTableWindowMap& rMap = GetTabWinMap(); + OQueryTableWindow* pSourceWin = static_cast< OQueryTableWindow*>(rMap[pData->getReferencingTable()->GetWinName()].get()); + OQueryTableWindow* pDestWin = static_cast< OQueryTableWindow*>(rMap[pData->getReferencedTable()->GetWinName()].get()); + // first we have to look if the this connection already exists + OTableConnection* pConn = GetTabConn(pSourceWin,pDestWin,true); + bool bNew = true; + if ( pConn ) + { + pConn->GetData()->CopyFrom( *pData ); + bNew = false; + } + else + { + // create a new connection and append it + VclPtrInstance<OQueryTableConnection> pQConn(this, pData); + GetConnection(pQConn); + pConn = pQConn; + } + connectionModified(this,pConn,bNew); + if ( !bNew && pConn == GetSelectedConn() ) // our connection was selected before so we have to reselect it + SelectConn( pConn ); +} + +bool OQueryTableView::RemoveConnection(VclPtr<OTableConnection>& rConnection, bool /*_bDelete*/) +{ + VclPtr<OQueryTableConnection> xConnection(static_cast<OQueryTableConnection*>(rConnection.get())); + + // we don't want that our connection will be deleted, we put it in the undo manager + bool bRet = OJoinTableView::RemoveConnection(rConnection, false); + + // add undo action + addUndoAction(this, + std::make_unique<OQueryDelTabConnUndoAction>(this), + xConnection.get(), + true); + + return bRet; +} + +OQueryTableWindow* OQueryTableView::FindTable(const OUString& rAliasName) +{ + OSL_ENSURE(!rAliasName.isEmpty(), "OQueryTableView::FindTable : the AliasName should not be empty !"); + // (it is harmless but does not make sense and indicates that there is probably an error in the caller) + OTableWindowMap::const_iterator aIter = GetTabWinMap().find(rAliasName); + if(aIter != GetTabWinMap().end()) + return static_cast<OQueryTableWindow*>(aIter->second.get()); + return nullptr; +} + +bool OQueryTableView::FindTableFromField(const OUString& rFieldName, OTableFieldDescRef const & rInfo, sal_uInt16& rCnt) +{ + rCnt = 0; + for (auto const& tabWin : GetTabWinMap()) + { + if(static_cast<OQueryTableWindow*>(tabWin.second.get())->ExistsField(rFieldName, rInfo)) + ++rCnt; + } + // TODO JNA : what should we rCnt > 1? + + return rCnt == 1; +} + +bool OQueryTableView::ContainsTabWin(const OTableWindow& rTabWin) +{ + + for (auto const& tabWin : GetTabWinMap()) + { + if ( tabWin.second == &rTabWin ) + { + return true; + } + } + + return false; +} + +void OQueryTableView::RemoveTabWin(OTableWindow* pTabWin) +{ + OSL_ENSURE(pTabWin != nullptr, "OQueryTableView::RemoveTabWin : Window should not be NULL !"); + + if(!(pTabWin && ContainsTabWin(*pTabWin))) // #i122589# check if registered before deleting + return; + + // I need my parent so it can be informed about the deletion + OQueryDesignView* pParent = static_cast<OQueryDesignView*>(getDesignView()); + + SfxUndoManager& rUndoMgr = m_pView->getController().GetUndoManager(); + rUndoMgr.EnterListAction(DBA_RES(STR_QUERY_UNDO_TABWINDELETE) , OUString(), 0, ViewShellId(-1)); + + // add the Undo-Action + std::unique_ptr<OQueryTabWinDelUndoAct> pUndoAction(new OQueryTabWinDelUndoAct(this)); + pUndoAction->SetTabWin(static_cast< OQueryTableWindow*>(pTabWin)); + + // and hide the window + HideTabWin(static_cast< OQueryTableWindow*>(pTabWin), pUndoAction.get()); + + // Undo Actions and delete the fields in SelectionBrowseBox + pParent->TableDeleted( static_cast< OQueryTableWindowData*>(pTabWin->GetData().get())->GetAliasName() ); + + m_pView->getController().addUndoActionAndInvalidate( std::move(pUndoAction) ); + rUndoMgr.LeaveListAction(); + + modified(); + if ( m_pAccessible ) + m_pAccessible->notifyAccessibleEvent( AccessibleEventId::CHILD, + makeAny(pTabWin->GetAccessible()), + Any() + ); +} + +void OQueryTableView::EnsureVisible(const OTableWindow* pWin) +{ + + Invalidate(InvalidateFlags::NoChildren); + OJoinTableView::EnsureVisible(pWin); +} + +void OQueryTableView::GetConnection(OQueryTableConnection* pConn) +{ + // add to me and the document + + addConnection( pConn ); +} + +void OQueryTableView::DropConnection(VclPtr<OQueryTableConnection> const & rConn) +{ + // Pay attention to the selection + // remove from me and the document + VclPtr<OTableConnection> xConn(rConn.get()); + RemoveConnection(xConn, false); +} + +void OQueryTableView::HideTabWin( OQueryTableWindow* pTabWin, OQueryTabWinUndoAct* pUndoAction ) +{ + OTableWindowMap& rTabWins = GetTabWinMap(); + + // Window + // save the position in its data + getDesignView()->SaveTabWinUIConfig(pTabWin); + // (I need to go via the parent, as only the parent knows the position of the scrollbars) + // and then out of the TabWins list and hide + OTableWindowMap::const_iterator aIter = std::find_if(rTabWins.begin(), rTabWins.end(), + [&pTabWin](const OTableWindowMap::value_type& rEntry) { return rEntry.second == pTabWin; }); + if (aIter != rTabWins.end()) + rTabWins.erase( aIter ); + + pTabWin->Hide(); // do not destroy it, as it is still in the undo list!! + + // the TabWin data must also be passed out of my responsibility + TTableWindowData& rTabWinDataList = m_pView->getController().getTableWindowData(); + rTabWinDataList.erase( std::remove(rTabWinDataList.begin(), rTabWinDataList.end(), pTabWin->GetData()), rTabWinDataList.end()); + // The data should not be destroyed as TabWin itself - which is still alive - needs them + // Either it goes back into my responsibility, (via ShowTabWin), then I add the data back, + // or the Undo-Action, which currently has full responsibility for the window + // and its data, gets destroyed and destroys both the window and its data + + if (m_pLastFocusTabWin == pTabWin) + m_pLastFocusTabWin = nullptr; + + // collect connections belonging to the window and pass to UndoAction + sal_Int16 nCnt = 0; + const auto& rTabConList = getTableConnections(); + auto aIter2 = rTabConList.begin(); + for(;aIter2 != rTabConList.end();)// the end may change + { + VclPtr<OTableConnection> xTmpEntry = *aIter2; + OQueryTableConnection* pTmpEntry = static_cast<OQueryTableConnection*>(xTmpEntry.get()); + OSL_ENSURE(pTmpEntry,"OQueryTableConnection is null!"); + if( pTmpEntry->GetAliasName(JTCS_FROM) == pTabWin->GetAliasName() || + pTmpEntry->GetAliasName(JTCS_TO) == pTabWin->GetAliasName() ) + { + // add to undo list + pUndoAction->InsertConnection(xTmpEntry); + + // call base class because we append an undo action + // but this time we are in an undo action list + OJoinTableView::RemoveConnection(xTmpEntry, false); + aIter2 = rTabConList.begin(); + ++nCnt; + } + else + ++aIter2; + } + + if (nCnt) + InvalidateConnections(); + + m_pView->getController().InvalidateFeature(ID_BROWSER_ADDTABLE); + + // inform the UndoAction that the window and connections belong to it + pUndoAction->SetOwnership(true); + + // by doing so, we have modified the document + m_pView->getController().setModified( true ); + m_pView->getController().InvalidateFeature(SID_BROWSER_CLEAR_QUERY); +} + +bool OQueryTableView::ShowTabWin( OQueryTableWindow* pTabWin, OQueryTabWinUndoAct* pUndoAction, bool _bAppend ) +{ + + bool bSuccess = false; + + if (pTabWin) + { + if (pTabWin->Init()) + { + TTableWindowData::value_type pData = pTabWin->GetData(); + OSL_ENSURE(pData != nullptr, "OQueryTableView::ShowTabWin : TabWin has no data !"); + // If there is a position and size defined, we use them + if (pData->HasPosition() && pData->HasSize()) + { + Size aSize(CalcZoom(pData->GetSize().Width()),CalcZoom(pData->GetSize().Height())); + pTabWin->SetPosSizePixel(pData->GetPosition(), aSize); + } + else + // else set a default position + SetDefaultTabWinPosSize(pTabWin); + + // Show the window and add to the list + OUString sName = static_cast< OQueryTableWindowData*>(pData.get())->GetAliasName(); + OSL_ENSURE(GetTabWinMap().find(sName) == GetTabWinMap().end(),"Alias name already in list!"); + GetTabWinMap().emplace(sName,pTabWin); + + pTabWin->Show(); + + pTabWin->PaintImmediately(); + // We must call Update() in order to show the connections in the window correctly. This sounds strange, + // but the Listbox has an internal Member which is initialized when the Listbox is first shown (after the Listbox + // is filled in Init). This Member will eventually be needed for + // GetEntryPos, and then in turn by the Connection, when its starting point to the window must be determined. + + // the Connections + auto rTableCon = pUndoAction->GetTabConnList(); + for(const auto& conn : rTableCon) + addConnection(conn); // add all connections from the undo action + + rTableCon.clear(); + + // and add the window's data to the list (of the document) + if(_bAppend) + m_pView->getController().getTableWindowData().push_back(pTabWin->GetData()); + + m_pView->getController().InvalidateFeature(ID_BROWSER_ADDTABLE); + + // and inform the UndoAction that the window belongs to me + pUndoAction->SetOwnership(false); + + bSuccess = true; + } + else + { + // Initialisation failed + // (for example when the Connection to the database is not available at the moment) + pTabWin->clearListBox(); + pTabWin->disposeOnce(); + } + } + + // show that I have changed the document + if(!m_pView->getController().isReadOnly()) + m_pView->getController().setModified( true ); + + m_pView->getController().InvalidateFeature(SID_BROWSER_CLEAR_QUERY); + + return bSuccess; +} + +void OQueryTableView::InsertField(const OTableFieldDescRef& rInfo) +{ + OSL_ENSURE(getDesignView() != nullptr, "OQueryTableView::InsertField : has no Parent !"); + static_cast<OQueryDesignView*>(getDesignView())->InsertField(rInfo); +} + +bool OQueryTableView::ExistsAVisitedConn(const OQueryTableWindow* pFrom) const +{ + for(const auto& conn : getTableConnections()) + { + OQueryTableConnection* pTemp = static_cast<OQueryTableConnection*>(conn.get()); + if (pTemp->IsVisited() && + (pFrom == static_cast< OQueryTableWindow*>(pTemp->GetSourceWin()) || pFrom == static_cast< OQueryTableWindow*>(pTemp->GetDestWin()))) + return true; + } + + return false; +} + +void OQueryTableView::onNoColumns_throw() +{ + OUString sError(DBA_RES(STR_STATEMENT_WITHOUT_RESULT_SET)); + ::dbtools::throwSQLException( sError, ::dbtools::StandardSQLState::GENERAL_ERROR, nullptr ); +} + +bool OQueryTableView::supressCrossNaturalJoin(const TTableConnectionData::value_type& _pData) const +{ + OQueryTableConnectionData* pQueryData = static_cast<OQueryTableConnectionData*>(_pData.get()); + return pQueryData && (pQueryData->GetJoinType() == CROSS_JOIN); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QueryTextView.cxx b/dbaccess/source/ui/querydesign/QueryTextView.cxx new file mode 100644 index 000000000..963314690 --- /dev/null +++ b/dbaccess/source/ui/querydesign/QueryTextView.cxx @@ -0,0 +1,110 @@ +/* -*- 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 <QueryTextView.hxx> +#include <querycontainerwindow.hxx> +#include <sqledit.hxx> +#include <undosqledit.hxx> +#include <QueryDesignView.hxx> + +using namespace dbaui; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; + +// end of temp classes +OQueryTextView::OQueryTextView(OQueryContainerWindow* _pParent) + :Window(_pParent) +{ + m_pEdit = VclPtr<OSqlEdit>::Create(this); + m_pEdit->SetRightToLeft(false); + m_pEdit->ClearModifyFlag(); + m_pEdit->SaveValue(); + m_pEdit->SetPosPixel( Point( 0, 0 ) ); + m_pEdit->Show(); +} + +OQueryTextView::~OQueryTextView() +{ + disposeOnce(); +} + +void OQueryTextView::dispose() +{ + m_pEdit.disposeAndClear(); + vcl::Window::dispose(); +} + +void OQueryTextView::GetFocus() +{ + if ( m_pEdit ) + m_pEdit->GrabFocus(); +} + +void OQueryTextView::Resize() +{ + Window::Resize(); + m_pEdit->SetSizePixel( GetOutputSizePixel() ); +} + +OUString OQueryTextView::getStatement() const +{ + return m_pEdit->GetText(); +} + +void OQueryTextView::clear() +{ + std::unique_ptr<OSqlEditUndoAct> pUndoAct(new OSqlEditUndoAct( m_pEdit )); + + pUndoAct->SetOriginalText( m_pEdit->GetText() ); + getContainerWindow()->getDesignView()->getController().addUndoActionAndInvalidate( std::move(pUndoAct) ); + + m_pEdit->SetText(OUString()); +} + +void OQueryTextView::setStatement(const OUString& _rsStatement) +{ + m_pEdit->SetText(_rsStatement); +} + +void OQueryTextView::copy() +{ + if(!m_pEdit->IsInAccelAct() ) + m_pEdit->Copy(); +} + +bool OQueryTextView::isCutAllowed() const +{ + return !m_pEdit->GetSelected().isEmpty(); +} + +void OQueryTextView::cut() +{ + if(!m_pEdit->IsInAccelAct() ) + m_pEdit->Cut(); + getContainerWindow()->getDesignView()->getController().setModified(true); +} + +void OQueryTextView::paste() +{ + if(!m_pEdit->IsInAccelAct() ) + m_pEdit->Paste(); + getContainerWindow()->getDesignView()->getController().setModified(true); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/QueryViewSwitch.cxx b/dbaccess/source/ui/querydesign/QueryViewSwitch.cxx new file mode 100644 index 000000000..e7d6758f4 --- /dev/null +++ b/dbaccess/source/ui/querydesign/QueryViewSwitch.cxx @@ -0,0 +1,293 @@ +/* -*- 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 <QueryViewSwitch.hxx> +#include <QueryDesignView.hxx> +#include <QueryTextView.hxx> +#include <querycontainerwindow.hxx> +#include <adtabdlg.hxx> +#include <querycontroller.hxx> +#include <sqledit.hxx> + +using namespace dbaui; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::lang; + +OQueryViewSwitch::OQueryViewSwitch(OQueryContainerWindow* _pParent, OQueryController& _rController,const Reference< XComponentContext >& _rxContext) +: m_bAddTableDialogWasVisible(false) +{ + + m_pTextView = VclPtr<OQueryTextView>::Create(_pParent); + m_pDesignView = VclPtr<OQueryDesignView>::Create( _pParent, _rController, _rxContext ); +} + +OQueryViewSwitch::~OQueryViewSwitch() +{ + // destroy children + m_pDesignView.disposeAndClear(); + m_pTextView.disposeAndClear(); +} + +void OQueryViewSwitch::Construct() +{ + m_pDesignView->Construct( ); +} + +void OQueryViewSwitch::initialize() +{ + // initially be in SQL mode + m_pTextView->Show(); + m_pDesignView->initialize(); +} + +bool OQueryViewSwitch::checkStatement() +{ + if(m_pTextView->IsVisible()) + return true; + return m_pDesignView->checkStatement(); +} + +OUString OQueryViewSwitch::getStatement() +{ + if(m_pTextView->IsVisible()) + return m_pTextView->getStatement(); + return m_pDesignView->getStatement(); +} + +void OQueryViewSwitch::clear() +{ + if(m_pTextView->IsVisible()) + m_pTextView->clear(); + else + m_pDesignView->clear(); +} + +void OQueryViewSwitch::GrabFocus() +{ + if ( m_pTextView && m_pTextView->IsVisible() ) + m_pTextView->GrabFocus(); + else if ( m_pDesignView && m_pDesignView->IsVisible() ) + m_pDesignView->GrabFocus(); +} + +void OQueryViewSwitch::setStatement(const OUString& _rsStatement) +{ + if(m_pTextView->IsVisible()) + m_pTextView->setStatement(_rsStatement); +} + +void OQueryViewSwitch::copy() +{ + if(m_pTextView->IsVisible()) + m_pTextView->copy(); + else + m_pDesignView->copy(); +} + +bool OQueryViewSwitch::isCutAllowed() const +{ + if(m_pTextView->IsVisible()) + return m_pTextView->isCutAllowed(); + return m_pDesignView->isCutAllowed(); +} + +bool OQueryViewSwitch::isCopyAllowed() const +{ + if(m_pTextView->IsVisible()) + return true; + return m_pDesignView->isCopyAllowed(); +} + +bool OQueryViewSwitch::isPasteAllowed() const +{ + if(m_pTextView->IsVisible()) + return true; + return m_pDesignView->isPasteAllowed(); +} + +void OQueryViewSwitch::cut() +{ + if(m_pTextView->IsVisible()) + m_pTextView->cut(); + else + m_pDesignView->cut(); +} + +void OQueryViewSwitch::paste() +{ + if(m_pTextView->IsVisible()) + m_pTextView->paste(); + else + m_pDesignView->paste(); +} + +OQueryContainerWindow* OQueryViewSwitch::getContainer() const +{ + vcl::Window* pDesignParent = getDesignView() ? getDesignView()->GetParent() : nullptr; + return static_cast< OQueryContainerWindow* >( pDesignParent ); +} + +void OQueryViewSwitch::impl_forceSQLView() +{ + OAddTableDlg* pAddTabDialog( getAddTableDialog() ); + + // hide the "Add Table" dialog + m_bAddTableDialogWasVisible = pAddTabDialog != nullptr; + if (m_bAddTableDialogWasVisible) + pAddTabDialog->response(RET_CLOSE); + + // tell the views they're in/active + m_pDesignView->stopTimer(); + m_pTextView->getSqlEdit()->startTimer(); + + // set the most recent statement at the text view + m_pTextView->clear(); + m_pTextView->setStatement(static_cast<OQueryController&>(m_pDesignView->getController()).getStatement()); +} + +void OQueryViewSwitch::forceInitialView() +{ + OQueryController& rQueryController( static_cast< OQueryController& >( m_pDesignView->getController() ) ); + const bool bGraphicalDesign = rQueryController.isGraphicalDesign(); + if ( !bGraphicalDesign ) + impl_forceSQLView(); + else + { + // tell the text view it's inactive now + m_pTextView->getSqlEdit()->stopTimer(); + + // update the "Add Table" dialog + OAddTableDlg* pAddTabDialog( getAddTableDialog() ); + if ( pAddTabDialog ) + pAddTabDialog->Update(); + + // initialize the design view + m_pDesignView->initByFieldDescriptions( rQueryController.getFieldInformation() ); + + // tell the design view it's active now + m_pDesignView->startTimer(); + } + + impl_postViewSwitch( bGraphicalDesign, true ); +} + +bool OQueryViewSwitch::switchView( ::dbtools::SQLExceptionInfo* _pErrorInfo ) +{ + bool bRet = true; + bool bGraphicalDesign = static_cast<OQueryController&>(m_pDesignView->getController()).isGraphicalDesign(); + + if ( !bGraphicalDesign ) + { + impl_forceSQLView(); + } + else + { + // tell the text view it's inactive now + m_pTextView->getSqlEdit()->stopTimer(); + + // update the "Add Table" dialog + OAddTableDlg* pAddTabDialog( getAddTableDialog() ); + if ( pAddTabDialog ) + pAddTabDialog->Update(); + + // initialize the design view + bRet = m_pDesignView->initByParseIterator( _pErrorInfo ); + + // tell the design view it's active now + m_pDesignView->startTimer(); + } + + return impl_postViewSwitch( bGraphicalDesign, bRet ); +} + +bool OQueryViewSwitch::impl_postViewSwitch( const bool i_bGraphicalDesign, const bool i_bSuccess ) +{ + if ( i_bSuccess ) + { + m_pTextView->Show ( !i_bGraphicalDesign ); + m_pDesignView->Show ( i_bGraphicalDesign ); + OAddTableDlg* pAddTabDialog( getAddTableDialog() ); + if ( pAddTabDialog ) + if ( i_bGraphicalDesign && m_bAddTableDialogWasVisible ) + m_pDesignView->getController().runDialogAsync(); + + GrabFocus(); + } + + OQueryContainerWindow* pContainer = getContainer(); + if ( pContainer ) + pContainer->Resize(); + + m_pDesignView->getController().ClearUndoManager(); + m_pDesignView->getController().InvalidateAll(); + + return i_bSuccess; +} + +OAddTableDlg* OQueryViewSwitch::getAddTableDialog() +{ + if ( !m_pDesignView ) + return nullptr; + return m_pDesignView->getController().getAddTableDialog(); +} + +bool OQueryViewSwitch::isSlotEnabled(sal_Int32 _nSlotId) +{ + return m_pDesignView->isSlotEnabled(_nSlotId); +} + +void OQueryViewSwitch::setSlotEnabled(sal_Int32 _nSlotId, bool _bEnable) +{ + m_pDesignView->setSlotEnabled(_nSlotId,_bEnable); +} + +void OQueryViewSwitch::SaveUIConfig() +{ + if(m_pDesignView->IsVisible()) + m_pDesignView->SaveUIConfig(); +} + +void OQueryViewSwitch::SetPosSizePixel( Point _rPt,Size _rSize) +{ + m_pDesignView->SetPosSizePixel( _rPt,_rSize); + m_pDesignView->Resize(); + m_pTextView->SetPosSizePixel( _rPt,_rSize); +} + +Reference< XComponentContext > const & OQueryViewSwitch::getORB() const +{ + return m_pDesignView->getORB(); +} + +void OQueryViewSwitch::reset() +{ + m_pDesignView->reset(); + if ( !m_pDesignView->initByParseIterator( nullptr ) ) + return; + + switchView( nullptr ); +} + +void OQueryViewSwitch::setNoneVisbleRow(sal_Int32 _nRows) +{ + if(m_pDesignView) + m_pDesignView->setNoneVisbleRow(_nRows); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/SelectionBrowseBox.cxx b/dbaccess/source/ui/querydesign/SelectionBrowseBox.cxx new file mode 100644 index 000000000..98d782d8d --- /dev/null +++ b/dbaccess/source/ui/querydesign/SelectionBrowseBox.cxx @@ -0,0 +1,2695 @@ +/* -*- 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 "SelectionBrowseBox.hxx" +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <JoinExchange.hxx> +#include <QueryDesignView.hxx> +#include <querycontroller.hxx> +#include <sqlbison.hxx> +#include <QueryTableView.hxx> +#include <browserids.hxx> +#include <comphelper/stl_types.hxx> +#include <comphelper/string.hxx> +#include "TableFieldInfo.hxx" +#include <core_resource.hxx> +#include <strings.hrc> +#include <strings.hxx> +#include <helpids.h> +#include "QTableWindow.hxx" +#include <vcl/button.hxx> +#include <vcl/weld.hxx> +#include <vcl/settings.hxx> +#include "QueryDesignFieldUndoAct.hxx" +#include <sqlmessage.hxx> +#include <UITools.hxx> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <i18nlangtag/languagetag.hxx> +#include <vcl/treelistentry.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/svapp.hxx> + +using namespace ::svt; +using namespace ::dbaui; +using namespace ::connectivity; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::util; +using namespace ::com::sun::star::accessibility; + +#define DEFAULT_QUERY_COLS 20 +#define DEFAULT_SIZE GetTextWidth("0") * 30 +#define HANDLE_ID 0 +#define HANDLE_COLUMN_WIDTH 70 +#define SORT_COLUMN_NONE 0xFFFFFFFF + +namespace +{ + bool isFieldNameAsterisk(const OUString& _sFieldName ) + { + bool bAsterisk = _sFieldName.isEmpty() || _sFieldName.toChar() == '*'; + if ( !bAsterisk ) + { + sal_Int32 nTokenCount = comphelper::string::getTokenCount(_sFieldName, '.'); + if ( (nTokenCount == 2 && _sFieldName.getToken(1,'.')[0] == '*' ) + || (nTokenCount == 3 && _sFieldName.getToken(2,'.')[0] == '*' ) ) + { + bAsterisk = true; + } + } + return bAsterisk; + } + bool lcl_SupportsCoreSQLGrammar(const Reference< XConnection>& _xConnection) + { + bool bSupportsCoreGrammar = false; + if ( _xConnection.is() ) + { + try + { + Reference< XDatabaseMetaData > xMetaData = _xConnection->getMetaData(); + bSupportsCoreGrammar = xMetaData.is() && xMetaData->supportsCoreSQLGrammar(); + } + catch(Exception&) + { + } + } + return bSupportsCoreGrammar; + } +} + +OSelectionBrowseBox::OSelectionBrowseBox( vcl::Window* pParent ) + :EditBrowseBox( pParent,EditBrowseBoxFlags::NO_HANDLE_COLUMN_CONTENT, WB_3DLOOK, BrowserMode::COLUMNSELECTION | BrowserMode::KEEPHIGHLIGHT | BrowserMode::HIDESELECT | + BrowserMode::HIDECURSOR | BrowserMode::HLINES | BrowserMode::VLINES ) + ,m_nSeekRow(0) + ,m_nMaxColumns(0) + ,m_aFunctionStrings(DBA_RES(STR_QUERY_FUNCTIONS)) + ,m_nVisibleCount(0) + ,m_nLastSortColumn(SORT_COLUMN_NONE) + ,m_bOrderByUnRelated(true) + ,m_bGroupByUnRelated(true) + ,m_bStopTimer(false) + ,m_bWasEditing(false) + ,m_bDisableErrorBox(false) + ,m_bInUndoMode(false) +{ + SetHelpId(HID_CTL_QRYDGNCRIT); + + m_nMode = BrowserMode::COLUMNSELECTION | BrowserMode::HIDESELECT + | BrowserMode::KEEPHIGHLIGHT | BrowserMode::HIDECURSOR + | BrowserMode::HLINES | BrowserMode::VLINES + | BrowserMode::HEADERBAR_NEW ; + + m_pTextCell = VclPtr<Edit>::Create(&GetDataWindow(), 0); + m_pVisibleCell = VclPtr<CheckBoxControl>::Create(&GetDataWindow()); + m_pTableCell = VclPtr<ListBoxControl>::Create(&GetDataWindow()); + m_pFieldCell = VclPtr<ComboBoxControl>::Create(&GetDataWindow()); + m_pOrderCell = VclPtr<ListBoxControl>::Create(&GetDataWindow()); + m_pFunctionCell = VclPtr<ListBoxControl>::Create(&GetDataWindow()); + + m_pVisibleCell->SetHelpId(HID_QRYDGN_ROW_VISIBLE); + m_pTableCell->SetHelpId(HID_QRYDGN_ROW_TABLE); + m_pFieldCell->SetHelpId(HID_QRYDGN_ROW_FIELD); + weld::ComboBox& rOrderBox = m_pOrderCell->get_widget(); + m_pOrderCell->SetHelpId(HID_QRYDGN_ROW_ORDER); + m_pFunctionCell->SetHelpId(HID_QRYDGN_ROW_FUNCTION); + + // switch off triState of css::form::CheckBox + m_pVisibleCell->GetBox().EnableTriState( false ); + + vcl::Font aTitleFont = OutputDevice::GetDefaultFont( DefaultFontType::SANS_UNICODE,Window::GetSettings().GetLanguageTag().getLanguageType(),GetDefaultFontFlags::OnlyOne); + aTitleFont.SetFontSize(Size(0, 6)); + SetTitleFont(aTitleFont); + + const OUString aTxt(DBA_RES(STR_QUERY_SORTTEXT)); + for (sal_Int32 nIdx {0}; nIdx>=0;) + rOrderBox.append_text(aTxt.getToken(0, ';', nIdx)); + + m_bVisibleRow.insert(m_bVisibleRow.end(), BROW_ROW_CNT, true); + + m_bVisibleRow[BROW_FUNCTION_ROW] = false; // first hide + + m_timerInvalidate.SetTimeout(200); + m_timerInvalidate.SetInvokeHandler(LINK(this, OSelectionBrowseBox, OnInvalidateTimer)); + m_timerInvalidate.Start(); +} + +OSelectionBrowseBox::~OSelectionBrowseBox() +{ + disposeOnce(); +} + +void OSelectionBrowseBox::dispose() +{ + m_pTextCell.disposeAndClear(); + m_pVisibleCell.disposeAndClear(); + m_pFieldCell.disposeAndClear(); + m_pTableCell.disposeAndClear(); + m_pOrderCell.disposeAndClear(); + m_pFunctionCell.disposeAndClear(); + ::svt::EditBrowseBox::dispose(); +} + +void OSelectionBrowseBox::initialize() +{ + Reference< XConnection> xConnection = static_cast<OQueryController&>(getDesignView()->getController()).getConnection(); + if(xConnection.is()) + { + const IParseContext& rContext = static_cast<OQueryController&>(getDesignView()->getController()).getParser().getContext(); + const IParseContext::InternationalKeyCode eFunctions[] = { + IParseContext::InternationalKeyCode::Avg,IParseContext::InternationalKeyCode::Count,IParseContext::InternationalKeyCode::Max + ,IParseContext::InternationalKeyCode::Min,IParseContext::InternationalKeyCode::Sum + ,IParseContext::InternationalKeyCode::Every + ,IParseContext::InternationalKeyCode::Any + ,IParseContext::InternationalKeyCode::Some + ,IParseContext::InternationalKeyCode::StdDevPop + ,IParseContext::InternationalKeyCode::StdDevSamp + ,IParseContext::InternationalKeyCode::VarSamp + ,IParseContext::InternationalKeyCode::VarPop + ,IParseContext::InternationalKeyCode::Collect + ,IParseContext::InternationalKeyCode::Fusion + ,IParseContext::InternationalKeyCode::Intersection + }; + + OUString sGroup = m_aFunctionStrings.copy(m_aFunctionStrings.lastIndexOf(';')+1); + m_aFunctionStrings = m_aFunctionStrings.getToken(0, ';'); + + for (IParseContext::InternationalKeyCode eFunction : eFunctions) + { + m_aFunctionStrings += ";" + OStringToOUString(rContext.getIntlKeywordAscii(eFunction), RTL_TEXTENCODING_UTF8); + } + m_aFunctionStrings += ";" + sGroup; + + // Aggregate functions in general available only with Core SQL + // We slip in a few optionals one, too. + if ( lcl_SupportsCoreSQLGrammar(xConnection) ) + { + weld::ComboBox& rComboBox = m_pFunctionCell->get_widget(); + for (sal_Int32 nIdx {0}; nIdx>=0;) + rComboBox.append_text(m_aFunctionStrings.getToken(0, ';', nIdx)); + } + else // else only COUNT(*) and COUNT("table".*) + { + weld::ComboBox& rComboBox = m_pFunctionCell->get_widget(); + rComboBox.append_text(m_aFunctionStrings.getToken(0, ';')); + rComboBox.append_text(m_aFunctionStrings.getToken(2, ';')); // 2 -> COUNT + } + try + { + Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData(); + if ( xMetaData.is() ) + { + m_bOrderByUnRelated = xMetaData->supportsOrderByUnrelated(); + m_bGroupByUnRelated = xMetaData->supportsGroupByUnrelated(); + } + } + catch(Exception&) + { + } + } + + Init(); +} + +OQueryDesignView* OSelectionBrowseBox::getDesignView() +{ + OSL_ENSURE(static_cast<const OQueryDesignView*>(GetParent()),"Parent isn't an OQueryDesignView!"); + return static_cast<OQueryDesignView*>(GetParent()); +} + +OQueryDesignView* OSelectionBrowseBox::getDesignView() const +{ + OSL_ENSURE(static_cast<const OQueryDesignView*>(GetParent()),"Parent isn't an OQueryDesignView!"); + return static_cast<OQueryDesignView*>(GetParent()); +} + +namespace +{ + class OSelectionBrwBoxHeader : public ::svt::EditBrowserHeader + { + VclPtr<OSelectionBrowseBox> m_pBrowseBox; + protected: + virtual void Select() override; + public: + explicit OSelectionBrwBoxHeader(OSelectionBrowseBox* pParent); + virtual ~OSelectionBrwBoxHeader() override { disposeOnce(); } + virtual void dispose() override { m_pBrowseBox.clear(); ::svt::EditBrowserHeader::dispose(); } + }; + OSelectionBrwBoxHeader::OSelectionBrwBoxHeader(OSelectionBrowseBox* pParent) + : ::svt::EditBrowserHeader(pParent,WB_BUTTONSTYLE|WB_DRAG) + ,m_pBrowseBox(pParent) + { + } + + void OSelectionBrwBoxHeader::Select() + { + EditBrowserHeader::Select(); + m_pBrowseBox->GrabFocus(); + + BrowserMode nMode = m_pBrowseBox->GetMode(); + if ( 0 == m_pBrowseBox->GetSelectColumnCount() ) + { + m_pBrowseBox->DeactivateCell(); + // we are in the right mode if a row has been selected row + if ( nMode & BrowserMode::HIDESELECT ) + { + nMode &= ~BrowserMode::HIDESELECT; + nMode |= BrowserMode::MULTISELECTION; + m_pBrowseBox->SetMode( nMode ); + } + } + m_pBrowseBox->SelectColumnId( GetCurItemId() ); + m_pBrowseBox->DeactivateCell(); + } +} + +VclPtr<BrowserHeader> OSelectionBrowseBox::imp_CreateHeaderBar(BrowseBox* /*pParent*/) +{ + return VclPtr<OSelectionBrwBoxHeader>::Create(this); +} + +void OSelectionBrowseBox::ColumnMoved( sal_uInt16 nColId, bool _bCreateUndo ) +{ + EditBrowseBox::ColumnMoved( nColId ); + // swap the two columns + sal_uInt16 nNewPos = GetColumnPos( nColId ); + OTableFields& rFields = getFields(); + if ( rFields.size() > o3tl::make_unsigned(nNewPos-1) ) + { + sal_uInt16 nOldPos = 0; + bool bFoundElem = false; + for (auto const& field : rFields) + { + if (field->GetColumnId() == nColId) + { + bFoundElem = true; + break; + } + ++nOldPos; + } + + OSL_ENSURE( (nNewPos-1) != nOldPos && nOldPos < rFields.size(),"Old and new position are equal!"); + if (bFoundElem) + { + OTableFieldDescRef pOldEntry = rFields[nOldPos]; + rFields.erase(rFields.begin() + nOldPos); + rFields.insert(rFields.begin() + nNewPos - 1,pOldEntry); + + // create the undo action + if ( !m_bInUndoMode && _bCreateUndo ) + { + std::unique_ptr<OTabFieldMovedUndoAct> pUndoAct(new OTabFieldMovedUndoAct(this)); + pUndoAct->SetColumnPosition( nOldPos + 1); + pUndoAct->SetTabFieldDescr(pOldEntry); + + getDesignView()->getController().addUndoActionAndInvalidate(std::move(pUndoAct)); + } + } + } + else + OSL_FAIL("Invalid column id!"); +} + +void OSelectionBrowseBox::Init() +{ + + EditBrowseBox::Init(); + + // set the header bar + VclPtr<BrowserHeader> pNewHeaderBar = CreateHeaderBar(this); + pNewHeaderBar->SetMouseTransparent(false); + + SetHeaderBar(pNewHeaderBar); + SetMode(m_nMode); + + vcl::Font aFont( GetDataWindow().GetFont() ); + aFont.SetWeight( WEIGHT_NORMAL ); + GetDataWindow().SetFont( aFont ); + + Size aHeight; + const Control* pControls[] = { m_pTextCell,m_pVisibleCell,m_pTableCell,m_pFieldCell }; + + for (const Control* pControl : pControls) + { + const Size aTemp(pControl->GetOptimalSize()); + if ( aTemp.Height() > aHeight.Height() ) + aHeight.setHeight( aTemp.Height() ); + } + SetDataRowHeight(aHeight.Height()); + SetTitleLines(1); + // get number of visible rows + for(long i=0;i<BROW_ROW_CNT;i++) + { + if(m_bVisibleRow[i]) + m_nVisibleCount++; + } + RowInserted(0, m_nVisibleCount, false); + try + { + Reference< XConnection> xConnection = static_cast<OQueryController&>(getDesignView()->getController()).getConnection(); + if(xConnection.is()) + { + Reference< XDatabaseMetaData > xMetaData = xConnection->getMetaData(); + m_nMaxColumns = xMetaData.is() ? xMetaData->getMaxColumnsInSelect() : 0; + + } + else + m_nMaxColumns = 0; + } + catch(const SQLException&) + { + OSL_FAIL("Caught Exception when asking for database metadata options!"); + m_nMaxColumns = 0; + } +} + +void OSelectionBrowseBox::PreFill() +{ + SetUpdateMode(false); + + if (GetCurRow() != 0) + GoToRow(0); + + static_cast< OQueryController& >( getDesignView()->getController() ).clearFields(); + + DeactivateCell(); + + RemoveColumns(); + InsertHandleColumn( HANDLE_COLUMN_WIDTH ); + SetUpdateMode(true); +} + +void OSelectionBrowseBox::ClearAll() +{ + SetUpdateMode(false); + + OTableFields::const_reverse_iterator aIter = getFields().rbegin(); + for ( ;aIter != getFields().rend(); ++aIter ) + { + if ( !(*aIter)->IsEmpty() ) + { + RemoveField( (*aIter)->GetColumnId() ); + aIter = getFields().rbegin(); + } + } + m_nLastSortColumn = SORT_COLUMN_NONE; + SetUpdateMode(true); +} + +void OSelectionBrowseBox::SetReadOnly(bool bRO) +{ + if (bRO) + { + DeactivateCell(); + m_nMode &= ~BrowserMode::HIDECURSOR; + SetMode(m_nMode); + } + else + { + m_nMode |= BrowserMode::HIDECURSOR; + SetMode(m_nMode); + ActivateCell(); + } +} + +CellController* OSelectionBrowseBox::GetController(long nRow, sal_uInt16 nColId) +{ + if ( nColId > getFields().size() ) + return nullptr; + OTableFieldDescRef pEntry = getFields()[nColId-1]; + OSL_ENSURE(pEntry.is(), "OSelectionBrowseBox::GetController : invalid FieldDescription !"); + + if (!pEntry.is()) + return nullptr; + + if (static_cast<OQueryController&>(getDesignView()->getController()).isReadOnly()) + return nullptr; + + long nCellIndex = GetRealRow(nRow); + switch (nCellIndex) + { + case BROW_FIELD_ROW: + return new ComboBoxCellController(m_pFieldCell); + case BROW_TABLE_ROW: + return new ListBoxCellController(m_pTableCell); + case BROW_VIS_ROW: + return new CheckBoxCellController(m_pVisibleCell); + case BROW_ORDER_ROW: + return new ListBoxCellController(m_pOrderCell); + case BROW_FUNCTION_ROW: + return new ListBoxCellController(m_pFunctionCell); + default: + return new EditCellController(m_pTextCell); + } +} + +void OSelectionBrowseBox::InitController(CellControllerRef& /*rController*/, long nRow, sal_uInt16 nColId) +{ + OSL_ENSURE(nColId != BROWSER_INVALIDID,"An Invalid Id was set!"); + if ( nColId == BROWSER_INVALIDID ) + return; + sal_uInt16 nPos = GetColumnPos(nColId); + if ( nPos == 0 || nPos == BROWSER_INVALIDID || nPos > getFields().size() ) + return; + OTableFieldDescRef pEntry = getFields()[nPos-1]; + OSL_ENSURE(pEntry.is(), "OSelectionBrowseBox::InitController : invalid FieldDescription !"); + long nCellIndex = GetRealRow(nRow); + + switch (nCellIndex) + { + case BROW_FIELD_ROW: + { + weld::ComboBox& rComboBox = m_pFieldCell->get_widget(); + rComboBox.clear(); + rComboBox.set_entry_text(OUString()); + + OUString aField(pEntry->GetField()); + OUString aTable(pEntry->GetAlias()); + + getDesignView()->fillValidFields(aTable, rComboBox); + + // replace with alias.* + if (aField.trim() == "*") + { + aField = aTable + ".*"; + } + rComboBox.set_entry_text(aField); + } break; + case BROW_TABLE_ROW: + { + weld::ComboBox& rComboBox = m_pTableCell->get_widget(); + rComboBox.clear(); + enableControl(pEntry, m_pTableCell); + if ( !pEntry->isCondition() ) + { + for (auto const& tabWin : getDesignView()->getTableView()->GetTabWinMap()) + rComboBox.append_text(static_cast<OQueryTableWindow*>(tabWin.second.get())->GetAliasName()); + + rComboBox.insert_text(0, DBA_RES(STR_QUERY_NOTABLE)); + if (!pEntry->GetAlias().isEmpty()) + rComboBox.set_active_text(pEntry->GetAlias()); + else + rComboBox.set_active_text(DBA_RES(STR_QUERY_NOTABLE)); + } + } break; + case BROW_VIS_ROW: + { + m_pVisibleCell->GetBox().Check(pEntry->IsVisible()); + m_pVisibleCell->GetBox().SaveValue(); + + enableControl(pEntry,m_pTextCell); + + if(!pEntry->IsVisible() && pEntry->GetOrderDir() != ORDER_NONE && !m_bOrderByUnRelated) + { + // a column has to visible in order to show up in ORDER BY + pEntry->SetVisible(); + m_pVisibleCell->GetBox().Check(pEntry->IsVisible()); + m_pVisibleCell->GetBox().SaveValue(); + m_pVisibleCell->GetBox().Disable(); + m_pVisibleCell->GetBox().EnableInput(false); + OUString aMessage(DBA_RES(STR_QRY_ORDERBY_UNRELATED)); + OQueryDesignView* paDView = getDesignView(); + std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(paDView ? paDView->GetFrameWeld() : nullptr, + VclMessageType::Info, VclButtonsType::Ok, + aMessage)); + xInfoBox->run(); + } + } break; + case BROW_ORDER_ROW: + { + weld::ComboBox& rComboBox = m_pOrderCell->get_widget(); + rComboBox.set_active( + sal::static_int_cast< sal_uInt16 >(pEntry->GetOrderDir())); + enableControl(pEntry,m_pOrderCell); + break; + } + case BROW_COLUMNALIAS_ROW: + setTextCellContext(pEntry,pEntry->GetFieldAlias(),HID_QRYDGN_ROW_ALIAS); + break; + case BROW_FUNCTION_ROW: + setFunctionCell(pEntry); + break; + default: + { + sal_uInt16 nIdx = sal_uInt16(nCellIndex - BROW_CRIT1_ROW); + setTextCellContext(pEntry,pEntry->GetCriteria( nIdx ),HID_QRYDGN_ROW_CRIT); + } + } + Controller()->ClearModified(); +} + +void OSelectionBrowseBox::notifyTableFieldChanged(const OUString& _sOldAlias, const OUString& _sAlias, bool& _bListAction, sal_uInt16 _nColumnId) +{ + appendUndoAction(_sOldAlias,_sAlias,BROW_TABLE_ROW,_bListAction); + if ( m_bVisibleRow[BROW_TABLE_ROW] ) + RowModified(GetBrowseRow(BROW_TABLE_ROW), _nColumnId); +} + +void OSelectionBrowseBox::notifyFunctionFieldChanged(const OUString& _sOldFunctionName, const OUString& _sFunctionName, bool& _bListAction, sal_uInt16 _nColumnId) +{ + appendUndoAction(_sOldFunctionName,_sFunctionName,BROW_FUNCTION_ROW,_bListAction); + if ( !m_bVisibleRow[BROW_FUNCTION_ROW] ) + SetRowVisible(BROW_FUNCTION_ROW, true); + RowModified(GetBrowseRow(BROW_FUNCTION_ROW), _nColumnId); +} + +void OSelectionBrowseBox::clearEntryFunctionField(const OUString& _sFieldName,OTableFieldDescRef const & _pEntry, bool& _bListAction,sal_uInt16 _nColumnId) +{ + if ( !(isFieldNameAsterisk( _sFieldName ) && (!_pEntry->isNoneFunction() || _pEntry->IsGroupBy())) ) + return; + + OUString sFunctionName; + GetFunctionName(SQL_TOKEN_COUNT,sFunctionName); + OUString sOldLocalizedFunctionName = _pEntry->GetFunction(); + if ( sOldLocalizedFunctionName != sFunctionName || _pEntry->IsGroupBy() ) + { + // append undo action for the function field + _pEntry->SetFunctionType(FKT_NONE); + _pEntry->SetFunction(OUString()); + _pEntry->SetGroupBy(false); + notifyFunctionFieldChanged(sOldLocalizedFunctionName,_pEntry->GetFunction(),_bListAction,_nColumnId); + } +} + +bool OSelectionBrowseBox::fillColumnRef(const OSQLParseNode* _pColumnRef, const Reference< XConnection >& _rxConnection, OTableFieldDescRef const & _pEntry, bool& _bListAction ) +{ + OSL_ENSURE(_pColumnRef,"No valid parsenode!"); + OUString sColumnName,sTableRange; + OSQLParseTreeIterator::getColumnRange(_pColumnRef,_rxConnection,sColumnName,sTableRange); + return fillColumnRef(sColumnName,sTableRange,_rxConnection->getMetaData(),_pEntry,_bListAction); +} + +bool OSelectionBrowseBox::fillColumnRef(const OUString& _sColumnName, const OUString& _sTableRange, const Reference<XDatabaseMetaData>& _xMetaData, OTableFieldDescRef const & _pEntry, bool& _bListAction) +{ + bool bError = false; + ::comphelper::UStringMixEqual bCase(_xMetaData->supportsMixedCaseQuotedIdentifiers()); + // check if the table name is the same + if ( !_sTableRange.isEmpty() && (bCase(_pEntry->GetTable(),_sTableRange) || bCase(_pEntry->GetAlias(),_sTableRange)) ) + { // a table was already inserted and the tables contains that column name + + if ( !_pEntry->GetTabWindow() ) + { // fill tab window + OUString sOldAlias = _pEntry->GetAlias(); + if ( !fillEntryTable(_pEntry,_pEntry->GetTable()) ) + fillEntryTable(_pEntry,_pEntry->GetAlias()); // only when the first failed + if ( !bCase(sOldAlias,_pEntry->GetAlias()) ) + notifyTableFieldChanged(sOldAlias,_pEntry->GetAlias(),_bListAction,GetCurColumnId()); + } + } + // check if the table window + OQueryTableWindow* pEntryTab = static_cast<OQueryTableWindow*>(_pEntry->GetTabWindow()); + if ( !pEntryTab ) // no table found with this name so we have to travel through all tables + { + sal_uInt16 nTabCount = 0; + if ( !static_cast<OQueryTableView*>(getDesignView()->getTableView())->FindTableFromField(_sColumnName,_pEntry,nTabCount) ) // error occurred: column not in table window + { + OUString sErrorMsg(DBA_RES(RID_STR_FIELD_DOESNT_EXIST)); + sErrorMsg = sErrorMsg.replaceFirst("$name$",_sColumnName); + OSQLErrorBox aWarning(GetFrameWeld(), sErrorMsg); + aWarning.run(); + bError = true; + } + else + { + pEntryTab = static_cast<OQueryTableWindow*>(_pEntry->GetTabWindow()); + notifyTableFieldChanged(OUString(),_pEntry->GetAlias(),_bListAction,GetCurColumnId()); + } + } + if ( pEntryTab ) // here we got a valid table + _pEntry->SetField(_sColumnName); + + return bError; +} + +bool OSelectionBrowseBox::saveField(OUString& _sFieldName ,OTableFieldDescRef const & _pEntry, bool& _bListAction) +{ + bool bError = false; + + OQueryController& rController = static_cast<OQueryController&>(getDesignView()->getController()); + + // first look if the name can be found in our tables + sal_uInt16 nTabCount = 0; + OUString sOldAlias = _pEntry->GetAlias(); + if ( static_cast<OQueryTableView*>(getDesignView()->getTableView())->FindTableFromField(_sFieldName,_pEntry,nTabCount) ) + { + // append undo action for the alias name + _pEntry->SetField(_sFieldName); + notifyTableFieldChanged(sOldAlias,_pEntry->GetAlias(),_bListAction,GetCurColumnId()); + clearEntryFunctionField(_sFieldName,_pEntry,_bListAction,_pEntry->GetColumnId()); + return bError; + } + + Reference<XConnection> xConnection( rController.getConnection() ); + Reference< XDatabaseMetaData > xMetaData; + if ( xConnection.is() ) + xMetaData = xConnection->getMetaData(); + OSL_ENSURE( xMetaData.is(), "OSelectionBrowseBox::saveField: invalid connection/meta data!" ); + if ( !xMetaData.is() ) + return true; + + OUString sErrorMsg; + // second test if the name can be set as select columns in a pseudo statement + // we have to look which entries we should quote + + const OUString sFieldAlias = _pEntry->GetFieldAlias(); + ::connectivity::OSQLParser& rParser( rController.getParser() ); + { + // automatically add parentheses around subqueries + OUString devnull; + std::unique_ptr<OSQLParseNode> pParseNode = rParser.parseTree( devnull, _sFieldName, true ); + if (pParseNode == nullptr) + pParseNode = rParser.parseTree( devnull, _sFieldName ); + if (pParseNode != nullptr && SQL_ISRULE(pParseNode, select_statement)) + _sFieldName = "(" + _sFieldName + ")"; + } + + std::unique_ptr<OSQLParseNode> pParseNode; + { + // 4 passes in trying to interpret the field name + // - don't quote the field name, parse internationally + // - don't quote the field name, parse en-US + // - quote the field name, parse internationally + // - quote the field name, parse en-US + size_t nPass = 4; + OUString sQuotedFullFieldName(::dbtools::quoteName( xMetaData->getIdentifierQuoteString(), _sFieldName )); + OUString sFullFieldName(_sFieldName); + + if ( _pEntry->isAggregateFunction() ) + { + OSL_ENSURE(!_pEntry->GetFunction().isEmpty(),"No empty Function name allowed here! ;-("); + sQuotedFullFieldName = _pEntry->GetFunction() + "(" + sQuotedFullFieldName + ")"; + sFullFieldName = _pEntry->GetFunction() + "(" + sFullFieldName + ")"; + } + + do + { + bool bQuote = ( nPass <= 2 ); + bool bInternational = ( nPass % 2 ) == 0; + + OUString sSql {"SELECT "}; + if ( bQuote ) + sSql += sQuotedFullFieldName; + else + sSql += sFullFieldName; + + if ( !sFieldAlias.isEmpty() ) + { // always quote the alias name: there cannot be a function in it + sSql += " " + ::dbtools::quoteName( xMetaData->getIdentifierQuoteString(), sFieldAlias ); + } + sSql += " FROM x"; + + pParseNode = rParser.parseTree( sErrorMsg, sSql, bInternational ); + } + while ( ( pParseNode == nullptr ) && ( --nPass > 0 ) ); + } + + if ( pParseNode == nullptr ) + { + // something different which we have to check + OUString sErrorMessage( DBA_RES( STR_QRY_COLUMN_NOT_FOUND ) ); + sErrorMessage = sErrorMessage.replaceFirst("$name$",_sFieldName); + OSQLErrorBox aWarning(GetFrameWeld(), sErrorMessage); + aWarning.run(); + + return true; + } + + // we got a valid select column + // find what type of column has be inserted + ::connectivity::OSQLParseNode* pSelection = pParseNode->getChild(2); + if ( SQL_ISRULE(pSelection,selection) ) // we found the asterisk + { + _pEntry->SetField(_sFieldName); + clearEntryFunctionField(_sFieldName,_pEntry,_bListAction,_pEntry->GetColumnId()); + } + else // travel through the select column parse node + { + OTableFieldDescRef aSelEntry = _pEntry; + sal_uInt16 nColumnId = aSelEntry->GetColumnId(); + + sal_uInt32 nCount = pSelection->count(); + for (sal_uInt32 i = 0; i < nCount; ++i) + { + if ( i > 0 ) // may we have to append more than one field + { + sal_uInt16 nColumnPosition; + aSelEntry = FindFirstFreeCol(nColumnPosition); + if ( !aSelEntry.is() ) + { + AppendNewCol(); + aSelEntry = FindFirstFreeCol(nColumnPosition); + } + ++nColumnPosition; + nColumnId = GetColumnId(nColumnPosition); + } + + ::connectivity::OSQLParseNode* pChild = pSelection->getChild( i ); + OSL_ENSURE(SQL_ISRULE(pChild,derived_column), "No derived column found!"); + // get the column alias + OUString sColumnAlias = OSQLParseTreeIterator::getColumnAlias(pChild); + if ( !sColumnAlias.isEmpty() ) // we found an as clause + { + OUString aSelectionAlias = aSelEntry->GetFieldAlias(); + aSelEntry->SetFieldAlias( sColumnAlias ); + // append undo + appendUndoAction(aSelectionAlias,aSelEntry->GetFieldAlias(),BROW_COLUMNALIAS_ROW,_bListAction); + if ( m_bVisibleRow[BROW_COLUMNALIAS_ROW] ) + RowModified(GetBrowseRow(BROW_COLUMNALIAS_ROW), nColumnId); + } + + ::connectivity::OSQLParseNode* pColumnRef = pChild->getChild(0); + if ( + pColumnRef->getKnownRuleID() != OSQLParseNode::subquery && + pColumnRef->count() == 3 && + SQL_ISPUNCTUATION(pColumnRef->getChild(0),"(") && + SQL_ISPUNCTUATION(pColumnRef->getChild(2),")") + ) + pColumnRef = pColumnRef->getChild(1); + + if ( SQL_ISRULE(pColumnRef,column_ref) ) // we found a valid column name or more column names + { + // look if we can find the corresponding table + bError = fillColumnRef( pColumnRef, xConnection, aSelEntry, _bListAction ); + + // we found a simple column so we must clear the function fields but only when the column name is '*' + // and the function is different to count + clearEntryFunctionField(_sFieldName,aSelEntry,_bListAction,nColumnId); + } + // do we have an aggregate function and only a function? + else if ( SQL_ISRULE(pColumnRef,general_set_fct) ) + { + OUString sLocalizedFunctionName; + if ( GetFunctionName(pColumnRef->getChild(0)->getTokenID(),sLocalizedFunctionName) ) + { + OUString sOldLocalizedFunctionName = aSelEntry->GetFunction(); + aSelEntry->SetFunction(sLocalizedFunctionName); + sal_uInt32 nFunCount = pColumnRef->count() - 1; + sal_Int32 nFunctionType = FKT_AGGREGATE; + bool bQuote = false; + // may be there exists only one parameter which is a column, fill all information into our fields + if ( nFunCount == 4 && SQL_ISRULE(pColumnRef->getChild(3),column_ref) ) + bError = fillColumnRef( pColumnRef->getChild(3), xConnection, aSelEntry, _bListAction ); + else if ( nFunCount == 3 ) // we have a COUNT(*) here, so take the first table + bError = fillColumnRef( "*", OUString(), xMetaData, aSelEntry, _bListAction ); + else + { + nFunctionType |= FKT_NUMERIC; + bQuote = true; + aSelEntry->SetDataType(DataType::DOUBLE); + aSelEntry->SetFieldType(TAB_NORMAL_FIELD); + } + + // now parse the parameters + OUString sParameters; + for(sal_uInt32 function = 2; function < nFunCount; ++function) // we only want to parse the parameters of the function + pColumnRef->getChild(function)->parseNodeToStr( sParameters, xConnection, &rParser.getContext(), true, bQuote ); + + aSelEntry->SetFunctionType(nFunctionType); + aSelEntry->SetField(sParameters); + if ( aSelEntry->IsGroupBy() ) + { + sOldLocalizedFunctionName = m_aFunctionStrings.copy(m_aFunctionStrings.lastIndexOf(';')+1); + aSelEntry->SetGroupBy(false); + } + + // append undo action + notifyFunctionFieldChanged(sOldLocalizedFunctionName,sLocalizedFunctionName,_bListAction, nColumnId); + } + else + OSL_FAIL("Unsupported function inserted!"); + + } + else + { + // so we first clear the function field + clearEntryFunctionField(_sFieldName,aSelEntry,_bListAction,nColumnId); + OUString sFunction; + pColumnRef->parseNodeToStr( sFunction, + xConnection, + &rController.getParser().getContext(), + true); // quote is to true because we need quoted elements inside the function + + getDesignView()->fillFunctionInfo(pColumnRef,sFunction,aSelEntry); + + if( SQL_ISRULEOR3(pColumnRef, position_exp, extract_exp, fold) || + SQL_ISRULEOR3(pColumnRef, char_substring_fct, length_exp, char_value_fct) ) + // a calculation has been found ( can be calc and function ) + { + // now parse the whole statement + sal_uInt32 nFunCount = pColumnRef->count(); + OUString sParameters; + for(sal_uInt32 function = 0; function < nFunCount; ++function) + pColumnRef->getChild(function)->parseNodeToStr( sParameters, xConnection, &rParser.getContext(), true ); + + sOldAlias = aSelEntry->GetAlias(); + sal_Int32 nNewFunctionType = aSelEntry->GetFunctionType() | FKT_NUMERIC | FKT_OTHER; + aSelEntry->SetFunctionType(nNewFunctionType); + aSelEntry->SetField(sParameters); + } + else + { + aSelEntry->SetFieldAlias(sColumnAlias); + if ( SQL_ISRULE(pColumnRef,set_fct_spec) ) + aSelEntry->SetFunctionType(/*FKT_NUMERIC | */FKT_OTHER); + else + aSelEntry->SetFunctionType(FKT_NUMERIC | FKT_OTHER); + } + + aSelEntry->SetAlias(OUString()); + notifyTableFieldChanged(sOldAlias,aSelEntry->GetAlias(),_bListAction, nColumnId); + } + + if ( i > 0 && !InsertField(aSelEntry,BROWSER_INVALIDID,true,false).is() ) // may we have to append more than one field + { // the field could not be inserted + OUString sErrorMessage( DBA_RES( RID_STR_FIELD_DOESNT_EXIST ) ); + sErrorMessage = sErrorMessage.replaceFirst("$name$",aSelEntry->GetField()); + OSQLErrorBox aWarning(GetFrameWeld(), sErrorMessage); + aWarning.run(); + bError = true; + } + } + } + + return bError; +} + +bool OSelectionBrowseBox::SaveModified() +{ + OQueryController& rController = static_cast<OQueryController&>(getDesignView()->getController()); + OTableFieldDescRef pEntry; + sal_uInt16 nCurrentColumnPos = GetColumnPos(GetCurColumnId()); + if(getFields().size() > o3tl::make_unsigned(nCurrentColumnPos - 1)) + pEntry = getEntry(nCurrentColumnPos - 1); + + bool bWasEmpty = pEntry.is() && pEntry->IsEmpty(); + bool bError = false; + bool bListAction = false; + + if (pEntry.is() && Controller().is() && Controller()->IsModified()) + { + // for the Undo-action + OUString strOldCellContents,sNewValue; + long nRow = GetRealRow(GetCurRow()); + bool bAppendRow = false; + switch (nRow) + { + case BROW_VIS_ROW: + { + bool bOldValue = m_pVisibleCell->GetBox().GetSavedValue() != TRISTATE_FALSE; + strOldCellContents = bOldValue ? OUStringLiteral("1") : OUStringLiteral("0"); + sNewValue = !bOldValue ? OUStringLiteral("1") : OUStringLiteral("0"); + } + if((m_bOrderByUnRelated || pEntry->GetOrderDir() == ORDER_NONE) && + (m_bGroupByUnRelated || !pEntry->IsGroupBy())) + { + pEntry->SetVisible(m_pVisibleCell->GetBox().IsChecked()); + } + else + { + pEntry->SetVisible(); + m_pVisibleCell->GetBox().Check(); + } + break; + + case BROW_FIELD_ROW: + { + weld::ComboBox& rComboBox = m_pFieldCell->get_widget(); + OUString aFieldName(rComboBox.get_active_text()); + try + { + if (aFieldName.isEmpty()) + { + OTableFieldDescRef pNewEntry = new OTableFieldDesc(); + pNewEntry->SetColumnId( pEntry->GetColumnId() ); + std::replace(getFields().begin(),getFields().end(),pEntry,pNewEntry); + sal_uInt16 nCol = GetCurColumnId(); + for (int i = 0; i < m_nVisibleCount; i++) // redraw column + RowModified(i,nCol); + } + else + { + strOldCellContents = pEntry->GetField(); + bListAction = true; + if ( !m_bInUndoMode ) + rController.GetUndoManager().EnterListAction(OUString(),OUString(),0,ViewShellId(-1)); + + sal_Int32 nPos = rComboBox.find_text(aFieldName); + OUString aAliasName = pEntry->GetAlias(); + if ( nPos != -1 && aAliasName.isEmpty() && aFieldName.indexOf('.') >= 0 ) + { // special case, we have a table field so we must cut the table name + OUString sTableAlias = aFieldName.getToken(0,'.'); + pEntry->SetAlias(sTableAlias); + OUString sColumnName = aFieldName.copy(sTableAlias.getLength()+1); + const Reference<XConnection>& xConnection = rController.getConnection(); + if ( !xConnection.is() ) + return false; + bError = fillColumnRef( sColumnName, sTableAlias, xConnection->getMetaData(), pEntry, bListAction ); + } + else + bError = true; + + if ( bError ) + bError = saveField(aFieldName,pEntry,bListAction); + } + } + catch(Exception&) + { + bError = true; + } + if ( bError ) + { + sNewValue = aFieldName; + if ( !m_bInUndoMode ) + static_cast<OQueryController&>(getDesignView()->getController()).GetUndoManager().LeaveListAction(); + bListAction = false; + } + else + sNewValue = pEntry->GetField(); + rController.InvalidateFeature( ID_BROWSER_QUERY_EXECUTE ); + } + break; + + case BROW_TABLE_ROW: + { + weld::ComboBox& rComboBox = m_pTableCell->get_widget(); + OUString aAliasName = rComboBox.get_active_text(); + strOldCellContents = pEntry->GetAlias(); + if (rComboBox.get_active() != 0) + { + pEntry->SetAlias(aAliasName); + // we have to set the table name as well as the table window + OJoinTableView::OTableWindowMap& rTabWinList = getDesignView()->getTableView()->GetTabWinMap(); + OJoinTableView::OTableWindowMap::const_iterator aIter = rTabWinList.find(aAliasName); + if(aIter != rTabWinList.end()) + { + OQueryTableWindow* pEntryTab = static_cast<OQueryTableWindow*>(aIter->second.get()); + if (pEntryTab) + { + pEntry->SetTable(pEntryTab->GetTableName()); + pEntry->SetTabWindow(pEntryTab); + } + } + } + else + { + pEntry->SetAlias(OUString()); + pEntry->SetTable(OUString()); + pEntry->SetTabWindow(nullptr); + } + sNewValue = pEntry->GetAlias(); + + } break; + + case BROW_ORDER_ROW: + { + strOldCellContents = OUString::number(static_cast<sal_uInt16>(pEntry->GetOrderDir())); + weld::ComboBox& rComboBox = m_pOrderCell->get_widget(); + sal_Int32 nIdx = rComboBox.get_active(); + if (nIdx == -1) + nIdx = 0; + pEntry->SetOrderDir(EOrderDir(nIdx)); + if(!m_bOrderByUnRelated) + { + pEntry->SetVisible(); + m_pVisibleCell->GetBox().Check(); + RowModified(GetBrowseRow(BROW_VIS_ROW), GetCurColumnId()); + } + sNewValue = OUString::number(static_cast<sal_uInt16>(pEntry->GetOrderDir())); + } break; + + case BROW_COLUMNALIAS_ROW: + strOldCellContents = pEntry->GetFieldAlias(); + pEntry->SetFieldAlias(m_pTextCell->GetText()); + sNewValue = pEntry->GetFieldAlias(); + break; + case BROW_FUNCTION_ROW: + { + strOldCellContents = pEntry->GetFunction(); + weld::ComboBox& rComboBox = m_pFunctionCell->get_widget(); + sal_Int32 nPos = rComboBox.get_active(); + // these functions are only available in CORE + OUString sFunctionName = rComboBox.get_text(nPos); + OUString sGroupFunctionName = m_aFunctionStrings.copy(m_aFunctionStrings.lastIndexOf(';')+1); + bool bGroupBy = false; + if ( sGroupFunctionName == sFunctionName ) // check if the function name is GROUP + { + bGroupBy = true; + + if ( !m_bGroupByUnRelated && !pEntry->IsVisible() ) + { + // we have to change the visible flag, so we must append also an undo action + pEntry->SetVisible(); + m_pVisibleCell->GetBox().Check(); + appendUndoAction("0","1",BROW_VIS_ROW,bListAction); + RowModified(GetBrowseRow(BROW_VIS_ROW), GetCurColumnId()); + } + + pEntry->SetFunction(OUString()); + pEntry->SetFunctionType(pEntry->GetFunctionType() & ~FKT_AGGREGATE ); + } + else if ( nPos ) // we found an aggregate function + { + pEntry->SetFunctionType(pEntry->GetFunctionType() | FKT_AGGREGATE ); + pEntry->SetFunction(sFunctionName); + } + else + { + sFunctionName.clear(); + pEntry->SetFunction(OUString()); + pEntry->SetFunctionType(pEntry->GetFunctionType() & ~FKT_AGGREGATE ); + } + + pEntry->SetGroupBy(bGroupBy); + + sNewValue = sFunctionName; + } + break; + default: + { + Reference< XConnection> xConnection = static_cast<OQueryController&>(getDesignView()->getController()).getConnection(); + if(!xConnection.is()) + break; + + sal_uInt16 nIdx = sal_uInt16(nRow - BROW_CRIT1_ROW); + OUString aText = comphelper::string::stripStart(m_pTextCell->GetText(), ' '); + + OUString aCrit; + if(!aText.isEmpty()) + { + OUString aErrorMsg; + Reference<XPropertySet> xColumn; + std::unique_ptr<OSQLParseNode> pParseNode = getDesignView()->getPredicateTreeFromEntry(pEntry,aText,aErrorMsg,xColumn); + + if (pParseNode) + { + pParseNode->parseNodeToPredicateStr(aCrit, + xConnection, + static_cast<OQueryController&>(getDesignView()->getController()).getNumberFormatter(), + xColumn, + pEntry->GetAlias(), + getDesignView()->getLocale(), + getDesignView()->getDecimalSeparator(), + &(static_cast<OQueryController&>(getDesignView()->getController()).getParser().getContext())); + } + else + { + if(xColumn.is()) + { + sal_Int32 nType = 0; + xColumn->getPropertyValue(PROPERTY_TYPE) >>= nType; + switch(nType) + { + case DataType::CHAR: + case DataType::VARCHAR: + case DataType::LONGVARCHAR: + case DataType::CLOB: + if(!aText.startsWith("'") || !aText.endsWith("'")) + { + aText = aText.replaceAll("'", "''"); + aText = "'" + aText + "'"; + } + break; + default: + ; + } + ::connectivity::OSQLParser& rParser = static_cast<OQueryController&>(getDesignView()->getController()).getParser(); + pParseNode = rParser.predicateTree(aErrorMsg, + aText, + static_cast<OQueryController&>(getDesignView()->getController()).getNumberFormatter(), + xColumn); + if (pParseNode) + { + pParseNode->parseNodeToPredicateStr(aCrit, + xConnection, + static_cast<OQueryController&>(getDesignView()->getController()).getNumberFormatter(), + xColumn, + pEntry->GetAlias(), + getDesignView()->getLocale(), + getDesignView()->getDecimalSeparator(), + &(static_cast<OQueryController&>(getDesignView()->getController()).getParser().getContext())); + } + else + { + if ( !m_bDisableErrorBox ) + { + OSQLWarningBox aWarning(GetFrameWeld(), aErrorMsg); + aWarning.run(); + } + bError = true; + } + } + else + { + if ( !m_bDisableErrorBox ) + { + OSQLWarningBox aWarning(GetFrameWeld(), aErrorMsg); + aWarning.run(); + } + bError = true; + } + } + } + strOldCellContents = pEntry->GetCriteria(nIdx); + pEntry->SetCriteria(nIdx, aCrit); + sNewValue = pEntry->GetCriteria(nIdx); + if(!aCrit.isEmpty() && nRow >= (GetRowCount()-1)) + bAppendRow = true; + } + } + if( !bError && Controller().is() ) + Controller()->ClearModified(); + + RowModified(GetCurRow(), GetCurColumnId()); + + if ( bAppendRow ) + { + RowInserted( GetRowCount()-1 ); + m_bVisibleRow.push_back(true); + ++m_nVisibleCount; + } + + if(!bError) + { + // and now the undo-action for the total + appendUndoAction(strOldCellContents,sNewValue,nRow); + + } + } + + // did I store data in a FieldDescription which was empty before and which is not empty anymore after the changes? + if ( pEntry.is() && bWasEmpty && !pEntry->IsEmpty() && !bError ) + { + // Default to visible + pEntry->SetVisible(); + appendUndoAction("0","1",BROW_VIS_ROW,bListAction); + RowModified(BROW_VIS_ROW, GetCurColumnId()); + + // if required add empty columns + sal_uInt16 nDummy; + CheckFreeColumns(nDummy); + } + + if ( bListAction && !m_bInUndoMode ) + static_cast<OQueryController&>(getDesignView()->getController()).GetUndoManager().LeaveListAction(); + + return pEntry != nullptr && !bError; +} + +bool OSelectionBrowseBox::SeekRow(long nRow) +{ + m_nSeekRow = nRow; + return nRow < m_nVisibleCount; +} + +void OSelectionBrowseBox::PaintCell(OutputDevice& rDev, const tools::Rectangle& rRect, sal_uInt16 nColumnId) const +{ + rDev.SetClipRegion(vcl::Region(rRect)); + + OTableFieldDescRef pEntry; + sal_uInt16 nPos = GetColumnPos(nColumnId); + if(getFields().size() > o3tl::make_unsigned(nPos - 1)) + pEntry = getFields()[nPos - 1]; + + if (!pEntry.is()) + return; + + long nRow = GetRealRow(m_nSeekRow); + if (nRow == BROW_VIS_ROW) + PaintTristate(rRect, pEntry->IsVisible() ? TRISTATE_TRUE : TRISTATE_FALSE); + else + rDev.DrawText(rRect, GetCellText(nRow, nColumnId),DrawTextFlags::VCenter); + + rDev.SetClipRegion( ); +} + +void OSelectionBrowseBox::PaintStatusCell(OutputDevice& rDev, const tools::Rectangle& rRect) const +{ + tools::Rectangle aRect(rRect); + aRect.TopLeft().AdjustY( -2 ); + OUString aLabel(DBA_RES(STR_QUERY_HANDLETEXT)); + + // from BROW_CRIT2_ROW onwards all rows are shown "or" + sal_Int32 nToken = (m_nSeekRow >= GetBrowseRow(BROW_CRIT2_ROW)) + ? BROW_CRIT2_ROW : GetRealRow(m_nSeekRow); + rDev.DrawText(aRect, aLabel.getToken(nToken, ';'),DrawTextFlags::VCenter); +} + +void OSelectionBrowseBox::RemoveColumn(sal_uInt16 _nColumnId) +{ + OQueryController& rController = static_cast<OQueryController&>(getDesignView()->getController()); + + sal_uInt16 nPos = GetColumnPos(_nColumnId); + // the control should always have exactly one more column: the HandleColumn + OSL_ENSURE((nPos == 0) || (nPos <= getFields().size()), "OSelectionBrowseBox::RemoveColumn : invalid parameter nColId"); + // ColId is synonymous to Position, and the condition should be valid + + sal_uInt16 nCurCol = GetCurColumnId(); + long nCurrentRow = GetCurRow(); + + DeactivateCell(); + + getFields().erase( getFields().begin() + (nPos - 1) ); + OTableFieldDescRef pEntry = new OTableFieldDesc(); + pEntry->SetColumnId(_nColumnId); + getFields().push_back(pEntry); + + EditBrowseBox::RemoveColumn( _nColumnId ); + InsertDataColumn( _nColumnId , OUString(), DEFAULT_SIZE ); + + // redraw + tools::Rectangle aInvalidRect = GetInvalidRect( _nColumnId ); + Invalidate( aInvalidRect ); + + ActivateCell( nCurrentRow, nCurCol ); + + rController.setModified( true ); + + invalidateUndoRedo(); +} + +void OSelectionBrowseBox::RemoveField(sal_uInt16 nColumnId ) +{ + OQueryController& rController = static_cast<OQueryController&>(getDesignView()->getController()); + + sal_uInt16 nPos = GetColumnPos(nColumnId); + OSL_ENSURE(getFields().size() > o3tl::make_unsigned(nPos-1),"ID is to great!"); + + OTableFieldDescRef pDesc = getEntry(static_cast<sal_uInt32>(nPos - 1)) ; + pDesc->SetColWidth( static_cast<sal_uInt16>(GetColumnWidth(nColumnId)) ); // was not stored this before + + // trigger UndoAction + if ( !m_bInUndoMode ) + { + std::unique_ptr<OTabFieldDelUndoAct> pUndoAction(new OTabFieldDelUndoAct( this )); + pUndoAction->SetTabFieldDescr(pDesc); + pUndoAction->SetColumnPosition(nPos); + rController.addUndoActionAndInvalidate( std::move(pUndoAction) ); + } + + RemoveColumn(nColumnId); + + invalidateUndoRedo(); +} + +void OSelectionBrowseBox::adjustSelectionMode( bool _bClickedOntoHeader, bool _bClickedOntoHandleCol ) +{ + // if a Header has been selected it should be shown otherwise not + if ( _bClickedOntoHeader ) + { + if (0 == GetSelectColumnCount() ) + // I am in the correct mode if a selected column exists + if ( BrowserMode::HIDESELECT == ( m_nMode & BrowserMode::HIDESELECT ) ) + { + m_nMode &= ~BrowserMode::HIDESELECT; + m_nMode |= BrowserMode::MULTISELECTION; + SetMode( m_nMode ); + } + } + else if ( BrowserMode::HIDESELECT != ( m_nMode & BrowserMode::HIDESELECT ) ) + { + if ( GetSelectColumnCount() != 0 ) + SetNoSelection(); + + if ( _bClickedOntoHandleCol ) + { + m_nMode |= BrowserMode::HIDESELECT; + m_nMode &= ~BrowserMode::MULTISELECTION; + SetMode( m_nMode ); + } + } +} + +void OSelectionBrowseBox::MouseButtonDown(const BrowserMouseEvent& rEvt) +{ + if( rEvt.IsLeft() ) + { + bool bOnHandle = HANDLE_ID == rEvt.GetColumnId(); + bool bOnHeader = ( rEvt.GetRow() < 0 ) && !bOnHandle; + adjustSelectionMode( bOnHeader, bOnHandle ); + } + EditBrowseBox::MouseButtonDown(rEvt); +} + +void OSelectionBrowseBox::MouseButtonUp(const BrowserMouseEvent& rEvt) +{ + EditBrowseBox::MouseButtonUp( rEvt ); + static_cast<OQueryController&>(getDesignView()->getController()).InvalidateFeature( ID_BROWSER_QUERY_EXECUTE ); +} + +void OSelectionBrowseBox::KeyInput( const KeyEvent& rEvt ) +{ + if (IsColumnSelected(GetCurColumnId())) + { + if (rEvt.GetKeyCode().GetCode() == KEY_DELETE && // Delete rows + !rEvt.GetKeyCode().IsShift() && + !rEvt.GetKeyCode().IsMod1()) + { + RemoveField(GetCurColumnId()); + return; + } + } + EditBrowseBox::KeyInput(rEvt); +} + +sal_Int8 OSelectionBrowseBox::AcceptDrop( const BrowserAcceptDropEvent& rEvt ) +{ + sal_Int8 nDropAction = DND_ACTION_NONE; + if ( rEvt.GetRow() >= -1 ) + { + if ( IsEditing() ) + { + // allow the asterisk again + m_bDisableErrorBox = true; + SaveModified(); + m_bDisableErrorBox = false; + DeactivateCell(); + } + // check if the format is already supported, if not deactivate the current cell and try again + if ( OJoinExchObj::isFormatAvailable(GetDataFlavors()) ) + nDropAction = DND_ACTION_LINK; + } + + return nDropAction; +} + +sal_Int8 OSelectionBrowseBox::ExecuteDrop( const BrowserExecuteDropEvent& _rEvt ) +{ + + TransferableDataHelper aDropped(_rEvt.maDropEvent.Transferable); + if (!OJoinExchObj::isFormatAvailable(aDropped.GetDataFlavorExVector())) + { + OSL_FAIL("OSelectionBrowseBox::ExecuteDrop: this should never have passed AcceptDrop!"); + return DND_ACTION_NONE; + } + + // insert the field at the selected position + OJoinExchangeData jxdSource = OJoinExchObj::GetSourceDescription(_rEvt.maDropEvent.Transferable); + InsertField(jxdSource); + + return DND_ACTION_LINK; +} + +OTableFieldDescRef const & OSelectionBrowseBox::AppendNewCol( sal_uInt16 nCnt) +{ + // one or more can be created, but the first one will is not returned + sal_uInt32 nCount = getFields().size(); + for (sal_uInt16 i=0 ; i<nCnt ; i++) + { + OTableFieldDescRef pEmptyEntry = new OTableFieldDesc(); + getFields().push_back(pEmptyEntry); + sal_uInt16 nColumnId = sal::static_int_cast< sal_uInt16 >(getFields().size()); + pEmptyEntry->SetColumnId( nColumnId ); + + InsertDataColumn( nColumnId , OUString(), DEFAULT_SIZE ); + } + + return getFields()[nCount]; +} + +void OSelectionBrowseBox::DeleteFields(const OUString& rAliasName) +{ + if (getFields().empty()) + return; + + sal_uInt16 nColId = GetCurColumnId(); + sal_uInt32 nRow = GetCurRow(); + + bool bWasEditing = IsEditing(); + if (bWasEditing) + DeactivateCell(); + + auto aIter = std::find_if(getFields().rbegin(), getFields().rend(), + [&rAliasName](const OTableFieldDescRef pEntry) { return pEntry->GetAlias() == rAliasName; }); + if (aIter != getFields().rend()) + { + sal_uInt16 nPos = sal::static_int_cast<sal_uInt16>(std::distance(aIter, getFields().rend())); + RemoveField( GetColumnId( nPos ) ); + } + + if (bWasEditing) + ActivateCell(nRow , nColId); +} + +void OSelectionBrowseBox::SetColWidth(sal_uInt16 nColId, long nNewWidth) +{ + bool bWasEditing = IsEditing(); + if (bWasEditing) + DeactivateCell(); + + // create the BaseClass + SetColumnWidth(nColId, nNewWidth); + + // tell it the FieldDescription + OTableFieldDescRef pEntry = getEntry(GetColumnPos(nColId) - 1); + if (pEntry.is()) + pEntry->SetColWidth(sal_uInt16(GetColumnWidth(nColId))); + + if (bWasEditing) + ActivateCell(GetCurRow(), GetCurColumnId()); +} + +tools::Rectangle OSelectionBrowseBox::GetInvalidRect( sal_uInt16 nColId ) +{ + // The rectangle is the full output area of the window + tools::Rectangle aInvalidRect( Point(0,0), GetOutputSizePixel() ); + + // now update the left side + tools::Rectangle aFieldRect(GetCellRect( 0, nColId )); // used instead of GetFieldRectPixel + aInvalidRect.SetLeft( aFieldRect.Left() ); + + return aInvalidRect; +} + +void OSelectionBrowseBox::InsertColumn(const OTableFieldDescRef& pEntry, sal_uInt16& _nColumnPosition) +{ + // the control should have exactly one more column: the HandleColumn + OSL_ENSURE(_nColumnPosition == BROWSER_INVALIDID || (_nColumnPosition <= static_cast<long>(getFields().size())), "OSelectionBrowseBox::InsertColumn : invalid parameter nColId."); + // -1 means at the end. Count means at the end, others denotes a correct position + + sal_uInt16 nCurCol = GetCurColumnId(); + long nCurrentRow = GetCurRow(); + + DeactivateCell(); + + // remember the column id of the current position + sal_uInt16 nColumnId = GetColumnId(_nColumnPosition); + // put at the end of the list if too small or too big, + if ((_nColumnPosition == BROWSER_INVALIDID) || (_nColumnPosition >= getFields().size())) // append the field + { + if (FindFirstFreeCol(_nColumnPosition) == nullptr) // no more free columns + { + AppendNewCol(); + _nColumnPosition = sal::static_int_cast< sal_uInt16 >( + getFields().size()); + } + else + ++_nColumnPosition; // within the list + nColumnId = GetColumnId(_nColumnPosition); + pEntry->SetColumnId( nColumnId ); + getFields()[ _nColumnPosition - 1] = pEntry; + } + + // check if the column ids are identical, if not we have to move + if ( pEntry->GetColumnId() != nColumnId ) + { + sal_uInt16 nOldPosition = GetColumnPos(pEntry->GetColumnId()); + OSL_ENSURE( nOldPosition != 0,"Old position was 0. Not possible!"); + SetColumnPos(pEntry->GetColumnId(),_nColumnPosition); + // we have to delete an empty field for the fields list, because the columns must have equal length + if ( nOldPosition > 0 && nOldPosition <= getFields().size() ) + getFields()[nOldPosition - 1] = pEntry; + + ColumnMoved(pEntry->GetColumnId(),false); + } + + if ( pEntry->GetFunctionType() & FKT_AGGREGATE ) + { + OUString sFunctionName = pEntry->GetFunction(); + if ( GetFunctionName(sal_uInt32(-1),sFunctionName) ) + pEntry->SetFunction(sFunctionName); + } + + nColumnId = pEntry->GetColumnId(); + + SetColWidth(nColumnId,getDesignView()->getColWidth(GetColumnPos(nColumnId)-1)); + // redraw + tools::Rectangle aInvalidRect = GetInvalidRect( nColumnId ); + Invalidate( aInvalidRect ); + + ActivateCell( nCurrentRow, nCurCol ); + static_cast<OQueryController&>(getDesignView()->getController()).setModified( true ); + + invalidateUndoRedo(); +} + +OTableFieldDescRef OSelectionBrowseBox::InsertField(const OJoinExchangeData& jxdSource) +{ + OQueryTableWindow* pSourceWin = static_cast<OQueryTableWindow*>(jxdSource.pListBox->GetTabWin()); + if (!pSourceWin) + return nullptr; + + // name and position of the selected field + OUString aFieldName = jxdSource.pListBox->GetEntryText(jxdSource.pEntry); + sal_uInt32 nFieldIndex = jxdSource.pListBox->GetModel()->GetAbsPos(jxdSource.pEntry); + OTableFieldInfo* pInf = static_cast<OTableFieldInfo*>(jxdSource.pEntry->GetUserData()); + + // construct DragInfo, such that I use the other InsertField + OTableFieldDescRef aInfo = new OTableFieldDesc(pSourceWin->GetTableName(),aFieldName); + aInfo->SetTabWindow(pSourceWin); + aInfo->SetFieldIndex(nFieldIndex); + aInfo->SetFieldType(pInf->GetKeyType()); + aInfo->SetAlias(pSourceWin->GetAliasName()); + + aInfo->SetDataType(pInf->GetDataType()); + aInfo->SetVisible(); + + return InsertField(aInfo); +} + +OTableFieldDescRef OSelectionBrowseBox::InsertField(const OTableFieldDescRef& _rInfo, sal_uInt16 _nColumnPosition, bool bVis, bool bActivate) +{ + + if(m_nMaxColumns && m_nMaxColumns <= FieldsCount()) + return nullptr; + if (bActivate) + SaveModified(); + + // new column description + OTableFieldDescRef pEntry = _rInfo; + pEntry->SetVisible(bVis); + + // insert column + InsertColumn( pEntry, _nColumnPosition ); + + if ( !m_bInUndoMode ) + { + // trigger UndoAction + std::unique_ptr<OTabFieldCreateUndoAct> pUndoAction(new OTabFieldCreateUndoAct( this )); + pUndoAction->SetTabFieldDescr( pEntry ); + pUndoAction->SetColumnPosition(_nColumnPosition); + getDesignView()->getController().addUndoActionAndInvalidate( std::move(pUndoAction) ); + } + + return pEntry; +} + +sal_uInt16 OSelectionBrowseBox::FieldsCount() +{ + sal_uInt16 nCount = 0; + for (auto const& field : getFields()) + { + if (field.is() && !field->IsEmpty()) + ++nCount; + } + + return nCount; +} + +OTableFieldDescRef OSelectionBrowseBox::FindFirstFreeCol(sal_uInt16& _rColumnPosition ) +{ + + _rColumnPosition = BROWSER_INVALIDID; + + for (auto const& field : getFields()) + { + ++_rColumnPosition; + OTableFieldDescRef pEntry = field; + if ( pEntry.is() && pEntry->IsEmpty() ) + return pEntry; + } + + return nullptr; +} + +void OSelectionBrowseBox::CheckFreeColumns(sal_uInt16& _rColumnPosition) +{ + if (FindFirstFreeCol(_rColumnPosition) == nullptr) + { + // it is full, so append a pack of columns + AppendNewCol(DEFAULT_QUERY_COLS); + OSL_VERIFY(FindFirstFreeCol(_rColumnPosition).is()); + } +} + +void OSelectionBrowseBox::AddGroupBy( const OTableFieldDescRef& rInfo ) +{ + Reference< XConnection> xConnection = static_cast<OQueryController&>(getDesignView()->getController()).getConnection(); + if(!xConnection.is()) + return; + OSL_ENSURE(!rInfo->IsEmpty(),"AddGroupBy:: OTableFieldDescRef should not be empty!"); + OTableFieldDescRef pEntry; + const Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData(); + const ::comphelper::UStringMixEqual bCase(xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers()); + //sal_Bool bAppend = sal_False; + + bool bAllFieldsSearched = true; + for (auto const& field : getFields()) + { + pEntry = field; + OSL_ENSURE(pEntry.is(),"OTableFieldDescRef was null!"); + + const OUString aField = pEntry->GetField(); + const OUString aAlias = pEntry->GetAlias(); + + if (bCase(aField,rInfo->GetField()) && + bCase(aAlias,rInfo->GetAlias()) && + pEntry->GetFunctionType() == rInfo->GetFunctionType() && + pEntry->GetFunction() == rInfo->GetFunction()) + { + if ( pEntry->isNumericOrAggregateFunction() && rInfo->IsGroupBy() ) + { + pEntry->SetGroupBy(false); + // we do want to consider that bAllFieldsSearched still true here + // bAllFieldsSearched = false; + break; + } + else + { + if ( !pEntry->IsGroupBy() && !pEntry->HasCriteria() ) // here we have a where condition which is no having clause + { + pEntry->SetGroupBy(rInfo->IsGroupBy()); + if(!m_bGroupByUnRelated && pEntry->IsGroupBy()) + pEntry->SetVisible(); + bAllFieldsSearched = false; + break; + } + } + + } + } + + if (bAllFieldsSearched) + { + OTableFieldDescRef pTmp = InsertField(rInfo, BROWSER_INVALIDID, false, false ); + if ( pTmp->isNumericOrAggregateFunction() && rInfo->IsGroupBy() ) // the GroupBy is inherited from rInfo + pTmp->SetGroupBy(false); + } +} + +void OSelectionBrowseBox::DuplicateConditionLevel( const sal_uInt16 nLevel) +{ + const sal_uInt16 nNewLevel = nLevel +1; + for (auto const& field : getFields()) + { + const OTableFieldDescRef& pEntry = field; + OUString sValue = pEntry->GetCriteria(nLevel); + if ( !sValue.isEmpty() ) + { + pEntry->SetCriteria( nNewLevel, sValue); + if ( nNewLevel == (m_nVisibleCount-BROW_CRIT1_ROW-1) ) + { + RowInserted( GetRowCount()-1 ); + m_bVisibleRow.push_back(true); + ++m_nVisibleCount; + } + m_bVisibleRow[BROW_CRIT1_ROW + nNewLevel] = true; + } + } +} + +void OSelectionBrowseBox::AddCondition( const OTableFieldDescRef& rInfo, const OUString& rValue, const sal_uInt16 nLevel,bool _bAddOrOnOneLine ) +{ + Reference< XConnection> xConnection = static_cast<OQueryController&>(getDesignView()->getController()).getConnection(); + if(!xConnection.is()) + return; + OSL_ENSURE(rInfo.is() && !rInfo->IsEmpty(),"AddCondition:: OTableFieldDescRef should not be Empty!"); + + OTableFieldDescRef pLastEntry; + Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData(); + ::comphelper::UStringMixEqual bCase(xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers()); + + bool bAllFieldsSearched = true; + for (auto const& field : getFields()) + { + const OTableFieldDescRef& pEntry = field; + const OUString aField = pEntry->GetField(); + const OUString aAlias = pEntry->GetAlias(); + + if (bCase(aField,rInfo->GetField()) && + bCase(aAlias,rInfo->GetAlias()) && + pEntry->GetFunctionType() == rInfo->GetFunctionType() && + pEntry->GetFunction() == rInfo->GetFunction() && + pEntry->IsGroupBy() == rInfo->IsGroupBy() ) + { + if ( pEntry->isNumericOrAggregateFunction() && rInfo->IsGroupBy() ) + pEntry->SetGroupBy(false); + else + { + if(!m_bGroupByUnRelated && pEntry->IsGroupBy()) + pEntry->SetVisible(); + } + if (pEntry->GetCriteria(nLevel).isEmpty() ) + { + pEntry->SetCriteria( nLevel, rValue); + if(nLevel == (m_nVisibleCount-BROW_CRIT1_ROW-1)) + { + RowInserted( GetRowCount()-1 ); + m_bVisibleRow.push_back(true); + ++m_nVisibleCount; + } + m_bVisibleRow[BROW_CRIT1_ROW + nLevel] = true; + bAllFieldsSearched = false; + break; + } + if ( _bAddOrOnOneLine ) + { + pLastEntry = pEntry; + } + } + } + if ( pLastEntry.is() ) + { + OUString sCriteria = rValue; + OUString sOldCriteria = pLastEntry->GetCriteria( nLevel ); + if ( !sOldCriteria.isEmpty() ) + { + sCriteria = "( " + sOldCriteria + " OR " + rValue + " )"; + } + pLastEntry->SetCriteria( nLevel, sCriteria); + if(nLevel == (m_nVisibleCount-BROW_CRIT1_ROW-1)) + { + RowInserted( GetRowCount()-1 ); + m_bVisibleRow.push_back(true); + ++m_nVisibleCount; + } + m_bVisibleRow[BROW_CRIT1_ROW + nLevel] = true; + } + else if (bAllFieldsSearched) + { + OTableFieldDescRef pTmp = InsertField(rInfo, BROWSER_INVALIDID, false, false ); + if ( pTmp->isNumericOrAggregateFunction() && rInfo->IsGroupBy() ) // the GroupBy was inherited from rInfo + pTmp->SetGroupBy(false); + if ( pTmp.is() ) + { + pTmp->SetCriteria( nLevel, rValue); + if(nLevel == (m_nVisibleCount-BROW_CRIT1_ROW-1)) + { + RowInserted( GetRowCount()-1 ); + m_bVisibleRow.push_back(true); + ++m_nVisibleCount; + } + } + } +} + +void OSelectionBrowseBox::AddOrder( const OTableFieldDescRef& rInfo, const EOrderDir eDir, sal_uInt32 _nCurrentPos) +{ + if (_nCurrentPos == 0) + m_nLastSortColumn = SORT_COLUMN_NONE; + + Reference< XConnection> xConnection = static_cast<OQueryController&>(getDesignView()->getController()).getConnection(); + if(!xConnection.is()) + return; + OSL_ENSURE(!rInfo->IsEmpty(),"AddOrder:: OTableFieldDescRef should not be Empty!"); + OTableFieldDescRef pEntry; + Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData(); + ::comphelper::UStringMixEqual bCase(xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers()); + + bool bAppend = false; + sal_uInt32 nPos = 0; + bool bAllFieldsSearched = true; + for (auto const& field : getFields()) + { + pEntry = field; + OUString aField = pEntry->GetField(); + OUString aAlias = pEntry->GetAlias(); + + if (bCase(aField,rInfo->GetField()) && + bCase(aAlias,rInfo->GetAlias())) + { + bAppend = (m_nLastSortColumn != SORT_COLUMN_NONE) && (nPos <= m_nLastSortColumn); + if ( bAppend ) + { + // we do want to consider that bAllFieldsSearched still true here + // bAllFieldsSearched = false; + break; + } + else + { + if ( !m_bOrderByUnRelated ) + pEntry->SetVisible(); + pEntry->SetOrderDir( eDir ); + m_nLastSortColumn = nPos; + } + bAllFieldsSearched = false; + break; + } + ++nPos; + } + + if (bAllFieldsSearched) + { + OTableFieldDescRef pTmp = InsertField(rInfo, BROWSER_INVALIDID, false, false ); + if(pTmp.is()) + { + m_nLastSortColumn = pTmp->GetColumnId() - 1; + if ( !m_bOrderByUnRelated && !bAppend ) + pTmp->SetVisible(); + pTmp->SetOrderDir( eDir ); + } + } +} + +bool OSelectionBrowseBox::Save() +{ + bool bRet = true; + if (IsModified()) + bRet = SaveModified(); + return bRet; +} + +void OSelectionBrowseBox::CellModified() +{ + long nRow = GetRealRow(GetCurRow()); + switch (nRow) + { + case BROW_VIS_ROW: + { + OTableFieldDescRef pEntry = getEntry(GetColumnPos(GetCurColumnId()) - 1); + + weld::ComboBox& rComboBox = m_pOrderCell->get_widget(); + sal_Int32 nIdx = rComboBox.get_active(); + if(!m_bOrderByUnRelated && nIdx > 0 && + nIdx != -1 && + !pEntry->IsEmpty() && + pEntry->GetOrderDir() != ORDER_NONE) + { + m_pVisibleCell->GetBox().Check(); + pEntry->SetVisible(); + } + else + pEntry->SetVisible(m_pVisibleCell->GetBox().IsChecked()); + } + break; + } + static_cast<OQueryController&>(getDesignView()->getController()).setModified( true ); +} + +void OSelectionBrowseBox::Fill() +{ + OSL_ENSURE(ColCount() >= 1, "OSelectionBrowseBox::Fill : please call only after inserting the handle column !"); + + sal_uInt16 nColCount = ColCount() - 1; + if (nColCount < DEFAULT_QUERY_COLS) + AppendNewCol(DEFAULT_QUERY_COLS - nColCount); +} + +Size OSelectionBrowseBox::CalcOptimalSize( const Size& _rAvailable ) +{ + Size aReturn( _rAvailable.Width(), GetTitleHeight() ); + + aReturn.AdjustHeight(( m_nVisibleCount ? m_nVisibleCount : 15 ) * GetDataRowHeight() ); + aReturn.AdjustHeight(40 ); // just some space + + return aReturn; +} + +void OSelectionBrowseBox::Command(const CommandEvent& rEvt) +{ + switch (rEvt.GetCommand()) + { + case CommandEventId::ContextMenu: + { + Point aMenuPos( rEvt.GetMousePosPixel() ); + + if (!rEvt.IsMouseEvent()) + { + if ( 1 == GetSelectColumnCount() ) + { + sal_uInt16 nSelId = GetColumnId( + sal::static_int_cast< sal_uInt16 >( + FirstSelectedColumn() ) ); + ::tools::Rectangle aColRect( GetFieldRectPixel( 0, nSelId, false ) ); + + aMenuPos = aColRect.TopCenter(); + } + else + { + EditBrowseBox::Command(rEvt); + return; + } + } + + sal_uInt16 nColId = GetColumnId(GetColumnAtXPosPixel( aMenuPos.X() )); + long nRow = GetRowAtYPosPixel( aMenuPos.Y() ); + + if (nRow < 0 && nColId > HANDLE_ID ) + { + if ( !IsColumnSelected( nColId ) ) + { + adjustSelectionMode( true /* clicked onto a header */ , false /* not onto the handle col */ ); + SelectColumnId( nColId ); + } + + if (!static_cast<OQueryController&>(getDesignView()->getController()).isReadOnly()) + { + VclBuilder aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "dbaccess/ui/querycolmenu.ui", ""); + VclPtr<PopupMenu> aContextMenu(aBuilder.get_menu("menu")); + sal_uInt16 nItemId = aContextMenu->Execute(this, aMenuPos); + if (nItemId == aContextMenu->GetItemId("delete")) + RemoveField(nColId); + else if (nItemId == aContextMenu->GetItemId("width")) + adjustBrowseBoxColumnWidth( this, nColId ); + } + } + else if(nRow >= 0 && nColId <= HANDLE_ID) + { + if (!static_cast<OQueryController&>(getDesignView()->getController()).isReadOnly()) + { + VclBuilder aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "dbaccess/ui/queryfuncmenu.ui", ""); + VclPtr<PopupMenu> aContextMenu(aBuilder.get_menu("menu")); + aContextMenu->CheckItem("functions", m_bVisibleRow[BROW_FUNCTION_ROW]); + aContextMenu->CheckItem("tablename", m_bVisibleRow[BROW_TABLE_ROW]); + aContextMenu->CheckItem("alias", m_bVisibleRow[BROW_COLUMNALIAS_ROW]); + aContextMenu->CheckItem("distinct", static_cast<OQueryController&>(getDesignView()->getController()).isDistinct()); + + aContextMenu->Execute(this, aMenuPos); + + OString sIdent = aContextMenu->GetCurItemIdent(); + if (sIdent == "functions") + { + SetRowVisible(BROW_FUNCTION_ROW, !IsRowVisible(BROW_FUNCTION_ROW)); + static_cast<OQueryController&>(getDesignView()->getController()).InvalidateFeature( SID_QUERY_VIEW_FUNCTIONS ); + } + else if (sIdent == "tablename") + { + SetRowVisible(BROW_TABLE_ROW, !IsRowVisible(BROW_TABLE_ROW)); + static_cast<OQueryController&>(getDesignView()->getController()).InvalidateFeature( SID_QUERY_VIEW_TABLES ); + } + else if (sIdent == "alias") + { + SetRowVisible(BROW_COLUMNALIAS_ROW, !IsRowVisible(BROW_COLUMNALIAS_ROW)); + static_cast<OQueryController&>(getDesignView()->getController()).InvalidateFeature( SID_QUERY_VIEW_ALIASES ); + } + else if (sIdent == "distinct") + { + static_cast<OQueryController&>(getDesignView()->getController()).setDistinct(!static_cast<OQueryController&>(getDesignView()->getController()).isDistinct()); + static_cast<OQueryController&>(getDesignView()->getController()).setModified( true ); + static_cast<OQueryController&>(getDesignView()->getController()).InvalidateFeature( SID_QUERY_DISTINCT_VALUES ); + } + + static_cast<OQueryController&>(getDesignView()->getController()).setModified( true ); + } + } + else + { + EditBrowseBox::Command(rEvt); + return; + } + + [[fallthrough]]; + } + default: + EditBrowseBox::Command(rEvt); + } +} + +bool OSelectionBrowseBox::IsRowVisible(sal_uInt16 _nWhich) const +{ + OSL_ENSURE(_nWhich<(m_bVisibleRow.size()), "OSelectionBrowseBox::IsRowVisible : invalid parameter !"); + return m_bVisibleRow[_nWhich]; +} + +void OSelectionBrowseBox::SetRowVisible(sal_uInt16 _nWhich, bool _bVis) +{ + OSL_ENSURE(_nWhich<m_bVisibleRow.size(), "OSelectionBrowseBox::SetRowVisible : invalid parameter !"); + + bool bWasEditing = IsEditing(); + if (bWasEditing) + DeactivateCell(); + + // do this before removing or inserting rows, as this triggers ActivateCell-calls, which rely on m_bVisibleRow + m_bVisibleRow[_nWhich] = !m_bVisibleRow[_nWhich]; + + long nId = GetBrowseRow(_nWhich); + if (_bVis) + { + RowInserted(nId); + ++m_nVisibleCount; + } + else + { + RowRemoved(nId); + --m_nVisibleCount; + } + + if (bWasEditing) + ActivateCell(); +} + +long OSelectionBrowseBox::GetBrowseRow(long nRowId) const +{ + sal_uInt16 nCount(0); + for(long i = 0 ; i < nRowId ; ++i) + { + if ( m_bVisibleRow[i] ) + ++nCount; + } + return nCount; +} + +long OSelectionBrowseBox::GetRealRow(long nRowId) const +{ + long nErg=0,i; + const long nCount = m_bVisibleRow.size(); + for(i=0;i < nCount; ++i) + { + if(m_bVisibleRow[i] && nErg++ == nRowId) + break; + } + OSL_ENSURE(nErg <= long(m_bVisibleRow.size()),"nErg cannot be greater than BROW_ROW_CNT!"); + return i; +} + +static const long nVisibleRowMask[] = + { + 0x0001, + 0x0002, + 0x0004, + 0x0008, + 0x0010, + 0x0020, + 0x0040, + 0x0080, + 0x0100, + 0x0200, + 0x0400, + 0x0800 + }; +sal_Int32 OSelectionBrowseBox::GetNoneVisibleRows() const +{ + sal_Int32 nErg(0); + // only the first 11 rows are interesting + sal_Int32 const nSize = SAL_N_ELEMENTS(nVisibleRowMask); + for(sal_Int32 i=0;i<nSize;i++) + { + if(!m_bVisibleRow[i]) + nErg |= nVisibleRowMask[i]; + } + return nErg; +} + +void OSelectionBrowseBox::SetNoneVisbleRow(long nRows) +{ + // only the first 11 rows are interesting + sal_Int32 const nSize = SAL_N_ELEMENTS(nVisibleRowMask); + for(sal_Int32 i=0;i< nSize;i++) + m_bVisibleRow[i] = !(nRows & nVisibleRowMask[i]); +} + +OUString OSelectionBrowseBox::GetCellText(long nRow, sal_uInt16 nColId) const +{ + + sal_uInt16 nPos = GetColumnPos(nColId); + + OTableFieldDescRef pEntry = getFields()[nPos-1]; + OSL_ENSURE(pEntry != nullptr, "OSelectionBrowseBox::GetCellText : invalid column id, prepare for GPF ... "); + if ( pEntry->IsEmpty() ) + return OUString(); + + OUString aText; + switch (nRow) + { + case BROW_TABLE_ROW: + aText = pEntry->GetAlias(); + break; + case BROW_FIELD_ROW: + { + OUString aField = pEntry->GetField(); + if (!aField.isEmpty() && aField[0] == '*') // * replace with alias.* + { + aField = pEntry->GetAlias(); + if(!aField.isEmpty()) + aField += "."; + aField += "*"; + } + aText = aField; + } break; + case BROW_ORDER_ROW: + if (pEntry->GetOrderDir() != ORDER_NONE) + aText = DBA_RES(STR_QUERY_SORTTEXT).getToken(sal::static_int_cast< sal_uInt16 >(pEntry->GetOrderDir()), ';'); + break; + case BROW_VIS_ROW: + break; + case BROW_COLUMNALIAS_ROW: + aText = pEntry->GetFieldAlias(); + break; + case BROW_FUNCTION_ROW: + // we always show the group function at first + if ( pEntry->IsGroupBy() ) + aText = m_aFunctionStrings.copy(m_aFunctionStrings.lastIndexOf(';')+1); + else if ( pEntry->isNumericOrAggregateFunction() ) + aText = pEntry->GetFunction(); + break; + default: + aText = pEntry->GetCriteria(sal_uInt16(nRow - BROW_CRIT1_ROW)); + } + return aText; +} + +bool OSelectionBrowseBox::GetFunctionName(sal_uInt32 _nFunctionTokenId, OUString& rFkt) +{ + weld::ComboBox& rComboBox = m_pFunctionCell->get_widget(); + switch(_nFunctionTokenId) + { + case SQL_TOKEN_COUNT: + rFkt = (rComboBox.get_count() < 3) ? rComboBox.get_text(1) : rComboBox.get_text(2); + break; + case SQL_TOKEN_AVG: + rFkt = rComboBox.get_text(1); + break; + case SQL_TOKEN_MAX: + rFkt = rComboBox.get_text(3); + break; + case SQL_TOKEN_MIN: + rFkt = rComboBox.get_text(4); + break; + case SQL_TOKEN_SUM: + rFkt = rComboBox.get_text(5); + break; + case SQL_TOKEN_EVERY: + rFkt = rComboBox.get_text(6); + break; + case SQL_TOKEN_ANY: + rFkt = rComboBox.get_text(7); + break; + case SQL_TOKEN_SOME: + rFkt = rComboBox.get_text(8); + break; + case SQL_TOKEN_STDDEV_POP: + rFkt = rComboBox.get_text(9); + break; + case SQL_TOKEN_STDDEV_SAMP: + rFkt = rComboBox.get_text(10); + break; + case SQL_TOKEN_VAR_SAMP: + rFkt = rComboBox.get_text(11); + break; + case SQL_TOKEN_VAR_POP: + rFkt = rComboBox.get_text(12); + break; + case SQL_TOKEN_COLLECT: + rFkt = rComboBox.get_text(13); + break; + case SQL_TOKEN_FUSION: + rFkt = rComboBox.get_text(14); + break; + case SQL_TOKEN_INTERSECTION: + rFkt = rComboBox.get_text(15); + break; + default: + { + const sal_Int32 nStopIdx = m_aFunctionStrings.lastIndexOf(';'); // grouping is not counted + for (sal_Int32 nIdx {0}; nIdx<nStopIdx;) + { + const OUString sFunc {m_aFunctionStrings.getToken(0, ';', nIdx)}; + if (rFkt.equalsIgnoreAsciiCase(sFunc)) + { + rFkt = sFunc; + return true; + } + } + return false; + } + } + + return true; +} + +OUString OSelectionBrowseBox::GetCellContents(sal_Int32 nCellIndex, sal_uInt16 nColId) +{ + if ( GetCurColumnId() == nColId && !m_bInUndoMode ) + SaveModified(); + + sal_uInt16 nPos = GetColumnPos(nColId); + OTableFieldDescRef pEntry = getFields()[nPos - 1]; + OSL_ENSURE(pEntry != nullptr, "OSelectionBrowseBox::GetCellContents : invalid column id, prepare for GPF ... "); + + switch (nCellIndex) + { + case BROW_VIS_ROW : + return pEntry->IsVisible() ? OUStringLiteral("1") : OUStringLiteral("0"); + case BROW_ORDER_ROW: + { + sal_Int32 nIdx = m_pOrderCell->get_widget().get_active(); + if (nIdx == -1) + nIdx = 0; + return OUString::number(nIdx); + } + default: + return GetCellText(nCellIndex, nColId); + } +} + +void OSelectionBrowseBox::SetCellContents(sal_Int32 nRow, sal_uInt16 nColId, const OUString& strNewText) +{ + bool bWasEditing = IsEditing() && (GetCurColumnId() == nColId) && IsRowVisible(static_cast<sal_uInt16>(nRow)) && (GetCurRow() == static_cast<sal_uInt16>(GetBrowseRow(nRow))); + if (bWasEditing) + DeactivateCell(); + + sal_uInt16 nPos = GetColumnPos(nColId); + OTableFieldDescRef pEntry = getEntry(nPos - 1); + OSL_ENSURE(pEntry != nullptr, "OSelectionBrowseBox::SetCellContents : invalid column id, prepare for GPF ... "); + + switch (nRow) + { + case BROW_VIS_ROW: + pEntry->SetVisible(strNewText == "1"); + break; + case BROW_FIELD_ROW: + pEntry->SetField(strNewText); + break; + case BROW_TABLE_ROW: + pEntry->SetAlias(strNewText); + break; + case BROW_ORDER_ROW: + { + sal_uInt16 nIdx = static_cast<sal_uInt16>(strNewText.toInt32()); + pEntry->SetOrderDir(EOrderDir(nIdx)); + } break; + case BROW_COLUMNALIAS_ROW: + pEntry->SetFieldAlias(strNewText); + break; + case BROW_FUNCTION_ROW: + { + OUString sGroupFunctionName = m_aFunctionStrings.copy(m_aFunctionStrings.lastIndexOf(';')+1); + pEntry->SetFunction(strNewText); + // first reset this two member + sal_Int32 nFunctionType = pEntry->GetFunctionType(); + nFunctionType &= ~FKT_AGGREGATE; + pEntry->SetFunctionType(nFunctionType); + if ( pEntry->IsGroupBy() && !sGroupFunctionName.equalsIgnoreAsciiCase(strNewText) ) + pEntry->SetGroupBy(false); + + if ( sGroupFunctionName.equalsIgnoreAsciiCase(strNewText) ) + pEntry->SetGroupBy(true); + else if ( !strNewText.isEmpty() ) + { + nFunctionType |= FKT_AGGREGATE; + pEntry->SetFunctionType(nFunctionType); + } + } break; + default: + pEntry->SetCriteria(sal_uInt16(nRow - BROW_CRIT1_ROW), strNewText); + } + + long nCellIndex = GetRealRow(nRow); + if(IsRowVisible(static_cast<sal_uInt16>(nRow))) + RowModified(nCellIndex, nColId); + + // the appropriate field-description is now empty -> set Visible to sal_False (now it is consistent to normal empty rows) + if (pEntry->IsEmpty()) + pEntry->SetVisible(false); + + if (bWasEditing) + ActivateCell(nCellIndex, nColId); + + static_cast<OQueryController&>(getDesignView()->getController()).setModified( true ); +} + +void OSelectionBrowseBox::ColumnResized(sal_uInt16 nColId) +{ + if (static_cast<OQueryController&>(getDesignView()->getController()).isReadOnly()) + return; + // The resizing of columns can't be suppressed (BrowseBox doesn't support that) so we have to do this + // fake. It's not _that_ bad : the user may change column widths while in read-only mode to see all details + // but the changes aren't permanent ... + + sal_uInt16 nPos = GetColumnPos(nColId); + OSL_ENSURE(nPos <= getFields().size(),"ColumnResized:: nColId should not be greater than List::count!"); + OTableFieldDescRef pEntry = getEntry(nPos-1); + OSL_ENSURE(pEntry.is(), "OSelectionBrowseBox::ColumnResized : invalid FieldDescription !"); + static_cast<OQueryController&>(getDesignView()->getController()).setModified( true ); + EditBrowseBox::ColumnResized(nColId); + + if ( pEntry.is()) + { + if ( !m_bInUndoMode ) + { + // create the undo action + std::unique_ptr<OTabFieldSizedUndoAct> pUndo(new OTabFieldSizedUndoAct(this)); + pUndo->SetColumnPosition( nPos ); + pUndo->SetOriginalWidth(pEntry->GetColWidth()); + getDesignView()->getController().addUndoActionAndInvalidate(std::move(pUndo)); + } + pEntry->SetColWidth(sal_uInt16(GetColumnWidth(nColId))); + } +} + +sal_uInt32 OSelectionBrowseBox::GetTotalCellWidth(long nRowId, sal_uInt16 nColId) +{ + sal_uInt16 nPos = GetColumnPos(nColId); + OSL_ENSURE((nPos == 0) || (nPos <= getFields().size()), "OSelectionBrowseBox::GetTotalCellWidth : invalid parameter nColId"); + + OTableFieldDescRef pEntry = getFields()[nPos-1]; + OSL_ENSURE(pEntry.is(), "OSelectionBrowseBox::GetTotalCellWidth : invalid FieldDescription !"); + + long nRow = GetRealRow(nRowId); + OUString strText(GetCellText(nRow, nColId)); + return GetDataWindow().LogicToPixel(Size(GetDataWindow().GetTextWidth(strText),0)).Width(); +} + +bool OSelectionBrowseBox::isCutAllowed() const +{ + bool bCutAllowed = false; + long nRow = GetRealRow(GetCurRow()); + switch (nRow) + { + case BROW_VIS_ROW: + case BROW_ORDER_ROW: + case BROW_TABLE_ROW: + case BROW_FUNCTION_ROW: + break; + case BROW_FIELD_ROW: + { + weld::ComboBox& rComboBox = m_pFieldCell->get_widget(); + int nStartPos, nEndPos; + bCutAllowed = rComboBox.get_entry_selection_bounds(nStartPos, nEndPos); + break; + } + default: + bCutAllowed = !m_pTextCell->GetSelected().isEmpty(); + break; + } + return bCutAllowed; +} + +void OSelectionBrowseBox::cut() +{ + long nRow = GetRealRow(GetCurRow()); + switch (nRow) + { + case BROW_FIELD_ROW: + { + weld::ComboBox& rComboBox = m_pFieldCell->get_widget(); + rComboBox.cut_entry_clipboard(); + break; + } + default: + m_pTextCell->Cut(); + m_pTextCell->SetModifyFlag(); + } + SaveModified(); + RowModified(GetBrowseRow(nRow), GetCurColumnId()); + + invalidateUndoRedo(); +} + +void OSelectionBrowseBox::paste() +{ + long nRow = GetRealRow(GetCurRow()); + switch (nRow) + { + case BROW_FIELD_ROW: + { + weld::ComboBox& rComboBox = m_pFieldCell->get_widget(); + rComboBox.paste_entry_clipboard(); + break; + } + default: + m_pTextCell->Paste(); + m_pTextCell->SetModifyFlag(); + } + RowModified(GetBrowseRow(nRow), GetCurColumnId()); + invalidateUndoRedo(); +} + +bool OSelectionBrowseBox::isPasteAllowed() const +{ + bool bPasteAllowed = true; + long nRow = GetRealRow(GetCurRow()); + switch (nRow) + { + case BROW_VIS_ROW: + case BROW_ORDER_ROW: + case BROW_TABLE_ROW: + case BROW_FUNCTION_ROW: + bPasteAllowed = false; + break; + } + return bPasteAllowed; +} + +bool OSelectionBrowseBox::isCopyAllowed() const +{ + return isCutAllowed(); +} + +void OSelectionBrowseBox::copy() +{ + long nRow = GetRealRow(GetCurRow()); + switch (nRow) + { + case BROW_FIELD_ROW: + { + weld::ComboBox& rComboBox = m_pFieldCell->get_widget(); + rComboBox.copy_entry_clipboard(); + break; + } + default: + m_pTextCell->Copy(); + } +} + +void OSelectionBrowseBox::appendUndoAction(const OUString& _rOldValue, const OUString& _rNewValue, sal_Int32 _nRow, bool& _bListAction) +{ + if ( !m_bInUndoMode && _rNewValue != _rOldValue ) + { + if ( !_bListAction ) + { + _bListAction = true; + static_cast<OQueryController&>(getDesignView()->getController()).GetUndoManager().EnterListAction(OUString(),OUString(),0,ViewShellId(-1)); + } + appendUndoAction(_rOldValue,_rNewValue,_nRow); + } +} + +void OSelectionBrowseBox::appendUndoAction(const OUString& _rOldValue,const OUString& _rNewValue,sal_Int32 _nRow) +{ + if ( !m_bInUndoMode && _rNewValue != _rOldValue ) + { + std::unique_ptr<OTabFieldCellModifiedUndoAct> pUndoAct(new OTabFieldCellModifiedUndoAct(this)); + pUndoAct->SetCellIndex(_nRow); + OSL_ENSURE(GetColumnPos(GetCurColumnId()) != BROWSER_INVALIDID,"Current position isn't valid!"); + pUndoAct->SetColumnPosition( GetColumnPos(GetCurColumnId()) ); + pUndoAct->SetCellContents(_rOldValue); + getDesignView()->getController().addUndoActionAndInvalidate(std::move(pUndoAct)); + } +} + +IMPL_LINK_NOARG(OSelectionBrowseBox, OnInvalidateTimer, Timer *, void) +{ + static_cast<OQueryController&>(getDesignView()->getController()).InvalidateFeature(SID_CUT); + static_cast<OQueryController&>(getDesignView()->getController()).InvalidateFeature(SID_COPY); + static_cast<OQueryController&>(getDesignView()->getController()).InvalidateFeature(SID_PASTE); + if(!m_bStopTimer) + m_timerInvalidate.Start(); +} + +void OSelectionBrowseBox::stopTimer() +{ + m_bStopTimer = true; + if (m_timerInvalidate.IsActive()) + m_timerInvalidate.Stop(); +} + +void OSelectionBrowseBox::startTimer() +{ + m_bStopTimer = false; + if (!m_timerInvalidate.IsActive()) + m_timerInvalidate.Start(); +} + +OTableFields& OSelectionBrowseBox::getFields() const +{ + OQueryController& rController = static_cast<OQueryController&>(getDesignView()->getController()); + return rController.getTableFieldDesc(); +} + +void OSelectionBrowseBox::enableControl(const OTableFieldDescRef& _rEntry,Window* _pControl) +{ + bool bEnable = !_rEntry->isCondition(); + _pControl->Enable(bEnable); + _pControl->EnableInput(bEnable); +} + +void OSelectionBrowseBox::setTextCellContext(const OTableFieldDescRef& _rEntry,const OUString& _sText,const OString& _sHelpId) +{ + m_pTextCell->SetText(_sText); + m_pTextCell->ClearModifyFlag(); + if (!m_pTextCell->HasFocus()) + m_pTextCell->GrabFocus(); + + enableControl(_rEntry,m_pTextCell); + + if (m_pTextCell->GetHelpId() != _sHelpId) + // as TextCell is used in various contexts I will delete the cached HelpText + m_pTextCell->SetHelpText(OUString()); + m_pTextCell->SetHelpId(_sHelpId); +} + +void OSelectionBrowseBox::invalidateUndoRedo() +{ + OQueryController& rController = static_cast<OQueryController&>(getDesignView()->getController()); + rController.InvalidateFeature( ID_BROWSER_UNDO ); + rController.InvalidateFeature( ID_BROWSER_REDO ); + rController.InvalidateFeature( ID_BROWSER_QUERY_EXECUTE ); +} + +OTableFieldDescRef OSelectionBrowseBox::getEntry(OTableFields::size_type _nPos) +{ + // we have to check if we need a new entry at this position + OTableFields& aFields = getFields(); + OSL_ENSURE(aFields.size() > _nPos,"ColID is to great!"); + + OTableFieldDescRef pEntry = aFields[_nPos]; + OSL_ENSURE(pEntry.is(),"Invalid entry!"); + if ( !pEntry.is() ) + { + pEntry = new OTableFieldDesc(); + pEntry->SetColumnId( + GetColumnId(sal::static_int_cast< sal_uInt16 >(_nPos+1))); + aFields[_nPos] = pEntry; + } + return pEntry; +} + +void OSelectionBrowseBox::GetFocus() +{ + if(!IsEditing() && !m_bWasEditing) + ActivateCell(); + EditBrowseBox::GetFocus(); +} + +void OSelectionBrowseBox::DeactivateCell(bool _bUpdate) +{ + m_bWasEditing = true; + EditBrowseBox::DeactivateCell(_bUpdate); + m_bWasEditing = false; +} + +OUString OSelectionBrowseBox::GetRowDescription( sal_Int32 _nRow ) const +{ + OUString aLabel(DBA_RES(STR_QUERY_HANDLETEXT)); + + // from BROW_CRIT2_ROW onwards all rows are shown as "or" + sal_Int32 nToken = (_nRow >= GetBrowseRow(BROW_CRIT2_ROW)) + ? BROW_CRIT2_ROW : GetRealRow(_nRow); + return aLabel.getToken(nToken, ';'); +} + +OUString OSelectionBrowseBox::GetAccessibleObjectName( ::vcl::AccessibleBrowseBoxObjType _eObjType,sal_Int32 _nPosition) const +{ + OUString sRetText; + switch( _eObjType ) + { + case ::vcl::BBTYPE_ROWHEADERCELL: + sRetText = GetRowDescription(_nPosition); + break; + default: + sRetText = EditBrowseBox::GetAccessibleObjectDescription(_eObjType,_nPosition); + } + return sRetText; +} + +bool OSelectionBrowseBox::fillEntryTable(OTableFieldDescRef const & _pEntry,const OUString& _sTableName) +{ + bool bRet = false; + OJoinTableView::OTableWindowMap& rTabWinList = getDesignView()->getTableView()->GetTabWinMap(); + OJoinTableView::OTableWindowMap::const_iterator aIter = rTabWinList.find(_sTableName); + if(aIter != rTabWinList.end()) + { + OQueryTableWindow* pEntryTab = static_cast<OQueryTableWindow*>(aIter->second.get()); + if (pEntryTab) + { + _pEntry->SetTable(pEntryTab->GetTableName()); + _pEntry->SetTabWindow(pEntryTab); + bRet = true; + } + } + return bRet; +} + +void OSelectionBrowseBox::setFunctionCell(OTableFieldDescRef const & _pEntry) +{ + Reference< XConnection> xConnection = static_cast<OQueryController&>(getDesignView()->getController()).getConnection(); + if ( !xConnection.is() ) + return; + + // Aggregate functions in general only available with Core SQL + if ( lcl_SupportsCoreSQLGrammar(xConnection) ) + { + sal_Int32 nIdx {0}; + // if we have an asterisk, no other function than count is allowed + weld::ComboBox& rComboBox = m_pFunctionCell->get_widget(); + rComboBox.clear(); + rComboBox.append_text(m_aFunctionStrings.getToken(0, ';', nIdx)); + if ( isFieldNameAsterisk(_pEntry->GetField()) ) + rComboBox.append_text(m_aFunctionStrings.getToken(1, ';', nIdx)); // 2nd token: COUNT + else + { + const bool bSkipLastToken {_pEntry->isNumeric()}; + while (nIdx>0) + { + const OUString sTok {m_aFunctionStrings.getToken(0, ';', nIdx)}; + if (bSkipLastToken && nIdx<0) + break; + rComboBox.append_text(sTok); + } + } + + if ( _pEntry->IsGroupBy() ) + { + OSL_ENSURE(!_pEntry->isNumeric(),"Not allowed to combine group by and numeric values!"); + rComboBox.set_active_text(rComboBox.get_text(rComboBox.get_count() - 1)); + } + else if (rComboBox.find_text(_pEntry->GetFunction()) != -1) + rComboBox.set_active_text(_pEntry->GetFunction()); + else + rComboBox.set_active(0); + + enableControl(_pEntry, m_pFunctionCell); + } + else + { + // only COUNT(*) and COUNT("table".*) allowed + bool bCountRemoved = !isFieldNameAsterisk(_pEntry->GetField()); + weld::ComboBox& rComboBox = m_pFunctionCell->get_widget(); + if ( bCountRemoved ) + rComboBox.remove(1); + + if ( !bCountRemoved && rComboBox.get_count() < 2) + rComboBox.append_text(m_aFunctionStrings.getToken(2, ';')); // 2 -> COUNT + + if (rComboBox.find_text(_pEntry->GetFunction()) != -1) + rComboBox.set_active_text(_pEntry->GetFunction()); + else + rComboBox.set_active(0); + } +} + +Reference< XAccessible > OSelectionBrowseBox::CreateAccessibleCell( sal_Int32 _nRow, sal_uInt16 _nColumnPos ) +{ + OTableFieldDescRef pEntry; + if(getFields().size() > o3tl::make_unsigned(_nColumnPos - 1)) + pEntry = getFields()[_nColumnPos - 1]; + + if ( _nRow == BROW_VIS_ROW && pEntry.is() ) + return EditBrowseBox::CreateAccessibleCheckBoxCell( _nRow, _nColumnPos,pEntry->IsVisible() ? TRISTATE_TRUE : TRISTATE_FALSE ); + + return EditBrowseBox::CreateAccessibleCell( _nRow, _nColumnPos ); +} + +bool OSelectionBrowseBox::HasFieldByAliasName(const OUString& rFieldName, OTableFieldDescRef const & rInfo) const +{ + for (auto const& field : getFields()) + { + if ( field->GetFieldAlias() == rFieldName ) + { + *rInfo = *field; + return true; + } + } + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/SelectionBrowseBox.hxx b/dbaccess/source/ui/querydesign/SelectionBrowseBox.hxx new file mode 100644 index 000000000..1668b4913 --- /dev/null +++ b/dbaccess/source/ui/querydesign/SelectionBrowseBox.hxx @@ -0,0 +1,322 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_SELECTIONBROWSEBOX_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_SELECTIONBROWSEBOX_HXX + +#include <svtools/editbrowsebox.hxx> +#include <TableFieldDescription.hxx> +#include <TableWindowListBox.hxx> +#include <QEnumTypes.hxx> +#include <com/sun/star/sdbc/XConnection.hpp> + +namespace connectivity +{ + class OSQLParseNode; +} + +namespace dbaui +{ +#define BROW_FIELD_ROW 0 +#define BROW_COLUMNALIAS_ROW 1 +#define BROW_TABLE_ROW 2 +#define BROW_ORDER_ROW 3 +#define BROW_VIS_ROW 4 +#define BROW_FUNCTION_ROW 5 +#define BROW_CRIT1_ROW 6 +#define BROW_CRIT2_ROW 7 +#define BROW_CRIT3_ROW 8 +#define BROW_CRIT4_ROW 9 +#define BROW_CRIT5_ROW 10 +#define BROW_CRIT6_ROW 11 +#define BROW_ROW_CNT 12 + + class OQueryDesignView; + class OSelectionBrowseBox final : public ::svt::EditBrowseBox + { + friend class OQueryDesignView; + std::vector<bool> m_bVisibleRow; // at pos we find the RowId + Timer m_timerInvalidate; + + long m_nSeekRow; + BrowserMode m_nMode; // remember the BrowseModes + VclPtr<Edit> m_pTextCell; + VclPtr< ::svt::CheckBoxControl> m_pVisibleCell; + VclPtr< ::svt::ComboBoxControl> m_pFieldCell; + VclPtr< ::svt::ListBoxControl> m_pFunctionCell; + VclPtr< ::svt::ListBoxControl> m_pTableCell; + VclPtr< ::svt::ListBoxControl> m_pOrderCell; + + sal_Int32 m_nMaxColumns; // maximum number of columns in a Select-Statement + + OUString m_aFunctionStrings; + sal_uInt16 m_nVisibleCount; // maximum number of visible rows + sal_uInt32 m_nLastSortColumn; // index of last (highest) sort column + bool m_bOrderByUnRelated; + bool m_bGroupByUnRelated; + bool m_bStopTimer; + bool m_bWasEditing; + bool m_bDisableErrorBox; + bool m_bInUndoMode; + + DECL_LINK(OnInvalidateTimer, Timer*, void); + public: + explicit OSelectionBrowseBox( vcl::Window* pParent ); + virtual ~OSelectionBrowseBox() override; + virtual void dispose() override; + + void initialize(); + OTableFieldDescRef InsertField( const OJoinExchangeData& jxdSource ); + OTableFieldDescRef InsertField( const OTableFieldDescRef& rInfo, sal_uInt16 _nColumnPosition = BROWSER_INVALIDID, bool bVis=true, bool bActivate=true ); + void InsertColumn( const OTableFieldDescRef& pEntry, sal_uInt16& _nColumnPosition ); + void RemoveColumn( sal_uInt16 _nColumnId ); + void DeleteFields( const OUString& rAliasName ); + + bool HasFieldByAliasName(const OUString& rFieldName, OTableFieldDescRef const & rInfo) const; + + // AddGroupBy:: inserts a field with function == grouping. If the fields already exists and uses an aggregate function, + // the flag is not set + void AddGroupBy( const OTableFieldDescRef& rInfo ); + void AddCondition( const OTableFieldDescRef& rInfo, + const OUString& rValue, + const sal_uInt16 nLevel, + bool _bAddOrOnOneLine ); + void DuplicateConditionLevel( const sal_uInt16 nLevel); + void AddOrder(const OTableFieldDescRef& rInfo, const EOrderDir eDir, sal_uInt32 _nCurrentPos); + void ClearAll(); + OTableFieldDescRef const & AppendNewCol( sal_uInt16 nCnt=1 ); + bool Save(); + OQueryDesignView* getDesignView(); + OQueryDesignView* getDesignView() const; + sal_uInt16 FieldsCount(); + + void SetColWidth(sal_uInt16 nColId, long lNewWidth); + // unlike SetColumnWidth of the base class it checks an active cell in this column + + OUString GetCellContents(sal_Int32 nCellIndex, sal_uInt16 nColId); + void SetCellContents(sal_Int32 nCellIndex, sal_uInt16 nColId, const OUString& strNewText); + // cell content (formatted as string) set/return + sal_Int32 GetNoneVisibleRows() const; + void SetNoneVisbleRow(long nRows); + bool IsRowVisible(sal_uInt16 _nWhich) const; + void SetRowVisible(sal_uInt16 _nWhich, bool _bVis); + + void SetReadOnly(bool bRO); + // calculate an optimal size. Basically, this takes into account the number of visible rows. + Size CalcOptimalSize( const Size& _rAvailable ); + + // can the current content be cut + bool isPasteAllowed() const; + bool isCutAllowed() const; + bool isCopyAllowed() const; + void cut(); + void paste(); + void copy(); + + virtual void GetFocus() override; + virtual void DeactivateCell(bool bUpdate = true) override; + virtual void ColumnMoved( sal_uInt16 nColId ) override { ColumnMoved(nColId,true); } + void ColumnMoved( sal_uInt16 nColId, bool _bCreateUndo); + + void Fill(); + void PreFill(); + + /** Disables the generation of undo actions + */ + void EnterUndoMode() { m_bInUndoMode = true; } + /** Enables the generation of undo actions + */ + void LeaveUndoMode() { m_bInUndoMode = false; } + + /** GetCellText returns the text at the given position + @param _nRow + the number of the row + @param _nColId + the ID of the column + @return + the text out of the cell + */ + virtual OUString GetCellText(long _nRow, sal_uInt16 _nColId) const override; + + /** returns the description of the row. + @param _nRow + The row number. + @return + The header text of the specified row. + */ + virtual OUString GetRowDescription( sal_Int32 _nRow ) const override; + + /** return the name of the specified object. + @param eObjType + The type to ask for + @param _nPosition + The position of a tablecell (index position), header bar column/row cell + @return + The name of the specified object. + */ + virtual OUString GetAccessibleObjectName( ::vcl::AccessibleBrowseBoxObjType eObjType,sal_Int32 _nPosition = -1) const override; + + // IAccessibleTableProvider + /** Creates the accessible object of a data table cell. + @param nRow The row index of the cell. + @param nColumnId The column ID of the cell. + @return The XAccessible interface of the specified cell. */ + virtual css::uno::Reference< css::accessibility::XAccessible > CreateAccessibleCell( sal_Int32 nRow, sal_uInt16 nColumnId ) override; + + private: + virtual bool SeekRow( long nRow ) override; + + virtual void PaintStatusCell(OutputDevice& rDev, const tools::Rectangle& rRect) const override; + virtual void PaintCell(OutputDevice& rDev, const tools::Rectangle& rRect, + sal_uInt16 nColumnId ) const override; + + virtual sal_Int8 AcceptDrop( const BrowserAcceptDropEvent& rEvt ) override; + virtual sal_Int8 ExecuteDrop( const BrowserExecuteDropEvent& rEvt ) override; + virtual void MouseButtonDown( const BrowserMouseEvent& rEvt ) override; + virtual void MouseButtonUp( const BrowserMouseEvent& rEvt ) override; + virtual void KeyInput( const KeyEvent& rEvt ) override; + virtual void Command(const CommandEvent& rEvt) override; + + virtual ::svt::CellController* GetController(long nRow, sal_uInt16 nCol) override; + virtual void InitController(::svt::CellControllerRef& rController, long nRow, sal_uInt16 nCol) override; + virtual void CellModified() override; + virtual bool SaveModified() override; + virtual void Init() override; + virtual void ColumnResized( sal_uInt16 nColId ) override; + + virtual sal_uInt32 GetTotalCellWidth(long nRow, sal_uInt16 nColId) override; + + // if you want to have an own header ... + virtual VclPtr<BrowserHeader> imp_CreateHeaderBar(BrowseBox* pParent) override; + + void stopTimer(); + void startTimer(); + + OTableFieldDescRef FindFirstFreeCol(sal_uInt16& _rColumnPosition); + + // rCol contains the number (in pOTableFieldDescList) of the first column, which itself tells it is empty + // if there are none, rCol is undefined and the returnvalue NULL + void CheckFreeColumns(sal_uInt16& _rColumnPosition); + // checks if empty columns are available, if not, a new pack is appended + // rCol contains the number of the first empty column (in pOTableFieldDescList) + + void RemoveField( sal_uInt16 nId ); + tools::Rectangle GetInvalidRect( sal_uInt16 nColId ); + long GetRealRow(long nRow) const; + long GetBrowseRow(long nRowId) const; + bool GetFunctionName(sal_uInt32 _nFunctionTokenId, OUString& rFkt); + void appendUndoAction(const OUString& _rOldValue,const OUString& _rNewValue,sal_Int32 _nRow, bool& _bListAction); + void appendUndoAction(const OUString& _rOldValue,const OUString& _rNewValue,sal_Int32 _nRow); + OTableFields& getFields() const; + static void enableControl(const OTableFieldDescRef& _rEntry,Window* _pControl); + void setTextCellContext(const OTableFieldDescRef& _rEntry,const OUString& _sText,const OString& _sHelpId); + void invalidateUndoRedo(); + OTableFieldDescRef getEntry(OTableFields::size_type _nPos); + + void adjustSelectionMode( bool _bClickedOntoHeader, bool _bClickedOntoHandleCol ); + + /** save the field change in save modified + @param _sFieldName + The field name inserted by the user. + @param _pEntry + The entry which will contain the necessary entries. + @param _bListAction + Will be set to <TRUE/> when we are in a list action otherwise <FALSE/> + @return + <TRUE/> if an error occurred otherwise <FALSE/> + */ + bool saveField(OUString& _sFieldName, OTableFieldDescRef const & _pEntry, bool& _bListAction); + + /** sets the table window at the _pEntry + @param _pEntry + The entry where the window should be set. + @param _sTableName + The table name to search for. + @return + <TRUE/> if the table name was set otherwise <FALSE/> + */ + bool fillEntryTable(OTableFieldDescRef const & _pEntry,const OUString& _sTableName); + + /** uses the parse node to fill all information into the field + @param _pColumnRef + The parse node used to fill the info into the field. + @param _xMetaData + Use to parse the node to a string. + @param _pEntry + The entry which will contain the necessary entries. + @param _bListAction + Will be set to <TRUE/> when we are in a list action otherwise <FALSE/> + @return + <TRUE/> if an error occurred otherwise <FALSE/> + */ + bool fillColumnRef( const ::connectivity::OSQLParseNode* _pColumnRef, + const css::uno::Reference< css::sdbc::XConnection >& _rxConnection, + OTableFieldDescRef const & _pEntry, + bool& _bListAction); + bool fillColumnRef( const OUString& _sColumnName, + const OUString& _sTableRange, + const css::uno::Reference< css::sdbc::XDatabaseMetaData >& _xMetaData, + OTableFieldDescRef const & _pEntry, + bool& _bListAction); + + /** append an undo action for the table field + @param _sOldAlias + The old table alias. + @param _sAlias + The new alias name. + @param _bListAction + Will be set to <TRUE/> when we are in a list action otherwise <FALSE/> + */ + void notifyTableFieldChanged(const OUString& _sOldAlias,const OUString& _sAlias, bool& _bListAction,sal_uInt16 _nColumnId); + + /** append an undo action for the function field + @param _sOldFunctionName + The old value. + @param _sFunctionName + The new function name. + @param _bListAction + Will be set to <TRUE/> when we are in a list action otherwise <FALSE/> + */ + void notifyFunctionFieldChanged(const OUString& _sOldFunctionName,const OUString& _sFunctionName, bool& _bListAction,sal_uInt16 _nColumnId); + + /** clears the function fields of the submitted entry if it doesn't match the SQL standard and append an undo action. + E.q. AGGREGATE functions are only valid when the field name isn't an asterisk + @param _sFieldName + The field name. + @param _pEntry + The entry to be cleared + @param _bListAction + When <TRUE/> a list action will be created. + */ + void clearEntryFunctionField(const OUString& _sFieldName,OTableFieldDescRef const & _pEntry, bool& _bListAction,sal_uInt16 _nColumnId); + + /** remove or insert the necessary function types + @param _pEntry + The currently edited entry. + */ + void setFunctionCell(OTableFieldDescRef const & _pEntry); + + using ::svt::EditBrowseBox::AcceptDrop; + using ::svt::EditBrowseBox::ExecuteDrop; + using ::svt::EditBrowseBox::MouseButtonDown; + using ::svt::EditBrowseBox::MouseButtonUp; + }; +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_SELECTIONBROWSEBOX_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/TableConnection.cxx b/dbaccess/source/ui/querydesign/TableConnection.cxx new file mode 100644 index 000000000..ada4b5990 --- /dev/null +++ b/dbaccess/source/ui/querydesign/TableConnection.cxx @@ -0,0 +1,190 @@ +/* -*- 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 <TableConnection.hxx> +#include <ConnectionLine.hxx> +#include <TableConnectionData.hxx> +#include <JoinTableView.hxx> + +using namespace dbaui; +using namespace comphelper; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::accessibility; + +namespace dbaui +{ + OTableConnection::OTableConnection( OJoinTableView* _pContainer,const TTableConnectionData::value_type& _pTabConnData ) + :Window(_pContainer) + ,m_pData( _pTabConnData ) + ,m_pParent( _pContainer ) + ,m_bSelected( false ) + { + Init(); + Show(); + } + + OTableConnection::OTableConnection( const OTableConnection& _rConn ) + : VclReferenceBase() + ,Window(_rConn.m_pParent.get()) + ,m_pData(_rConn.GetData()->NewInstance()) + ,m_pParent(nullptr) + { + *this = _rConn; + } + + void OTableConnection::Init() + { + // initialise linelist with defaults + OConnectionLineDataVec& rLineData = GetData()->GetConnLineDataList(); + m_vConnLine.reserve(rLineData.size()); + for (auto const& elem : rLineData) + m_vConnLine.emplace_back( new OConnectionLine(this, elem) ); + } + + void OTableConnection::clearLineData() + { + m_vConnLine.clear(); + } + void OTableConnection::UpdateLineList() + { + // delete linelist + clearLineData(); + + Init(); + } + + OTableConnection& OTableConnection::operator=( const OTableConnection& rConn ) + { + if( &rConn == this ) + return *this; + + // delete linelist + clearLineData(); + + // copy linelist + if(! rConn.GetConnLineList().empty() ) + { + const std::vector<std::unique_ptr<OConnectionLine>>& rLine = rConn.GetConnLineList(); + m_vConnLine.reserve(rLine.size()); + for (auto const& elem : rLine) + m_vConnLine.emplace_back( new OConnectionLine(*elem)); + } + + // as the data are not mine, I also do not delete the old + m_pData->CopyFrom(*rConn.GetData()); + // CopyFrom is virtual, therefore it is not a problem if m_pData is a derived type of OTableConnectionData + + m_bSelected = rConn.m_bSelected; + m_pParent = rConn.m_pParent; + + return *this; + } + + void OTableConnection::RecalcLines() + { + // call RecalcLines on each line + for( const auto& pLine : m_vConnLine ) + pLine->RecalcLine(); + } + OTableWindow* OTableConnection::GetSourceWin() const + { + TTableWindowData::value_type pRef = GetData()->getReferencingTable(); + OTableWindow* pRet = m_pParent->GetTabWindow( pRef->GetWinName() ); + if ( !pRet ) + { + pRet = m_pParent->GetTabWindow( pRef->GetComposedName() ); + } + return pRet; + } + OTableWindow* OTableConnection::GetDestWin() const + { + TTableWindowData::value_type pRef = GetData()->getReferencedTable(); + OTableWindow* pRet = m_pParent->GetTabWindow( pRef->GetWinName() ); + if ( !pRet ) + { + pRet = m_pParent->GetTabWindow( pRef->GetComposedName() ); + } + return pRet; + } + + void OTableConnection::Select() + { + m_bSelected = true; + m_pParent->Invalidate( GetBoundingRect(), InvalidateFlags::NoChildren); + } + + void OTableConnection::Deselect() + { + m_bSelected = false; + InvalidateConnection(); + } + + bool OTableConnection::CheckHit( const Point& rMousePos ) const + { + // check if the point hit our line + return std::any_of(m_vConnLine.begin(), + m_vConnLine.end(), + [&rMousePos] + ( const std::unique_ptr<OConnectionLine> & pLine ) + { return pLine->CheckHit( rMousePos ); } ); + } + + void OTableConnection::InvalidateConnection() + { + tools::Rectangle rcBounding = GetBoundingRect(); + rcBounding.AdjustBottom(1 ); + rcBounding.AdjustRight(1 ); + // I believe Invalidate and Draw(Rectangle) do not behave consistent: in any case it + // could explain, why without the fake here when deleting a connection a dash remains at the lower end: + // Invalidate records obviously one pixel line less as Draw. + // Or everything works differently... in any case it works... + m_pParent->Invalidate( rcBounding, InvalidateFlags::NoChildren ); + } + + tools::Rectangle OTableConnection::GetBoundingRect() const + { + // determine all lines of the surrounding rectangle + tools::Rectangle aBoundingRect( Point(0,0), Point(0,0) ); + tools::Rectangle aTempRect; + for (auto const& elem : m_vConnLine) + { + aTempRect = elem->GetBoundingRect(); + + // is the BoundingRect of this line valid? + if( (aTempRect.GetWidth()!=1) && (aTempRect.GetHeight()!=1) ) + { + if( (aBoundingRect.GetWidth()==1) && (aBoundingRect.GetHeight()==1) ) + aBoundingRect = aTempRect; + else + aBoundingRect.Union( aTempRect ); + } + } + + return aBoundingRect; + } + + void OTableConnection::Draw(vcl::RenderContext& rRenderContext, const tools::Rectangle& /*rRect*/) + { + // Draw line + for( const auto& pLine : m_vConnLine ) + pLine->Draw( &rRenderContext ); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/TableConnectionData.cxx b/dbaccess/source/ui/querydesign/TableConnectionData.cxx new file mode 100644 index 000000000..107d8a9d1 --- /dev/null +++ b/dbaccess/source/ui/querydesign/TableConnectionData.cxx @@ -0,0 +1,147 @@ +/* -*- 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 <TableConnectionData.hxx> +#include <osl/diagnose.h> + +using namespace dbaui; + +OTableConnectionData::OTableConnectionData() +{ + Init(); +} + +OTableConnectionData::OTableConnectionData(const TTableWindowData::value_type& _pReferencingTable + ,const TTableWindowData::value_type& _pReferencedTable ) + :m_pReferencingTable(_pReferencingTable) + ,m_pReferencedTable(_pReferencedTable) +{ + Init(); +} + +void OTableConnectionData::Init() +{ + // initialise linedatalist with defaults + OSL_ENSURE(m_vConnLineData.empty(), "OTableConnectionData::Init() : call only with empty line list!"); + ResetConnLines(); + // this creates the defaults +} + +OTableConnectionData::OTableConnectionData( const OTableConnectionData& rConnData ) +{ + *this = rConnData; +} + +void OTableConnectionData::CopyFrom(const OTableConnectionData& rSource) +{ + *this = rSource; + // here I revert to the (non-virtual) operator =, which only copies my members +} + +OTableConnectionData::~OTableConnectionData() +{ + // delete LineDataList + OConnectionLineDataVec().swap(m_vConnLineData); +} + +OTableConnectionData& OTableConnectionData::operator=( const OTableConnectionData& rConnData ) +{ + if (&rConnData == this) + return *this; + + m_pReferencingTable = rConnData.m_pReferencingTable; + m_pReferencedTable = rConnData.m_pReferencedTable; + m_aConnName = rConnData.m_aConnName; + + // clear line list + ResetConnLines(); + + // and copy + for (auto const& elem : rConnData.GetConnLineDataList()) + m_vConnLineData.push_back(new OConnectionLineData(*elem)); + + return *this; +} + +void OTableConnectionData::SetConnLine( sal_uInt16 nIndex, const OUString& rSourceFieldName, const OUString& rDestFieldName ) +{ + if (sal_uInt16(m_vConnLineData.size()) < nIndex) + return; + + // == still allowed, this corresponds to an Append + + if (m_vConnLineData.size() == nIndex) + { + AppendConnLine(rSourceFieldName, rDestFieldName); + return; + } + + OConnectionLineDataRef pConnLineData = m_vConnLineData[nIndex]; + OSL_ENSURE(pConnLineData != nullptr, "OTableConnectionData::SetConnLine : have invalid LineData object"); + + pConnLineData->SetSourceFieldName( rSourceFieldName ); + pConnLineData->SetDestFieldName( rDestFieldName ); +} + +bool OTableConnectionData::AppendConnLine( const OUString& rSourceFieldName, const OUString& rDestFieldName ) +{ + for (auto const& elem : m_vConnLineData) + { + if(elem->GetDestFieldName() == rDestFieldName && elem->GetSourceFieldName() == rSourceFieldName) + return true; + } + OConnectionLineDataRef pNew = new OConnectionLineData(rSourceFieldName, rDestFieldName); + if (!pNew.is()) + return false; + + m_vConnLineData.push_back(pNew); + return true; +} + +void OTableConnectionData::ResetConnLines() +{ + OConnectionLineDataVec().swap(m_vConnLineData); +} + +std::shared_ptr<OTableConnectionData> OTableConnectionData::NewInstance() const +{ + return std::make_shared<OTableConnectionData>(); +} + +OConnectionLineDataVec::size_type OTableConnectionData::normalizeLines() +{ + // remove empty lines + OConnectionLineDataVec::size_type nCount = m_vConnLineData.size(); + OConnectionLineDataVec::size_type nRet = nCount; + for(OConnectionLineDataVec::size_type i = 0; i < nCount;) + { + if(m_vConnLineData[i]->GetSourceFieldName().isEmpty() && m_vConnLineData[i]->GetDestFieldName().isEmpty()) + { + m_vConnLineData.erase(m_vConnLineData.begin()+i); + --nCount; + if (i < nRet) + nRet=i; + } + else + ++i; + } + return nRet; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/TableFieldDescription.cxx b/dbaccess/source/ui/querydesign/TableFieldDescription.cxx new file mode 100644 index 000000000..95476cafd --- /dev/null +++ b/dbaccess/source/ui/querydesign/TableFieldDescription.cxx @@ -0,0 +1,196 @@ +/* -*- 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 <TableFieldDescription.hxx> + +#include <osl/diagnose.h> +#include <com/sun/star/sdbc/DataType.hpp> +#include <comphelper/namedvaluecollection.hxx> +#include <vcl/window.hxx> + +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace comphelper; +using namespace dbaui; + +OTableFieldDesc::OTableFieldDesc() + :m_pTabWindow(nullptr) + ,m_eDataType(1000) + ,m_eFunctionType( FKT_NONE ) + ,m_eFieldType(TAB_NORMAL_FIELD) + ,m_eOrderDir( ORDER_NONE ) + ,m_nIndex(0) + ,m_nColWidth(0) + ,m_nColumnId(sal_uInt16(-1)) + ,m_bGroupBy(false) + ,m_bVisible(false) +{ +} + +OTableFieldDesc::OTableFieldDesc(const OTableFieldDesc& rRS) + : ::salhelper::SimpleReferenceObject() + , m_pTabWindow(nullptr) +{ + *this = rRS; +} + +OTableFieldDesc::OTableFieldDesc(const OUString& rT, const OUString& rF ) + :m_pTabWindow(nullptr) + ,m_eDataType(1000) + ,m_eFunctionType( FKT_NONE ) + ,m_eFieldType(TAB_NORMAL_FIELD) + ,m_eOrderDir( ORDER_NONE ) + ,m_nIndex(0) + ,m_nColWidth(0) + ,m_nColumnId(sal_uInt16(-1)) + ,m_bGroupBy(false) + ,m_bVisible(false) +{ + SetField( rF ); SetTable( rT ); +} + +OTableFieldDesc::~OTableFieldDesc() +{ +} + +OTableFieldDesc& OTableFieldDesc::operator=( const OTableFieldDesc& rRS ) +{ + if (&rRS == this) + return *this; + + m_aCriteria = rRS.GetCriteria(); + m_aTableName = rRS.GetTable(); + m_aAliasName = rRS.GetAlias(); // table range + m_aFieldName = rRS.GetField(); // column + m_aFieldAlias = rRS.GetFieldAlias(); // column alias + m_aFunctionName = rRS.GetFunction(); + m_pTabWindow = rRS.GetTabWindow(); + m_eDataType = rRS.GetDataType(); + m_eFunctionType = rRS.GetFunctionType(); + m_eFieldType = rRS.GetFieldType(); + m_eOrderDir = rRS.GetOrderDir(); + m_nIndex = rRS.GetFieldIndex(); + m_nColWidth = rRS.GetColWidth(); + m_nColumnId = rRS.m_nColumnId; + m_bGroupBy = rRS.IsGroupBy(); + m_bVisible = rRS.IsVisible(); + + return *this; +} + +void OTableFieldDesc::SetCriteria( sal_uInt16 nIdx, const OUString& rCrit) +{ + if (nIdx < m_aCriteria.size()) + m_aCriteria[nIdx] = rCrit; + else + { + m_aCriteria.insert(m_aCriteria.end(), nIdx - m_aCriteria.size(), OUString()); + m_aCriteria.push_back(rCrit); + } +} + +OUString OTableFieldDesc::GetCriteria( sal_uInt16 nIdx ) const +{ + OUString aRetStr; + if( nIdx < m_aCriteria.size()) + aRetStr = m_aCriteria[nIdx]; + + return aRetStr; +} + +namespace +{ + struct SelectPropertyValueAsString + { + OUString operator()( const PropertyValue& i_rPropValue ) const + { + OUString sValue; + OSL_VERIFY( i_rPropValue.Value >>= sValue ); + return sValue; + } + }; +} + +void OTableFieldDesc::Load( const css::beans::PropertyValue& i_rSettings, const bool i_bIncludingCriteria ) +{ + + ::comphelper::NamedValueCollection aFieldDesc( i_rSettings.Value ); + m_aAliasName = aFieldDesc.getOrDefault( "AliasName", m_aAliasName ); + m_aTableName = aFieldDesc.getOrDefault( "TableName", m_aTableName ); + m_aFieldName = aFieldDesc.getOrDefault( "FieldName", m_aFieldName ); + m_aFieldAlias = aFieldDesc.getOrDefault( "FieldAlias", m_aFieldAlias ); + m_aFunctionName = aFieldDesc.getOrDefault( "FunctionName", m_aFunctionName ); + m_eDataType = aFieldDesc.getOrDefault( "DataType", m_eDataType ); + m_eFunctionType = aFieldDesc.getOrDefault( "FunctionType", m_eFunctionType ); + m_nColWidth = aFieldDesc.getOrDefault( "ColWidth", m_nColWidth ); + m_bGroupBy = aFieldDesc.getOrDefault( "GroupBy", m_bGroupBy ); + m_bVisible = aFieldDesc.getOrDefault( "Visible", m_bVisible ); + + m_eFieldType = static_cast< ETableFieldType >( aFieldDesc.getOrDefault( "FieldType", static_cast< sal_Int32 >( m_eFieldType ) ) ); + m_eOrderDir = static_cast< EOrderDir >( aFieldDesc.getOrDefault( "OrderDir", static_cast< sal_Int32 >( m_eOrderDir ) ) ); + + if ( i_bIncludingCriteria ) + { + const Sequence< PropertyValue > aCriteria( aFieldDesc.getOrDefault( "Criteria", Sequence< PropertyValue >() ) ); + m_aCriteria.resize( aCriteria.getLength() ); + std::transform( + aCriteria.begin(), + aCriteria.end(), + m_aCriteria.begin(), + SelectPropertyValueAsString() + ); + } +} + +void OTableFieldDesc::Save( ::comphelper::NamedValueCollection& o_rSettings, const bool i_bIncludingCriteria ) +{ + + o_rSettings.put( "AliasName", m_aAliasName ); + o_rSettings.put( "TableName", m_aTableName ); + o_rSettings.put( "FieldName", m_aFieldName ); + o_rSettings.put( "FieldAlias", m_aFieldAlias ); + o_rSettings.put( "FunctionName", m_aFunctionName ); + o_rSettings.put( "DataType", m_eDataType ); + o_rSettings.put( "FunctionType", m_eFunctionType ); + o_rSettings.put( "FieldType", static_cast<sal_Int32>(m_eFieldType) ); + o_rSettings.put( "OrderDir", static_cast<sal_Int32>(m_eOrderDir) ); + o_rSettings.put( "ColWidth", m_nColWidth ); + o_rSettings.put( "GroupBy", m_bGroupBy ); + o_rSettings.put( "Visible", m_bVisible ); + + if ( !i_bIncludingCriteria ) + return; + + if ( m_aCriteria.empty() ) + return; + + sal_Int32 c = 0; + Sequence< PropertyValue > aCriteria( m_aCriteria.size() ); + for (auto const& criteria : m_aCriteria) + { + aCriteria[c].Name = "Criterion_" + OUString::number( c ); + aCriteria[c].Value <<= criteria; + ++c; + } + + o_rSettings.put( "Criteria", aCriteria ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/TableFieldInfo.cxx b/dbaccess/source/ui/querydesign/TableFieldInfo.cxx new file mode 100644 index 000000000..15c60b833 --- /dev/null +++ b/dbaccess/source/ui/querydesign/TableFieldInfo.cxx @@ -0,0 +1,33 @@ +/* -*- 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 "TableFieldInfo.hxx" + +using namespace dbaui; + +OTableFieldInfo::OTableFieldInfo() : + m_eFieldType(TAB_NORMAL_FIELD), m_eDataType(1000) +{ +} + +OTableFieldInfo::~OTableFieldInfo() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/TableFieldInfo.hxx b/dbaccess/source/ui/querydesign/TableFieldInfo.hxx new file mode 100644 index 000000000..7d0e83b4c --- /dev/null +++ b/dbaccess/source/ui/querydesign/TableFieldInfo.hxx @@ -0,0 +1,46 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_TABLEFIELDINFO_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_TABLEFIELDINFO_HXX + +#include <QEnumTypes.hxx> +#include <sal/types.h> + +namespace dbaui +{ + class OTableFieldInfo + { + private: + ETableFieldType m_eFieldType; + sal_Int32 m_eDataType; + + public: + OTableFieldInfo(); + ~OTableFieldInfo(); + + ETableFieldType GetKeyType() const { return m_eFieldType; } + void SetKey(ETableFieldType bKey) { m_eFieldType = bKey; } + sal_Int32 GetDataType() const { return m_eDataType; } + void SetDataType(sal_Int32 eTyp) { m_eDataType = eTyp; } + }; +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_TABLEFIELDINFO_HXX + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/TableWindow.cxx b/dbaccess/source/ui/querydesign/TableWindow.cxx new file mode 100644 index 000000000..d32ba27b9 --- /dev/null +++ b/dbaccess/source/ui/querydesign/TableWindow.cxx @@ -0,0 +1,744 @@ +/* -*- 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 <vcl/svapp.hxx> +#include <vcl/wall.hxx> +#include <vcl/settings.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> +#include <vcl/ptrstyle.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> +#include <vcl/treelistentry.hxx> +#include <vcl/builder.hxx> + +using namespace dbaui; +using namespace ::utl; +using namespace ::com::sun::star; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::lang; +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, const TTableWindowData::value_type& pTabWinData ) + : ::comphelper::OContainerListener(m_aMutex) + ,Window( pParent, WB_3DLOOK|WB_MOVEABLE ) + ,m_aTypeImage( VclPtr<FixedImage>::Create(this) ) + ,m_xTitle( VclPtr<OTableWindowTitle>::Create(this) ) + ,m_pData( 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->GetEntryCount()==0,"Forgot to call EmptyListbox()!"); + } + m_xListBox.disposeAndClear(); + if ( m_pContainerListener.is() ) + m_pContainerListener->dispose(); + + m_aTypeImage.disposeAndClear(); + 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() +{ + m_xListBox->Clear(); + 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 + Image aPrimKeyImage(StockImage::Yes, BMP_PRIMARY_KEY); + + if (GetData()->IsShowAll()) + { + SvTreeListEntry* pEntry = m_xListBox->InsertEntry( OUString("*") ); + pEntry->SetUserData( createUserData(nullptr,false) ); + } + + Reference<XNameAccess> xPKeyColumns; + try + { + xPKeyColumns = dbtools::getPrimaryKeyColumns_throw(m_pData->getTable()); + } + catch(Exception&) + { + OSL_FAIL("Exception occurred!"); + } + try + { + Reference< XNameAccess > xColumns = m_pData->getColumns(); + if( xColumns.is() ) + { + Sequence< OUString> aColumns = xColumns->getElementNames(); + const OUString* pIter = aColumns.getConstArray(); + const OUString* pEnd = pIter + aColumns.getLength(); + + SvTreeListEntry* pEntry = nullptr; + for (; pIter != pEnd; ++pIter) + { + bool bPrimaryKeyColumn = xPKeyColumns.is() && xPKeyColumns->hasByName( *pIter ); + // is this column in the primary key + if ( bPrimaryKeyColumn ) + pEntry = m_xListBox->InsertEntry(*pIter, aPrimKeyImage, aPrimKeyImage); + else + pEntry = m_xListBox->InsertEntry(*pIter); + + Reference<XPropertySet> xColumn(xColumns->getByName(*pIter),UNO_QUERY); + if ( xColumn.is() ) + pEntry->SetUserData( createUserData(xColumn,bPrimaryKeyColumn) ); + } + } + } + catch(Exception&) + { + OSL_FAIL("Exception occurred!"); + } +} + +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; + + SvTreeListEntry* pEntry = m_xListBox->First(); + + while(pEntry) + { + void* pUserData = pEntry->GetUserData(); + deleteUserData(pUserData); + SvTreeListEntry* pNextEntry = m_xListBox->Next(pEntry); + m_xListBox->GetModel()->Remove(pEntry); + pEntry = pNextEntry; + } +} + +void OTableWindow::impl_updateImage() +{ + ImageProvider aImageProvider( getDesignView()->getController().getConnection() ); + + Image aImage; + aImageProvider.getImages( GetComposedName(), m_pData->isQuery() ? DatabaseObject::QUERY : DatabaseObject::TABLE, aImage ); + + if ( !aImage ) + { + OSL_FAIL( "OTableWindow::impl_updateImage: no images!" ); + return; + } + + m_aTypeImage->SetModeImage( aImage ); + m_aTypeImage->Show(); +} + +bool OTableWindow::Init() +{ + // create list box if necessary + if ( !m_xListBox ) + { + m_xListBox = VclPtr<OTableWindowListBox>::Create(this); + OSL_ENSURE( m_xListBox != nullptr, "OTableWindow::Init() : CreateListBox returned NULL !" ); + m_xListBox->SetSelectionMode( SelectionMode::Multiple ); + } + + // Set the title + m_xTitle->SetText( m_pData->GetWinName() ); + m_xTitle->Show(); + + m_xListBox->Show(); + + // add the fields to the ListBox + clearListBox(); + FillListBox(); + m_xListBox->SelectAll( false ); + + 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())); + + long nTitleHeight = CalcZoom( GetTextHeight() )+ CalcZoom( 4 ); + + // Set the title and ListBox + long n5Pos = CalcZoom(5); + long nPositionX = n5Pos; + long nPositionY = n5Pos; + + // position the image which indicates the type + m_aTypeImage->SetPosPixel( Point( nPositionX, nPositionY ) ); + Size aImageSize( m_aTypeImage->GetImage().GetSizePixel() ); + m_aTypeImage->SetSizePixel( aImageSize ); + + if ( nTitleHeight < aImageSize.Height() ) + nTitleHeight = aImageSize.Height(); + + nPositionX += aImageSize.Width() + CalcZoom( 2 ); + m_xTitle->SetPosSizePixel( Point( nPositionX, nPositionY ), Size( aOutSize.Width() - nPositionX - n5Pos, nTitleHeight ) ); + + 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 ) +{ + vcl::Font aFont = m_xTitle->GetFont(); + aFont.SetWeight( bBold?WEIGHT_BOLD:WEIGHT_NORMAL ); + m_xTitle->SetFont( aFont ); + m_xTitle->Invalidate(); +} + +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 && m_xListBox->GetSelectionCount() != 0) + m_xListBox->SelectAll(false); +} + +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::HandleKeyInput( const KeyEvent& rEvt ) +{ + const vcl::KeyCode& rCode = rEvt.GetKeyCode(); + sal_uInt16 nCode = rCode.GetCode(); + bool bShift = rCode.IsShift(); + bool bCtrl = rCode.IsMod1(); + + bool bHandle = false; + + if( !bCtrl && !bShift && (nCode==KEY_DELETE) ) + { + Remove(); + bHandle = true; + } + return bHandle; +} + +bool OTableWindow::ExistsAConn() const +{ + return getTableView()->ExistsAConn(this); +} + +void OTableWindow::EnumValidFields(std::vector< OUString>& arrstrFields) +{ + arrstrFields.clear(); + // This default implementation counts every item in the ListBox ... for any other behaviour it must be over-written + if ( m_xListBox ) + { + arrstrFields.reserve(m_xListBox->GetEntryCount()); + SvTreeListEntry* pEntryLoop = m_xListBox->First(); + while (pEntryLoop) + { + arrstrFields.push_back(m_xListBox->GetEntryText(pEntryLoop)); + pEntryLoop = m_xListBox->Next(pEntryLoop); + } + } +} + +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(*this, 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 + { + SvTreeListEntry* pCurrent = m_xListBox->GetCurEntry(); + if ( pCurrent ) + ptWhere = m_xListBox->GetEntryPosition(pCurrent); + else + ptWhere = m_xTitle->GetPosPixel(); + } + + VclBuilder aBuilder(nullptr, VclBuilderContainer::getUIRootDir(), "dbaccess/ui/jointablemenu.ui", ""); + VclPtr<PopupMenu> aContextMenu(aBuilder.get_menu("menu")); + if (aContextMenu->Execute(this, ptWhere)) + Remove(); + } + break; + } + default: + Window::Command(rEvt); + } +} + +bool OTableWindow::PreNotify(NotifyEvent& rNEvt) +{ + bool bHandled = false; + switch (rNEvt.GetType()) + { + case MouseNotifyEvent::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 MouseNotifyEvent::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->GetText(); +} + +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: */ diff --git a/dbaccess/source/ui/querydesign/TableWindowAccess.cxx b/dbaccess/source/ui/querydesign/TableWindowAccess.cxx new file mode 100644 index 000000000..3cd87ded7 --- /dev/null +++ b/dbaccess/source/ui/querydesign/TableWindowAccess.cxx @@ -0,0 +1,250 @@ +/* -*- 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 <TableWindowAccess.hxx> +#include <TableWindow.hxx> +#include <TableWindowListBox.hxx> +#include <JoinTableView.hxx> +#include <com/sun/star/accessibility/AccessibleRole.hpp> +#include <com/sun/star/accessibility/AccessibleRelationType.hpp> +#include <com/sun/star/lang/IndexOutOfBoundsException.hpp> +#include <comphelper/sequence.hxx> + +namespace dbaui +{ + using namespace ::com::sun::star::accessibility; + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star; + + OTableWindowAccess::OTableWindowAccess(OTableWindow* _pTable) + :VCLXAccessibleComponent(_pTable->GetComponentInterface().is() ? _pTable->GetWindowPeer() : nullptr) + ,m_pTable(_pTable) + { + } + void SAL_CALL OTableWindowAccess::disposing() + { + m_pTable = nullptr; + VCLXAccessibleComponent::disposing(); + } + void OTableWindowAccess::ProcessWindowEvent( const VclWindowEvent& rVclWindowEvent ) + { + if ( rVclWindowEvent.GetId() == VclEventId::ObjectDying ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + m_pTable = nullptr; + } + + VCLXAccessibleComponent::ProcessWindowEvent( rVclWindowEvent ); + } + Any SAL_CALL OTableWindowAccess::queryInterface( const Type& aType ) + { + Any aRet(VCLXAccessibleComponent::queryInterface( aType )); + return aRet.hasValue() ? aRet : OTableWindowAccess_BASE::queryInterface( aType ); + } + Sequence< Type > SAL_CALL OTableWindowAccess::getTypes( ) + { + return ::comphelper::concatSequences(VCLXAccessibleComponent::getTypes(),OTableWindowAccess_BASE::getTypes()); + } + OUString SAL_CALL OTableWindowAccess::getImplementationName() + { + return "org.openoffice.comp.dbu.TableWindowAccessibility"; + } + Sequence< OUString > SAL_CALL OTableWindowAccess::getSupportedServiceNames() + { + return { "com.sun.star.accessibility.Accessible", + "com.sun.star.accessibility.AccessibleContext" }; + } + // XAccessibleContext + sal_Int32 SAL_CALL OTableWindowAccess::getAccessibleChildCount( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + sal_Int32 nCount = 0; + if(m_pTable) + { + ++nCount; + if(m_pTable->GetListBox()) + ++nCount; + } + return nCount; + } + Reference< XAccessible > SAL_CALL OTableWindowAccess::getAccessibleChild( sal_Int32 i ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + Reference< XAccessible > aRet; + if (m_pTable && !m_pTable->IsDisposed()) + { + switch(i) + { + case 0: + { + VclPtr<OTableWindowTitle> xCtrl(m_pTable->GetTitleCtrl()); + if (xCtrl) + aRet = xCtrl->GetAccessible(); + break; + } + case 1: + { + VclPtr<OTableWindowListBox> xCtrl(m_pTable->GetListBox()); + if (xCtrl) + aRet = xCtrl->GetAccessible(); + break; + } + default: + throw IndexOutOfBoundsException(); + } + } + return aRet; + } + sal_Int32 SAL_CALL OTableWindowAccess::getAccessibleIndexInParent( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + sal_Int32 nIndex = -1; + if( m_pTable ) + { + // search the position of our table window in the table window map + bool bFoundElem = false; + for (auto const& tabWin : m_pTable->getTableView()->GetTabWinMap()) + { + if (tabWin.second == m_pTable) + { + bFoundElem = true; + break; + } + ++nIndex; + } + nIndex = bFoundElem? nIndex : -1; + } + return nIndex; + } + sal_Int16 SAL_CALL OTableWindowAccess::getAccessibleRole( ) + { + return AccessibleRole::PANEL; // ? or may be an AccessibleRole::WINDOW + } + Reference< XAccessibleRelationSet > SAL_CALL OTableWindowAccess::getAccessibleRelationSet( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + return this; + } + // XAccessibleComponent + Reference< XAccessible > SAL_CALL OTableWindowAccess::getAccessibleAtPoint( const awt::Point& _aPoint ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + Reference< XAccessible > aRet; + if(m_pTable && !m_pTable->IsDisposed()) + { + Point aPoint(_aPoint.X,_aPoint.Y); + tools::Rectangle aRect(m_pTable->GetDesktopRectPixel()); + if( aRect.IsInside(aPoint) ) + aRet = this; + else if( m_pTable->GetListBox()->GetDesktopRectPixel().IsInside(aPoint)) + aRet = m_pTable->GetListBox()->GetAccessible(); + } + return aRet; + } + Reference< XAccessible > OTableWindowAccess::getParentChild(sal_Int32 _nIndex) + { + Reference< XAccessible > xReturn; + Reference< XAccessible > xParent = getAccessibleParent(); + if ( xParent.is() ) + { + Reference< XAccessibleContext > xParentContext = xParent->getAccessibleContext(); + if ( xParentContext.is() ) + { + xReturn = xParentContext->getAccessibleChild(_nIndex); + } + } + return xReturn; + } + + sal_Int32 SAL_CALL OTableWindowAccess::getRelationCount( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + return m_pTable ? m_pTable->getTableView()->getConnectionCount(m_pTable) : sal_Int32(0); + } + AccessibleRelation SAL_CALL OTableWindowAccess::getRelation( sal_Int32 nIndex ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if( nIndex < 0 || nIndex >= getRelationCount() ) + throw IndexOutOfBoundsException(); + + AccessibleRelation aRet; + if( m_pTable ) + { + OJoinTableView* pView = m_pTable->getTableView(); + auto aIter = pView->getTableConnections(m_pTable) + nIndex; + aRet.TargetSet.realloc(1); + aRet.TargetSet[0] = getParentChild(aIter - pView->getTableConnections().begin()); + aRet.RelationType = AccessibleRelationType::CONTROLLER_FOR; + } + return aRet; + } + sal_Bool SAL_CALL OTableWindowAccess::containsRelation( sal_Int16 aRelationType ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + return AccessibleRelationType::CONTROLLER_FOR == aRelationType + && m_pTable && m_pTable->getTableView()->ExistsAConn(m_pTable); + } + AccessibleRelation SAL_CALL OTableWindowAccess::getRelationByType( sal_Int16 aRelationType ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + if( AccessibleRelationType::CONTROLLER_FOR == aRelationType && m_pTable) + { + OJoinTableView* pView = m_pTable->getTableView(); + const auto& rConnectionList = pView->getTableConnections(); + + auto aIter = pView->getTableConnections(m_pTable); + auto aEnd = rConnectionList.end(); + std::vector< Reference<XInterface> > aRelations; + aRelations.reserve(5); // just guessing + // TODO JNA aIter comes from pView->getTableConnections(m_pTable) + // and aEnd comes from pView->getTableConnections().end() + for (; aIter != aEnd ; ++aIter ) + { + uno::Reference<uno::XInterface> xInterface( + getParentChild(aIter - rConnectionList.begin())); + aRelations.push_back(xInterface); + } + + Sequence< Reference<XInterface> > aSeq(aRelations.data(), aRelations.size()); + return AccessibleRelation(AccessibleRelationType::CONTROLLER_FOR,aSeq); + } + return AccessibleRelation(); + } + OUString SAL_CALL OTableWindowAccess::getTitledBorderText( ) + { + return getAccessibleName( ); + } + OUString SAL_CALL OTableWindowAccess::getAccessibleName( ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + OUString sAccessibleName; + if ( m_pTable ) + sAccessibleName = m_pTable->getTitle(); + return sAccessibleName; + } + Reference< XAccessibleContext > SAL_CALL OTableWindowAccess::getAccessibleContext( ) + { + return this; + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/TableWindowData.cxx b/dbaccess/source/ui/querydesign/TableWindowData.cxx new file mode 100644 index 000000000..d07c8214a --- /dev/null +++ b/dbaccess/source/ui/querydesign/TableWindowData.cxx @@ -0,0 +1,135 @@ +/* -*- 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 <TableWindowData.hxx> +#include <osl/diagnose.h> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdbcx/XColumnsSupplier.hpp> +#include <com/sun/star/sdbcx/XKeysSupplier.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/lang/XComponent.hpp> + +using namespace dbaui; +using namespace ::com::sun::star::lang; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; + +OTableWindowData::OTableWindowData( const Reference< XPropertySet>& _xTable + ,const OUString& _rComposedName + ,const OUString& rTableName + ,const OUString& rWinName ) + :m_xTable(_xTable) + ,m_aTableName( rTableName ) + ,m_aWinName( rWinName ) + ,m_sComposedName(_rComposedName) + ,m_aPosition( Point(-1,-1) ) + ,m_aSize( Size(-1,-1) ) + ,m_bShowAll( true ) + ,m_bIsQuery(false) + ,m_bIsValid(true) +{ + if( m_aWinName.isEmpty() ) + m_aWinName = m_aTableName; + + listen(); +} + +OTableWindowData::~OTableWindowData() +{ + Reference<XComponent> xComponent( m_xTable, UNO_QUERY ); + if ( xComponent.is() ) + stopComponentListening( xComponent ); +} + +bool OTableWindowData::HasPosition() const +{ + return ( (m_aPosition.X() != -1) && (m_aPosition.Y() != -1) ); +} + +bool OTableWindowData::HasSize() const +{ + return ( (m_aSize.Width() != -1) && (m_aSize.Height() !=-1) ); +} + +void OTableWindowData::_disposing( const css::lang::EventObject& /*_rSource*/ ) +{ + ::osl::MutexGuard aGuard( m_aMutex ); + // it doesn't matter which one was disposed + m_xColumns.clear(); + m_xKeys.clear(); + m_xTable.clear(); +} + +bool OTableWindowData::init(const Reference< XConnection >& _xConnection,bool _bAllowQueries) +{ + OSL_ENSURE(!m_xTable.is(),"We are already connected to a table!"); + + ::osl::MutexGuard aGuard( m_aMutex ); + + Reference< XQueriesSupplier > xSupQueries( _xConnection, UNO_QUERY_THROW ); + Reference< XNameAccess > xQueries( xSupQueries->getQueries(), UNO_SET_THROW ); + bool bIsKnownQuery = _bAllowQueries && xQueries->hasByName( m_sComposedName ); + + Reference< XTablesSupplier > xSupTables( _xConnection, UNO_QUERY_THROW ); + Reference< XNameAccess > xTables( xSupTables->getTables(), UNO_SET_THROW ); + bool bIsKnownTable = xTables->hasByName( m_sComposedName ); + + if ( bIsKnownQuery ) + m_xTable.set( xQueries->getByName( m_sComposedName ), UNO_QUERY ); + else if ( bIsKnownTable ) + m_xTable.set( xTables->getByName( m_sComposedName ), UNO_QUERY ); + else + m_bIsValid = false; + + // if we survived so far, we know whether it's a query + m_bIsQuery = bIsKnownQuery; + + listen(); + + Reference< XIndexAccess > xColumnsAsIndex( m_xColumns,UNO_QUERY ); + return xColumnsAsIndex.is() && ( xColumnsAsIndex->getCount() > 0 ); +} + +void OTableWindowData::listen() +{ + if ( !m_xTable.is() ) + return; + + // listen for the object being disposed + Reference<XComponent> xComponent( m_xTable, UNO_QUERY ); + if ( xComponent.is() ) + startComponentListening( xComponent ); + + // obtain the columns + Reference< XColumnsSupplier > xColumnsSups( m_xTable, UNO_QUERY); + if ( xColumnsSups.is() ) + m_xColumns = xColumnsSups->getColumns(); + + Reference<XKeysSupplier> xKeySup(m_xTable,UNO_QUERY); + if ( xKeySup.is() ) + m_xKeys = xKeySup->getKeys(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/TableWindowListBox.cxx b/dbaccess/source/ui/querydesign/TableWindowListBox.cxx new file mode 100644 index 000000000..a4ba5f002 --- /dev/null +++ b/dbaccess/source/ui/querydesign/TableWindowListBox.cxx @@ -0,0 +1,370 @@ +/* -*- 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 <TableWindowListBox.hxx> +#include <TableWindow.hxx> +#include <JoinController.hxx> +#include <JoinExchange.hxx> +#include <JoinTableView.hxx> +#include <JoinDesignView.hxx> +#include <osl/diagnose.h> +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <vcl/svapp.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> + +using namespace dbaui; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::datatransfer; + +OJoinExchangeData::OJoinExchangeData(OTableWindowListBox* pBox) + : pListBox(pBox) + , pEntry(pBox->FirstSelected()) +{ } + +const sal_uLong SCROLLING_TIMESPAN = 500; +const long LISTBOX_SCROLLING_AREA = 6; +OTableWindowListBox::OTableWindowListBox( OTableWindow* pParent ) + :SvTreeListBox( pParent, WB_HASBUTTONS | WB_BORDER) + ,m_aMousePos( Point(0,0) ) + ,m_pTabWin( pParent ) + ,m_nDropEvent(nullptr) + ,m_nUiEvent(nullptr) + ,m_bReallyScrolled( false ) +{ + m_aScrollTimer.SetTimeout( SCROLLING_TIMESPAN ); + SetDoubleClickHdl( LINK(this, OTableWindowListBox, OnDoubleClick) ); + + SetSelectionMode(SelectionMode::Single); + + SetHighlightRange( ); +} + +void OTableWindowListBox::dragFinished( ) +{ + // first show the error msg when existing + m_pTabWin->getDesignView()->getController().showError(m_pTabWin->getDesignView()->getController().clearOccurredError()); + // second look for ui activities which should happen after d&d + if (m_nUiEvent) + Application::RemoveUserEvent(m_nUiEvent); + m_nUiEvent = Application::PostUserEvent(LINK(this, OTableWindowListBox, LookForUiHdl), nullptr, true); +} + +OTableWindowListBox::~OTableWindowListBox() +{ + disposeOnce(); +} + +void OTableWindowListBox::dispose() +{ + if (m_nDropEvent) + Application::RemoveUserEvent(m_nDropEvent); + if (m_nUiEvent) + Application::RemoveUserEvent(m_nUiEvent); + if( m_aScrollTimer.IsActive() ) + m_aScrollTimer.Stop(); + m_pTabWin.clear(); + SvTreeListBox::dispose(); +} + +SvTreeListEntry* OTableWindowListBox::GetEntryFromText( const OUString& rEntryText ) +{ + // iterate through the list + SvTreeList* pTreeList = GetModel(); + SvTreeListEntry* pEntry = pTreeList->First(); + OJoinDesignView* pView = m_pTabWin->getDesignView(); + OJoinController& rController = pView->getController(); + + try + { + bool bCase = false; + const Reference<XConnection>& xConnection = rController.getConnection(); + if(xConnection.is()) + { + Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData(); + if(xMeta.is()) + bCase = xMeta->supportsMixedCaseQuotedIdentifiers(); + } + while( pEntry ) + { + if(bCase ? rEntryText == GetEntryText(pEntry) : rEntryText.equalsIgnoreAsciiCase(GetEntryText(pEntry))) + { + return pEntry; + } + pEntry = pTreeList->Next(pEntry); + } + } + catch(SQLException&) + { + } + + return nullptr; +} + +void OTableWindowListBox::NotifyScrolled() +{ + m_bReallyScrolled = true; +} + +void OTableWindowListBox::NotifyEndScroll() +{ + if (m_bReallyScrolled) + // connections of this table, if any, should be redrawn + m_pTabWin->getTableView()->Invalidate(InvalidateFlags::NoChildren); + + // without InvalidateFlags::NoChildren all tables would be redrawn, + // so: flickering + m_bReallyScrolled = false; +} + +bool OTableWindowListBox::PreNotify(NotifyEvent& rNEvt) +{ + bool bHandled = false; + switch (rNEvt.GetType()) + { + case MouseNotifyEvent::KEYINPUT: + { + const KeyEvent* pKeyEvent = rNEvt.GetKeyEvent(); + const vcl::KeyCode& rCode = pKeyEvent->GetKeyCode(); + + if (rCode.GetCode() != KEY_RETURN) + { + if(m_pTabWin) + { + bHandled = m_pTabWin->HandleKeyInput(*pKeyEvent); + } + break; + } + + if (rCode.IsMod1() || rCode.IsMod2() || rCode.IsShift()) + break; + if (FirstSelected()) + static_cast<OTableWindow*>(Window::GetParent())->OnEntryDoubleClicked(FirstSelected()); + break; + } + default: + break; + } + + if (!bHandled) + return SvTreeListBox::PreNotify(rNEvt); + return true; +} + +IMPL_LINK_NOARG( OTableWindowListBox, ScrollUpHdl, Timer*, void ) +{ + SvTreeListEntry* pEntry = GetEntry( m_aMousePos ); + if( !pEntry ) + return; + + if( pEntry != Last() ) + { + ScrollOutputArea( -1 ); + pEntry = GetEntry( m_aMousePos ); + Select( pEntry ); + } +} + +IMPL_LINK_NOARG( OTableWindowListBox, ScrollDownHdl, Timer*, void ) +{ + SvTreeListEntry* pEntry = GetEntry( m_aMousePos ); + if( !pEntry ) + return; + + if( pEntry != Last() ) + { + ScrollOutputArea( 1 ); + pEntry = GetEntry( m_aMousePos ); + Select( pEntry ); + } +} + +void OTableWindowListBox::StartDrag( sal_Int8 /*nAction*/, const Point& /*rPosPixel*/ ) +{ + OJoinTableView* pCont = m_pTabWin->getTableView(); + if (!pCont->getDesignView()->getController().isReadOnly() && pCont->getDesignView()->getController().isConnected()) + { + // asterisk was not allowed to be copied to selection browsebox + bool bFirstNotAllowed = FirstSelected() == First() && m_pTabWin->GetData()->IsShowAll(); + EndSelection(); + // create a description of the source + OJoinExchangeData jxdSource(this); + // put it into an exchange object + rtl::Reference<OJoinExchObj> pJoin = new OJoinExchObj(jxdSource,bFirstNotAllowed); + pJoin->StartDrag(this, DND_ACTION_LINK, this); + } +} + +sal_Int8 OTableWindowListBox::AcceptDrop( const AcceptDropEvent& _rEvt ) +{ + sal_Int8 nDND_Action = DND_ACTION_NONE; + // check the format + if ( !OJoinExchObj::isFormatAvailable(GetDataFlavorExVector(),SotClipboardFormatId::SBA_TABID) // this means that the first entry is to be dragged + && OJoinExchObj::isFormatAvailable(GetDataFlavorExVector()) ) + { // don't drop into the window if it's the drag source itself + + // remove the selection if the dragging operation is leaving the window + if (_rEvt.mbLeaving) + SelectAll(false); + else + { + // hit test + m_aMousePos = _rEvt.maPosPixel; + Size aOutputSize = GetOutputSizePixel(); + SvTreeListEntry* pEntry = GetEntry( m_aMousePos ); + if( !pEntry ) + return DND_ACTION_NONE; + + // Scrolling Areas + tools::Rectangle aBottomScrollArea( Point(0, aOutputSize.Height()-LISTBOX_SCROLLING_AREA), + Size(aOutputSize.Width(), LISTBOX_SCROLLING_AREA) ); + tools::Rectangle aTopScrollArea( Point(0,0), Size(aOutputSize.Width(), LISTBOX_SCROLLING_AREA) ); + + // scroll up if the pointer is on the upper scroll area + if( aBottomScrollArea.IsInside(m_aMousePos) ) + { + if( !m_aScrollTimer.IsActive() ) + { + m_aScrollTimer.SetInvokeHandler( LINK(this, OTableWindowListBox, ScrollUpHdl) ); + ScrollUpHdl( nullptr ); + } + } + // scroll down if the pointer is on the lower scroll area + else if( aTopScrollArea.IsInside(m_aMousePos) ) + { + if( !m_aScrollTimer.IsActive() ) + { + m_aScrollTimer.SetInvokeHandler( LINK(this, OTableWindowListBox, ScrollDownHdl) ); + ScrollDownHdl( nullptr ); + } + } + else + { + if( m_aScrollTimer.IsActive() ) + m_aScrollTimer.Stop(); + } + + // automatically select right entry when dragging + if ((FirstSelected() != pEntry) || NextSelected(FirstSelected())) + SelectAll(false); + Select(pEntry); + + // one cannot drop on the first (*) entry + if(!( m_pTabWin->GetData()->IsShowAll() && (pEntry==First()) )) + nDND_Action = DND_ACTION_LINK; + } + } + return nDND_Action; +} + +IMPL_LINK_NOARG( OTableWindowListBox, LookForUiHdl, void*, void ) +{ + m_nUiEvent = nullptr; + m_pTabWin->getTableView()->lookForUiActivities(); +} + +IMPL_LINK_NOARG( OTableWindowListBox, DropHdl, void*, void ) +{ + // create the connection + m_nDropEvent = nullptr; + OSL_ENSURE(m_pTabWin,"No TableWindow!"); + try + { + OJoinTableView* pCont = m_pTabWin->getTableView(); + OSL_ENSURE(pCont,"No QueryTableView!"); + pCont->AddConnection(m_aDropInfo.aSource, m_aDropInfo.aDest); + } + catch(const SQLException& e) + { + // remember the exception so that we can show them later when d&d is finished + m_pTabWin->getDesignView()->getController().setErrorOccurred(::dbtools::SQLExceptionInfo(e)); + } +} + +sal_Int8 OTableWindowListBox::ExecuteDrop( const ExecuteDropEvent& _rEvt ) +{ + TransferableDataHelper aDropped(_rEvt.maDropEvent.Transferable); + if ( OJoinExchObj::isFormatAvailable(aDropped.GetDataFlavorExVector())) + { // don't drop into the window if it's the drag source itself + m_aDropInfo.aSource = OJoinExchangeData(this); + m_aDropInfo.aDest = OJoinExchObj::GetSourceDescription(_rEvt.maDropEvent.Transferable); + + if (m_nDropEvent) + Application::RemoveUserEvent(m_nDropEvent); + m_nDropEvent = Application::PostUserEvent(LINK(this, OTableWindowListBox, DropHdl), nullptr, true); + + return DND_ACTION_LINK; + } + return DND_ACTION_NONE; +} + +void OTableWindowListBox::LoseFocus() +{ + if(m_pTabWin) + m_pTabWin->setActive(false); + SvTreeListBox::LoseFocus(); +} + +void OTableWindowListBox::GetFocus() +{ + if(m_pTabWin) + m_pTabWin->setActive(); + + if (GetCurEntry() != nullptr) + { + if ( GetSelectionCount() == 0 || GetCurEntry() != FirstSelected() ) + { + if ( FirstSelected() ) + Select(FirstSelected(), false); + Select(GetCurEntry()); + } + else + ShowFocusRect(FirstSelected()); + } + SvTreeListBox::GetFocus(); +} + +IMPL_LINK_NOARG( OTableWindowListBox, OnDoubleClick, SvTreeListBox *, bool ) +{ + // tell my parent + vcl::Window* pParent = Window::GetParent(); + OSL_ENSURE(pParent != nullptr, "OTableWindowListBox::OnDoubleClick : have no Parent !"); + + static_cast<OTableWindow*>(pParent)->OnEntryDoubleClicked(GetHdlEntry()); + + return false; +} + +void OTableWindowListBox::Command(const CommandEvent& rEvt) +{ + switch (rEvt.GetCommand()) + { + case CommandEventId::ContextMenu: + { + static_cast<OTableWindow*>(Window::GetParent())->Command(rEvt); + break; + } + default: + SvTreeListBox::Command(rEvt); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/TableWindowTitle.cxx b/dbaccess/source/ui/querydesign/TableWindowTitle.cxx new file mode 100644 index 000000000..952063d5d --- /dev/null +++ b/dbaccess/source/ui/querydesign/TableWindowTitle.cxx @@ -0,0 +1,187 @@ +/* -*- 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 <TableWindowTitle.hxx> +#include <TableWindow.hxx> +#include <vcl/svapp.hxx> +#include <vcl/help.hxx> +#include <vcl/settings.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/event.hxx> +#include <TableWindowListBox.hxx> +#include <TableConnection.hxx> +#include <JoinController.hxx> + +using namespace dbaui; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::uno; +OTableWindowTitle::OTableWindowTitle( OTableWindow* pParent ) : + FixedText( pParent, WB_3DLOOK|WB_LEFT|WB_NOLABEL|WB_VCENTER ) + ,m_pTabWin( pParent ) +{ + // set background- and text colour + StyleSettings aSystemStyle = Application::GetSettings().GetStyleSettings(); + SetBackground(Wallpaper(aSystemStyle.GetFaceColor())); + SetTextColor(aSystemStyle.GetButtonTextColor()); + + vcl::Font aFont( GetFont() ); + aFont.SetTransparent( true ); + SetFont( aFont ); +} + +OTableWindowTitle::~OTableWindowTitle() +{ + disposeOnce(); +} + +void OTableWindowTitle::dispose() +{ + m_pTabWin.clear(); + FixedText::dispose(); +} + +void OTableWindowTitle::GetFocus() +{ + if(m_pTabWin) + m_pTabWin->GetFocus(); + else + FixedText::GetFocus(); +} + +void OTableWindowTitle::LoseFocus() +{ + if (m_pTabWin) + m_pTabWin->LoseFocus(); + else + FixedText::LoseFocus(); +} + +void OTableWindowTitle::RequestHelp( const HelpEvent& rHEvt ) +{ + if(!m_pTabWin) + return; + + OUString aHelpText = m_pTabWin->GetComposedName(); + if( aHelpText.isEmpty()) + return; + + // show help + tools::Rectangle aItemRect(Point(0,0),GetSizePixel()); + aItemRect = LogicToPixel( aItemRect ); + Point aPt = OutputToScreenPixel( aItemRect.TopLeft() ); + aItemRect.SetLeft( aPt.X() ); + aItemRect.SetTop( aPt.Y() ); + aPt = OutputToScreenPixel( aItemRect.BottomRight() ); + aItemRect.SetRight( aPt.X() ); + aItemRect.SetBottom( aPt.Y() ); + if( rHEvt.GetMode() == HelpEventMode::BALLOON ) + Help::ShowBalloon( this, aItemRect.Center(), aItemRect, aHelpText); + else + Help::ShowQuickHelp( this, aItemRect, aHelpText ); +} + +void OTableWindowTitle::Command( const CommandEvent& rEvt ) +{ + if ( rEvt.GetCommand() == CommandEventId::ContextMenu ) + { + GrabFocus(); + if ( m_pTabWin ) + { + // tdf#94709 - protect shutdown code-path. + VclPtr<OTableWindow> xTabWin(m_pTabWin); + xTabWin->Command( rEvt ); + } + else + Control::Command(rEvt); + } +} + +void OTableWindowTitle::KeyInput( const KeyEvent& rEvt ) +{ + if ( m_pTabWin ) + m_pTabWin->KeyInput( rEvt ); +} + +void OTableWindowTitle::MouseButtonDown( const MouseEvent& rEvt ) +{ + if( rEvt.IsLeft() ) + { + if( rEvt.GetClicks() == 2) + { + Size aSize(GetTextWidth(GetText()) + 20, + m_pTabWin->GetSizePixel().Height() - m_pTabWin->GetListBox()->GetSizePixel().Height()); + + aSize.AdjustHeight((m_pTabWin->GetListBox()->GetEntryCount() + 2) * m_pTabWin->GetListBox()->GetEntryHeight() ); + if(m_pTabWin->GetSizePixel() != aSize) + { + m_pTabWin->SetSizePixel(aSize); + + OJoinTableView* pView = m_pTabWin->getTableView(); + OSL_ENSURE(pView,"No OJoinTableView!"); + for (auto& conn : pView->getTableConnections()) + conn->RecalcLines(); + + pView->InvalidateConnections(); + pView->getDesignView()->getController().setModified(true); + pView->Invalidate(InvalidateFlags::NoChildren); + } + } + else + { + Point aPos = rEvt.GetPosPixel(); + aPos = OutputToScreenPixel( aPos ); + OJoinTableView* pView = m_pTabWin->getTableView(); + OSL_ENSURE(pView,"No OJoinTableView!"); + pView->NotifyTitleClicked( static_cast<OTableWindow*>(GetParent()), aPos ); + } + GrabFocus(); + } + else + Control::MouseButtonDown( rEvt ); +} + +void OTableWindowTitle::DataChanged(const DataChangedEvent& rDCEvt) +{ + if (rDCEvt.GetType() == DataChangedEventType::SETTINGS) + { + // assume worst-case: colours have changed, therefore I have to adept + StyleSettings aSystemStyle = Application::GetSettings().GetStyleSettings(); + SetBackground(Wallpaper(aSystemStyle.GetFaceColor())); + SetTextColor(aSystemStyle.GetButtonTextColor()); + } +} + +void OTableWindowTitle::StateChanged( StateChangedType nType ) +{ + Window::StateChanged( nType ); + + if ( nType == StateChangedType::Zoom ) + { + const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings(); + + vcl::Font aFont = rStyleSettings.GetGroupFont(); + if ( IsControlFont() ) + aFont.Merge( GetControlFont() ); + SetZoomedPointFont(*this, aFont); + + Resize(); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/class.jpg b/dbaccess/source/ui/querydesign/class.jpg Binary files differnew file mode 100644 index 000000000..b1a3b6d27 --- /dev/null +++ b/dbaccess/source/ui/querydesign/class.jpg diff --git a/dbaccess/source/ui/querydesign/limitboxcontroller.cxx b/dbaccess/source/ui/querydesign/limitboxcontroller.cxx new file mode 100644 index 000000000..47aa5c98c --- /dev/null +++ b/dbaccess/source/ui/querydesign/limitboxcontroller.cxx @@ -0,0 +1,321 @@ +/* -*- 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/. + */ + +#include "limitboxcontroller.hxx" +#include <apitools.hxx> +#include <uiservices.hxx> + +#include <com/sun/star/frame/XDispatchProvider.hpp> +#include <com/sun/star/frame/XFrame.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> +#include <com/sun/star/util/XURLTransformer.hpp> + +#include <vcl/InterimItemWindow.hxx> +#include <vcl/event.hxx> +#include <vcl/svapp.hxx> +#include <vcl/window.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <comphelper/processfactory.hxx> + +#include <core_resource.hxx> +#include <dbu_reghelper.hxx> +#include <strings.hrc> + +using namespace ::com::sun::star; + +namespace +{ + +/// Default values +sal_Int64 const aDefLimitAry[] = +{ + 5, + 10, + 20, + 50 +}; + +} + +namespace dbaui +{ + +/** + * Input box to add limit to an SQL query (maximum number of result's rows) + * This box is reachable on the Query Design Toolbar + */ +class LimitBox final : public InterimItemWindow +{ +public: + LimitBox(vcl::Window* pParent, LimitBoxController* pCtrl) + : InterimItemWindow(pParent, "dbaccess/ui/limitbox.ui", "LimitBox") + , m_pControl( pCtrl ) + , m_xWidget(m_xBuilder->weld_combo_box("limit")) + { + LoadDefaultLimits(); + + m_xWidget->connect_key_press(LINK(this, LimitBox, KeyInputHdl)); + m_xWidget->connect_entry_activate(LINK(this, LimitBox, ActivateHdl)); + m_xWidget->connect_changed(LINK(this, LimitBox, ChangeHdl)); + m_xWidget->connect_focus_out(LINK(this, LimitBox, FocusOutHdl)); + m_xWidget->set_entry_width_chars(6); + SetSizePixel(m_xContainer->get_preferred_size()); + } + + virtual void dispose() override + { + m_xWidget.reset(); + InterimItemWindow::dispose(); + } + + virtual ~LimitBox() override + { + disposeOnce(); + } + + void set_sensitive(bool bSensitive) + { + m_xWidget->set_sensitive(bSensitive); + } + + virtual void GetFocus() override + { + if (m_xWidget) + m_xWidget->grab_focus(); + InterimItemWindow::GetFocus(); + } + + void set_value(int nLimit) + { + if (nLimit < 0) + m_xWidget->set_active(0); + else + m_xWidget->set_entry_text(OUString::number(nLimit)); + m_xWidget->save_value(); + } + +private: + LimitBoxController* m_pControl; + std::unique_ptr<weld::ComboBox> m_xWidget; + + DECL_LINK(KeyInputHdl, const KeyEvent&, bool); + DECL_LINK(ActivateHdl, weld::ComboBox&, bool); + DECL_LINK(ChangeHdl, weld::ComboBox&, void); + DECL_LINK(FocusOutHdl, weld::Widget&, void); + + void Apply() + { + if (!m_xWidget->get_value_changed_from_saved()) + return; + uno::Sequence< beans::PropertyValue > aArgs( 1 ); + aArgs[0].Name = "DBLimit.Value"; + sal_Int64 nLimit; + OUString sActiveText = m_xWidget->get_active_text(); + if (sActiveText == DBA_RES(STR_QUERY_LIMIT_ALL)) + nLimit = -1; + else + { + nLimit = m_xWidget->get_active_text().toInt64(); + if (nLimit < 0) + nLimit = -1; + } + set_value(nLimit); + aArgs[0].Value <<= nLimit; + m_pControl->dispatchCommand( aArgs ); + } + + ///Initialize entries + void LoadDefaultLimits() + { + m_xWidget->freeze(); + m_xWidget->append_text(DBA_RES(STR_QUERY_LIMIT_ALL)); + for (auto nIndex : aDefLimitAry) + { + m_xWidget->append_text(OUString::number(nIndex)); + } + m_xWidget->thaw(); + } +}; + +IMPL_LINK(LimitBox, KeyInputHdl, const KeyEvent&, rKEvt, bool) +{ + bool bHandled = false; + const sal_uInt16 nCode = rKEvt.GetKeyCode().GetCode(); + switch (nCode) + { + case KEY_ESCAPE: + m_xWidget->set_entry_text(m_xWidget->get_saved_value()); + bHandled = true; + break; + case KEY_RETURN: + { + bHandled = ActivateHdl(*m_xWidget); + break; + } + } + return bHandled || ChildKeyInput(rKEvt); +} + +IMPL_LINK_NOARG(LimitBox, FocusOutHdl, weld::Widget&, void) +{ + if (!m_xWidget || m_xWidget->has_focus()) // comboboxes can be comprised of multiple widgets, ensure all have lost focus + return; + Apply(); +} + +IMPL_LINK(LimitBox, ChangeHdl, weld::ComboBox&, rComboBox, void) +{ + if (rComboBox.changed_by_direct_pick()) + ActivateHdl(rComboBox); +} + +IMPL_LINK_NOARG(LimitBox, ActivateHdl, weld::ComboBox&, bool) +{ + GrabFocusToDocument(); + Apply(); + return true; +} + +LimitBoxController::LimitBoxController( + const uno::Reference< uno::XComponentContext >& rxContext ) : + svt::ToolboxController( rxContext, + uno::Reference< frame::XFrame >(), + ".uno:DBLimit" ), + m_xLimitBox( nullptr ) +{ +} + +LimitBoxController::~LimitBoxController() +{ +} + +/// XInterface +uno::Any SAL_CALL LimitBoxController::queryInterface( const uno::Type& aType ) +{ + uno::Any a = ToolboxController::queryInterface( aType ); + if ( a.hasValue() ) + return a; + + return ::cppu::queryInterface( aType, static_cast< lang::XServiceInfo* >( this )); +} + +void SAL_CALL LimitBoxController::acquire() throw () +{ + ToolboxController::acquire(); +} + +void SAL_CALL LimitBoxController::release() throw () +{ + ToolboxController::release(); +} + + +/// XServiceInfo +IMPLEMENT_SERVICE_INFO_IMPLNAME_STATIC(LimitBoxController, "org.libreoffice.comp.dbu.LimitBoxController") +IMPLEMENT_SERVICE_INFO_SUPPORTS(LimitBoxController) +IMPLEMENT_SERVICE_INFO_GETSUPPORTED1_STATIC(LimitBoxController, "com.sun.star.frame.ToolbarController") + +uno::Reference< uno::XInterface > + LimitBoxController::Create(const uno::Reference< css::lang::XMultiServiceFactory >& _rxORB) +{ + return static_cast< XServiceInfo* >(new LimitBoxController( comphelper::getComponentContext(_rxORB) )); +} + +/// XComponent +void SAL_CALL LimitBoxController::dispose() +{ + svt::ToolboxController::dispose(); + + SolarMutexGuard aSolarMutexGuard; + m_xLimitBox.disposeAndClear(); +} + +/// XStatusListener +void SAL_CALL LimitBoxController::statusChanged( + const frame::FeatureStateEvent& rEvent ) +{ + if ( !m_xLimitBox ) + return; + + SolarMutexGuard aSolarMutexGuard; + if ( rEvent.FeatureURL.Path == "DBLimit" ) + { + if ( rEvent.IsEnabled ) + { + m_xLimitBox->set_sensitive(true); + sal_Int64 nLimit = 0; + if (rEvent.State >>= nLimit) + m_xLimitBox->set_value(nLimit); + } + else + m_xLimitBox->set_sensitive(false); + } +} + +/// XToolbarController +void SAL_CALL LimitBoxController::execute( sal_Int16 /*KeyModifier*/ ) +{ +} + +void SAL_CALL LimitBoxController::click() +{ +} + +void SAL_CALL LimitBoxController::doubleClick() +{ +} + +uno::Reference< awt::XWindow > SAL_CALL LimitBoxController::createPopupWindow() +{ + return uno::Reference< awt::XWindow >(); +} + +uno::Reference< awt::XWindow > SAL_CALL LimitBoxController::createItemWindow( + const uno::Reference< awt::XWindow >& xParent ) +{ + uno::Reference< awt::XWindow > xItemWindow; + + VclPtr<vcl::Window> pParent = VCLUnoHelper::GetWindow( xParent ); + if ( pParent ) + { + SolarMutexGuard aSolarMutexGuard; + m_xLimitBox = VclPtr<LimitBox>::Create(pParent, this); + xItemWindow = VCLUnoHelper::GetInterface(m_xLimitBox); + } + + return xItemWindow; +} + +void LimitBoxController::dispatchCommand( + const uno::Sequence< beans::PropertyValue >& rArgs ) +{ + uno::Reference< frame::XDispatchProvider > xDispatchProvider( m_xFrame, uno::UNO_QUERY ); + if ( xDispatchProvider.is() ) + { + util::URL aURL; + uno::Reference< frame::XDispatch > xDispatch; + uno::Reference< util::XURLTransformer > xURLTransformer = getURLTransformer(); + + aURL.Complete = ".uno:DBLimit"; + xURLTransformer->parseStrict( aURL ); + xDispatch = xDispatchProvider->queryDispatch( aURL, OUString(), 0 ); + if ( xDispatch.is() ) + xDispatch->dispatch( aURL, rArgs ); + } +} + +} // dbaui namespace + +extern "C" void createRegistryInfo_LimitBoxController() +{ + static ::dbaui::OMultiInstanceAutoRegistration< ::dbaui::LimitBoxController > aAutoRegistration; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/limitboxcontroller.hxx b/dbaccess/source/ui/querydesign/limitboxcontroller.hxx new file mode 100644 index 000000000..cf3727625 --- /dev/null +++ b/dbaccess/source/ui/querydesign/limitboxcontroller.hxx @@ -0,0 +1,76 @@ +/* -*- 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/. + */ + +#ifndef INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_LIMITBOXCONTROLLER_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_LIMITBOXCONTROLLER_HXX + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <connectivity/CommonTools.hxx> +#include <svtools/toolboxcontroller.hxx> +#include <rtl/ustring.hxx> +#include <vcl/vclptr.hxx> + +namespace dbaui +{ + +class LimitBox; + +/** + * A ToolboxController to paste LimitBox onto the Query Design Toolbar + * It is communicating with querycontroller and this channel make enable + * to set\get the value of limitbox when switching between views + */ +class LimitBoxController: public svt::ToolboxController, + public css::lang::XServiceInfo +{ + public: + explicit LimitBoxController( + const css::uno::Reference< css::uno::XComponentContext >& rxContext ); + virtual ~LimitBoxController() override; + + /// XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type& aType ) override; + virtual void SAL_CALL acquire() throw () override; + virtual void SAL_CALL release() throw () override; + + /// XServiceInfo + DECLARE_SERVICE_INFO(); + /// @throws css::uno::RuntimeException + static OUString getImplementationName_Static( ); + /// @throws css::uno::RuntimeException + static css::uno::Sequence< OUString > getSupportedServiceNames_Static( ); + static css::uno::Reference< css::uno::XInterface > + Create(const css::uno::Reference< css::lang::XMultiServiceFactory >&); + + /// XComponent + virtual void SAL_CALL dispose() override; + + /// XStatusListener + virtual void SAL_CALL statusChanged( const css::frame::FeatureStateEvent& Event ) override; + + /// XToolbarController + virtual void SAL_CALL execute( sal_Int16 KeyModifier ) override; + virtual void SAL_CALL click() override; + virtual void SAL_CALL doubleClick() override; + virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createPopupWindow() override; + virtual css::uno::Reference< css::awt::XWindow > SAL_CALL createItemWindow( const css::uno::Reference< css::awt::XWindow >& Parent ) override; + + void dispatchCommand( const css::uno::Sequence< css::beans::PropertyValue >& rArgs ); + using svt::ToolboxController::dispatchCommand; + + private: + VclPtr<LimitBox> m_xLimitBox; +}; + +} ///dbaui namespace + +#endif /// INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_LIMITBOXCONTROLLER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/querycontainerwindow.cxx b/dbaccess/source/ui/querydesign/querycontainerwindow.cxx new file mode 100644 index 000000000..aef183bb6 --- /dev/null +++ b/dbaccess/source/ui/querydesign/querycontainerwindow.cxx @@ -0,0 +1,223 @@ +/* -*- 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 <querycontainerwindow.hxx> +#include <QueryDesignView.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> +#include <JoinController.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <strings.hxx> +#include <sfx2/sfxsids.hrc> +#include <vcl/event.hxx> +#include <UITools.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/frame/Frame.hpp> +#include <com/sun/star/util/XCloseable.hpp> + +namespace dbaui +{ + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::lang; + using namespace ::com::sun::star::frame; + using namespace ::com::sun::star::beans; + + // OQueryContainerWindow + OQueryContainerWindow::OQueryContainerWindow(vcl::Window* pParent, OQueryController& _rController,const Reference< XComponentContext >& _rxContext) + :ODataView( pParent, _rController, _rxContext ) + ,m_pViewSwitch(nullptr) + ,m_pBeamer(nullptr) + { + m_pViewSwitch = new OQueryViewSwitch( this, _rController, _rxContext ); + + m_pSplitter = VclPtr<Splitter>::Create(this,WB_VSCROLL); + m_pSplitter->Hide(); + m_pSplitter->SetSplitHdl( LINK( this, OQueryContainerWindow, SplitHdl ) ); + m_pSplitter->SetBackground( Wallpaper( Application::GetSettings().GetStyleSettings().GetDialogColor() ) ); + } + OQueryContainerWindow::~OQueryContainerWindow() + { + disposeOnce(); + } + void OQueryContainerWindow::dispose() + { + { + OQueryViewSwitch* pTemp = m_pViewSwitch; + m_pViewSwitch = nullptr; + delete pTemp; + } + if ( m_pBeamer ) + ::dbaui::notifySystemWindow(this,m_pBeamer,::comphelper::mem_fun(&TaskPaneList::RemoveWindow)); + m_pBeamer.clear(); + if ( m_xBeamer.is() ) + { + Reference< css::util::XCloseable > xCloseable(m_xBeamer,UNO_QUERY); + m_xBeamer = nullptr; + if(xCloseable.is()) + xCloseable->close(false); // false - holds the ownership of this frame + } + + m_pSplitter.disposeAndClear(); + ODataView::dispose(); + } + bool OQueryContainerWindow::switchView( ::dbtools::SQLExceptionInfo* _pErrorInfo ) + { + return m_pViewSwitch->switchView( _pErrorInfo ); + } + + void OQueryContainerWindow::forceInitialView() + { + return m_pViewSwitch->forceInitialView(); + } + + void OQueryContainerWindow::resizeAll( const tools::Rectangle& _rPlayground ) + { + tools::Rectangle aPlayground( _rPlayground ); + + if ( m_pBeamer && m_pBeamer->IsVisible() ) + { + // calc pos and size of the splitter + Point aSplitPos = m_pSplitter->GetPosPixel(); + Size aSplitSize = m_pSplitter->GetOutputSizePixel(); + aSplitSize.setWidth( aPlayground.GetWidth() ); + + if ( aSplitPos.Y() <= aPlayground.Top() ) + aSplitPos.setY( aPlayground.Top() + sal_Int32( aPlayground.GetHeight() * 0.2 ) ); + + if ( aSplitPos.Y() + aSplitSize.Height() > aPlayground.GetHeight() ) + aSplitPos.setY( aPlayground.GetHeight() - aSplitSize.Height() ); + + // set pos and size of the splitter + m_pSplitter->SetPosSizePixel( aSplitPos, aSplitSize ); + m_pSplitter->SetDragRectPixel( aPlayground ); + + // set pos and size of the beamer + Size aBeamerSize( aPlayground.GetWidth(), aSplitPos.Y() ); + m_pBeamer->SetPosSizePixel( aPlayground.TopLeft(), aBeamerSize ); + + // shrink the playground by the size which is occupied by the beamer + aPlayground.SetTop( aSplitPos.Y() + aSplitSize.Height() ); + } + + ODataView::resizeAll( aPlayground ); + } + + void OQueryContainerWindow::resizeDocumentView( tools::Rectangle& _rPlayground ) + { + m_pViewSwitch->SetPosSizePixel( _rPlayground.TopLeft(), Size( _rPlayground.GetWidth(), _rPlayground.GetHeight() ) ); + + ODataView::resizeDocumentView( _rPlayground ); + } + + void OQueryContainerWindow::GetFocus() + { + ODataView::GetFocus(); + if(m_pViewSwitch) + m_pViewSwitch->GrabFocus(); + } + IMPL_LINK_NOARG( OQueryContainerWindow, SplitHdl, Splitter*, void ) + { + m_pSplitter->SetPosPixel( Point( m_pSplitter->GetPosPixel().X(),m_pSplitter->GetSplitPosPixel() ) ); + Resize(); + } + + void OQueryContainerWindow::Construct() + { + m_pViewSwitch->Construct(); + } + + void OQueryContainerWindow::disposingPreview() + { + if ( m_pBeamer ) + { + // here I know that we will be destroyed from the frame + ::dbaui::notifySystemWindow(this,m_pBeamer,::comphelper::mem_fun(&TaskPaneList::RemoveWindow)); + m_pBeamer = nullptr; + m_xBeamer = nullptr; + m_pSplitter->Hide(); + Resize(); + } + } + bool OQueryContainerWindow::PreNotify( NotifyEvent& rNEvt ) + { + if (rNEvt.GetType() == MouseNotifyEvent::GETFOCUS && m_pViewSwitch) + { + OJoinController& rController = m_pViewSwitch->getDesignView()->getController(); + rController.InvalidateFeature(SID_CUT); + rController.InvalidateFeature(SID_COPY); + rController.InvalidateFeature(SID_PASTE); + } + return ODataView::PreNotify(rNEvt); + } + void OQueryContainerWindow::showPreview(const Reference<XFrame>& _xFrame) + { + if(m_pBeamer) + return; + + m_pBeamer = VclPtr<OBeamer>::Create(this); + + ::dbaui::notifySystemWindow(this,m_pBeamer,::comphelper::mem_fun(&TaskPaneList::AddWindow)); + + m_xBeamer = Frame::create( m_pViewSwitch->getORB() ); + m_xBeamer->initialize( VCLUnoHelper::GetInterface ( m_pBeamer ) ); + + // notify layout manager to not create internal toolbars + try + { + Reference < XPropertySet > xLMPropSet(m_xBeamer->getLayoutManager(), UNO_QUERY); + if ( xLMPropSet.is() ) + { + const OUString aAutomaticToolbars( "AutomaticToolbars" ); + xLMPropSet->setPropertyValue( aAutomaticToolbars, Any( false )); + } + } + catch( Exception& ) + { + } + + m_xBeamer->setName(FRAME_NAME_QUERY_PREVIEW); + + // append our frame + Reference < XFramesSupplier > xSup(_xFrame,UNO_QUERY); + Reference < XFrames > xFrames = xSup->getFrames(); + xFrames->append( Reference<XFrame>(m_xBeamer,UNO_QUERY_THROW) ); + + Size aSize = GetOutputSizePixel(); + Size aBeamer(aSize.Width(),sal_Int32(aSize.Height()*0.33)); + + const long nFrameHeight = LogicToPixel(Size(0, 3), MapMode(MapUnit::MapAppFont)).Height(); + Point aPos(0,aBeamer.Height()+nFrameHeight); + + m_pBeamer->SetPosSizePixel(Point(0,0),aBeamer); + m_pBeamer->Show(); + + m_pSplitter->SetPosSizePixel( Point(0,aBeamer.Height()), Size(aSize.Width(),nFrameHeight) ); + // a default pos for the splitter, so that the listbox is about 80 (logical) pixels wide + m_pSplitter->SetSplitPosPixel( aBeamer.Height() ); + m_pViewSwitch->SetPosSizePixel(aPos,Size(aBeamer.Width(),aSize.Height() - aBeamer.Height()-nFrameHeight)); + + m_pSplitter->Show(); + + Resize(); + } + +} // namespace dbaui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/querycontroller.cxx b/dbaccess/source/ui/querydesign/querycontroller.cxx new file mode 100644 index 000000000..2a9935675 --- /dev/null +++ b/dbaccess/source/ui/querydesign/querycontroller.cxx @@ -0,0 +1,1819 @@ +/* -*- 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 <browserids.hxx> +#include <core_resource.hxx> +#include <strings.hrc> +#include <strings.hxx> +#include <query.hrc> +#include <dbu_reghelper.hxx> +#include <stringconstants.hxx> +#include <defaultobjectnamecheck.hxx> +#include <dlgsave.hxx> +#include <uiservices.hxx> +#include <querycontainerwindow.hxx> +#include <querycontroller.hxx> +#include <QueryDesignView.hxx> +#include <QueryTableView.hxx> +#include <sqlmessage.hxx> +#include <TableConnectionData.hxx> +#include <TableFieldDescription.hxx> +#include <UITools.hxx> +#include <QueryPropertiesDialog.hxx> + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/frame/FrameSearchFlag.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdb/SQLContext.hpp> +#include <com/sun/star/sdb/XQueriesSupplier.hpp> +#include <com/sun/star/sdb/XQueryDefinitionsSupplier.hpp> +#include <com/sun/star/sdb/XSQLQueryComposerFactory.hpp> +#include <com/sun/star/sdbcx/XAppend.hpp> +#include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp> +#include <com/sun/star/sdbcx/XDrop.hpp> +#include <com/sun/star/sdbcx/XTablesSupplier.hpp> +#include <com/sun/star/sdbcx/XViewsSupplier.hpp> +#include <com/sun/star/ui/dialogs/XExecutableDialog.hpp> +#include <com/sun/star/util/XCloseable.hpp> +#include <com/sun/star/util/VetoException.hpp> +#include <com/sun/star/ui/XUIElement.hpp> + +#include <comphelper/propertysequence.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/property.hxx> +#include <comphelper/types.hxx> +#include <connectivity/dbexception.hxx> +#include <connectivity/dbtools.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <svl/undo.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <tools/diagnose_ex.h> +#include <osl/diagnose.h> +#include <vcl/stdtext.hxx> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <osl/mutex.hxx> +#include <memory> +#include <vector> + +extern "C" void createRegistryInfo_OQueryControl() +{ + static ::dbaui::OMultiInstanceAutoRegistration< ::dbaui::OQueryController > aAutoRegistration; +} + +namespace dbaui +{ + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::frame; + using namespace ::com::sun::star::util; + using namespace ::com::sun::star::lang; + + namespace { + + class OViewController : public OQueryController + { + virtual OUString SAL_CALL getImplementationName() override + { + return getImplementationName_Static(); + } + virtual Sequence< OUString> SAL_CALL getSupportedServiceNames() override + { + return getSupportedServiceNames_Static(); + } + + public: + explicit OViewController(const Reference< XComponentContext >& _rM) : OQueryController(_rM){} + + // need by registration + /// @throws RuntimeException + static OUString getImplementationName_Static() + { + return "org.openoffice.comp.dbu.OViewDesign"; + } + /// @throws RuntimeException + static Sequence< OUString > getSupportedServiceNames_Static() + { + Sequence<OUString> aSupported { "com.sun.star.sdb.ViewDesign" }; + return aSupported; + } + static Reference< XInterface > Create(const Reference< XMultiServiceFactory >& _rM) + { + return *(new OViewController(comphelper::getComponentContext(_rM))); + } + }; + + } +} + +extern "C" void createRegistryInfo_OViewControl() +{ + static ::dbaui::OMultiInstanceAutoRegistration< ::dbaui::OViewController > aAutoRegistration; +} + +namespace dbaui +{ + using namespace ::connectivity; + + namespace + { + OUString lcl_getObjectResourceString(const char* pResId, sal_Int32 _nCommandType) + { + OUString sMessageText = DBA_RES(pResId); + OUString sObjectType = DBA_RES(RSC_QUERY_OBJECT_TYPE[_nCommandType]); + sMessageText = sMessageText.replaceFirst( "$object$", sObjectType ); + return sMessageText; + } + } + +using namespace ::com::sun::star::io; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::sdbcx; +using namespace ::com::sun::star::sdbc; +using namespace ::com::sun::star::sdb; +using namespace ::com::sun::star::ui; +using namespace ::com::sun::star::ui::dialogs; +using namespace ::com::sun::star::awt; +using namespace ::dbtools; + +using namespace ::comphelper; + +namespace +{ + void ensureToolbars( OQueryController& _rController, bool _bDesign ) + { + Reference< css::frame::XLayoutManager > xLayoutManager = OGenericUnoController::getLayoutManager( _rController.getFrame() ); + if ( !xLayoutManager.is() ) + return; + + xLayoutManager->lock(); + static const char s_sDesignToolbar[] = "private:resource/toolbar/designobjectbar"; + static const char s_sSqlToolbar[] = "private:resource/toolbar/sqlobjectbar"; + if ( _bDesign ) + { + xLayoutManager->destroyElement( s_sSqlToolbar ); + xLayoutManager->createElement( s_sDesignToolbar ); + } + else + { + xLayoutManager->destroyElement( s_sDesignToolbar ); + xLayoutManager->createElement( s_sSqlToolbar ); + } + xLayoutManager->unlock(); + xLayoutManager->doLayout(); + } + + /** + * The value of m_nLimit is updated when LimitBox loses its focus + * So in those case when execution needs recent data, grab the focus + * (e.g. execute SQL statement, change views) + */ + void grabFocusFromLimitBox( OQueryController& _rController ) + { + Reference< XLayoutManager > xLayoutManager = OGenericUnoController::getLayoutManager( _rController.getFrame() ); + Reference< XUIElement > xUIElement = xLayoutManager->getElement("private:resource/toolbar/designobjectbar"); + if (xUIElement.is()) + { + Reference< XWindow > xWindow(xUIElement->getRealInterface(), css::uno::UNO_QUERY); + VclPtr<vcl::Window> pWindow = VCLUnoHelper::GetWindow( xWindow ); + if( pWindow && pWindow->HasChildPathFocus() ) + { + pWindow->GrabFocusToDocument(); + } + } + } +} + +OUString SAL_CALL OQueryController::getImplementationName() +{ + return getImplementationName_Static(); +} + +OUString OQueryController::getImplementationName_Static() +{ + return "org.openoffice.comp.dbu.OQueryDesign"; +} + +Sequence< OUString> OQueryController::getSupportedServiceNames_Static() +{ + Sequence<OUString> aSupported { "com.sun.star.sdb.QueryDesign" }; + return aSupported; +} + +Sequence< OUString> SAL_CALL OQueryController::getSupportedServiceNames() +{ + return getSupportedServiceNames_Static(); +} + +Reference< XInterface > OQueryController::Create(const Reference<XMultiServiceFactory >& _rxFactory) +{ + return *(new OQueryController(comphelper::getComponentContext(_rxFactory))); +} + +OQueryController::OQueryController(const Reference< XComponentContext >& _rM) + :OJoinController(_rM) + ,OQueryController_PBase( getBroadcastHelper() ) + ,m_pParseContext( new svxform::OSystemParseContext ) + ,m_aSqlParser( _rM, m_pParseContext.get() ) + ,m_nLimit(-1) + ,m_nVisibleRows(0x400) + ,m_nSplitPos(-1) + ,m_nCommandType( CommandType::QUERY ) + ,m_bGraphicalDesign(false) + ,m_bDistinct(false) + ,m_bEscapeProcessing(true) +{ + InvalidateAll(); + + registerProperty( PROPERTY_ACTIVECOMMAND, PROPERTY_ID_ACTIVECOMMAND, PropertyAttribute::READONLY | PropertyAttribute::BOUND, + &m_sStatement, cppu::UnoType<decltype(m_sStatement)>::get() ); + registerProperty( PROPERTY_ESCAPE_PROCESSING, PROPERTY_ID_ESCAPE_PROCESSING, PropertyAttribute::READONLY | PropertyAttribute::BOUND, + &m_bEscapeProcessing, cppu::UnoType<decltype(m_bEscapeProcessing)>::get() ); +} + +OQueryController::~OQueryController() +{ + if ( !getBroadcastHelper().bDisposed && !getBroadcastHelper().bInDispose ) + { + OSL_FAIL("Please check who doesn't dispose this component!"); + // increment ref count to prevent double call of Dtor + osl_atomic_increment( &m_refCount ); + dispose(); + } +} + +IMPLEMENT_FORWARD_XINTERFACE2( OQueryController, OJoinController, OQueryController_PBase ) +IMPLEMENT_FORWARD_XTYPEPROVIDER2( OQueryController, OJoinController, OQueryController_PBase ) + +Reference< XPropertySetInfo > SAL_CALL OQueryController::getPropertySetInfo() +{ + Reference< XPropertySetInfo > xInfo( createPropertySetInfo( getInfoHelper() ) ); + return xInfo; +} + +void SAL_CALL OQueryController::getFastPropertyValue( Any& o_rValue, sal_Int32 i_nHandle ) const +{ + switch ( i_nHandle ) + { + case PROPERTY_ID_CURRENT_QUERY_DESIGN: + { + ::comphelper::NamedValueCollection aCurrentDesign; + aCurrentDesign.put( "GraphicalDesign", isGraphicalDesign() ); + aCurrentDesign.put( OUString(PROPERTY_ESCAPE_PROCESSING), m_bEscapeProcessing ); + + if ( isGraphicalDesign() ) + { + getContainer()->SaveUIConfig(); + saveViewSettings( aCurrentDesign, true ); + aCurrentDesign.put( "Statement", m_sStatement ); + } + else + { + aCurrentDesign.put( "Statement", getContainer()->getStatement() ); + } + + o_rValue <<= aCurrentDesign.getPropertyValues(); + } + break; + + default: + OPropertyContainer::getFastPropertyValue( o_rValue, i_nHandle ); + break; + } +} + +::cppu::IPropertyArrayHelper& OQueryController::getInfoHelper() +{ + return *getArrayHelper(); +} + +::cppu::IPropertyArrayHelper* OQueryController::createArrayHelper( ) const +{ + Sequence< Property > aProps; + describeProperties( aProps ); + + // one additional property: + const sal_Int32 nLength = aProps.getLength(); + aProps.realloc( nLength + 1 ); + aProps[ nLength ] = Property( + "CurrentQueryDesign", + PROPERTY_ID_CURRENT_QUERY_DESIGN, + ::cppu::UnoType< Sequence< PropertyValue > >::get(), + PropertyAttribute::READONLY + ); + + std::sort( + aProps.begin(), + aProps.end(), + ::comphelper::PropertyCompareByName() + ); + + return new ::cppu::OPropertyArrayHelper(aProps); +} + +void OQueryController::deleteIterator() +{ + if(m_pSqlIterator) + { + delete m_pSqlIterator->getParseTree(); + m_pSqlIterator->dispose(); + m_pSqlIterator.reset(); + } +} + +void OQueryController::disposing() +{ + OQueryController_PBase::disposing(); + + deleteIterator(); + + m_pParseContext.reset(); + + clearFields(); + OTableFields().swap(m_vUnUsedFieldsDesc); + + ::comphelper::disposeComponent(m_xComposer); + OJoinController::disposing(); + OQueryController_PBase::disposing(); +} + +void OQueryController::clearFields() +{ + OTableFields().swap(m_vTableFieldDesc); +} + +FeatureState OQueryController::GetState(sal_uInt16 _nId) const +{ + FeatureState aReturn; + aReturn.bEnabled = true; + // (disabled automatically) + + switch (_nId) + { + case ID_BROWSER_EDITDOC: + if ( editingCommand() ) + aReturn.bEnabled = false; + else if ( editingView() && !m_xAlterView.is() ) + aReturn.bEnabled = false; + else + aReturn = OJoinController::GetState( _nId ); + break; + + case ID_BROWSER_ESCAPEPROCESSING: + aReturn.bChecked = !m_bEscapeProcessing; + aReturn.bEnabled = ( m_pSqlIterator != nullptr ) && !m_bGraphicalDesign; + break; + case SID_RELATION_ADD_RELATION: + aReturn.bEnabled = isEditable() && m_bGraphicalDesign && m_vTableData.size() > 1; + break; + case ID_BROWSER_SAVEASDOC: + aReturn.bEnabled = !editingCommand() && (!m_bGraphicalDesign || !(m_vTableFieldDesc.empty() || m_vTableData.empty())); + break; + case ID_BROWSER_SAVEDOC: + aReturn.bEnabled = isEditable() && (!m_bGraphicalDesign || !(m_vTableFieldDesc.empty() || m_vTableData.empty())); + break; + case SID_PRINTDOCDIRECT: + break; + case ID_BROWSER_CUT: + aReturn.bEnabled = isEditable() && getContainer() && getContainer()->isCutAllowed(); + break; + case ID_BROWSER_COPY: + aReturn.bEnabled = getContainer() && getContainer()->isCopyAllowed(); + break; + case ID_BROWSER_PASTE: + aReturn.bEnabled = isEditable() && getContainer() && getContainer()->isPasteAllowed(); + break; + case ID_BROWSER_SQL: + aReturn.bEnabled = m_bEscapeProcessing && m_pSqlIterator; + aReturn.bChecked = m_bGraphicalDesign; + break; + case SID_BROWSER_CLEAR_QUERY: + aReturn.bEnabled = isEditable() && (!m_sStatement.isEmpty() || !m_vTableData.empty()); + break; + case SID_QUERY_VIEW_FUNCTIONS: + case SID_QUERY_VIEW_TABLES: + case SID_QUERY_VIEW_ALIASES: + aReturn.bChecked = getContainer() && getContainer()->isSlotEnabled(_nId); + aReturn.bEnabled = m_bGraphicalDesign; + break; + case SID_QUERY_DISTINCT_VALUES: + aReturn.bEnabled = m_bGraphicalDesign && isEditable(); + aReturn.bChecked = m_bDistinct; + break; + case SID_QUERY_LIMIT: + aReturn.bEnabled = m_bGraphicalDesign; + if( aReturn.bEnabled ) + aReturn.aValue <<= m_nLimit; + break; + case SID_QUERY_PROP_DLG: + aReturn.bEnabled = m_bGraphicalDesign; + break; + case ID_BROWSER_QUERY_EXECUTE: + aReturn.bEnabled = true; + break; + case SID_DB_QUERY_PREVIEW: + aReturn.bEnabled = true; + aReturn.bChecked = getContainer() && getContainer()->getPreviewFrame().is(); + break; +#if OSL_DEBUG_LEVEL > 0 + case ID_EDIT_QUERY_SQL: + break; + case ID_EDIT_QUERY_DESIGN: + break; +#endif + case ID_BROWSER_ADDTABLE: + if ( !m_bGraphicalDesign ) + { + aReturn.bEnabled = false; + break; + } + [[fallthrough]]; + default: + aReturn = OJoinController::GetState(_nId); + break; + } + return aReturn; +} + +void OQueryController::Execute(sal_uInt16 _nId, const Sequence< PropertyValue >& aArgs) +{ + switch(_nId) + { + case ID_BROWSER_ESCAPEPROCESSING: + setEscapeProcessing_fireEvent( !m_bEscapeProcessing ); + if ( !editingView() ) + setModified(true); + InvalidateFeature(ID_BROWSER_SQL); + break; + case ID_BROWSER_SAVEASDOC: + case ID_BROWSER_SAVEDOC: + grabFocusFromLimitBox(*this); + doSaveAsDoc(ID_BROWSER_SAVEASDOC == _nId); + break; + case SID_RELATION_ADD_RELATION: + { + OJoinDesignView* pView = getJoinView(); + if( pView ) + static_cast<OQueryTableView*>(pView->getTableView())->createNewConnection(); + } + break; + case SID_PRINTDOCDIRECT: + break; + case ID_BROWSER_CUT: + getContainer()->cut(); + break; + case ID_BROWSER_COPY: + getContainer()->copy(); + break; + case ID_BROWSER_PASTE: + getContainer()->paste(); + break; + case ID_BROWSER_SQL: + { + grabFocusFromLimitBox(*this); + if ( !getContainer()->checkStatement() ) + break; + SQLExceptionInfo aError; + try + { + OUString aErrorMsg; + setStatement_fireEvent( getContainer()->getStatement() ); + if(m_sStatement.isEmpty() && m_pSqlIterator) + { + // change the view of the data + delete m_pSqlIterator->getParseTree(); + m_pSqlIterator->setParseTree(nullptr); + m_bGraphicalDesign = !m_bGraphicalDesign; + impl_setViewMode( &aError ); + } + else + { + std::unique_ptr<::connectivity::OSQLParseNode> pNode = m_aSqlParser.parseTree(aErrorMsg,m_sStatement,m_bGraphicalDesign); + if ( pNode ) + { + delete m_pSqlIterator->getParseTree(); + m_pSqlIterator->setParseTree(pNode.release()); + m_pSqlIterator->traverseAll(); + + if ( m_pSqlIterator->hasErrors() ) + { + aError = m_pSqlIterator->getErrors(); + } + else + { + const OSQLTables& rTabs = m_pSqlIterator->getTables(); + if ( m_pSqlIterator->getStatementType() != OSQLStatementType::Select || rTabs.empty() ) + { + aError = SQLException( + DBA_RES(STR_QRY_NOSELECT), + nullptr, + "S1000", + 1000, + Any() + ); + } + else + { + // change the view of the data + m_bGraphicalDesign = !m_bGraphicalDesign; + OUString sNewStatement; + m_pSqlIterator->getParseTree()->parseNodeToStr( sNewStatement, getConnection() ); + setStatement_fireEvent( sNewStatement ); + getContainer()->SaveUIConfig(); + m_vTableConnectionData.clear(); + impl_setViewMode( &aError ); + } + } + } + else + { + aError = SQLException( + DBA_RES(STR_QRY_SYNTAX), + nullptr, + "S1000", + 1000, + Any() + ); + } + } + } + catch(const SQLException&) + { + aError = ::cppu::getCaughtException(); + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + + if ( aError.isValid() ) + showError( aError ); + + if(m_bGraphicalDesign) + { + InvalidateFeature(ID_BROWSER_ADDTABLE); + InvalidateFeature(SID_RELATION_ADD_RELATION); + } + } + break; + case SID_BROWSER_CLEAR_QUERY: + { + GetUndoManager().EnterListAction(DBA_RES(STR_QUERY_UNDO_TABWINDELETE), OUString(), 0, ViewShellId(-1) ); + getContainer()->clear(); + GetUndoManager().LeaveListAction(); + + setStatement_fireEvent( OUString() ); + if(m_bGraphicalDesign) + InvalidateFeature(ID_BROWSER_ADDTABLE); + } + break; + case SID_QUERY_VIEW_FUNCTIONS: + case SID_QUERY_VIEW_TABLES: + case SID_QUERY_VIEW_ALIASES: + getContainer()->setSlotEnabled(_nId,!getContainer()->isSlotEnabled(_nId)); + setModified(true); + break; + case SID_QUERY_DISTINCT_VALUES: + m_bDistinct = !m_bDistinct; + setModified(true); + break; + case SID_QUERY_LIMIT: + if ( aArgs.hasElements() && aArgs[0].Name == "DBLimit.Value" ) + { + aArgs[0].Value >>= m_nLimit; + setModified(true); + } + break; + case SID_QUERY_PROP_DLG: + grabFocusFromLimitBox(*this); + execute_QueryPropDlg(); + break; + case ID_BROWSER_QUERY_EXECUTE: + grabFocusFromLimitBox(*this); + if ( getContainer()->checkStatement() ) + executeQuery(); + break; + case SID_DB_QUERY_PREVIEW: + try + { + Reference< css::util::XCloseable > xCloseFrame( getContainer()->getPreviewFrame(), UNO_QUERY ); + if ( xCloseFrame.is() ) + { + try + { + xCloseFrame->close( true ); + } + catch(const Exception&) + { + OSL_FAIL( "OQueryController::Execute(SID_DB_QUERY_PREVIEW): *nobody* is expected to veto closing the preview frame!" ); + } + } + else + Execute(ID_BROWSER_QUERY_EXECUTE,Sequence< PropertyValue >()); + } + catch(const Exception&) + { + } + break; + default: + OJoinController::Execute(_nId,aArgs); + return; // else we would invalidate twice + } + InvalidateFeature(_nId); +} + +void OQueryController::impl_showAutoSQLViewError( const css::uno::Any& _rErrorDetails ) +{ + SQLContext aErrorContext; + aErrorContext.Message = lcl_getObjectResourceString( STR_ERROR_PARSING_STATEMENT, m_nCommandType ); + aErrorContext.Context = *this; + aErrorContext.Details = lcl_getObjectResourceString( STR_INFO_OPENING_IN_SQL_VIEW, m_nCommandType ); + aErrorContext.NextException = _rErrorDetails; + showError( aErrorContext ); +} + +void OQueryController::impl_setViewMode( ::dbtools::SQLExceptionInfo* _pErrorInfo ) +{ + OSL_PRECOND( getContainer(), "OQueryController::impl_setViewMode: illegal call!" ); + + bool wasModified = isModified(); + + SQLExceptionInfo aError; + bool bSuccess = getContainer()->switchView( &aError ); + if ( !bSuccess ) + { + m_bGraphicalDesign = !m_bGraphicalDesign; + // restore old state + getContainer()->switchView( nullptr ); + // don't pass &aError here, this would overwrite the error which the first switchView call + // returned in this location. + if ( _pErrorInfo ) + *_pErrorInfo = aError; + else + showError( aError ); + } + else + { + ensureToolbars( *this, m_bGraphicalDesign ); + } + + setModified( wasModified ); +} + +void OQueryController::impl_initialize() +{ + OJoinController::impl_initialize(); + + const NamedValueCollection& rArguments( getInitParams() ); + + OUString sCommand; + m_nCommandType = CommandType::QUERY; + + // reading parameters: + + // legacy parameters first (later overwritten by regular parameters) + OUString sIndependentSQLCommand; + if ( rArguments.get_ensureType( "IndependentSQLCommand", sIndependentSQLCommand ) ) + { + OSL_FAIL( "OQueryController::impl_initialize: IndependentSQLCommand is regognized for compatibility only!" ); + sCommand = sIndependentSQLCommand; + m_nCommandType = CommandType::COMMAND; + } + + OUString sCurrentQuery; + if ( rArguments.get_ensureType( "CurrentQuery", sCurrentQuery ) ) + { + OSL_FAIL( "OQueryController::impl_initialize: CurrentQuery is regognized for compatibility only!" ); + sCommand = sCurrentQuery; + m_nCommandType = CommandType::QUERY; + } + + bool bCreateView( false ); + if ( rArguments.get_ensureType( "CreateView", bCreateView ) && bCreateView ) + { + OSL_FAIL( "OQueryController::impl_initialize: CurrentQuery is regognized for compatibility only!" ); + m_nCommandType = CommandType::TABLE; + } + + // non-legacy parameters which overwrite the legacy parameters + rArguments.get_ensureType( PROPERTY_COMMAND, sCommand ); + rArguments.get_ensureType( PROPERTY_COMMAND_TYPE, m_nCommandType ); + + // translate Command/Type into proper members + // TODO/Later: all this (including those members) should be hidden behind some abstract interface, + // which is implemented for all the three commands + switch ( m_nCommandType ) + { + case CommandType::QUERY: + m_sName = sCommand; + break; + case CommandType::TABLE: + m_sName = sCommand; + break; + case CommandType::COMMAND: + setStatement_fireEvent( sCommand ); + m_sName.clear(); + break; + default: + OSL_FAIL( "OQueryController::impl_initialize: logic error in code!" ); + throw RuntimeException(); + } + + // more legacy parameters + bool bGraphicalDesign( true ); + if ( rArguments.get_ensureType( PROPERTY_QUERYDESIGNVIEW, bGraphicalDesign ) ) + { + OSL_FAIL( "OQueryController::impl_initialize: QueryDesignView is regognized for compatibility only!" ); + m_bGraphicalDesign = bGraphicalDesign; + } + + // more non-legacy + rArguments.get_ensureType( PROPERTY_GRAPHICAL_DESIGN, m_bGraphicalDesign ); + + bool bEscapeProcessing( true ); + if ( rArguments.get_ensureType( PROPERTY_ESCAPE_PROCESSING, bEscapeProcessing ) ) + { + setEscapeProcessing_fireEvent( bEscapeProcessing ); + + OSL_ENSURE( m_bEscapeProcessing || !m_bGraphicalDesign, "OQueryController::impl_initialize: can't do the graphical design without escape processing!" ); + if ( !m_bEscapeProcessing ) + m_bGraphicalDesign = false; + } + + // initial design + bool bForceInitialDesign = false; + Sequence< PropertyValue > aCurrentQueryDesignProps; + aCurrentQueryDesignProps = rArguments.getOrDefault( "CurrentQueryDesign", aCurrentQueryDesignProps ); + + if ( aCurrentQueryDesignProps.hasElements() ) + { + ::comphelper::NamedValueCollection aCurrentQueryDesign( aCurrentQueryDesignProps ); + if ( aCurrentQueryDesign.has( OUString(PROPERTY_GRAPHICAL_DESIGN) ) ) + { + aCurrentQueryDesign.get_ensureType( PROPERTY_GRAPHICAL_DESIGN, m_bGraphicalDesign ); + } + if ( aCurrentQueryDesign.has( OUString(PROPERTY_ESCAPE_PROCESSING) ) ) + { + aCurrentQueryDesign.get_ensureType( PROPERTY_ESCAPE_PROCESSING, m_bEscapeProcessing ); + } + if ( aCurrentQueryDesign.has( "Statement" ) ) + { + OUString sStatement; + aCurrentQueryDesign.get_ensureType( "Statement", sStatement ); + aCurrentQueryDesign.remove( "Statement" ); + setStatement_fireEvent( sStatement ); + } + + loadViewSettings( aCurrentQueryDesign ); + + bForceInitialDesign = true; + } + + if ( !ensureConnected() ) + { // we have no connection so what else should we do + m_bGraphicalDesign = false; + if ( editingView() ) + { + connectionLostMessage(); + throw SQLException(); + } + } + + // check the view capabilities + if ( isConnected() && editingView() ) + { + Reference< XViewsSupplier > xViewsSup( getConnection(), UNO_QUERY ); + Reference< XNameAccess > xViews; + if ( xViewsSup.is() ) + xViews = xViewsSup->getViews(); + + if ( !xViews.is() ) + { // we can't create views so we ask if the user wants to create a query instead + m_nCommandType = CommandType::QUERY; + bool bClose = false; + { + OUString aTitle(DBA_RES(STR_QUERYDESIGN_NO_VIEW_SUPPORT)); + OUString aMessage(DBA_RES(STR_QUERYDESIGN_NO_VIEW_ASK)); + OSQLMessageBox aDlg(getFrameWeld(), aTitle, aMessage, MessBoxStyle::YesNo | MessBoxStyle::DefaultYes, MessageType::Query); + bClose = aDlg.run() == RET_NO; + } + if ( bClose ) + throw VetoException(); + } + + // now if we are to edit an existing view, check whether this is possible + if ( !m_sName.isEmpty() ) + { + Any aView( xViews->getByName( m_sName ) ); + // will throw if there is no such view + if ( !( aView >>= m_xAlterView ) ) + { + throw IllegalArgumentException( + DBA_RES(STR_NO_ALTER_VIEW_SUPPORT), + *this, + 1 + ); + } + } + } + + OSL_ENSURE(getDataSource().is(),"OQueryController::impl_initialize: need a datasource!"); + + try + { + getContainer()->initialize(); + impl_reset( bForceInitialDesign ); + + SQLExceptionInfo aError; + const bool bAttemptedGraphicalDesign = m_bGraphicalDesign; + + if ( bForceInitialDesign ) + { + getContainer()->forceInitialView(); + } + else + { + impl_setViewMode( &aError ); + } + + if ( aError.isValid() && bAttemptedGraphicalDesign && !m_bGraphicalDesign ) + { + // we tried initializing the graphical view, this failed, and we were automatically switched to SQL + // view => tell this to the user + if ( !editingView() ) + { + impl_showAutoSQLViewError( aError.get() ); + } + } + + ClearUndoManager(); + + if ( m_bGraphicalDesign + && ( ( m_sName.isEmpty() && !editingCommand() ) + || ( m_sStatement.isEmpty() && editingCommand() ) + ) + ) + { + Application::PostUserEvent( LINK( this, OQueryController, OnExecuteAddTable ) ); + } + + setModified(false); + } + catch(const SQLException& e) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + // we caught an exception so we switch to text only mode + { + m_bGraphicalDesign = false; + getContainer()->initialize(); + OSQLMessageBox aBox(getFrameWeld(), e); + aBox.run(); + } + throw; + } +} + +void OQueryController::onLoadedMenu(const Reference< css::frame::XLayoutManager >& /*_xLayoutManager*/) +{ + ensureToolbars( *this, m_bGraphicalDesign ); +} + +OUString OQueryController::getPrivateTitle( ) const +{ + if ( m_sName.isEmpty() ) + { + if ( !editingCommand() ) + { + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + OUString aDefaultName = DBA_RES(editingView() ? STR_VIEW_TITLE : STR_QRY_TITLE); + return aDefaultName.getToken(0, ' ') + OUString::number(getCurrentStartNumber()); + } + } + return m_sName; +} + +void OQueryController::setQueryComposer() +{ + if(!isConnected()) + return; + + Reference< XSQLQueryComposerFactory > xFactory(getConnection(), UNO_QUERY); + OSL_ENSURE(xFactory.is(),"Connection doesn't support a querycomposer"); + if ( !(xFactory.is() && getContainer()) ) + return; + + try + { + m_xComposer = xFactory->createQueryComposer(); + getContainer()->setStatement(m_sStatement); + } + catch(const Exception&) + { + m_xComposer = nullptr; + } + OSL_ENSURE(m_xComposer.is(),"No querycomposer available!"); + Reference<XTablesSupplier> xTablesSup(getConnection(), UNO_QUERY); + deleteIterator(); + m_pSqlIterator.reset(new ::connectivity::OSQLParseTreeIterator( getConnection(), xTablesSup->getTables(), m_aSqlParser )); +} + +bool OQueryController::Construct(vcl::Window* pParent) +{ + // TODO: we have to check if we should create the text view or the design view + + setView( VclPtr<OQueryContainerWindow>::Create( pParent, *this, getORB() ) ); + + return OJoinController::Construct(pParent); +} + +OJoinDesignView* OQueryController::getJoinView() +{ + return getContainer()->getDesignView(); +} + +void OQueryController::describeSupportedFeatures() +{ + OJoinController::describeSupportedFeatures(); + implDescribeSupportedFeature( ".uno:SaveAs", ID_BROWSER_SAVEASDOC, CommandGroup::DOCUMENT ); + implDescribeSupportedFeature( ".uno:SbaNativeSql", ID_BROWSER_ESCAPEPROCESSING,CommandGroup::FORMAT ); + implDescribeSupportedFeature( ".uno:DBViewFunctions", SID_QUERY_VIEW_FUNCTIONS, CommandGroup::VIEW ); + implDescribeSupportedFeature( ".uno:DBViewTableNames", SID_QUERY_VIEW_TABLES, CommandGroup::VIEW ); + implDescribeSupportedFeature( ".uno:DBViewAliases", SID_QUERY_VIEW_ALIASES, CommandGroup::VIEW ); + implDescribeSupportedFeature( ".uno:DBDistinctValues", SID_QUERY_DISTINCT_VALUES, CommandGroup::FORMAT ); + implDescribeSupportedFeature( ".uno:DBChangeDesignMode",ID_BROWSER_SQL, CommandGroup::VIEW ); + implDescribeSupportedFeature( ".uno:DBClearQuery", SID_BROWSER_CLEAR_QUERY, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:SbaExecuteSql", ID_BROWSER_QUERY_EXECUTE, CommandGroup::VIEW ); + implDescribeSupportedFeature( ".uno:DBAddRelation", SID_RELATION_ADD_RELATION, CommandGroup::EDIT ); + implDescribeSupportedFeature( ".uno:DBQueryPreview", SID_DB_QUERY_PREVIEW, CommandGroup::VIEW ); + implDescribeSupportedFeature( ".uno:DBLimit", SID_QUERY_LIMIT, CommandGroup::FORMAT ); + implDescribeSupportedFeature( ".uno:DBQueryPropertiesDialog", SID_QUERY_PROP_DLG, CommandGroup::FORMAT ); + +#if OSL_DEBUG_LEVEL > 0 + implDescribeSupportedFeature( ".uno:DBShowParseTree", ID_EDIT_QUERY_SQL ); + implDescribeSupportedFeature( ".uno:DBMakeDisjunct", ID_EDIT_QUERY_DESIGN ); +#endif +} + +void OQueryController::impl_onModifyChanged() +{ + OJoinController::impl_onModifyChanged(); + InvalidateFeature(SID_BROWSER_CLEAR_QUERY); + InvalidateFeature(ID_BROWSER_SAVEASDOC); + InvalidateFeature(ID_BROWSER_QUERY_EXECUTE); +} + +void SAL_CALL OQueryController::disposing( const EventObject& Source ) +{ + SolarMutexGuard aGuard; + + if ( getContainer() && Source.Source.is() ) + { + if ( Source.Source == m_aCurrentFrame.getFrame() ) + { // our frame is being disposed -> close the preview window (if we have one) + Reference< XFrame2 > xPreviewFrame( getContainer()->getPreviewFrame() ); + ::comphelper::disposeComponent( xPreviewFrame ); + } + else if ( Source.Source == getContainer()->getPreviewFrame() ) + { + getContainer()->disposingPreview(); + } + } + + OJoinController_BASE::disposing(Source); +} + +void OQueryController::reconnect(bool _bUI) +{ + deleteIterator(); + ::comphelper::disposeComponent(m_xComposer); + + OJoinController::reconnect( _bUI ); + + if (isConnected()) + { + setQueryComposer(); + } + else + { + if(m_bGraphicalDesign) + { + m_bGraphicalDesign = false; + // don't call Execute(SQL) because this changes the sql statement + impl_setViewMode( nullptr ); + } + InvalidateAll(); + } +} + +void OQueryController::saveViewSettings( ::comphelper::NamedValueCollection& o_rViewSettings, const bool i_includingCriteria ) const +{ + saveTableWindows( o_rViewSettings ); + + ::comphelper::NamedValueCollection aAllFieldsData; + ::comphelper::NamedValueCollection aFieldData; + sal_Int32 i = 1; + for (auto const& fieldDesc : m_vTableFieldDesc) + { + if ( !fieldDesc->IsEmpty() ) + { + aFieldData.clear(); + fieldDesc->Save( aFieldData, i_includingCriteria ); + + const OUString sFieldSettingName = "Field" + OUString::number( i ); + aAllFieldsData.put( sFieldSettingName, aFieldData.getPropertyValues() ); + } + ++i; + } + + o_rViewSettings.put( "Fields", aAllFieldsData.getPropertyValues() ); + o_rViewSettings.put( "SplitterPosition", m_nSplitPos ); + o_rViewSettings.put( "VisibleRows", m_nVisibleRows ); +} + +void OQueryController::loadViewSettings( const ::comphelper::NamedValueCollection& o_rViewSettings ) +{ + loadTableWindows( o_rViewSettings ); + + m_nSplitPos = o_rViewSettings.getOrDefault( "SplitterPosition", m_nSplitPos ); + m_nVisibleRows = o_rViewSettings.getOrDefault( "VisibleRows", m_nVisibleRows ); + m_aFieldInformation = o_rViewSettings.getOrDefault( "Fields", m_aFieldInformation ); +} + +void OQueryController::execute_QueryPropDlg() +{ + QueryPropertiesDialog aQueryPropDlg(getContainer()->GetFrameWeld(), m_bDistinct, m_nLimit); + + if (aQueryPropDlg.run() == RET_OK) + { + m_bDistinct = aQueryPropDlg.getDistinct(); + m_nLimit = aQueryPropDlg.getLimit(); + InvalidateFeature( SID_QUERY_DISTINCT_VALUES ); + InvalidateFeature( SID_QUERY_LIMIT, nullptr, true ); + } +} + +sal_Int32 OQueryController::getColWidth(sal_uInt16 _nColPos) const +{ + if ( _nColPos < m_aFieldInformation.getLength() ) + { + rtl::Reference<OTableFieldDesc> pField( new OTableFieldDesc()); + pField->Load( m_aFieldInformation[ _nColPos ], false ); + return pField->GetColWidth(); + } + return 0; +} + +Reference<XNameAccess> OQueryController::getObjectContainer() const +{ + Reference< XNameAccess > xElements; + if ( editingView() ) + { + Reference< XViewsSupplier > xViewsSupp( getConnection(), UNO_QUERY ); + if ( xViewsSupp.is() ) + xElements = xViewsSupp->getViews(); + } + else + { + Reference< XQueriesSupplier > xQueriesSupp( getConnection(), UNO_QUERY ); + if ( xQueriesSupp.is() ) + xElements = xQueriesSupp->getQueries(); + else + { + Reference< XQueryDefinitionsSupplier > xQueryDefsSupp( getDataSource(), UNO_QUERY ); + if ( xQueryDefsSupp.is() ) + xElements = xQueryDefsSupp->getQueryDefinitions(); + } + } + + OSL_ENSURE( xElements.is(), "OQueryController::getObjectContainer: unable to obtain the container!" ); + return xElements; +} + +void OQueryController::executeQuery() +{ + // we don't need to check the connection here because we already check the composer + // which can't live without his connection + OUString sTranslatedStmt = translateStatement( false ); + + OUString sDataSourceName = getDataSourceName(); + if ( sDataSourceName.isEmpty() || sTranslatedStmt.isEmpty() ) + return; + + try + { + getContainer()->showPreview( getFrame() ); + InvalidateFeature(SID_DB_QUERY_PREVIEW); + + URL aWantToDispatch; + aWantToDispatch.Complete = ".component:DB/DataSourceBrowser"; + + OUString sFrameName( FRAME_NAME_QUERY_PREVIEW ); + sal_Int32 nSearchFlags = FrameSearchFlag::CHILDREN; + + Reference< XDispatch> xDisp; + Reference< XDispatchProvider> xProv( getFrame()->findFrame( sFrameName, nSearchFlags ), UNO_QUERY ); + if(!xProv.is()) + { + xProv.set( getFrame(), UNO_QUERY ); + if (xProv.is()) + xDisp = xProv->queryDispatch(aWantToDispatch, sFrameName, nSearchFlags); + } + else + { + xDisp = xProv->queryDispatch(aWantToDispatch, sFrameName, FrameSearchFlag::SELF); + } + if (xDisp.is()) + { + auto aProps(::comphelper::InitPropertySequence( + { + { PROPERTY_DATASOURCENAME, makeAny(sDataSourceName) }, + { PROPERTY_COMMAND_TYPE, makeAny(CommandType::COMMAND) }, + { PROPERTY_COMMAND, makeAny(sTranslatedStmt) }, + { PROPERTY_ENABLE_BROWSER, makeAny(false) }, + { PROPERTY_ACTIVE_CONNECTION, makeAny(getConnection()) }, + { PROPERTY_UPDATE_CATALOGNAME, makeAny(m_sUpdateCatalogName) }, + { PROPERTY_UPDATE_SCHEMANAME, makeAny(m_sUpdateSchemaName) }, + { PROPERTY_UPDATE_TABLENAME, makeAny(OUString()) }, + { PROPERTY_ESCAPE_PROCESSING, makeAny(m_bEscapeProcessing) } + })); + + xDisp->dispatch(aWantToDispatch, aProps); + // check the state of the beamer + // be notified when the beamer frame is closed + Reference< XComponent > xComponent = getFrame()->findFrame( sFrameName, nSearchFlags ); + if (xComponent.is()) + { + OSL_ENSURE(Reference< XFrame >(xComponent, UNO_QUERY).get() == getContainer()->getPreviewFrame().get(), + "OQueryController::executeQuery: oops ... which window do I have here?"); + Reference< XEventListener> xEvtL(static_cast<cppu::OWeakObject*>(this),UNO_QUERY); + xComponent->addEventListener(xEvtL); + } + } + else + { + OSL_FAIL("Couldn't create a beamer window!"); + } + } + catch(const Exception&) + { + OSL_FAIL("Couldn't create a beamer window!"); + } +} + +bool OQueryController::askForNewName(const Reference<XNameAccess>& _xElements, bool _bSaveAs) +{ + OSL_ENSURE( !editingCommand(), "OQueryController::askForNewName: not to be called when designing an independent statement!" ); + if ( editingCommand() ) + return false; + + OSL_PRECOND( _xElements.is(), "OQueryController::askForNewName: invalid container!" ); + if ( !_xElements.is() ) + return false; + + bool bRet = true; + bool bNew = _bSaveAs || !_xElements->hasByName( m_sName ); + if(bNew) + { + OUString aDefaultName; + if (!m_sName.isEmpty()) + aDefaultName = m_sName; + else + { + OUString sName = DBA_RES(editingView() ? STR_VIEW_TITLE : STR_QRY_TITLE); + aDefaultName = ::dbtools::createUniqueName(_xElements, sName.getToken(0, ' ')); + } + + DynamicTableOrQueryNameCheck aNameChecker( getConnection(), CommandType::QUERY ); + OSaveAsDlg aDlg( + getFrameWeld(), + m_nCommandType, + getORB(), + getConnection(), + aDefaultName, + aNameChecker, + SADFlags::NONE ); + + bRet = ( aDlg.run() == RET_OK ); + if ( bRet ) + { + m_sName = aDlg.getName(); + if ( editingView() ) + { + m_sUpdateCatalogName = aDlg.getCatalog(); + m_sUpdateSchemaName = aDlg.getSchema(); + } + } + } + return bRet; +} + +bool OQueryController::doSaveAsDoc(bool _bSaveAs) +{ + OSL_ENSURE(isEditable(),"Slot ID_BROWSER_SAVEDOC should not be enabled!"); + if ( !editingCommand() && !haveDataSource() ) + { + OUString aMessage(DBA_RES(STR_DATASOURCE_DELETED)); + OSQLWarningBox aBox(getFrameWeld(), aMessage); + aBox.run(); + return false; + } + + Reference< XNameAccess > xElements = getObjectContainer(); + if ( !xElements.is() ) + return false; + + if ( !getContainer()->checkStatement() ) + return false; + + OUString sTranslatedStmt = translateStatement(); + if ( editingCommand() ) + { + setModified( false ); + // this is all we need to do here. translateStatement implicitly set our m_sStatement, and + // notified it, and that's all + return true; + } + + if ( sTranslatedStmt.isEmpty() ) + return false; + + // first we need a name for our query so ask the user + // did we get a name + OUString sOriginalName( m_sName ); + if ( !askForNewName( xElements, _bSaveAs ) || m_sName.isEmpty() ) + return false; + + SQLExceptionInfo aInfo; + bool bSuccess = false; + bool bNew = false; + try + { + bNew = _bSaveAs + || ( !xElements->hasByName( m_sName ) ); + + Reference<XPropertySet> xQuery; + if ( bNew ) // just to make sure the query already exists + { + // drop the query, in case it already exists + if ( xElements->hasByName( m_sName ) ) + { + Reference< XDrop > xNameCont( xElements, UNO_QUERY ); + if ( xNameCont.is() ) + xNameCont->dropByName( m_sName ); + else + { + Reference< XNameContainer > xCont( xElements, UNO_QUERY ); + if ( xCont.is() ) + xCont->removeByName( m_sName ); + } + } + + // create a new (empty, uninitialized) query resp. view + Reference< XDataDescriptorFactory > xFact( xElements, UNO_QUERY ); + if ( xFact.is() ) + { + xQuery = xFact->createDataDescriptor(); + // to set the name is only allowed when the query is new + xQuery->setPropertyValue( PROPERTY_NAME, makeAny( m_sName ) ); + } + else + { + Reference< XSingleServiceFactory > xSingleFac( xElements, UNO_QUERY ); + if ( xSingleFac.is() ) + xQuery.set(xSingleFac->createInstance(), css::uno::UNO_QUERY); + } + } + else + { + xElements->getByName( m_sName ) >>= xQuery; + } + if ( !xQuery.is() ) + throw RuntimeException(); + + // the new commands + if ( editingView() && !bNew ) + { + OSL_ENSURE( xQuery == m_xAlterView, "OQueryController::doSaveAsDoc: already have another alterable view ...!?" ); + m_xAlterView.set( xQuery, UNO_QUERY_THROW ); + m_xAlterView->alterCommand( sTranslatedStmt ); + } + else + { // we're creating a query, or a *new* view + xQuery->setPropertyValue( PROPERTY_COMMAND, makeAny( sTranslatedStmt ) ); + + if ( editingView() ) + { + xQuery->setPropertyValue( PROPERTY_CATALOGNAME, makeAny( m_sUpdateCatalogName ) ); + xQuery->setPropertyValue( PROPERTY_SCHEMANAME, makeAny( m_sUpdateSchemaName ) ); + } + + if ( editingQuery() ) + { + xQuery->setPropertyValue( PROPERTY_UPDATE_TABLENAME, makeAny( OUString() ) ); + xQuery->setPropertyValue( PROPERTY_ESCAPE_PROCESSING, css::uno::makeAny( m_bEscapeProcessing ) ); + + xQuery->setPropertyValue( PROPERTY_LAYOUTINFORMATION, getViewData() ); + } + } + + if ( bNew ) + { + Reference< XAppend > xAppend( xElements, UNO_QUERY ); + if ( xAppend.is() ) + { + xAppend->appendByDescriptor( xQuery ); + } + else + { + Reference< XNameContainer > xCont( xElements, UNO_QUERY ); + if ( xCont.is() ) + xCont->insertByName( m_sName, makeAny( xQuery ) ); + } + + if ( editingView() ) + { + Reference< XPropertySet > xViewProps; + if ( xElements->hasByName( m_sName ) ) + xViewProps.set( xElements->getByName( m_sName ), UNO_QUERY ); + + if ( !xViewProps.is() ) // correct name and try again + m_sName = ::dbtools::composeTableName( getMetaData(), xQuery, ::dbtools::EComposeRule::InDataManipulation, false ); + + OSL_ENSURE( xElements->hasByName( m_sName ), "OQueryController::doSaveAsDoc: newly created view does not exist!" ); + + if ( xElements->hasByName( m_sName ) ) + m_xAlterView.set( xElements->getByName( m_sName ), UNO_QUERY ); + + // now check if our datasource has set a tablefilter and if so, append the new table name to it + ::dbaui::appendToFilter(getConnection(), m_sName, getORB(), getFrameWeld()); + } + Reference< XTitleChangeListener> xEventListener(impl_getTitleHelper_throw(),UNO_QUERY); + if ( xEventListener.is() ) + { + TitleChangedEvent aEvent; + xEventListener->titleChanged(aEvent); + } + releaseNumberForComponent(); + } + + setModified( false ); + bSuccess = true; + + } + catch(const SQLException&) + { + if ( !bNew ) + m_sName = sOriginalName; + aInfo = SQLExceptionInfo( ::cppu::getCaughtException() ); + } + catch(const Exception&) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + if ( !bNew ) + m_sName = sOriginalName; + } + + showError( aInfo ); + + // if we successfully saved a view we were creating, then close the designer + if ( bSuccess && editingView() && !m_xAlterView.is() ) + { + closeTask(); + } + + if ( bSuccess && editingView() ) + InvalidateFeature( ID_BROWSER_EDITDOC ); + + return bSuccess; +} + +namespace { +struct CommentStrip +{ + OUString maComment; + bool mbLastOnLine; + CommentStrip( const OUString& rComment, bool bLastOnLine ) + : maComment( rComment), mbLastOnLine( bLastOnLine) {} +}; + +} + +/** Obtain all comments in a query. + + See also delComment() implementation for OSQLParser::parseTree(). + */ +static std::vector< CommentStrip > getComment( const OUString& rQuery ) +{ + std::vector< CommentStrip > aRet; + // First a quick search if there is any "--" or "//" or "/*", if not then + // the whole copying loop is pointless. + if (rQuery.indexOf( "--" ) < 0 && rQuery.indexOf( "//" ) < 0 && + rQuery.indexOf( "/*" ) < 0) + return aRet; + + const sal_Unicode* pCopy = rQuery.getStr(); + const sal_Int32 nQueryLen = rQuery.getLength(); + bool bIsText1 = false; // "text" + bool bIsText2 = false; // 'text' + bool bComment2 = false; // /* comment */ + bool bComment = false; // -- or // comment + OUStringBuffer aBuf; + for (sal_Int32 i=0; i < nQueryLen; ++i) + { + if (bComment2) + { + aBuf.append( &pCopy[i], 1); + if ((i+1) < nQueryLen) + { + if (pCopy[i]=='*' && pCopy[i+1]=='/') + { + bComment2 = false; + aBuf.append( &pCopy[++i], 1); + aRet.emplace_back( aBuf.makeStringAndClear(), false); + } + } + else + { + // comment can't close anymore, actually an error, but... + aRet.emplace_back( aBuf.makeStringAndClear(), false); + } + continue; + } + if (pCopy[i] == '\n' || i == nQueryLen-1) + { + if (bComment) + { + if (i == nQueryLen-1 && pCopy[i] != '\n') + aBuf.append( &pCopy[i], 1); + aRet.emplace_back( aBuf.makeStringAndClear(), true); + bComment = false; + } + else if (!aRet.empty()) + aRet.back().mbLastOnLine = true; + } + else if (!bComment) + { + if (pCopy[i] == '\"' && !bIsText2) + bIsText1 = !bIsText1; + else if (pCopy[i] == '\'' && !bIsText1) + bIsText2 = !bIsText2; + if (!bIsText1 && !bIsText2 && (i+1) < nQueryLen) + { + if ((pCopy[i]=='-' && pCopy[i+1]=='-') || (pCopy[i]=='/' && pCopy[i+1]=='/')) + bComment = true; + else if (pCopy[i]=='/' && pCopy[i+1]=='*') + bComment2 = true; + } + } + if (bComment || bComment2) + aBuf.append( &pCopy[i], 1); + } + return aRet; +} + +/** Concat/insert comments that were previously obtained with getComment(). + + NOTE: The current parser implementation does not preserve newlines, so all + comments are always appended to the entire query, also inline comments + that would need positioning anyway that can't be obtained after + recomposition. This is ugly but at least allows commented queries while + preserving the comments _somehow_. + */ +static OUString concatComment( const OUString& rQuery, const std::vector< CommentStrip >& rComments ) +{ + // No comments => return query. + if (rComments.empty()) + return rQuery; + + const sal_Unicode* pBeg = rQuery.getStr(); + const sal_Int32 nLen = rQuery.getLength(); + const size_t nComments = rComments.size(); + // Obtaining the needed size once should be faster than reallocating. + // Also add a blank or linefeed for each comment. + sal_Int32 nBufSize = nLen + nComments; + for (auto const& comment : rComments) + nBufSize += comment.maComment.getLength(); + OUStringBuffer aBuf( nBufSize ); + sal_Int32 nIndBeg = 0; + sal_Int32 nIndLF = rQuery.indexOf('\n'); + size_t i = 0; + while (nIndLF >= 0 && i < nComments) + { + aBuf.append( pBeg + nIndBeg, nIndLF - nIndBeg); + do + { + aBuf.append( rComments[i].maComment); + } while (!rComments[i++].mbLastOnLine && i < nComments); + aBuf.append( pBeg + nIndLF, 1); // the LF + nIndBeg = nIndLF + 1; + nIndLF = (nIndBeg < nLen ? rQuery.indexOf( '\n', nIndBeg) : -1); + } + // Append remainder of query. + if (nIndBeg < nLen) + aBuf.append( pBeg + nIndBeg, nLen - nIndBeg); + // Append all remaining comments, preserve lines. + bool bNewLine = false; + for ( ; i < nComments; ++i) + { + if (!bNewLine) + aBuf.append( ' '); + aBuf.append( rComments[i].maComment); + if (rComments[i].mbLastOnLine) + { + aBuf.append( '\n'); + bNewLine = true; + } + else + bNewLine = false; + } + return aBuf.makeStringAndClear(); +} + +OUString OQueryController::translateStatement( bool _bFireStatementChange ) +{ + // now set the properties + setStatement_fireEvent( getContainer()->getStatement(), _bFireStatementChange ); + OUString sTranslatedStmt; + if(!m_sStatement.isEmpty() && m_xComposer.is() && m_bEscapeProcessing) + { + try + { + OUString aErrorMsg; + + std::vector< CommentStrip > aComments = getComment( m_sStatement); + + std::unique_ptr<::connectivity::OSQLParseNode> pNode = m_aSqlParser.parseTree( aErrorMsg, m_sStatement, m_bGraphicalDesign ); + if(pNode) + { + pNode->parseNodeToStr( sTranslatedStmt, getConnection() ); + } + + m_xComposer->setQuery(sTranslatedStmt); + sTranslatedStmt = m_xComposer->getComposedQuery(); + sTranslatedStmt = concatComment( sTranslatedStmt, aComments); + } + catch(const SQLException& e) + { + ::dbtools::SQLExceptionInfo aInfo(e); + showError(aInfo); + // an error occurred so we clear the statement + sTranslatedStmt.clear(); + } + } + else if(m_sStatement.isEmpty()) + { + showError(SQLException(DBA_RES(STR_QRY_NOSELECT), nullptr, "S1000", 1000, Any())); + } + else + sTranslatedStmt = m_sStatement; + + return sTranslatedStmt; +} + +short OQueryController::saveModified() +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( getMutex() ); + short nRet = RET_YES; + if ( !isConnected() || !isModified() ) + return nRet; + + if ( !m_bGraphicalDesign + || ( !m_vTableFieldDesc.empty() + && !m_vTableData.empty() + ) + ) + { + OUString sMessageText( lcl_getObjectResourceString( STR_QUERY_SAVEMODIFIED, m_nCommandType ) ); + + std::unique_ptr<weld::MessageDialog> xQueryBox(Application::CreateMessageDialog(getFrameWeld(), + VclMessageType::Question, VclButtonsType::YesNo, + sMessageText)); + xQueryBox->add_button(GetStandardText(StandardButtonType::Cancel), RET_CANCEL); + xQueryBox->set_default_response(RET_YES); + + nRet = xQueryBox->run(); + if ( ( nRet == RET_YES ) + && !doSaveAsDoc( false ) + ) + { + nRet = RET_CANCEL; + } + } + return nRet; +} + +void OQueryController::impl_reset( const bool i_bForceCurrentControllerSettings ) +{ + bool bValid = false; + + Sequence< PropertyValue > aLayoutInformation; + // get command from the query if a query name was supplied + if ( !i_bForceCurrentControllerSettings && !editingCommand() ) + { + if ( !m_sName.isEmpty() ) + { + Reference< XNameAccess > xQueries = getObjectContainer(); + if ( xQueries.is() ) + { + Reference< XPropertySet > xProp; + if( xQueries->hasByName( m_sName ) && ( xQueries->getByName( m_sName ) >>= xProp ) && xProp.is() ) + { + OUString sNewStatement; + xProp->getPropertyValue( PROPERTY_COMMAND ) >>= sNewStatement; + setStatement_fireEvent( sNewStatement ); + + if ( editingQuery() ) + { + bool bNewEscapeProcessing( true ); + xProp->getPropertyValue( PROPERTY_ESCAPE_PROCESSING ) >>= bNewEscapeProcessing; + setEscapeProcessing_fireEvent( bNewEscapeProcessing ); + } + + m_bGraphicalDesign = m_bGraphicalDesign && m_bEscapeProcessing; + bValid = true; + + try + { + if ( editingQuery() ) + xProp->getPropertyValue( PROPERTY_LAYOUTINFORMATION ) >>= aLayoutInformation; + } + catch( const Exception& ) + { + OSL_FAIL( "OQueryController::impl_reset: could not retrieve the layout information from the query!" ); + } + } + } + } + } + else + { + bValid = true; + // assume that we got all necessary information during initialization + } + + if ( bValid ) + { + // load the layoutInformation + if ( aLayoutInformation.hasElements() ) + { + try + { + loadViewSettings( aLayoutInformation ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + } + + if ( !m_sStatement.isEmpty() ) + { + setQueryComposer(); + + bool bError( false ); + + if ( !m_pSqlIterator ) + { + bError = true; + } + else if ( m_bEscapeProcessing ) + { + OUString aErrorMsg; + std::unique_ptr< ::connectivity::OSQLParseNode > pNode( + m_aSqlParser.parseTree( aErrorMsg, m_sStatement, m_bGraphicalDesign ) ); + + if (pNode) + { + delete m_pSqlIterator->getParseTree(); + m_pSqlIterator->setParseTree( pNode.release() ); + m_pSqlIterator->traverseAll(); + if ( m_pSqlIterator->hasErrors() ) + { + if ( !i_bForceCurrentControllerSettings && m_bGraphicalDesign && !editingView() ) + { + impl_showAutoSQLViewError( makeAny( m_pSqlIterator->getErrors() ) ); + } + bError = true; + } + } + else + { + if ( !i_bForceCurrentControllerSettings && !editingView() ) + { + OUString aTitle(DBA_RES(STR_SVT_SQL_SYNTAX_ERROR)); + OSQLMessageBox aDlg(getFrameWeld(), aTitle, aErrorMsg); + aDlg.run(); + } + bError = true; + } + } + + if ( bError ) + { + m_bGraphicalDesign = false; + if ( editingView() ) + // if we're editing a view whose statement could not be parsed, default to "no escape processing" + setEscapeProcessing_fireEvent( false ); + } + } + } + + if(!m_pSqlIterator) + setQueryComposer(); + OSL_ENSURE(m_pSqlIterator,"No SQLIterator set!"); + + getContainer()->setNoneVisbleRow(m_nVisibleRows); +} + +void OQueryController::reset() +{ + impl_reset(); + getContainer()->reset(); + ClearUndoManager(); +} + +void OQueryController::setStatement_fireEvent( const OUString& _rNewStatement, bool _bFireStatementChange ) +{ + Any aOldValue = makeAny( m_sStatement ); + m_sStatement = _rNewStatement; + Any aNewValue = makeAny( m_sStatement ); + + sal_Int32 nHandle = PROPERTY_ID_ACTIVECOMMAND; + if ( _bFireStatementChange ) + fire( &nHandle, &aNewValue, &aOldValue, 1, false ); +} + +void OQueryController::setEscapeProcessing_fireEvent( const bool _bEscapeProcessing ) +{ + if ( _bEscapeProcessing == m_bEscapeProcessing ) + return; + + Any aOldValue = makeAny( m_bEscapeProcessing ); + m_bEscapeProcessing = _bEscapeProcessing; + Any aNewValue = makeAny( m_bEscapeProcessing ); + + sal_Int32 nHandle = PROPERTY_ID_ESCAPE_PROCESSING; + fire( &nHandle, &aNewValue, &aOldValue, 1, false ); +} + +IMPL_LINK_NOARG( OQueryController, OnExecuteAddTable, void*, void ) +{ + Execute( ID_BROWSER_ADDTABLE,Sequence<PropertyValue>() ); +} + +bool OQueryController::allowViews() const +{ + return true; +} + +bool OQueryController::allowQueries() const +{ + OSL_ENSURE( getSdbMetaData().isConnected(), "OQueryController::allowQueries: illegal call!" ); + if ( !getSdbMetaData().supportsSubqueriesInFrom() ) + return false; + + const NamedValueCollection& rArguments( getInitParams() ); + sal_Int32 nCommandType = rArguments.getOrDefault( PROPERTY_COMMAND_TYPE, sal_Int32(CommandType::QUERY) ); + bool bCreatingView = ( nCommandType == CommandType::TABLE ); + return !bCreatingView; +} + +Any SAL_CALL OQueryController::getViewData() +{ + ::osl::MutexGuard aGuard( getMutex() ); + + getContainer()->SaveUIConfig(); + + ::comphelper::NamedValueCollection aViewSettings; + saveViewSettings( aViewSettings, false ); + + return makeAny( aViewSettings.getPropertyValues() ); +} + +void SAL_CALL OQueryController::restoreViewData(const Any& /*Data*/) +{ + // TODO +} + +} // namespace dbaui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/querydlg.cxx b/dbaccess/source/ui/querydesign/querydlg.cxx new file mode 100644 index 000000000..422eeb935 --- /dev/null +++ b/dbaccess/source/ui/querydesign/querydlg.cxx @@ -0,0 +1,313 @@ +/* -*- 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 "querydlg.hxx" +#include <JoinController.hxx> +#include <JoinDesignView.hxx> +#include <strings.hrc> +#include <tools/diagnose_ex.h> +#include "QTableConnectionData.hxx" +#include <core_resource.hxx> +#include <QueryTableView.hxx> +#include <com/sun/star/sdbc/XDatabaseMetaData.hpp> +#include <com/sun/star/sdbc/SQLException.hpp> +#include <RelationControl.hxx> + +#define ID_INNER_JOIN 1 +#define ID_LEFT_JOIN 2 +#define ID_RIGHT_JOIN 3 +#define ID_FULL_JOIN 4 +#define ID_CROSS_JOIN 5 + +using namespace dbaui; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::sdbc; + +DlgQryJoin::DlgQryJoin(const OQueryTableView* pParent, + const TTableConnectionData::value_type& _pData, + const OJoinTableView::OTableWindowMap* _pTableMap, + const Reference< XConnection >& _xConnection, + bool _bAllowTableSelect) + : GenericDialogController(pParent->GetFrameWeld(), "dbaccess/ui/joindialog.ui", "JoinDialog") + , eJoinType(static_cast<OQueryTableConnectionData*>(_pData.get())->GetJoinType()) + , m_pOrigConnData(_pData) + , m_xConnection(_xConnection) + , m_xML_HelpText(m_xBuilder->weld_label("helptext")) + , m_xPB_OK(m_xBuilder->weld_button("ok")) + , m_xLB_JoinType(m_xBuilder->weld_combo_box("type")) + , m_xCBNatural(m_xBuilder->weld_check_button("natural")) +{ + Size aSize(m_xML_HelpText->get_approximate_digit_width() * 44, + m_xML_HelpText->get_text_height() * 6); + //alternatively loop through the STR_QUERY_* strings with their STR_JOIN_TYPE_HINT + //suffix to find the longest entry at runtime + m_xML_HelpText->set_size_request(aSize.Width(), aSize.Height()); + + // Copy connection + m_pConnData = _pData->NewInstance(); + m_pConnData->CopyFrom(*_pData); + + m_xTableControl.reset(new OTableListBoxControl(m_xBuilder.get(), _pTableMap, this)); + + m_xCBNatural->set_active(static_cast<OQueryTableConnectionData*>(m_pConnData.get())->isNatural()); + + if( _bAllowTableSelect ) + { + m_xTableControl->Init( m_pConnData ); + m_xTableControl->fillListBoxes(); + } + else + { + m_xTableControl->fillAndDisable(m_pConnData); + m_xTableControl->Init( m_pConnData ); + } + + m_xTableControl->lateUIInit(); + + bool bSupportFullJoin = false; + Reference<XDatabaseMetaData> xMeta; + try + { + xMeta = m_xConnection->getMetaData(); + if ( xMeta.is() ) + bSupportFullJoin = xMeta->supportsFullOuterJoins(); + } + catch(SQLException&) + { + } + bool bSupportOuterJoin = false; + try + { + if ( xMeta.is() ) + bSupportOuterJoin= xMeta->supportsOuterJoins(); + } + catch(SQLException&) + { + } + + setJoinType(eJoinType); + + m_xPB_OK->connect_clicked(LINK(this, DlgQryJoin, OKClickHdl)); + + m_xLB_JoinType->connect_changed(LINK(this,DlgQryJoin,LBChangeHdl)); + m_xCBNatural->connect_toggled(LINK(this,DlgQryJoin,NaturalToggleHdl)); + + if ( pParent->getDesignView()->getController().isReadOnly() ) + { + m_xLB_JoinType->set_sensitive(false); + m_xCBNatural->set_sensitive(false); + m_xTableControl->Disable(); + } + else + { + for (sal_Int32 i = 0; i < m_xLB_JoinType->get_count();) + { + const sal_Int32 nJoinTyp = m_xLB_JoinType->get_id(i).toInt32(); + if ( !bSupportFullJoin && nJoinTyp == ID_FULL_JOIN ) + m_xLB_JoinType->remove(i); + else if ( !bSupportOuterJoin && (nJoinTyp == ID_LEFT_JOIN || nJoinTyp == ID_RIGHT_JOIN) ) + m_xLB_JoinType->remove(i); + else + ++i; + } + + m_xTableControl->NotifyCellChange(); + m_xTableControl->enableRelation(!static_cast<OQueryTableConnectionData*>(m_pConnData.get())->isNatural() && eJoinType != CROSS_JOIN ); + } +} + +DlgQryJoin::~DlgQryJoin() +{ +} + +IMPL_LINK_NOARG( DlgQryJoin, LBChangeHdl, weld::ComboBox&, void ) +{ + if (!m_xLB_JoinType->get_value_changed_from_saved()) + return; + + m_xLB_JoinType->save_value(); + m_xML_HelpText->set_label(OUString()); + + m_xTableControl->enableRelation(true); + + OUString sFirstWinName = m_pConnData->getReferencingTable()->GetWinName(); + OUString sSecondWinName = m_pConnData->getReferencedTable()->GetWinName(); + const EJoinType eOldJoinType = eJoinType; + const char* pResId = nullptr; + const sal_Int32 nPos = m_xLB_JoinType->get_active(); + const sal_Int32 nJoinType = m_xLB_JoinType->get_id(nPos).toInt32(); + bool bAddHint = true; + switch ( nJoinType ) + { + default: + case ID_INNER_JOIN: + pResId = STR_QUERY_INNER_JOIN; + bAddHint = false; + eJoinType = INNER_JOIN; + break; + case ID_LEFT_JOIN: + pResId = STR_QUERY_LEFTRIGHT_JOIN; + eJoinType = LEFT_JOIN; + break; + case ID_RIGHT_JOIN: + { + pResId = STR_QUERY_LEFTRIGHT_JOIN; + eJoinType = RIGHT_JOIN; + OUString sTemp = sFirstWinName; + sFirstWinName = sSecondWinName; + sSecondWinName = sTemp; + } + break; + case ID_FULL_JOIN: + pResId = STR_QUERY_FULL_JOIN; + eJoinType = FULL_JOIN; + break; + case ID_CROSS_JOIN: + { + pResId = STR_QUERY_CROSS_JOIN; + eJoinType = CROSS_JOIN; + + m_pConnData->ResetConnLines(); + m_xTableControl->lateInit(); + m_xCBNatural->set_active(false); + m_xTableControl->enableRelation(false); + m_pConnData->AppendConnLine("",""); + m_xPB_OK->set_sensitive(true); + } + break; + } + + m_xCBNatural->set_sensitive(eJoinType != CROSS_JOIN); + + if ( eJoinType != eOldJoinType && eOldJoinType == CROSS_JOIN ) + { + m_pConnData->ResetConnLines(); + } + if ( eJoinType != CROSS_JOIN ) + { + m_xTableControl->NotifyCellChange(); + NaturalToggleHdl(*m_xCBNatural); + } + + m_xTableControl->Invalidate(); + + OUString sHelpText = DBA_RES(pResId); + if( nPos ) + { + sHelpText = sHelpText.replaceFirst( "%1", sFirstWinName ); + sHelpText = sHelpText.replaceFirst( "%2", sSecondWinName ); + } + if ( bAddHint ) + { + sHelpText += "\n" + DBA_RES( STR_JOIN_TYPE_HINT ); + } + + m_xML_HelpText->set_label( sHelpText ); +} + +IMPL_LINK_NOARG(DlgQryJoin, OKClickHdl, weld::Button&, void) +{ + m_pConnData->Update(); + m_pOrigConnData->CopyFrom( *m_pConnData ); + + m_xDialog->response(RET_OK); +} + +IMPL_LINK_NOARG(DlgQryJoin, NaturalToggleHdl, weld::ToggleButton&, void) +{ + bool bChecked = m_xCBNatural->get_active(); + static_cast<OQueryTableConnectionData*>(m_pConnData.get())->setNatural(bChecked); + m_xTableControl->enableRelation(!bChecked); + if ( !bChecked ) + return; + + m_pConnData->ResetConnLines(); + try + { + Reference<XNameAccess> xReferencedTableColumns(m_pConnData->getReferencedTable()->getColumns()); + Sequence< OUString> aSeq = m_pConnData->getReferencingTable()->getColumns()->getElementNames(); + const OUString* pIter = aSeq.getConstArray(); + const OUString* pEnd = pIter + aSeq.getLength(); + for(;pIter != pEnd;++pIter) + { + if ( xReferencedTableColumns->hasByName(*pIter) ) + m_pConnData->AppendConnLine(*pIter,*pIter); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("dbaccess"); + } + m_xTableControl->NotifyCellChange(); + m_xTableControl->Invalidate(); +} + +void DlgQryJoin::setValid(bool _bValid) +{ + m_xPB_OK->set_sensitive(_bValid || eJoinType == CROSS_JOIN ); +} + +void DlgQryJoin::notifyConnectionChange( ) +{ + setJoinType( static_cast<OQueryTableConnectionData*>(m_pConnData.get())->GetJoinType() ); + m_xCBNatural->set_active(static_cast<OQueryTableConnectionData*>(m_pConnData.get())->isNatural()); + NaturalToggleHdl(*m_xCBNatural); +} + +void DlgQryJoin::setJoinType(EJoinType _eNewJoinType) +{ + eJoinType = _eNewJoinType; + m_xCBNatural->set_sensitive(eJoinType != CROSS_JOIN); + + sal_Int32 nJoinType = 0; + switch ( eJoinType ) + { + default: + case INNER_JOIN: + nJoinType = ID_INNER_JOIN; + break; + case LEFT_JOIN: + nJoinType = ID_LEFT_JOIN; + break; + case RIGHT_JOIN: + nJoinType = ID_RIGHT_JOIN; + break; + case FULL_JOIN: + nJoinType = ID_FULL_JOIN; + break; + case CROSS_JOIN: + nJoinType = ID_CROSS_JOIN; + break; + } + + const sal_Int32 nCount = m_xLB_JoinType->get_count(); + for (sal_Int32 i = 0; i < nCount; ++i) + { + if (nJoinType == m_xLB_JoinType->get_id(i).toInt32()) + { + m_xLB_JoinType->set_active(i); + break; + } + } + + LBChangeHdl(*m_xLB_JoinType); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/dbaccess/source/ui/querydesign/querydlg.hxx b/dbaccess/source/ui/querydesign/querydlg.hxx new file mode 100644 index 000000000..4631d7fbe --- /dev/null +++ b/dbaccess/source/ui/querydesign/querydlg.hxx @@ -0,0 +1,79 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYDLG_HXX +#define INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYDLG_HXX + +#include <vcl/weld.hxx> + +#include <QEnumTypes.hxx> + +#include <RelControliFace.hxx> +#include <JoinTableView.hxx> + + +namespace dbaui +{ + class OTableListBoxControl; + class OQueryTableView; + class DlgQryJoin final : public weld::GenericDialogController + , public IRelationControlInterface + { + EJoinType eJoinType; + TTableConnectionData::value_type m_pConnData; // contains left and right table + TTableConnectionData::value_type m_pOrigConnData; + css::uno::Reference< css::sdbc::XConnection > m_xConnection; + + std::unique_ptr<weld::Label> m_xML_HelpText; + std::unique_ptr<weld::Button> m_xPB_OK; + std::unique_ptr<weld::ComboBox> m_xLB_JoinType; + std::unique_ptr<weld::CheckButton> m_xCBNatural; + std::unique_ptr<OTableListBoxControl> m_xTableControl; + + DECL_LINK(OKClickHdl, weld::Button&, void); + DECL_LINK(LBChangeHdl, weld::ComboBox&, void); + DECL_LINK(NaturalToggleHdl, weld::ToggleButton&, void); + + /** setJoinType enables and set the new join type + @param _eNewJoinType the new jointype + */ + void setJoinType(EJoinType _eNewJoinType); + public: + DlgQryJoin( const OQueryTableView * pParent, + const TTableConnectionData::value_type& pData, + const OJoinTableView::OTableWindowMap* _pTableMap, + const css::uno::Reference< css::sdbc::XConnection >& _xConnection, + bool _bAllowTableSelect); + virtual ~DlgQryJoin() override; + EJoinType GetJoinType() const { return eJoinType; }; + + /** setValid set the valid inside, can be used for OK buttons + @param _bValid true when the using control allows an update + */ + virtual void setValid(bool _bValid) override; + + /** notifyConnectionChange is callback which is called when the table selection has changed and a new connection exists + @param _pConnectionData the connection which exists between the new tables + */ + virtual void notifyConnectionChange() override; + }; +} +#endif // INCLUDED_DBACCESS_SOURCE_UI_QUERYDESIGN_QUERYDLG_HXX + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |