summaryrefslogtreecommitdiffstats
path: root/dbaccess/source/ui/querydesign
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--dbaccess/source/ui/querydesign/ConnectionLine.cxx356
-rw-r--r--dbaccess/source/ui/querydesign/ConnectionLineAccess.cxx187
-rw-r--r--dbaccess/source/ui/querydesign/ConnectionLineData.cxx76
-rw-r--r--dbaccess/source/ui/querydesign/JAccess.cxx91
-rw-r--r--dbaccess/source/ui/querydesign/JoinController.cxx406
-rw-r--r--dbaccess/source/ui/querydesign/JoinDesignView.cxx100
-rw-r--r--dbaccess/source/ui/querydesign/JoinExchange.cxx114
-rw-r--r--dbaccess/source/ui/querydesign/JoinTableView.cxx1568
-rw-r--r--dbaccess/source/ui/querydesign/QTableConnection.cxx73
-rw-r--r--dbaccess/source/ui/querydesign/QTableConnection.hxx46
-rw-r--r--dbaccess/source/ui/querydesign/QTableConnectionData.cxx112
-rw-r--r--dbaccess/source/ui/querydesign/QTableConnectionData.hxx67
-rw-r--r--dbaccess/source/ui/querydesign/QTableWindow.cxx175
-rw-r--r--dbaccess/source/ui/querydesign/QTableWindow.hxx76
-rw-r--r--dbaccess/source/ui/querydesign/QTableWindowData.cxx34
-rw-r--r--dbaccess/source/ui/querydesign/QTableWindowData.hxx38
-rw-r--r--dbaccess/source/ui/querydesign/QueryAddTabConnUndoAction.hxx49
-rw-r--r--dbaccess/source/ui/querydesign/QueryDesignFieldUndoAct.hxx138
-rw-r--r--dbaccess/source/ui/querydesign/QueryDesignUndoAction.hxx39
-rw-r--r--dbaccess/source/ui/querydesign/QueryDesignView.cxx3441
-rw-r--r--dbaccess/source/ui/querydesign/QueryMoveTabWinUndoAct.cxx40
-rw-r--r--dbaccess/source/ui/querydesign/QueryMoveTabWinUndoAct.hxx54
-rw-r--r--dbaccess/source/ui/querydesign/QuerySizeTabWinUndoAct.hxx70
-rw-r--r--dbaccess/source/ui/querydesign/QueryTabConnUndoAction.cxx117
-rw-r--r--dbaccess/source/ui/querydesign/QueryTabConnUndoAction.hxx50
-rw-r--r--dbaccess/source/ui/querydesign/QueryTabWinShowUndoAct.hxx51
-rw-r--r--dbaccess/source/ui/querydesign/QueryTabWinUndoAct.cxx112
-rw-r--r--dbaccess/source/ui/querydesign/QueryTabWinUndoAct.hxx61
-rw-r--r--dbaccess/source/ui/querydesign/QueryTableView.cxx889
-rw-r--r--dbaccess/source/ui/querydesign/QueryTextView.cxx177
-rw-r--r--dbaccess/source/ui/querydesign/QueryViewSwitch.cxx292
-rw-r--r--dbaccess/source/ui/querydesign/SelectionBrowseBox.cxx2720
-rw-r--r--dbaccess/source/ui/querydesign/SelectionBrowseBox.hxx324
-rw-r--r--dbaccess/source/ui/querydesign/TableConnection.cxx190
-rw-r--r--dbaccess/source/ui/querydesign/TableConnectionData.cxx147
-rw-r--r--dbaccess/source/ui/querydesign/TableFieldDescription.cxx196
-rw-r--r--dbaccess/source/ui/querydesign/TableFieldInfo.cxx30
-rw-r--r--dbaccess/source/ui/querydesign/TableFieldInfo.hxx43
-rw-r--r--dbaccess/source/ui/querydesign/TableWindow.cxx716
-rw-r--r--dbaccess/source/ui/querydesign/TableWindowAccess.cxx250
-rw-r--r--dbaccess/source/ui/querydesign/TableWindowData.cxx135
-rw-r--r--dbaccess/source/ui/querydesign/TableWindowListBox.cxx292
-rw-r--r--dbaccess/source/ui/querydesign/TableWindowTitle.cxx97
-rw-r--r--dbaccess/source/ui/querydesign/class.jpgbin0 -> 224242 bytes
-rw-r--r--dbaccess/source/ui/querydesign/limitboxcontroller.cxx322
-rw-r--r--dbaccess/source/ui/querydesign/limitboxcontroller.hxx65
-rw-r--r--dbaccess/source/ui/querydesign/querycontainerwindow.cxx222
-rw-r--r--dbaccess/source/ui/querydesign/querycontroller.cxx1790
-rw-r--r--dbaccess/source/ui/querydesign/querydlg.cxx313
-rw-r--r--dbaccess/source/ui/querydesign/querydlg.hxx77
50 files changed, 17028 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..77a67004e
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/ConnectionLine.cxx
@@ -0,0 +1,356 @@
+/* -*- 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 tools::Long DESCRIPT_LINE_WIDTH = 15;
+const tools::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 tools::Long nRowHeight = pListBox->get_widget().get_height_rows(1);
+ 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 _nEntry the source or dest entry
+ @param _rNewConPos (in/out) the connection pos
+ @param _rNewDescrPos (in/out) the description pos
+ */
+ void calcPointsYValue(const OTableWindow* _pWin, int _nEntry, Point& _rNewConPos, Point& _rNewDescrPos)
+ {
+ const OTableWindowListBox* pListBox = _pWin->GetListBox();
+ _rNewConPos.setY( _pWin->GetPosPixel().Y() );
+
+ std::unique_ptr<weld::TreeIter> xEntry;
+ const weld::TreeView& rTreeView = pListBox->get_widget();
+
+ if (_nEntry != -1)
+ {
+ _rNewConPos.AdjustY(pListBox->GetPosPixel().Y() );
+ xEntry = rTreeView.make_iterator();
+ if (!rTreeView.get_iter_first(*xEntry) || !rTreeView.iter_nth_sibling(*xEntry, _nEntry))
+ xEntry.reset();
+ }
+
+ if (xEntry)
+ {
+ auto nEntryPos = rTreeView.get_row_area(*xEntry).Center().Y();
+
+ if( nEntryPos >= 0 )
+ {
+ _rNewConPos.AdjustY(nEntryPos);
+ }
+ else
+ {
+ const auto nRowHeight = rTreeView.get_height_rows(1);
+ _rNewConPos.AdjustY( -static_cast<tools::Long>( 0.5 * nRowHeight ) );
+ }
+
+ tools::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;
+
+ int nSourceEntry = pSourceWin->GetListBox()->GetEntryFromText( GetData()->GetSourceFieldName() );
+ int nDestEntry = pDestWin->GetListBox()->GetEntryFromText( GetData()->GetDestFieldName() );
+
+ // determine X-coordinates
+ Point aSourceCenter( 0, 0 );
+ Point aDestCenter( 0, 0 );
+
+ aSourceCenter.setX( pSourceWin->GetPosPixel().X() + static_cast<tools::Long>( 0.5*pSourceWin->GetSizePixel().Width() ) );
+ aDestCenter.setX( pDestWin->GetPosPixel().X() + static_cast<tools::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 && nSourceEntry != nDestEntry)
+ calcPointX2(pFirstWin,*pFirstConPos,*pFirstDescrPos);
+ else
+ calcPointX1(pFirstWin,*pFirstConPos,*pFirstDescrPos);
+ calcPointX2(pSecondWin,*pSecondConPos,*pSecondDescrPos);
+
+ // determine aSourceConnPosY
+ calcPointsYValue(pSourceWin, nSourceEntry, m_aSourceConnPos,m_aSourceDescrLinePos);
+
+ // determine aDestConnPosY
+ calcPointsYValue(pDestWin, nDestEntry, 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 = std::hypot(v.X(), 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( tools::Long(p1.X() + a * v.X()) );
+ q.setY( tools::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..f11ca853c
--- /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.Left(),aRect.Top(),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;
+ if( m_pLine )
+ {
+ aSeq = { m_pLine->GetSourceWin()->GetAccessible(),
+ 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..0857e0448
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/JoinController.cxx
@@ -0,0 +1,406 @@
+/* -*- 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, tools::Long nOffsetX, tools::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 ) );
+ }
+ assert(m_pDialogContext && "always exists at this point");
+ 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, tools::Long nOffsetX, tools::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..0dc88e2cc
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/JoinExchange.cxx
@@ -0,0 +1,114 @@
+/* -*- 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>
+
+namespace dbaui
+{
+ using namespace ::com::sun::star::uno;
+ using namespace ::com::sun::star::lang;
+ using namespace ::com::sun::star::datatransfer;
+
+ void OJoinExchObj::setDescriptors(const OJoinExchangeData& jxdSource,bool _bFirstEntry)
+ {
+ m_bFirstEntry = _bFirstEntry;
+ m_jxdSourceDescription = jxdSource;
+ }
+
+ OJoinExchObj::OJoinExchObj()
+ : m_bFirstEntry(false)
+ {
+ }
+
+ OJoinExchObj::~OJoinExchObj()
+ {
+ }
+
+ 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::getFromUnoTunnel<OJoinExchObj>(_rxObject);
+ if (pImplementation)
+ aReturn = pImplementation->m_jxdSourceDescription;
+ return aReturn;
+ }
+
+ const Sequence< sal_Int8 > & OJoinExchObj::getUnoTunnelId()
+ {
+ static const comphelper::UnoIdInit implId;
+ return implId.getSeq();
+ }
+
+ sal_Int64 SAL_CALL OJoinExchObj::getSomething( const Sequence< sal_Int8 >& _rIdentifier )
+ {
+ return comphelper::getSomethingImpl(_rIdentifier, this);
+ }
+
+ 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 = TransferDataContainer::queryInterface(_rType);
+ if (!aReturn.hasValue())
+ aReturn = OJoinExchObj_Base::queryInterface(_rType);
+ return aReturn;
+ }
+
+ void SAL_CALL OJoinExchObj::acquire( ) noexcept
+ {
+ TransferDataContainer::acquire( );
+ }
+
+ void SAL_CALL OJoinExchObj::release( ) noexcept
+ {
+ TransferDataContainer::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..055628133
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/JoinTableView.cxx
@@ -0,0 +1,1568 @@
+/* -*- 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/weldutils.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();
+ tools::Long nHScrollHeight = GetHScrollBar().GetSizePixel().Height();
+ tools::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
+ tools::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_aDragScrollIdle("dbaccess OJoinTableView m_aDragScrollIdle")
+ ,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 )
+{
+ 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,
+ Any(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(),
+ Any(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,
+ Any(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,tools::Long nDelta, bool bHoriz)
+ {
+ // adjust ScrollBar-Positions
+ ScrollBar& rBar = bHoriz ? _pView->GetHScrollBar() : _pView->GetVScrollBar() ;
+
+ tools::Long nOldThumbPos = rBar.GetThumbPos();
+ tools::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,tools::Long& _nScrollX,tools::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)
+{
+ tools::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)
+{
+ tools::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().Contains(aBottom.X()))
+ GetHScrollBar().SetRange( Range(0, aBottom.X()) );
+ if(!GetVScrollBar().GetRange().Contains(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( tools::Long nDelta, bool bHoriz, bool bPaintScrollBars )
+{
+ bool bRet = true;
+
+ // adjust ScrollBar-Positions
+ if( bPaintScrollBars )
+ {
+ if( bHoriz )
+ {
+ tools::Long nOldThumbPos = GetHScrollBar().GetThumbPos();
+ tools::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
+ {
+ tools::Long nOldThumbPos = GetVScrollBar().GetThumbPos();
+ tools::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()->get_widget().unselect_all();
+
+ pWin = pConn->GetDestWin();
+ if (pWin && pWin->GetListBox())
+ pWin->GetListBox()->get_widget().unselect_all();
+
+ 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->get_widget().unselect_all();
+ pDestBox->get_widget().unselect_all();
+
+ bool bScrolled = false;
+
+ const std::vector<std::unique_ptr<OConnectionLine>>& rLines = pConn->GetConnLineList();
+ auto aIter = rLines.rbegin();
+ for(;aIter != rLines.rend();++aIter)
+ {
+ if ((*aIter)->IsValid())
+ {
+ int nSourceEntry = pSourceBox->GetEntryFromText((*aIter)->GetData()->GetSourceFieldName());
+ if (nSourceEntry != -1)
+ {
+ pSourceBox->get_widget().select(nSourceEntry);
+ pSourceBox->get_widget().scroll_to_row(nSourceEntry);
+ bScrolled = true;
+ }
+
+ int nDestEntry = pDestBox->GetEntryFromText((*aIter)->GetData()->GetDestFieldName());
+ if (nDestEntry != -1)
+ {
+ pDestBox->get_widget().select(nDestEntry);
+ pDestBox->get_widget().scroll_to_row(nDestEntry);
+ bScrolled = true;
+ }
+ }
+ }
+
+ if (bScrolled)
+ {
+ // 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& rPos, VclPtr<OTableConnection>& rSelConnection)
+{
+ ::tools::Rectangle aRect(rPos, Size(1, 1));
+ weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect);
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, "dbaccess/ui/joinviewmenu.ui"));
+ std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu("menu"));
+ OString sIdent = xContextMenu->popup_at_rect(pPopupParent, aRect);
+ 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 _bSuppressCrossOrNaturalJoin) 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 ( _bSuppressCrossOrNaturalJoin )
+ {
+ if ( suppressCrossNaturalJoin(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(*GetOutDev(), 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(),
+ Any(_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::suppressCrossNaturalJoin(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..c6bd2e10d
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QTableConnection.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 .
+ */
+#pragma once
+
+#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; }
+
+ };
+}
+
+/* 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..ce66828fa
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QTableConnectionData.cxx
@@ -0,0 +1,112 @@
+/* -*- 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()
+ : 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..7ccbb03ac
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QTableConnectionData.hxx
@@ -0,0 +1,67 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#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; }
+ };
+
+}
+
+/* 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..6b129c32d
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QTableWindow.cxx
@@ -0,0 +1,175 @@
+/* -*- 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 <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(weld::TreeIter& rEntry)
+{
+ if (getTableView()->getDesignView()->getController().isReadOnly())
+ return;
+
+ weld::TreeView& rTreeView = m_xListBox->get_widget();
+ OTableFieldInfo* pInf = weld::fromId<OTableFieldInfo*>(rTreeView.get_id(rEntry));
+ OSL_ENSURE(pInf != nullptr, "OQueryTableWindow::OnEntryDoubleClicked : field doesn't have FieldInfo !");
+
+ // build up DragInfo
+ OTableFieldDescRef aInfo = new OTableFieldDesc(GetTableName(), rTreeView.get_text(rEntry));
+ aInfo->SetTabWindow(this);
+ aInfo->SetAlias(GetAliasName());
+ aInfo->SetFieldIndex(rTreeView.get_iter_index_in_parent(rEntry));
+ 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())
+ {
+ weld::TreeView& rTreeView = m_xListBox->get_widget();
+ std::unique_ptr<weld::TreeIter> xEntry(rTreeView.make_iterator());
+ bool bEntry = rTreeView.get_iter_first(*xEntry);
+ try
+ {
+ Reference<XDatabaseMetaData> xMeta = xConnection->getMetaData();
+ ::comphelper::UStringMixEqual bCase(xMeta.is() && xMeta->supportsMixedCaseQuotedIdentifiers());
+
+ while (bEntry)
+ {
+ if (bCase(strFieldName, rTreeView.get_text(*xEntry)))
+ {
+ OTableFieldInfo* pInf = weld::fromId<OTableFieldInfo*>(rTreeView.get_id(*xEntry));
+ assert(pInf && "OQueryTableWindow::ExistsField : field doesn't have FieldInfo !");
+
+ rInfo->SetTabWindow(this);
+ rInfo->SetField(strFieldName);
+ rInfo->SetTable(GetTableName());
+ rInfo->SetAlias(GetAliasName());
+ rInfo->SetFieldIndex(rTreeView.get_iter_index_in_parent(*xEntry));
+ rInfo->SetDataType(pInf->GetDataType());
+ bExists = true;
+ break;
+ }
+ bEntry = rTreeView.iter_next(*xEntry);
+ }
+ }
+ 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..72c698c83
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QTableWindow.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/.
+ *
+ * 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 .
+ */
+#pragma once
+
+#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(weld::TreeIter& rEntry) 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;
+ };
+}
+
+/* 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..327dc27f1
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QTableWindowData.hxx
@@ -0,0 +1,38 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#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); }
+ };
+}
+
+
+/* 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..dd641be1d
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QueryAddTabConnUndoAction.hxx
@@ -0,0 +1,49 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#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;
+ };
+}
+
+/* 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..13262f570
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QueryDesignFieldUndoAct.hxx
@@ -0,0 +1,138 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#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, TranslateId 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
+ {
+ tools::Long m_nNextWidth;
+
+ public:
+ explicit OTabFieldSizedUndoAct(OSelectionBrowseBox* pSelBrwBox) : OQueryDesignFieldUndoAct(pSelBrwBox, STR_QUERY_UNDO_SIZE_COLUMN), m_nNextWidth(0) { }
+
+ void SetOriginalWidth(tools::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, TranslateId 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) { }
+ };
+}
+
+/* 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..8a8742393
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QueryDesignUndoAction.hxx
@@ -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 .
+ */
+#pragma once
+
+#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, TranslateId pCommentID) : OCommentUndoAction(pCommentID), m_pOwner(pOwner) { }
+ };
+}
+
+/* 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..f5e4288e9
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QueryDesignView.cxx
@@ -0,0 +1,3441 @@
+/* -*- 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 <o3tl/string_view.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>
+#include <string_view>
+
+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
+{
+ const char C_AND[] = " AND ";
+ 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;
+ ::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 )
+ {
+ sal_uInt16 nCntAccount;
+ 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, u" ");
+ }
+ _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,
+ std::u16string_view 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 += OUString::Concat("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() + "(" + aTmpStr + ")");
+ aTmpStr = aTmpStr2;
+ }
+
+ if (!rFieldAlias.isEmpty() &&
+ (rFieldName.toChar() != '*' ||
+ field->isNumericOrAggregateFunction() ||
+ field->isOtherFunction()))
+ {
+ aTmpStr.append(" AS ");
+ aTmpStr.append(::dbtools::quoteName(aQuote, rFieldAlias));
+ }
+ aFieldListStr.append(aTmpStr);
+ aTmpStr.setLength(0);
+ 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 bCritsOnAsteriskWarning = 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 (!bCritsOnAsteriskWarning)
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(_pView->GetFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ DBA_RES(STR_QRY_CRITERIA_ON_ASTERISK)));
+ xBox->run();
+ }
+ bCritsOnAsteriskWarning = 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 bCritsOnAsteriskWarning = 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 (!bCritsOnAsteriskWarning)
+ {
+ std::unique_ptr<weld::MessageDialog> xBox(Application::CreateMessageDialog(_pView->GetFrameWeld(),
+ VclMessageType::Warning, VclButtonsType::Ok,
+ DBA_RES(STR_QRY_ORDERBY_ON_ASTERISK)));
+ xBox->run();
+ }
+ bCritsOnAsteriskWarning = 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::Concat(" ") + o3tl::getToken( u";ASC;DESC", 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, u"" );
+ 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, u" " );
+ 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 )
+ return true;
+
+ 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(OUString(comphelper::string::stripEnd(o3tl::getToken(aColumns,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 )
+ {
+ TranslateId 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);
+
+ setNoneVisibleRow(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(std::u16string_view 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(std::u16string_view sAliasName, weld::ComboBox& rFieldList)
+{
+ rFieldList.clear();
+
+ bool bAllTables = sAliasName.empty();
+
+ 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.insert(0, 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);
+ }
+ 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);
+ }
+ // ----------------- 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 " + 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();
+ rtl::Reference<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::setNoneVisibleRow(sal_Int32 _nRows)
+{
+ m_pSelectionBox->SetNoneVisibleRow(_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..398732815
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QueryMoveTabWinUndoAct.cxx
@@ -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 .
+ */
+
+#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..e6b58956e
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QueryMoveTabWinUndoAct.hxx
@@ -0,0 +1,54 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#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)
+ {
+ }
+}
+
+/* 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..de244ccb5
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QuerySizeTabWinUndoAct.hxx
@@ -0,0 +1,70 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#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;
+ }
+}
+
+/* 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..e3b6cd0e9
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QueryTabConnUndoAction.cxx
@@ -0,0 +1,117 @@
+/* -*- 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, TranslateId 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..21077074e
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QueryTabConnUndoAction.hxx
@@ -0,0 +1,50 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#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, TranslateId 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; }
+ };
+}
+
+
+/* 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..95b740de9
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QueryTabWinShowUndoAct.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 .
+ */
+
+#pragma once
+
+#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;
+ };
+}
+
+/* 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..2afe74db4
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QueryTabWinUndoAct.cxx
@@ -0,0 +1,112 @@
+/* -*- 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 "QueryDesignFieldUndoAct.hxx"
+#include <QueryTableView.hxx>
+
+using namespace dbaui;
+OQueryDesignFieldUndoAct::OQueryDesignFieldUndoAct(OSelectionBrowseBox* pSelBrwBox, TranslateId pCommentID)
+ : OCommentUndoAction(pCommentID)
+ , pOwner(pSelBrwBox)
+ , m_nColumnPosition(BROWSER_INVALIDID)
+{
+}
+
+OQueryDesignFieldUndoAct::~OQueryDesignFieldUndoAct()
+{
+ pOwner = nullptr;
+}
+
+OQueryTabWinUndoAct::OQueryTabWinUndoAct(OQueryTableView* pOwner, TranslateId 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);
+ tools::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..9b4330542
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QueryTabWinUndoAct.hxx
@@ -0,0 +1,61 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#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, TranslateId 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); }
+ };
+
+}
+
+/* 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..399697b09
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QueryTableView.cxx
@@ -0,0 +1,889 @@
+/* -*- 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,std::u16string_view _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(),
+ Any(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;
+ weld::TreeView& rSourceTreeView = jxdSource.pListBox->get_widget();
+ aSourceFieldName = rSourceTreeView.get_text(jxdSource.nEntry);
+ weld::TreeView& rDestTreeView = jxdDest.pListBox->get_widget();
+ aDestFieldName = rDestTreeView.get_text(jxdDest.nEntry);
+
+ 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.nEntry;
+ // Dest
+ nDestFieldIndex = jxdDest.nEntry;
+
+ // ... 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,
+ Any(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::suppressCrossNaturalJoin(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..daeb6ee14
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QueryTextView.cxx
@@ -0,0 +1,177 @@
+/* -*- 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 <svx/svxids.hrc>
+#include <QueryTextView.hxx>
+#include <querycontainerwindow.hxx>
+#include <helpids.h>
+#include <querycontroller.hxx>
+#include <sqledit.hxx>
+#include <undosqledit.hxx>
+
+using namespace dbaui;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+
+// end of temp classes
+OQueryTextView::OQueryTextView(OQueryContainerWindow* pParent, OQueryController& rController)
+ : InterimItemWindow(pParent, "dbaccess/ui/queryview.ui", "QueryView")
+ , m_rController(rController)
+ , m_xSQL(new SQLEditView(m_xBuilder->weld_scrolled_window("scrolledwindow", true)))
+ , m_xSQLEd(new weld::CustomWeld(*m_xBuilder, "sql", *m_xSQL))
+ , m_timerUndoActionCreation("dbaccess OQueryTextView m_timerUndoActionCreation")
+ , m_timerInvalidate("dbaccess OQueryTextView m_timerInvalidate")
+ , m_bStopTimer(false)
+{
+ m_xSQL->DisableInternalUndo();
+ m_xSQL->SetHelpId(HID_CTL_QRYSQLEDIT);
+ m_xSQL->SetModifyHdl(LINK(this, OQueryTextView, ModifyHdl));
+ m_xSQL->SetAcceptsTab(true);
+
+ m_timerUndoActionCreation.SetTimeout(1000);
+ m_timerUndoActionCreation.SetInvokeHandler(LINK(this, OQueryTextView, OnUndoActionTimer));
+
+ m_timerInvalidate.SetTimeout(200);
+ m_timerInvalidate.SetInvokeHandler(LINK(this, OQueryTextView, OnInvalidateTimer));
+ m_timerInvalidate.Start();
+}
+
+IMPL_LINK_NOARG(OQueryTextView, ModifyHdl, LinkParamNone*, void)
+{
+ if (m_timerUndoActionCreation.IsActive())
+ m_timerUndoActionCreation.Stop();
+ m_timerUndoActionCreation.Start();
+
+ if (!m_rController.isModified())
+ m_rController.setModified(true);
+
+ m_rController.InvalidateFeature(SID_SBA_QRY_EXECUTE);
+ m_rController.InvalidateFeature(SID_CUT);
+ m_rController.InvalidateFeature(SID_COPY);
+}
+
+IMPL_LINK_NOARG(OQueryTextView, OnUndoActionTimer, Timer*, void)
+{
+ OUString aText = m_xSQL->GetText();
+ if (aText == m_strOrigText)
+ return;
+
+ SfxUndoManager& rUndoMgr = m_rController.GetUndoManager();
+ std::unique_ptr<OSqlEditUndoAct> xUndoAct(new OSqlEditUndoAct(*this));
+
+ xUndoAct->SetOriginalText(m_strOrigText);
+ rUndoMgr.AddUndoAction(std::move(xUndoAct));
+
+ m_rController.InvalidateFeature(SID_UNDO);
+ m_rController.InvalidateFeature(SID_REDO);
+
+ m_strOrigText = aText;
+}
+
+IMPL_LINK_NOARG(OQueryTextView, OnInvalidateTimer, Timer*, void)
+{
+ m_rController.InvalidateFeature(SID_CUT);
+ m_rController.InvalidateFeature(SID_COPY);
+ if (!m_bStopTimer)
+ m_timerInvalidate.Start();
+}
+
+void OQueryTextView::startTimer()
+{
+ m_bStopTimer = false;
+ if (!m_timerInvalidate.IsActive())
+ m_timerInvalidate.Start();
+}
+
+void OQueryTextView::stopTimer()
+{
+ m_bStopTimer = true;
+ if (m_timerInvalidate.IsActive())
+ m_timerInvalidate.Stop();
+}
+
+OQueryTextView::~OQueryTextView() { disposeOnce(); }
+
+void OQueryTextView::dispose()
+{
+ if (m_timerUndoActionCreation.IsActive())
+ m_timerUndoActionCreation.Stop();
+
+ m_xSQLEd.reset();
+ m_xSQL.reset();
+ InterimItemWindow::dispose();
+}
+
+void OQueryTextView::GetFocus()
+{
+ if (m_xSQL)
+ {
+ m_xSQL->GrabFocus();
+ m_strOrigText = m_xSQL->GetText();
+ }
+ InterimItemWindow::GetFocus();
+}
+
+OUString OQueryTextView::getStatement() const { return m_xSQL->GetText(); }
+
+void OQueryTextView::clear()
+{
+ std::unique_ptr<OSqlEditUndoAct> xUndoAct(new OSqlEditUndoAct(*this));
+
+ xUndoAct->SetOriginalText(m_xSQL->GetText());
+ m_rController.addUndoActionAndInvalidate(std::move(xUndoAct));
+
+ SetSQLText(OUString());
+}
+
+void OQueryTextView::setStatement(const OUString& rsStatement) { SetSQLText(rsStatement); }
+
+OUString OQueryTextView::GetSQLText() const { return m_xSQL->GetText(); }
+
+void OQueryTextView::SetSQLText(const OUString& rNewText)
+{
+ if (m_timerUndoActionCreation.IsActive())
+ {
+ // create the trailing undo-actions
+ m_timerUndoActionCreation.Stop();
+ OnUndoActionTimer(nullptr);
+ }
+
+ m_xSQL->SetTextAndUpdate(rNewText);
+
+ m_strOrigText = rNewText;
+}
+
+void OQueryTextView::copy() { m_xSQL->Copy(); }
+
+bool OQueryTextView::isCutAllowed() const { return m_xSQL->HasSelection(); }
+
+void OQueryTextView::cut()
+{
+ m_xSQL->Cut();
+ m_rController.setModified(true);
+}
+
+void OQueryTextView::paste()
+{
+ m_xSQL->Paste();
+ m_rController.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..a51f2941a
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/QueryViewSwitch.cxx
@@ -0,0 +1,292 @@
+/* -*- 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>
+
+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, _rController);
+ 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->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->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->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::setNoneVisibleRow(sal_Int32 _nRows)
+{
+ if(m_pDesignView)
+ m_pDesignView->setNoneVisibleRow(_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..414d6bbdc
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/SelectionBrowseBox.cxx
@@ -0,0 +1,2720 @@
+/* -*- 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 <sal/config.h>
+
+#include <string_view>
+
+#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/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/commandevent.hxx>
+#include <vcl/svapp.hxx>
+#include <tools/diagnose_ex.h>
+#include <o3tl/string_view.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 && o3tl::getToken(_sFieldName,1,'.')[0] == '*' )
+ || (nTokenCount == 3 && o3tl::getToken(_sFieldName,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_timerInvalidate("dbaccess OSelectionBrowseBox m_timerInvalidate")
+ ,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<EditControl>::Create(&GetDataWindow());
+ 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->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(OUString(o3tl::getToken(aTxt, 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(tools::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&)
+ {
+ TOOLS_WARN_EXCEPTION( "dbaccess", "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(sal_Int32 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;
+
+ sal_Int32 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*/, sal_Int32 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 !");
+ sal_Int32 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 (o3tl::trim(aField) == u"*")
+ {
+ 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().set_active(pEntry->IsVisible());
+ m_pVisibleCell->GetBox().save_state();
+
+ 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().set_active(pEntry->IsVisible());
+ m_pVisibleCell->GetBox().save_state();
+ m_pVisibleCell->GetBox().set_sensitive(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()->SaveValue();
+}
+
+void OSelectionBrowseBox::notifyTableFieldChanged(const OUString& _sOldAlias, std::u16string_view _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, std::u16string_view _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, std::u16string_view _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.empty() && (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( "*", std::u16string_view(), 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()->IsValueChangedFromSaved())
+ {
+ // for the Undo-action
+ OUString strOldCellContents,sNewValue;
+ sal_Int32 nRow = GetRealRow(GetCurRow());
+ bool bAppendRow = false;
+ switch (nRow)
+ {
+ case BROW_VIS_ROW:
+ {
+ bool bOldValue = m_pVisibleCell->GetBox().get_saved_state() != TRISTATE_FALSE;
+ strOldCellContents
+ = bOldValue ? std::u16string_view(u"1") : std::u16string_view(u"0");
+ sNewValue
+ = !bOldValue ? std::u16string_view(u"1") : std::u16string_view(u"0");
+ }
+ if((m_bOrderByUnRelated || pEntry->GetOrderDir() == ORDER_NONE) &&
+ (m_bGroupByUnRelated || !pEntry->IsGroupBy()))
+ {
+ pEntry->SetVisible(m_pVisibleCell->GetBox().get_active());
+ }
+ else
+ {
+ pEntry->SetVisible();
+ m_pVisibleCell->GetBox().set_active(true);
+ }
+ 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().set_active(true);
+ 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->get_widget().get_text());
+ 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);
+ std::u16string_view sGroupFunctionName = m_aFunctionStrings.subView(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().set_active(true);
+ appendUndoAction("0",u"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->get_widget().get_text(), ' ');
+
+ 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()->SaveValue();
+
+ 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",u"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(sal_Int32 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;
+
+ sal_Int32 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();
+ sal_Int32 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, tools::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<tools::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();
+ sal_Int32 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
+ weld::TreeView& rTreeView = jxdSource.pListBox->get_widget();
+ OUString aFieldName = rTreeView.get_text(jxdSource.nEntry);
+ sal_uInt32 nFieldIndex = jxdSource.nEntry;
+ OTableFieldInfo* pInf = weld::fromId<OTableFieldInfo*>(rTreeView.get_id(jxdSource.nEntry));
+
+ // 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()
+{
+ sal_Int32 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().set_active(true);
+ pEntry->SetVisible();
+ }
+ else
+ pEntry->SetVisible(m_pVisibleCell->GetBox().get_active());
+ }
+ 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() ));
+ sal_Int32 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())
+ {
+ ::tools::Rectangle aRect(aMenuPos, Size(1, 1));
+ weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect);
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, "dbaccess/ui/querycolmenu.ui"));
+ std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu("menu"));
+ OString sIdent = xContextMenu->popup_at_rect(pPopupParent, aRect);
+ if (sIdent == "delete")
+ RemoveField(nColId);
+ else if (sIdent == "width")
+ adjustBrowseBoxColumnWidth( this, nColId );
+ }
+ }
+ else if(nRow >= 0 && nColId <= HANDLE_ID)
+ {
+ if (!static_cast<OQueryController&>(getDesignView()->getController()).isReadOnly())
+ {
+ ::tools::Rectangle aRect(aMenuPos, Size(1, 1));
+ weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect);
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, "dbaccess/ui/queryfuncmenu.ui"));
+ std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu("menu"));
+ xContextMenu->set_active("functions", m_bVisibleRow[BROW_FUNCTION_ROW]);
+ xContextMenu->set_active("tablename", m_bVisibleRow[BROW_TABLE_ROW]);
+ xContextMenu->set_active("alias", m_bVisibleRow[BROW_COLUMNALIAS_ROW]);
+ xContextMenu->set_active("distinct", static_cast<OQueryController&>(getDesignView()->getController()).isDistinct());
+
+ OString sIdent = xContextMenu->popup_at_rect(pPopupParent, aRect);
+ 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];
+
+ tools::Long nId = GetBrowseRow(_nWhich);
+ if (_bVis)
+ {
+ RowInserted(nId);
+ ++m_nVisibleCount;
+ }
+ else
+ {
+ RowRemoved(nId);
+ --m_nVisibleCount;
+ }
+
+ if (bWasEditing)
+ ActivateCell();
+}
+
+sal_Int32 OSelectionBrowseBox::GetBrowseRow(sal_Int32 nRowId) const
+{
+ sal_Int32 nCount(0);
+ for(sal_Int32 i = 0 ; i < nRowId ; ++i)
+ {
+ if ( m_bVisibleRow[i] )
+ ++nCount;
+ }
+ return nCount;
+}
+
+sal_Int32 OSelectionBrowseBox::GetRealRow(sal_Int32 nRowId) const
+{
+ sal_Int32 nErg=0,i;
+ const sal_Int32 nCount = m_bVisibleRow.size();
+ for(i=0;i < nCount; ++i)
+ {
+ if(m_bVisibleRow[i] && nErg++ == nRowId)
+ break;
+ }
+ OSL_ENSURE(nErg <= tools::Long(m_bVisibleRow.size()),"nErg cannot be greater than BROW_ROW_CNT!");
+ return i;
+}
+
+const tools::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::SetNoneVisibleRow(sal_Int32 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(sal_Int32 nRow, sal_uInt16 nColId) const
+{
+
+ sal_uInt16 nPos = GetColumnPos(nColId);
+ if ( nPos == 0 || nPos == BROWSER_INVALIDID || nPos > getFields().size() )
+ return OUString();
+
+ 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 OUString(pEntry->IsVisible() ? std::u16string_view(u"1") : std::u16string_view(u"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:
+ {
+ std::u16string_view sGroupFunctionName = m_aFunctionStrings.subView(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() && !o3tl::equalsIgnoreAsciiCase(sGroupFunctionName, strNewText) )
+ pEntry->SetGroupBy(false);
+
+ if ( o3tl::equalsIgnoreAsciiCase(sGroupFunctionName, 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);
+ }
+
+ tools::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(sal_Int32 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 !");
+
+ sal_Int32 nRow = GetRealRow(nRowId);
+ OUString strText(GetCellText(nRow, nColId));
+ return GetDataWindow().LogicToPixel(Size(GetDataWindow().GetTextWidth(strText),0)).Width();
+}
+
+bool OSelectionBrowseBox::isCutAllowed() const
+{
+ bool bCutAllowed = false;
+ sal_Int32 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:
+ {
+ weld::Entry& rEntry = m_pTextCell->get_widget();
+ int nStartPos, nEndPos;
+ bCutAllowed = rEntry.get_selection_bounds(nStartPos, nEndPos);
+ break;
+ }
+ }
+ return bCutAllowed;
+}
+
+void OSelectionBrowseBox::cut()
+{
+ sal_Int32 nRow = GetRealRow(GetCurRow());
+ switch (nRow)
+ {
+ case BROW_FIELD_ROW:
+ {
+ weld::ComboBox& rComboBox = m_pFieldCell->get_widget();
+ rComboBox.cut_entry_clipboard();
+ break;
+ }
+ default:
+ {
+ weld::Entry& rEntry = m_pTextCell->get_widget();
+ rEntry.cut_clipboard();
+ }
+ }
+ SaveModified();
+ RowModified(GetBrowseRow(nRow), GetCurColumnId());
+
+ invalidateUndoRedo();
+}
+
+void OSelectionBrowseBox::paste()
+{
+ sal_Int32 nRow = GetRealRow(GetCurRow());
+ switch (nRow)
+ {
+ case BROW_FIELD_ROW:
+ {
+ weld::ComboBox& rComboBox = m_pFieldCell->get_widget();
+ rComboBox.paste_entry_clipboard();
+ break;
+ }
+ default:
+ {
+ weld::Entry& rEntry = m_pTextCell->get_widget();
+ rEntry.paste_clipboard();
+ break;
+ }
+ }
+ RowModified(GetBrowseRow(nRow), GetCurColumnId());
+ invalidateUndoRedo();
+}
+
+bool OSelectionBrowseBox::isPasteAllowed() const
+{
+ bool bPasteAllowed = true;
+ sal_Int32 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()
+{
+ sal_Int32 nRow = GetRealRow(GetCurRow());
+ switch (nRow)
+ {
+ case BROW_FIELD_ROW:
+ {
+ weld::ComboBox& rComboBox = m_pFieldCell->get_widget();
+ rComboBox.copy_entry_clipboard();
+ break;
+ }
+ default:
+ {
+ weld::Entry& rEntry = m_pTextCell->get_widget();
+ rEntry.copy_clipboard();
+ break;
+ }
+ }
+}
+
+void OSelectionBrowseBox::appendUndoAction(const OUString& _rOldValue, std::u16string_view _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,std::u16string_view _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)
+{
+ weld::Entry& rEntry = m_pTextCell->get_widget();
+ rEntry.set_text(_sText);
+ rEntry.save_value();
+ 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( AccessibleBrowseBoxObjType _eObjType,sal_Int32 _nPosition) const
+{
+ OUString sRetText;
+ switch( _eObjType )
+ {
+ case AccessibleBrowseBoxObjType::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 ( _nColumnPos != 0 && _nColumnPos != BROWSER_INVALIDID && _nColumnPos <= getFields().size() )
+ 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(std::u16string_view 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..761870fb8
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/SelectionBrowseBox.hxx
@@ -0,0 +1,324 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#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;
+
+ sal_Int32 m_nSeekRow;
+ BrowserMode m_nMode; // remember the BrowseModes
+ VclPtr< ::svt::EditControl> 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(std::u16string_view 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, tools::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 SetNoneVisibleRow(sal_Int32 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(sal_Int32 _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( 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( sal_Int32 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(sal_Int32 nRow, sal_uInt16 nCol) override;
+ virtual void InitController(::svt::CellControllerRef& rController, sal_Int32 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(sal_Int32 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 );
+ sal_Int32 GetRealRow(sal_Int32 nRow) const;
+ sal_Int32 GetBrowseRow(sal_Int32 nRowId) const;
+ bool GetFunctionName(sal_uInt32 _nFunctionTokenId, OUString& rFkt);
+ void appendUndoAction(const OUString& _rOldValue,std::u16string_view _rNewValue,sal_Int32 _nRow, bool& _bListAction);
+ void appendUndoAction(const OUString& _rOldValue,std::u16string_view _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,
+ std::u16string_view _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,std::u16string_view _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,std::u16string_view _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;
+ };
+}
+
+/* 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..1e8604181
--- /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>
+
+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() );
+ auto pCriteria = aCriteria.getArray();
+ for (auto const& criteria : m_aCriteria)
+ {
+ pCriteria[c].Name = "Criterion_" + OUString::number( c );
+ pCriteria[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..808862c11
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/TableFieldInfo.cxx
@@ -0,0 +1,30 @@
+/* -*- 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)
+{
+}
+
+/* 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..e7d2c9b72
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/TableFieldInfo.hxx
@@ -0,0 +1,43 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#include <QEnumTypes.hxx>
+#include <sal/types.h>
+
+namespace dbaui
+{
+ class OTableFieldInfo
+ {
+ private:
+ ETableFieldType m_eFieldType;
+ sal_Int32 m_eDataType;
+
+ public:
+ 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; }
+ };
+}
+
+
+/* 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..b8afc951d
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/TableWindow.cxx
@@ -0,0 +1,716 @@
+/* -*- 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/settings.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/ptrstyle.hxx>
+#include <vcl/wall.hxx>
+#include <vcl/weldutils.hxx>
+#include <tools/diagnose_ex.h>
+
+#include <com/sun/star/container/XContainer.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/sdb/application/DatabaseObject.hpp>
+#include <bitmaps.hlst>
+#include <TableWindowAccess.hxx>
+#include <connectivity/dbtools.hxx>
+
+using namespace dbaui;
+using namespace ::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_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->get_widget().n_children()==0,"Forgot to call EmptyListbox()!");
+ }
+ m_xListBox.disposeAndClear();
+ if ( m_pContainerListener.is() )
+ m_pContainerListener->dispose();
+
+ m_xTitle.disposeAndClear();
+ vcl::Window::dispose();
+}
+
+const OJoinTableView* OTableWindow::getTableView() const
+{
+ OSL_ENSURE(static_cast<OJoinTableView*>(GetParent()),"No OJoinTableView!");
+ return static_cast<OJoinTableView*>(GetParent());
+}
+
+OJoinTableView* OTableWindow::getTableView()
+{
+ OSL_ENSURE(static_cast<OJoinTableView*>(GetParent()),"No OJoinTableView!");
+ return static_cast<OJoinTableView*>(GetParent());
+}
+
+OJoinDesignView* OTableWindow::getDesignView()
+{
+ OSL_ENSURE(static_cast<OJoinDesignView*>(GetParent()->GetParent()->GetParent()),"No OJoinDesignView!");
+ return static_cast<OJoinDesignView*>(GetParent()->GetParent()->GetParent());
+}
+
+void OTableWindow::SetPosPixel( const Point& rNewPos )
+{
+ Point aNewPosData = rNewPos + getTableView()->GetScrollOffset();
+ GetData()->SetPosition( aNewPosData );
+ Window::SetPosPixel( rNewPos );
+}
+
+void OTableWindow::SetSizePixel( const Size& rNewSize )
+{
+ Size aOutSize(rNewSize);
+ if( aOutSize.Width() < TABWIN_WIDTH_MIN )
+ aOutSize.setWidth( TABWIN_WIDTH_MIN );
+ if( aOutSize.Height() < TABWIN_HEIGHT_MIN )
+ aOutSize.setHeight( TABWIN_HEIGHT_MIN );
+
+ GetData()->SetSize( aOutSize );
+ Window::SetSizePixel( aOutSize );
+}
+
+void OTableWindow::SetPosSizePixel( const Point& rNewPos, const Size& rNewSize )
+{
+ SetPosPixel( rNewPos );
+ SetSizePixel( rNewSize );
+}
+
+void OTableWindow::FillListBox()
+{
+ clearListBox();
+ weld::TreeView& rTreeView = m_xListBox->get_widget();
+ assert(!rTreeView.n_children());
+
+ if ( !m_pContainerListener.is() )
+ {
+ Reference< XContainer> xContainer(m_pData->getColumns(),UNO_QUERY);
+ if ( xContainer.is() )
+ m_pContainerListener = new ::comphelper::OContainerListenerAdapter(this,xContainer);
+ }
+
+ // mark all primary keys with special image
+ OUString aPrimKeyImage(BMP_PRIMARY_KEY);
+
+ if (GetData()->IsShowAll())
+ {
+ rTreeView.append(weld::toId(createUserData(nullptr,false)), OUString("*"));
+ }
+
+ Reference<XNameAccess> xPKeyColumns;
+ try
+ {
+ xPKeyColumns = dbtools::getPrimaryKeyColumns_throw(m_pData->getTable());
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "dbaccess", "");
+ }
+ try
+ {
+ Reference< XNameAccess > xColumns = m_pData->getColumns();
+ if( xColumns.is() )
+ {
+ Sequence< OUString> aColumns = xColumns->getElementNames();
+ const OUString* pIter = aColumns.getConstArray();
+ const OUString* pEnd = pIter + aColumns.getLength();
+
+ for (; pIter != pEnd; ++pIter)
+ {
+ bool bPrimaryKeyColumn = xPKeyColumns.is() && xPKeyColumns->hasByName( *pIter );
+
+ OUString sId;
+ Reference<XPropertySet> xColumn(xColumns->getByName(*pIter),UNO_QUERY);
+ if (xColumn.is())
+ sId = weld::toId(createUserData(xColumn, bPrimaryKeyColumn));
+
+ rTreeView.append(sId, *pIter);
+
+ // is this column in the primary key
+ if ( bPrimaryKeyColumn )
+ rTreeView.set_image(rTreeView.n_children() - 1, aPrimKeyImage);
+ }
+
+ }
+ }
+ catch(Exception&)
+ {
+ TOOLS_WARN_EXCEPTION( "dbaccess", "");
+ }
+}
+
+void* OTableWindow::createUserData(const Reference< XPropertySet>& /*_xColumn*/,bool /*_bPrimaryKey*/)
+{
+ return nullptr;
+}
+
+void OTableWindow::deleteUserData(void*& _pUserData)
+{
+ OSL_ENSURE(!_pUserData,"INVALID call. Need to delete the userclass!");
+ _pUserData = nullptr;
+}
+
+void OTableWindow::clearListBox()
+{
+ if ( !m_xListBox )
+ return;
+
+ weld::TreeView& rTreeView = m_xListBox->get_widget();
+ rTreeView.all_foreach([this, &rTreeView](weld::TreeIter& rEntry){
+ void* pUserData = weld::fromId<void*>(rTreeView.get_id(rEntry));
+ deleteUserData(pUserData);
+ return false;
+ });
+
+ rTreeView.clear();
+}
+
+void OTableWindow::impl_updateImage()
+{
+ weld::Image& rImage = m_xTitle->GetImage();
+ ImageProvider aImageProvider( getDesignView()->getController().getConnection() );
+ rImage.set_from_icon_name(aImageProvider.getImageId(GetComposedName(), m_pData->isQuery() ? DatabaseObject::QUERY : DatabaseObject::TABLE));
+ rImage.show();
+}
+
+bool OTableWindow::Init()
+{
+ // create list box if necessary
+ if ( !m_xListBox )
+ {
+ m_xListBox = VclPtr<OTableWindowListBox>::Create(this);
+ assert(m_xListBox && "OTableWindow::Init() : CreateListBox returned NULL !");
+ m_xListBox->get_widget().set_selection_mode(SelectionMode::Multiple);
+ }
+
+ // Set the title
+ weld::Label& rLabel = m_xTitle->GetLabel();
+ rLabel.set_label(m_pData->GetWinName());
+ rLabel.set_tooltip_text(GetComposedName());
+ m_xTitle->Show();
+
+ m_xListBox->Show();
+
+ // add the fields to the ListBox
+ FillListBox();
+ m_xListBox->get_widget().unselect_all();
+
+ impl_updateImage();
+
+ return true;
+}
+
+void OTableWindow::DataChanged(const DataChangedEvent& rDCEvt)
+{
+ if (rDCEvt.GetType() == DataChangedEventType::SETTINGS)
+ {
+ // In the worst-case the colours have changed so
+ // adapt myself to the new colours
+ const StyleSettings& aSystemStyle = Application::GetSettings().GetStyleSettings();
+ SetBackground(Wallpaper(aSystemStyle.GetFaceColor()));
+ SetTextColor(aSystemStyle.GetButtonTextColor());
+ }
+}
+
+void OTableWindow::Paint(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect)
+{
+ tools::Rectangle aRect(Point(0,0), GetOutputSizePixel());
+ Window::Paint(rRenderContext, rRect);
+ Draw3DBorder(rRenderContext, aRect);
+}
+
+tools::Rectangle OTableWindow::getSizingRect(const Point& _rPos,const Size& _rOutputSize) const
+{
+ tools::Rectangle aSizingRect( GetPosPixel(), GetSizePixel() );
+
+ if( m_nSizingFlags & SizingFlags::Top )
+ {
+ if( _rPos.Y() < 0 )
+ aSizingRect.SetTop( 0 );
+ else
+ aSizingRect.SetTop( _rPos.Y() );
+ }
+
+ if( m_nSizingFlags & SizingFlags::Bottom )
+ {
+ if( _rPos.Y() > _rOutputSize.Height() )
+ aSizingRect.SetBottom( _rOutputSize.Height() );
+ else
+ aSizingRect.SetBottom( _rPos.Y() );
+ }
+
+ if( m_nSizingFlags & SizingFlags::Right )
+ {
+ if( _rPos.X() > _rOutputSize.Width() )
+ aSizingRect.SetRight( _rOutputSize.Width() );
+ else
+ aSizingRect.SetRight( _rPos.X() );
+ }
+
+ if( m_nSizingFlags & SizingFlags::Left )
+ {
+ if( _rPos.X() < 0 )
+ aSizingRect.SetLeft( 0 );
+ else
+ aSizingRect.SetLeft( _rPos.X() );
+ }
+ return aSizingRect;
+}
+
+void OTableWindow::setSizingFlag(const Point& _rPos)
+{
+ Size aOutSize = GetOutputSizePixel();
+ // Set the flags when the mouse cursor is in the sizing area
+ m_nSizingFlags = SizingFlags::NONE;
+
+ if( _rPos.X() < TABWIN_SIZING_AREA )
+ m_nSizingFlags |= SizingFlags::Left;
+
+ if( _rPos.Y() < TABWIN_SIZING_AREA )
+ m_nSizingFlags |= SizingFlags::Top;
+
+ if( _rPos.X() > aOutSize.Width()-TABWIN_SIZING_AREA )
+ m_nSizingFlags |= SizingFlags::Right;
+
+ if( _rPos.Y() > aOutSize.Height()-TABWIN_SIZING_AREA )
+ m_nSizingFlags |= SizingFlags::Bottom;
+}
+
+void OTableWindow::MouseMove( const MouseEvent& rEvt )
+{
+ Window::MouseMove(rEvt);
+
+ OJoinTableView* pCont = getTableView();
+ if (pCont->getDesignView()->getController().isReadOnly())
+ return;
+
+ Point aPos = rEvt.GetPosPixel();
+ setSizingFlag(aPos);
+ PointerStyle aPointer = PointerStyle::Arrow;
+
+ // Set the mouse cursor when it is in the sizing area
+ if ( m_nSizingFlags == SizingFlags::Top ||
+ m_nSizingFlags == SizingFlags::Bottom )
+ aPointer = PointerStyle::SSize;
+ else if ( m_nSizingFlags == SizingFlags::Left ||
+ m_nSizingFlags ==SizingFlags::Right )
+ aPointer = PointerStyle::ESize;
+ else if ( m_nSizingFlags == (SizingFlags::Left | SizingFlags::Top) ||
+ m_nSizingFlags == (SizingFlags::Right | SizingFlags::Bottom) )
+ aPointer = PointerStyle::SESize;
+ else if ( m_nSizingFlags == (SizingFlags::Right | SizingFlags::Top) ||
+ m_nSizingFlags == (SizingFlags::Left | SizingFlags::Bottom) )
+ aPointer = PointerStyle::NESize;
+
+ SetPointer( aPointer );
+}
+
+void OTableWindow::MouseButtonDown( const MouseEvent& rEvt )
+{
+ // When resizing, the parent must be informed that
+ // the window size of its child has changed
+ if( m_nSizingFlags != SizingFlags::NONE )
+ getTableView()->BeginChildSizing( this, GetPointer() );
+
+ Window::MouseButtonDown( rEvt );
+}
+
+void OTableWindow::Resize()
+{
+ // The window must not disappear so we enforce a minimum size
+ Size aOutSize = GetOutputSizePixel();
+ aOutSize = Size(CalcZoom(aOutSize.Width()),CalcZoom(aOutSize.Height()));
+
+ tools::Long nTitleHeight = CalcZoom( GetTextHeight() )+ CalcZoom( 4 );
+
+ // Set the title and ListBox
+ tools::Long n5Pos = CalcZoom(5);
+ tools::Long nPositionX = n5Pos;
+ tools::Long nPositionY = n5Pos;
+
+ Size aPreferredSize = m_xTitle->get_preferred_size();
+ if (nTitleHeight < aPreferredSize.Height())
+ nTitleHeight = aPreferredSize.Height();
+
+ m_xTitle->SetPosSizePixel( Point( nPositionX, nPositionY ), Size( aOutSize.Width() - nPositionX - n5Pos, nTitleHeight ) );
+
+ tools::Long nTitleToList = CalcZoom( 3 );
+
+ m_xListBox->SetPosSizePixel(
+ Point( n5Pos, nPositionY + nTitleHeight + nTitleToList ),
+ Size( aOutSize.Width() - 2 * n5Pos, aOutSize.Height() - ( nPositionY + nTitleHeight + nTitleToList ) - n5Pos )
+ );
+
+ Window::Invalidate();
+}
+
+void OTableWindow::SetBoldTitle( bool bBold )
+{
+ weld::Label& rLabel = m_xTitle->GetLabel();
+ vcl::Font aFont = rLabel.get_font();
+ aFont.SetWeight(bBold ? WEIGHT_BOLD : WEIGHT_NORMAL);
+ rLabel.set_font(aFont);
+}
+
+void OTableWindow::GetFocus()
+{
+ Window::GetFocus();
+ // we have to forward the focus to our listbox to enable keystrokes
+ if(m_xListBox)
+ m_xListBox->GrabFocus();
+}
+
+void OTableWindow::setActive(bool _bActive)
+{
+ SetBoldTitle( _bActive );
+ if (_bActive || !m_xListBox)
+ return;
+
+ weld::TreeView& rTreeView = m_xListBox->get_widget();
+ if (rTreeView.get_selected_index() != -1)
+ rTreeView.unselect_all();
+}
+
+void OTableWindow::Remove()
+{
+ // Delete the window
+ OJoinTableView* pTabWinCont = getTableView();
+ VclPtr<OTableWindow> aHoldSelf(this); // keep ourselves alive during the RemoveTabWin process
+ pTabWinCont->RemoveTabWin( this );
+ pTabWinCont->Invalidate();
+}
+
+bool OTableWindow::ExistsAConn() const
+{
+ return getTableView()->ExistsAConn(this);
+}
+
+void OTableWindow::EnumValidFields(std::vector< OUString>& arrstrFields)
+{
+ arrstrFields.clear();
+ weld::TreeView& rTreeView = m_xListBox->get_widget();
+
+ // This default implementation counts every item in the ListBox ... for any other behaviour it must be over-written
+ rTreeView.all_foreach([&rTreeView, &arrstrFields](weld::TreeIter& rEntry){
+ arrstrFields.push_back(rTreeView.get_text(rEntry));
+ return false;
+ });
+}
+
+void OTableWindow::StateChanged( StateChangedType nType )
+{
+ Window::StateChanged( nType );
+
+ // FIXME RenderContext
+
+ if ( nType != StateChangedType::Zoom )
+ return;
+
+ const StyleSettings& rStyleSettings = GetSettings().GetStyleSettings();
+
+ vcl::Font aFont = rStyleSettings.GetGroupFont();
+ if ( IsControlFont() )
+ aFont.Merge( GetControlFont() );
+ SetZoomedPointFont(*GetOutDev(), aFont);
+
+ m_xTitle->SetZoom(GetZoom());
+ m_xListBox->SetZoom(GetZoom());
+ Resize();
+ Invalidate();
+}
+
+Reference< XAccessible > OTableWindow::CreateAccessible()
+{
+ return new OTableWindowAccess(this);
+}
+
+void OTableWindow::Command(const CommandEvent& rEvt)
+{
+ switch (rEvt.GetCommand())
+ {
+ case CommandEventId::ContextMenu:
+ {
+ OJoinController& rController = getDesignView()->getController();
+ if(!rController.isReadOnly() && rController.isConnected())
+ {
+ Point ptWhere;
+ if ( rEvt.IsMouseEvent() )
+ ptWhere = rEvt.GetMousePosPixel();
+ else
+ {
+ weld::TreeView& rTreeView = m_xListBox->get_widget();
+ std::unique_ptr<weld::TreeIter> xCurrent = rTreeView.make_iterator();
+ if (rTreeView.get_cursor(xCurrent.get()))
+ ptWhere = rTreeView.get_row_area(*xCurrent).Center();
+ else
+ ptWhere = m_xTitle->GetPosPixel();
+ }
+
+ ::tools::Rectangle aRect(ptWhere, Size(1, 1));
+ weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect);
+ std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, "dbaccess/ui/jointablemenu.ui"));
+ std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu("menu"));
+ if (!xContextMenu->popup_at_rect(pPopupParent, aRect).isEmpty())
+ Remove();
+ }
+ break;
+ }
+ default:
+ Window::Command(rEvt);
+ }
+}
+
+bool OTableWindow::PreNotify(NotifyEvent& rNEvt)
+{
+ bool bHandled = false;
+ switch (rNEvt.GetType())
+ {
+ case 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->GetLabel().get_label();
+}
+
+void OTableWindow::_elementInserted( const container::ContainerEvent& /*_rEvent*/ )
+{
+ FillListBox();
+}
+
+void OTableWindow::_elementRemoved( const container::ContainerEvent& /*_rEvent*/ )
+{
+ FillListBox();
+}
+
+void OTableWindow::_elementReplaced( const container::ContainerEvent& /*_rEvent*/ )
+{
+ FillListBox();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/ui/querydesign/TableWindowAccess.cxx b/dbaccess/source/ui/querydesign/TableWindowAccess.cxx
new file mode 100644
index 000000000..787d257b5
--- /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>
+#include <vcl/vclevent.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.Contains(aPoint) )
+ aRet = this;
+ else if( m_pTable->GetListBox()->GetDesktopRectPixel().Contains(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 = { 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..3066f8429
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/TableWindowListBox.cxx
@@ -0,0 +1,292 @@
+/* -*- 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 <o3tl/string_view.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)
+ , nEntry(pBox->get_widget().get_selected_index())
+{
+}
+
+OTableWindowListBox::OTableWindowListBox(OTableWindow* pParent)
+ : InterimItemWindow(pParent, "dbaccess/ui/tablelistbox.ui", "TableListBox")
+ , m_xTreeView(m_xBuilder->weld_tree_view("treeview"))
+ , m_xDragDropTargetHelper(new TableWindowListBoxHelper(*this, m_xTreeView->get_drop_target()))
+ , m_pTabWin(pParent)
+ , m_nDropEvent(nullptr)
+ , m_nUiEvent(nullptr)
+{
+ m_xTreeView->connect_row_activated(LINK(this, OTableWindowListBox, OnDoubleClick));
+ m_xTreeView->connect_visible_range_changed(LINK(this, OTableWindowListBox, ScrollHdl));
+ m_xTreeView->connect_popup_menu(LINK(this, OTableWindowListBox, CommandHdl));
+
+ m_xHelper.set(new OJoinExchObj);
+ rtl::Reference<TransferDataContainer> xHelper(m_xHelper);
+ m_xTreeView->enable_drag_source(xHelper, DND_ACTION_LINK);
+ m_xTreeView->connect_drag_begin(LINK(this, OTableWindowListBox, DragBeginHdl));
+}
+
+IMPL_LINK(OTableWindowListBox, CommandHdl, const CommandEvent&, rCEvt, bool)
+{
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+ m_pTabWin->Command(rCEvt);
+ return true;
+}
+
+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);
+ m_pTabWin.clear();
+ m_xDragDropTargetHelper.reset();
+ m_xTreeView.reset();
+ InterimItemWindow::dispose();
+}
+
+int OTableWindowListBox::GetEntryFromText(std::u16string_view rEntryText)
+{
+ // iterate through the list
+ 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();
+ }
+ for (int nEntry = 0, nCount = m_xTreeView->n_children(); nEntry < nCount; ++nEntry)
+ {
+ if (bCase ? rEntryText == m_xTreeView->get_text(nEntry)
+ : o3tl::equalsIgnoreAsciiCase(rEntryText, m_xTreeView->get_text(nEntry)))
+ return nEntry;
+ }
+ }
+ catch (SQLException&)
+ {
+ }
+
+ return -1;
+}
+
+IMPL_LINK_NOARG(OTableWindowListBox, ScrollHdl, weld::TreeView&, void)
+{
+ // connections of this table, if any, should be redrawn
+ m_pTabWin->getTableView()->Invalidate(InvalidateFlags::NoChildren);
+}
+
+IMPL_LINK(OTableWindowListBox, DragBeginHdl, bool&, rUnsetDragIcon, bool)
+{
+ rUnsetDragIcon = false;
+ if (m_xTreeView->get_selected_index() == -1)
+ {
+ // no drag without a field
+ return true;
+ }
+
+ 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 = m_xTreeView->is_selected(0) && m_pTabWin->GetData()->IsShowAll();
+ // create a description of the source
+ OJoinExchangeData jxdSource(this);
+ // update the exchange object
+ m_xHelper->setDescriptors(jxdSource, bFirstNotAllowed);
+
+ return false;
+ }
+
+ return true;
+}
+
+sal_Int8 OTableWindowListBox::AcceptDrop(const AcceptDropEvent& _rEvt)
+{
+ // to enable the autoscroll when we're close to the edges
+ std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator());
+ bool bHasDestRow = m_xTreeView->get_dest_row_at_pos(_rEvt.maPosPixel, xEntry.get(), true);
+
+ sal_Int8 nDND_Action = DND_ACTION_NONE;
+ // check the format
+ if (!OJoinExchObj::isFormatAvailable(
+ m_xDragDropTargetHelper->GetDataFlavorExVector(),
+ SotClipboardFormatId::SBA_TABID) // this means that the first entry is to be dragged
+ && OJoinExchObj::isFormatAvailable(m_xDragDropTargetHelper->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)
+ m_xTreeView->unselect_all();
+ else
+ {
+ if (!bHasDestRow)
+ return DND_ACTION_NONE;
+
+ // automatically select right entry when dragging
+ m_xTreeView->unselect_all();
+ m_xTreeView->select(*xEntry);
+
+ // one cannot drop on the first (*) entry
+ if (!(m_pTabWin->GetData()->IsShowAll()
+ && (m_xTreeView->get_iter_index_in_parent(*xEntry) == 0)))
+ 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);
+
+ dragFinished();
+
+ return DND_ACTION_NONE;
+ }
+ return DND_ACTION_NONE;
+}
+
+void OTableWindowListBox::LoseFocus()
+{
+ if (m_pTabWin)
+ m_pTabWin->setActive(false);
+ InterimItemWindow::LoseFocus();
+}
+
+void OTableWindowListBox::GetFocus()
+{
+ if (m_pTabWin)
+ m_pTabWin->setActive();
+
+ if (m_xTreeView)
+ {
+ std::unique_ptr<weld::TreeIter> xCurrent = m_xTreeView->make_iterator();
+ if (m_xTreeView->get_cursor(xCurrent.get()))
+ {
+ m_xTreeView->unselect_all();
+ m_xTreeView->select(*xCurrent);
+ }
+ }
+
+ InterimItemWindow::GetFocus();
+}
+
+IMPL_LINK_NOARG(OTableWindowListBox, OnDoubleClick, weld::TreeView&, bool)
+{
+ // tell my parent
+ vcl::Window* pParent = Window::GetParent();
+ OSL_ENSURE(pParent != nullptr, "OTableWindowListBox::OnDoubleClick : have no Parent !");
+
+ std::unique_ptr<weld::TreeIter> xCurrent = m_xTreeView->make_iterator();
+ if (!m_xTreeView->get_cursor(xCurrent.get()))
+ return false;
+
+ static_cast<OTableWindow*>(pParent)->OnEntryDoubleClicked(*xCurrent);
+
+ return false;
+}
+
+void OTableWindowListBox::Command(const CommandEvent& rEvt)
+{
+ switch (rEvt.GetCommand())
+ {
+ case CommandEventId::ContextMenu:
+ {
+ static_cast<OTableWindow*>(Window::GetParent())->Command(rEvt);
+ break;
+ }
+ default:
+ InterimItemWindow::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..50eb19c28
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/TableWindowTitle.cxx
@@ -0,0 +1,97 @@
+/* -*- 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/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)
+ : InterimItemWindow(pParent, "dbaccess/ui/tabletitle.ui", "TableTitle")
+ , m_pTabWin( pParent )
+ , m_xLabel(m_xBuilder->weld_label("label"))
+ , m_xImage(m_xBuilder->weld_image("image"))
+{
+ m_xLabel->connect_mouse_press(LINK(this, OTableWindowTitle, MousePressHdl));
+}
+
+OTableWindowTitle::~OTableWindowTitle()
+{
+ disposeOnce();
+}
+
+void OTableWindowTitle::dispose()
+{
+ m_xImage.reset();
+ m_xLabel.reset();
+ m_pTabWin.clear();
+ InterimItemWindow::dispose();
+}
+
+IMPL_LINK(OTableWindowTitle, MousePressHdl, const MouseEvent&, rEvt, bool)
+{
+ if (rEvt.IsLeft())
+ {
+ if( rEvt.GetClicks() == 2)
+ {
+ Size aSize(GetTextWidth(GetText()) + 20,
+ m_pTabWin->GetSizePixel().Height() - m_pTabWin->GetListBox()->GetSizePixel().Height());
+
+ weld::TreeView& rTreeView = m_pTabWin->GetListBox()->get_widget();
+ aSize.AdjustHeight(rTreeView.get_height_rows(rTreeView.n_children() + 2));
+ 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 );
+ }
+ }
+ else if (rEvt.IsRight())
+ {
+ CommandEvent aCEvt(rEvt.GetPosPixel(), CommandEventId::ContextMenu, true);
+ // tdf#94709 - protect shutdown code-path.
+ VclPtr<OTableWindow> xTabWin(m_pTabWin);
+ xTabWin->Command(aCEvt);
+ }
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/dbaccess/source/ui/querydesign/class.jpg b/dbaccess/source/ui/querydesign/class.jpg
new file mode 100644
index 000000000..b1a3b6d27
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/class.jpg
Binary files differ
diff --git a/dbaccess/source/ui/querydesign/limitboxcontroller.cxx b/dbaccess/source/ui/querydesign/limitboxcontroller.cxx
new file mode 100644
index 000000000..8b6884338
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/limitboxcontroller.cxx
@@ -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/.
+ */
+
+#include "limitboxcontroller.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 <comphelper/propertyvalue.hxx>
+#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 <core_resource.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"))
+ {
+ InitControlBase(m_xWidget.get());
+
+ 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);
+ }
+
+ 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;
+ 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);
+ m_pControl->dispatchCommand({ comphelper::makePropertyValue("DBLimit.Value", nLimit) });
+ }
+
+ ///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() noexcept
+{
+ ToolboxController::acquire();
+}
+
+void SAL_CALL LimitBoxController::release() noexcept
+{
+ ToolboxController::release();
+}
+
+
+/// XServiceInfo
+OUString SAL_CALL LimitBoxController::getImplementationName()
+{
+ return "org.libreoffice.comp.dbu.LimitBoxController";
+}
+
+sal_Bool SAL_CALL LimitBoxController::supportsService(const OUString& _rServiceName)
+ {
+ const css::uno::Sequence< OUString > aSupported(getSupportedServiceNames());
+ for (const OUString& s : aSupported)
+ if (s == _rServiceName)
+ return true;
+
+ return false;
+ }
+
+css::uno::Sequence< OUString > SAL_CALL LimitBoxController::getSupportedServiceNames()
+{
+ return { "com.sun.star.frame.ToolbarController" };
+}
+
+/// 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" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+org_libreoffice_comp_dbu_LimitBoxController_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new ::dbaui::LimitBoxController(context));
+}
+
+/* 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..af1e5a666
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/limitboxcontroller.hxx
@@ -0,0 +1,65 @@
+/* -*- 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/.
+ */
+
+#pragma once
+
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <connectivity/CommonTools.hxx>
+#include <svtools/toolboxcontroller.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() noexcept override;
+ virtual void SAL_CALL release() noexcept override;
+
+ /// XServiceInfo
+ DECLARE_SERVICE_INFO();
+
+ /// 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
+
+/* 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..68b4c5c86
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/querycontainerwindow.cxx
@@ -0,0 +1,222 @@
+/* -*- 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() )
+ {
+ xLMPropSet->setPropertyValue( "AutomaticToolbars", 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 tools::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..230cb7f6e
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/querycontroller.cxx
@@ -0,0 +1,1790 @@
+/* -*- 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 <stringconstants.hxx>
+#include <defaultobjectnamecheck.hxx>
+#include <dlgsave.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/lang/XSingleServiceFactory.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/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 <o3tl/string_view.hxx>
+#include <memory>
+#include <vector>
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+org_openoffice_comp_dbu_OQueryDesign_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new ::dbaui::OQueryController(context));
+}
+
+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 "org.openoffice.comp.dbu.OViewDesign";
+ }
+ virtual Sequence< OUString> SAL_CALL getSupportedServiceNames() override
+ {
+ return { "com.sun.star.sdb.ViewDesign" };
+ }
+
+ public:
+ explicit OViewController(const Reference< XComponentContext >& _rM) : OQueryController(_rM){}
+ };
+
+ }
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+org_openoffice_comp_dbu_OViewDesign_get_implementation(
+ css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
+{
+ return cppu::acquire(new ::dbaui::OViewController(context));
+}
+
+namespace dbaui
+{
+ using namespace ::connectivity;
+
+ namespace
+ {
+ OUString lcl_getObjectResourceString(TranslateId 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 constexpr OUStringLiteral s_sDesignToolbar = u"private:resource/toolbar/designobjectbar";
+ static constexpr OUStringLiteral s_sSqlToolbar = u"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 "org.openoffice.comp.dbu.OQueryDesign";
+}
+
+Sequence< OUString> SAL_CALL OQueryController::getSupportedServiceNames()
+{
+ return { "com.sun.star.sdb.QueryDesign" };
+}
+
+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( 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 );
+ auto pProps = aProps.getArray();
+ pProps[ nLength ] = Property(
+ "CurrentQueryDesign",
+ PROPERTY_ID_CURRENT_QUERY_DESIGN,
+ ::cppu::UnoType< Sequence< PropertyValue > >::get(),
+ PropertyAttribute::READONLY
+ );
+
+ std::sort(
+ pProps,
+ pProps + aProps.getLength(),
+ ::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
+ {
+ 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
+ {
+ 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() )
+ {
+ 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( PROPERTY_GRAPHICAL_DESIGN ) )
+ {
+ aCurrentQueryDesign.get_ensureType( PROPERTY_GRAPHICAL_DESIGN, m_bGraphicalDesign );
+ }
+ if ( aCurrentQueryDesign.has( 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 o3tl::getToken(aDefaultName, 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, Any(sDataSourceName) },
+ { PROPERTY_COMMAND_TYPE, Any(CommandType::COMMAND) },
+ { PROPERTY_COMMAND, Any(sTranslatedStmt) },
+ { PROPERTY_ENABLE_BROWSER, Any(false) },
+ { PROPERTY_ACTIVE_CONNECTION, Any(getConnection()) },
+ { PROPERTY_UPDATE_CATALOGNAME, Any(m_sUpdateCatalogName) },
+ { PROPERTY_UPDATE_SCHEMANAME, Any(m_sUpdateSchemaName) },
+ { PROPERTY_UPDATE_TABLENAME, Any(OUString()) },
+ { PROPERTY_ESCAPE_PROCESSING, Any(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, Any( 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, Any( sTranslatedStmt ) );
+
+ if ( editingView() )
+ {
+ xQuery->setPropertyValue( PROPERTY_CATALOGNAME, Any( m_sUpdateCatalogName ) );
+ xQuery->setPropertyValue( PROPERTY_SCHEMANAME, Any( m_sUpdateSchemaName ) );
+ }
+
+ if ( editingQuery() )
+ {
+ xQuery->setPropertyValue( PROPERTY_UPDATE_TABLENAME, Any( OUString() ) );
+ xQuery->setPropertyValue( PROPERTY_ESCAPE_PROCESSING, css::uno::Any( 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, Any( 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( Any( 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()->setNoneVisibleRow(m_nVisibleRows);
+}
+
+void OQueryController::reset()
+{
+ impl_reset();
+ getContainer()->reset();
+ ClearUndoManager();
+}
+
+void OQueryController::setStatement_fireEvent( const OUString& _rNewStatement, bool _bFireStatementChange )
+{
+ Any aOldValue( m_sStatement );
+ m_sStatement = _rNewStatement;
+ Any aNewValue( 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( m_bEscapeProcessing );
+ m_bEscapeProcessing = _bEscapeProcessing;
+ Any aNewValue( 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 Any( 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..5a21998d9
--- /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;
+ TranslateId pResId;
+ 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::Toggleable&, 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..3da416c6e
--- /dev/null
+++ b/dbaccess/source/ui/querydesign/querydlg.hxx
@@ -0,0 +1,77 @@
+/* -*- 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 .
+ */
+#pragma once
+
+#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::Toggleable&, 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;
+ };
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */