1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <RelationControl.hxx>
22 #include <svtools/editbrowsebox.hxx>
23 #include <com/sun/star/beans/XPropertySet.hpp>
24 #include <comphelper/diagnose_ex.hxx>
25 #include <toolkit/helper/vclunohelper.hxx>
26 #include <TableConnectionData.hxx>
27 #include <TableConnection.hxx>
28 #include <TableWindow.hxx>
29 #include <com/sun/star/awt/XWindow.hpp>
30 #include <com/sun/star/container/XNameAccess.hpp>
31 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
32 #include <RelControliFace.hxx>
34 #include <o3tl/safeint.hxx>
35 #include <osl/diagnose.h>
42 #define SOURCE_COLUMN 1
47 using namespace ::com::sun::star::uno
;
48 using namespace ::com::sun::star::beans
;
49 using namespace ::com::sun::star::sdbcx
;
50 using namespace ::com::sun::star::container
;
53 typedef ::svt::EditBrowseBox ORelationControl_Base
;
54 class ORelationControl
: public ORelationControl_Base
56 friend class OTableListBoxControl
;
58 VclPtr
< ::svt::ListBoxControl
> m_pListCell
;
59 TTableConnectionData::value_type m_pConnData
;
60 OTableListBoxControl
* m_pBoxControl
;
61 tools::Long m_nDataPos
;
62 Reference
< XPropertySet
> m_xSourceDef
;
63 Reference
< XPropertySet
> m_xDestDef
;
64 enum opcode
{ DELETE
, INSERT
, MODIFY
};
65 typedef std::vector
< pair
< opcode
, pair
< OConnectionLineDataVec::size_type
, OConnectionLineDataVec::size_type
> > > ops_type
;
68 void fillListBox(const Reference
< XPropertySet
>& _xDest
);
69 /** returns the column id for the editbrowsebox
71 the column id SOURCE_COLUMN or DEST_COLUMN
73 @return the current column id either SOURCE_COLUMN or DEST_COLUMN depends on the connection data
75 sal_uInt16
getColumnIdent( sal_uInt16 _nColId
) const;
77 explicit ORelationControl(const css::uno::Reference
<css::awt::XWindow
>& rParent
);
78 void SetController(OTableListBoxControl
* pController
)
80 m_pBoxControl
= pController
;
83 /** searches for a connection between these two tables
89 void setWindowTables(const OTableWindow
* _pSource
,const OTableWindow
* _pDest
);
91 /** allows to access the connection data from outside
93 @return the connection data
95 const TTableConnectionData::value_type
& getData() const { return m_pConnData
; }
100 virtual ~ORelationControl() override
{ disposeOnce(); }
101 virtual void dispose() override
{ m_pListCell
.disposeAndClear(); ORelationControl_Base::dispose(); }
102 virtual void Resize() override
;
103 virtual Size
GetOptimalSize() const override
;
104 virtual bool PreNotify(NotifyEvent
& rNEvt
) override
;
106 virtual bool IsTabAllowed(bool bForward
) const override
;
108 void Init(const TTableConnectionData::value_type
& _pConnData
);
109 using ORelationControl_Base::Init
;
110 virtual void InitController( ::svt::CellControllerRef
& rController
, sal_Int32 nRow
, sal_uInt16 nCol
) override
;
111 virtual ::svt::CellController
* GetController( sal_Int32 nRow
, sal_uInt16 nCol
) override
;
112 virtual void PaintCell( OutputDevice
& rDev
, const tools::Rectangle
& rRect
, sal_uInt16 nColId
) const override
;
113 virtual bool SeekRow( sal_Int32 nRow
) override
;
114 virtual bool SaveModified() override
;
115 virtual OUString
GetCellText( sal_Int32 nRow
, sal_uInt16 nColId
) const override
;
117 virtual void CellModified() override
;
119 DECL_LINK( AsynchDeactivate
, void*, void );
122 DECL_LINK( AsynchActivate
, void*, void );
126 ORelationControl::ORelationControl(const css::uno::Reference
<css::awt::XWindow
>& rParent
)
127 : EditBrowseBox(VCLUnoHelper::GetWindow(rParent
),
128 EditBrowseBoxFlags::SMART_TAB_TRAVEL
| EditBrowseBoxFlags::NO_HANDLE_COLUMN_CONTENT
,
129 WB_TABSTOP
| WB_BORDER
,
130 BrowserMode::AUTOSIZE_LASTCOL
)
131 , m_pBoxControl(nullptr)
136 void ORelationControl::Init(const TTableConnectionData::value_type
& _pConnData
)
139 m_pConnData
= _pConnData
;
140 OSL_ENSURE(m_pConnData
, "No data supplied!");
142 m_pConnData
->normalizeLines();
145 void ORelationControl::lateInit()
149 m_xSourceDef
= m_pConnData
->getReferencingTable()->getTable();
150 m_xDestDef
= m_pConnData
->getReferencedTable()->getTable();
152 if ( ColCount() == 0 )
154 InsertDataColumn( SOURCE_COLUMN
, m_pConnData
->getReferencingTable()->GetWinName(), 100);
155 InsertDataColumn( DEST_COLUMN
, m_pConnData
->getReferencedTable()->GetWinName(), 100);
156 // If the Defs do not yet exits, we need to set them with SetSource-/-DestDef
158 m_pListCell
.reset( VclPtr
<ListBoxControl
>::Create( &GetDataWindow() ) );
161 SetMode( BrowserMode::COLUMNSELECTION
|
162 BrowserMode::HLINES
|
163 BrowserMode::VLINES
|
164 BrowserMode::HIDECURSOR
|
165 BrowserMode::HIDESELECT
|
166 BrowserMode::AUTO_HSCROLL
|
167 BrowserMode::AUTO_VSCROLL
);
170 // not the first call
171 RowRemoved(0, GetRowCount());
173 RowInserted(0, m_pConnData
->GetConnLineDataList().size() + 1); // add one extra row
176 void ORelationControl::Resize()
178 EditBrowseBox::Resize();
179 tools::Long nOutputWidth
= GetOutputSizePixel().Width() - 1;
180 SetColumnWidth(1, (nOutputWidth
/ 2));
181 SetColumnWidth(2, (nOutputWidth
/ 2));
184 bool ORelationControl::PreNotify(NotifyEvent
& rNEvt
)
186 if (rNEvt
.GetType() == NotifyEventType::LOSEFOCUS
&& !HasChildPathFocus() && !ControlHasFocus())
187 PostUserEvent(LINK(this, ORelationControl
, AsynchDeactivate
), nullptr, true);
188 else if (rNEvt
.GetType() == NotifyEventType::GETFOCUS
)
189 PostUserEvent(LINK(this, ORelationControl
, AsynchActivate
), nullptr, true);
191 return EditBrowseBox::PreNotify(rNEvt
);
194 IMPL_LINK_NOARG(ORelationControl
, AsynchActivate
, void*, void)
199 IMPL_LINK_NOARG(ORelationControl
, AsynchDeactivate
, void*, void)
204 bool ORelationControl::IsTabAllowed(bool bForward
) const
206 sal_Int32 nRow
= GetCurRow();
207 sal_uInt16 nCol
= GetCurColumnId();
209 bool bRet
= !( ( bForward
&& (nCol
== DEST_COLUMN
) && (nRow
== GetRowCount() - 1))
210 || (!bForward
&& (nCol
== SOURCE_COLUMN
) && (nRow
== 0)));
212 return bRet
&& EditBrowseBox::IsTabAllowed(bForward
);
215 bool ORelationControl::SaveModified()
217 sal_Int32 nRow
= GetCurRow();
218 if ( nRow
!= BROWSER_ENDOFSELECTION
)
220 weld::ComboBox
& rListBox
= m_pListCell
->get_widget();
221 OUString
sFieldName(rListBox
.get_active_text());
222 OConnectionLineDataVec
& rLines
= m_pConnData
->GetConnLineDataList();
223 if ( rLines
.size() <= o3tl::make_unsigned(nRow
) )
225 rLines
.emplace_back(new OConnectionLineData());
226 nRow
= rLines
.size() - 1;
227 // add new past-rLines row
228 m_ops
.emplace_back(INSERT
, make_pair(nRow
+1, nRow
+2));
231 OConnectionLineDataRef pConnLineData
= rLines
[nRow
];
233 switch( getColumnIdent( GetCurColumnId() ) )
236 pConnLineData
->SetSourceFieldName( sFieldName
);
239 pConnLineData
->SetDestFieldName( sFieldName
);
242 // the modification we just did does *not* need to be registered in m_ops;
243 // it is already taken into account (by the codepath that called us)
244 //m_ops.push_back(make_pair(MODIFY, make_pair(nRow, nRow+1)));
247 const OConnectionLineDataVec::size_type oldSize
= m_pConnData
->GetConnLineDataList().size();
248 OConnectionLineDataVec::size_type line
= m_pConnData
->normalizeLines();
249 const OConnectionLineDataVec::size_type newSize
= m_pConnData
->GetConnLineDataList().size();
250 assert(newSize
<= oldSize
);
251 m_ops
.emplace_back(MODIFY
, make_pair(line
, newSize
));
252 m_ops
.emplace_back(DELETE
, make_pair(newSize
, oldSize
));
257 sal_uInt16
ORelationControl::getColumnIdent( sal_uInt16 _nColId
) const
259 sal_uInt16 nId
= _nColId
;
260 if ( m_pConnData
->getReferencingTable() != m_pBoxControl
->getReferencingTable() )
261 nId
= ( _nColId
== SOURCE_COLUMN
) ? DEST_COLUMN
: SOURCE_COLUMN
;
265 OUString
ORelationControl::GetCellText( sal_Int32 nRow
, sal_uInt16 nColId
) const
268 if ( m_pConnData
->GetConnLineDataList().size() > o3tl::make_unsigned(nRow
) )
270 OConnectionLineDataRef pConnLineData
= m_pConnData
->GetConnLineDataList()[nRow
];
271 switch( getColumnIdent( nColId
) )
274 sText
= pConnLineData
->GetSourceFieldName();
277 sText
= pConnLineData
->GetDestFieldName();
284 void ORelationControl::InitController( CellControllerRef
& /*rController*/, sal_Int32 nRow
, sal_uInt16 nColumnId
)
287 OUString
sHelpId( HID_RELATIONDIALOG_LEFTFIELDCELL
);
289 Reference
< XPropertySet
> xDef
;
290 switch ( getColumnIdent(nColumnId
) )
294 sHelpId
= HID_RELATIONDIALOG_LEFTFIELDCELL
;
298 sHelpId
= HID_RELATIONDIALOG_RIGHTFIELDCELL
;
309 OUString sName
= GetCellText( nRow
, nColumnId
);
310 weld::ComboBox
& rList
= m_pListCell
->get_widget();
311 rList
.set_active_text(sName
);
312 if (rList
.get_active_text() != sName
)
314 rList
.append_text(sName
);
315 rList
.set_active_text(sName
);
318 rList
.set_help_id(sHelpId
);
321 CellController
* ORelationControl::GetController( sal_Int32
/*nRow*/, sal_uInt16
/*nColumnId*/ )
323 return new ListBoxCellController( m_pListCell
.get() );
326 bool ORelationControl::SeekRow( sal_Int32 nRow
)
332 void ORelationControl::PaintCell( OutputDevice
& rDev
, const tools::Rectangle
& rRect
, sal_uInt16 nColumnId
) const
334 OUString aText
= GetCellText( m_nDataPos
, nColumnId
);
336 Point
aPos( rRect
.TopLeft() );
337 Size
aTextSize( GetDataWindow().GetTextWidth( aText
), GetDataWindow().GetTextHeight() );
339 if( aPos
.X() < rRect
.Left() || aPos
.X() + aTextSize
.Width() > rRect
.Right() ||
340 aPos
.Y() < rRect
.Top() || aPos
.Y() + aTextSize
.Height() > rRect
.Bottom() )
342 rDev
.SetClipRegion(vcl::Region(rRect
));
345 rDev
.DrawText( aPos
, aText
);
347 if( rDev
.IsClipRegion() )
348 rDev
.SetClipRegion();
350 void ORelationControl::fillListBox(const Reference
< XPropertySet
>& _xDest
)
352 weld::ComboBox
& rList
= m_pListCell
->get_widget();
358 //sal_Int32 nRows = GetRowCount();
359 Reference
<XColumnsSupplier
> xSup(_xDest
,UNO_QUERY
);
360 Reference
<XNameAccess
> xColumns
= xSup
->getColumns();
361 for (auto& text
: xColumns
->getElementNames())
363 rList
.append_text(text
);
365 rList
.insert_text(0, OUString());
368 catch( const Exception
& )
370 DBG_UNHANDLED_EXCEPTION("dbaccess");
373 void ORelationControl::setWindowTables(const OTableWindow
* _pSource
,const OTableWindow
* _pDest
)
375 // If I edit here, hide
376 bool bWasEditing
= IsEditing();
380 if ( _pSource
&& _pDest
)
382 m_xSourceDef
= _pSource
->GetTable();
383 SetColumnTitle(1, _pSource
->GetName());
385 m_xDestDef
= _pDest
->GetTable();
386 SetColumnTitle(2, _pDest
->GetName());
388 const OJoinTableView
* pView
= _pSource
->getTableView();
389 OTableConnection
* pConn
= pView
->GetTabConn(_pSource
,_pDest
);
390 if ( pConn
&& !m_pConnData
->GetConnLineDataList().empty() )
392 m_pConnData
->CopyFrom(*pConn
->GetData());
393 m_pBoxControl
->getContainer()->notifyConnectionChange();
397 // no connection found so we clear our data
398 OConnectionLineDataVec
& rLines
= m_pConnData
->GetConnLineDataList();
399 for( const auto& rLine
: rLines
)
404 m_pConnData
->setReferencingTable(_pSource
->GetData());
405 m_pConnData
->setReferencedTable(_pDest
->GetData());
407 m_pConnData
->normalizeLines();
420 void ORelationControl::CellModified()
422 EditBrowseBox::CellModified();
424 assert(m_pBoxControl
);
425 m_pBoxControl
->NotifyCellChange();
428 Size
ORelationControl::GetOptimalSize() const
430 return LogicToPixel(Size(140, 80), MapMode(MapUnit::MapAppFont
));
433 OTableListBoxControl::OTableListBoxControl(weld::Builder
* _pParent
,
434 const OJoinTableView::OTableWindowMap
* _pTableMap
,
435 IRelationControlInterface
* _pParentDialog
)
436 : m_xLeftTable(_pParent
->weld_combo_box(u
"table1"_ustr
))
437 , m_xRightTable(_pParent
->weld_combo_box(u
"table2"_ustr
))
438 , m_xTable(_pParent
->weld_container(u
"relations"_ustr
))
439 , m_xTableCtrlParent(m_xTable
->CreateChildFrame())
440 , m_xRC_Tables(VclPtr
<ORelationControl
>::Create(m_xTableCtrlParent
))
441 , m_pTableMap(_pTableMap
)
442 , m_pParentDialog(_pParentDialog
)
444 Size aPrefSize
= m_xRC_Tables
->GetOptimalSize();
445 m_xTable
->set_size_request(aPrefSize
.Width(), aPrefSize
.Height());
447 m_xRC_Tables
->SetController(this);
448 m_xRC_Tables
->Init();
452 Link
<weld::ComboBox
&,void> aLink(LINK(this, OTableListBoxControl
, OnTableChanged
));
453 m_xLeftTable
->connect_changed(aLink
);
454 m_xRightTable
->connect_changed(aLink
);
457 OTableListBoxControl::~OTableListBoxControl()
459 m_xRC_Tables
.disposeAndClear();
460 m_xTableCtrlParent
->dispose();
461 m_xTableCtrlParent
.clear();
464 void OTableListBoxControl::fillListBoxes()
466 OSL_ENSURE( !m_pTableMap
->empty(), "OTableListBoxControl::fillListBoxes: no table window!");
467 OTableWindow
* pInitialLeft
= nullptr;
468 OTableWindow
* pInitialRight
= nullptr;
470 // Collect the names of all TabWins
471 for (auto const& elem
: *m_pTableMap
)
473 m_xLeftTable
->append_text(elem
.first
);
474 m_xRightTable
->append_text(elem
.first
);
478 pInitialLeft
= elem
.second
;
479 m_strCurrentLeft
= elem
.first
;
481 else if (!pInitialRight
)
483 pInitialRight
= elem
.second
;
484 m_strCurrentRight
= elem
.first
;
488 if ( !pInitialRight
)
490 pInitialRight
= pInitialLeft
;
491 m_strCurrentRight
= m_strCurrentLeft
;
494 // The corresponding Defs for my Controls
495 m_xRC_Tables
->setWindowTables(pInitialLeft
,pInitialRight
);
497 // The table selected in a ComboBox must not be available in the other
499 if ( m_pTableMap
->size() > 2 )
501 m_xLeftTable
->remove_text(m_strCurrentRight
);
502 m_xRightTable
->remove_text(m_strCurrentLeft
);
505 // Select the first one on the left side and on the right side,
506 // select the second one
507 m_xLeftTable
->set_active_text(m_strCurrentLeft
);
508 m_xRightTable
->set_active_text(m_strCurrentRight
);
510 m_xLeftTable
->grab_focus();
513 IMPL_LINK(OTableListBoxControl
, OnTableChanged
, weld::ComboBox
&, rListBox
, void)
515 OUString
strSelected(rListBox
.get_active_text());
516 OTableWindow
* pLeft
= nullptr;
517 OTableWindow
* pRight
= nullptr;
519 // Special treatment: If there are only two tables, we need to switch the other one too when changing in a LB
520 if ( m_pTableMap
->size() == 2 )
522 weld::ComboBox
* pOther
;
523 if (&rListBox
== m_xLeftTable
.get())
524 pOther
= m_xRightTable
.get();
526 pOther
= m_xLeftTable
.get();
527 pOther
->set_active(1 - pOther
->get_active());
529 OJoinTableView::OTableWindowMap::const_iterator aIter
= m_pTableMap
->begin();
530 OTableWindow
* pFirst
= aIter
->second
;
532 OTableWindow
* pSecond
= aIter
->second
;
534 if (m_xLeftTable
->get_active_text() == pFirst
->GetName())
547 // First we need the TableDef to the Table and with it the TabWin
548 OJoinTableView::OTableWindowMap::const_iterator aFind
= m_pTableMap
->find(strSelected
);
549 OTableWindow
* pLoop
= nullptr;
550 if( aFind
!= m_pTableMap
->end() )
551 pLoop
= aFind
->second
;
552 OSL_ENSURE(pLoop
!= nullptr, "ORelationDialog::OnTableChanged: invalid ListBox entry!");
553 // We need to find strSelect, because we filled the ListBoxes with the table names with which we compare now
554 if (&rListBox
== m_xLeftTable
.get())
556 // Insert the previously selected Entry on the left side on the right side
557 m_xRightTable
->append_text(m_strCurrentLeft
);
558 // Remove the currently selected Entry
559 m_xRightTable
->remove_text(strSelected
);
560 m_strCurrentLeft
= strSelected
;
564 OJoinTableView::OTableWindowMap::const_iterator aIter
= m_pTableMap
->find(m_xRightTable
->get_active_text());
565 OSL_ENSURE( aIter
!= m_pTableMap
->end(), "Invalid name");
566 if ( aIter
!= m_pTableMap
->end() )
567 pRight
= aIter
->second
;
569 m_xLeftTable
->grab_focus();
573 // Insert the previously selected Entry on the right side on the left side
574 m_xLeftTable
->append_text(m_strCurrentRight
);
575 // Remove the currently selected Entry
576 m_xLeftTable
->remove_text(strSelected
);
577 m_strCurrentRight
= strSelected
;
580 OJoinTableView::OTableWindowMap::const_iterator aIter
= m_pTableMap
->find(m_xLeftTable
->get_active_text());
581 OSL_ENSURE( aIter
!= m_pTableMap
->end(), "Invalid name");
582 if ( aIter
!= m_pTableMap
->end() )
583 pLeft
= aIter
->second
;
587 rListBox
.grab_focus();
589 m_xRC_Tables
->setWindowTables(pLeft
,pRight
);
594 void OTableListBoxControl::NotifyCellChange()
596 // Enable/disable the OK button, depending on having a valid situation
597 TTableConnectionData::value_type pConnData
= m_xRC_Tables
->getData();
598 const OConnectionLineDataVec
& rLines
= pConnData
->GetConnLineDataList();
599 bool bValid
= !rLines
.empty();
602 for (auto const& line
: rLines
)
604 bValid
= ! (line
->GetSourceFieldName().isEmpty() || line
->GetDestFieldName().isEmpty());
609 m_pParentDialog
->setValid(bValid
);
611 m_xRC_Tables
->DeactivateCell();
612 for (auto const& elem
: m_xRC_Tables
->m_ops
)
616 case ORelationControl::DELETE
:
617 m_xRC_Tables
->RowRemoved(elem
.second
.first
, elem
.second
.second
- elem
.second
.first
);
619 case ORelationControl::INSERT
:
620 m_xRC_Tables
->RowInserted(elem
.second
.first
, elem
.second
.second
- elem
.second
.first
);
622 case ORelationControl::MODIFY
:
623 for(OConnectionLineDataVec::size_type j
= elem
.second
.first
; j
< elem
.second
.second
; ++j
)
624 m_xRC_Tables
->RowModified(j
);
628 m_xRC_Tables
->ActivateCell();
629 m_xRC_Tables
->m_ops
.clear();
632 static void fillEntryAndDisable(weld::ComboBox
& _rListBox
,const OUString
& _sEntry
)
634 _rListBox
.append_text(_sEntry
);
635 _rListBox
.set_active(0);
636 _rListBox
.set_sensitive(false);
639 void OTableListBoxControl::fillAndDisable(const TTableConnectionData::value_type
& _pConnectionData
)
641 fillEntryAndDisable(*m_xLeftTable
, _pConnectionData
->getReferencingTable()->GetWinName());
642 fillEntryAndDisable(*m_xRightTable
, _pConnectionData
->getReferencedTable()->GetWinName());
645 void OTableListBoxControl::Init(const TTableConnectionData::value_type
& _pConnData
)
647 m_xRC_Tables
->Init(_pConnData
);
650 void OTableListBoxControl::lateUIInit()
652 m_xRC_Tables
->Show();
656 void OTableListBoxControl::lateInit()
658 m_xRC_Tables
->lateInit();
661 void OTableListBoxControl::Disable()
663 m_xLeftTable
->set_sensitive(false);
664 m_xRightTable
->set_sensitive(false);
665 m_xRC_Tables
->Disable();
668 void OTableListBoxControl::Invalidate()
670 m_xRC_Tables
->Invalidate();
673 void OTableListBoxControl::SaveModified()
675 m_xRC_Tables
->SaveModified();
678 TTableWindowData::value_type
const & OTableListBoxControl::getReferencingTable() const
680 return m_xRC_Tables
->getData()->getReferencingTable();
683 void OTableListBoxControl::enableRelation(bool _bEnable
)
686 m_xRC_Tables
->PostUserEvent(LINK(m_xRC_Tables
, ORelationControl
, AsynchDeactivate
));
687 m_xRC_Tables
->Enable(_bEnable
);
691 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */