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::sdbc
;
50 using namespace ::com::sun::star::sdbcx
;
51 using namespace ::com::sun::star::container
;
54 typedef ::svt::EditBrowseBox ORelationControl_Base
;
55 class ORelationControl
: public ORelationControl_Base
57 friend class OTableListBoxControl
;
59 VclPtr
< ::svt::ListBoxControl
> m_pListCell
;
60 TTableConnectionData::value_type m_pConnData
;
61 OTableListBoxControl
* m_pBoxControl
;
62 tools::Long m_nDataPos
;
63 Reference
< XPropertySet
> m_xSourceDef
;
64 Reference
< XPropertySet
> m_xDestDef
;
65 enum opcode
{ DELETE
, INSERT
, MODIFY
};
66 typedef std::vector
< pair
< opcode
, pair
< OConnectionLineDataVec::size_type
, OConnectionLineDataVec::size_type
> > > ops_type
;
69 void fillListBox(const Reference
< XPropertySet
>& _xDest
);
70 /** returns the column id for the editbrowsebox
72 the column id SOURCE_COLUMN or DEST_COLUMN
74 @return the current column id either SOURCE_COLUMN or DEST_COLUMN depends on the connection data
76 sal_uInt16
getColumnIdent( sal_uInt16 _nColId
) const;
78 explicit ORelationControl(const css::uno::Reference
<css::awt::XWindow
>& rParent
);
79 void SetController(OTableListBoxControl
* pController
)
81 m_pBoxControl
= pController
;
84 /** searches for a connection between these two tables
90 void setWindowTables(const OTableWindow
* _pSource
,const OTableWindow
* _pDest
);
92 /** allows to access the connection data from outside
94 @return the connection data
96 const TTableConnectionData::value_type
& getData() const { return m_pConnData
; }
101 virtual ~ORelationControl() override
{ disposeOnce(); }
102 virtual void dispose() override
{ m_pListCell
.disposeAndClear(); ORelationControl_Base::dispose(); }
103 virtual void Resize() override
;
104 virtual Size
GetOptimalSize() const override
;
105 virtual bool PreNotify(NotifyEvent
& rNEvt
) override
;
107 virtual bool IsTabAllowed(bool bForward
) const override
;
109 void Init(const TTableConnectionData::value_type
& _pConnData
);
110 using ORelationControl_Base::Init
;
111 virtual void InitController( ::svt::CellControllerRef
& rController
, sal_Int32 nRow
, sal_uInt16 nCol
) override
;
112 virtual ::svt::CellController
* GetController( sal_Int32 nRow
, sal_uInt16 nCol
) override
;
113 virtual void PaintCell( OutputDevice
& rDev
, const tools::Rectangle
& rRect
, sal_uInt16 nColId
) const override
;
114 virtual bool SeekRow( sal_Int32 nRow
) override
;
115 virtual bool SaveModified() override
;
116 virtual OUString
GetCellText( sal_Int32 nRow
, sal_uInt16 nColId
) const override
;
118 virtual void CellModified() override
;
120 DECL_LINK( AsynchDeactivate
, void*, void );
123 DECL_LINK( AsynchActivate
, void*, void );
127 ORelationControl::ORelationControl(const css::uno::Reference
<css::awt::XWindow
>& rParent
)
128 : EditBrowseBox(VCLUnoHelper::GetWindow(rParent
),
129 EditBrowseBoxFlags::SMART_TAB_TRAVEL
| EditBrowseBoxFlags::NO_HANDLE_COLUMN_CONTENT
,
130 WB_TABSTOP
| WB_BORDER
,
131 BrowserMode::AUTOSIZE_LASTCOL
)
132 , m_pBoxControl(nullptr)
137 void ORelationControl::Init(const TTableConnectionData::value_type
& _pConnData
)
140 m_pConnData
= _pConnData
;
141 OSL_ENSURE(m_pConnData
, "No data supplied!");
143 m_pConnData
->normalizeLines();
146 void ORelationControl::lateInit()
150 m_xSourceDef
= m_pConnData
->getReferencingTable()->getTable();
151 m_xDestDef
= m_pConnData
->getReferencedTable()->getTable();
153 if ( ColCount() == 0 )
155 InsertDataColumn( SOURCE_COLUMN
, m_pConnData
->getReferencingTable()->GetWinName(), 100);
156 InsertDataColumn( DEST_COLUMN
, m_pConnData
->getReferencedTable()->GetWinName(), 100);
157 // If the Defs do not yet exits, we need to set them with SetSource-/-DestDef
159 m_pListCell
.reset( VclPtr
<ListBoxControl
>::Create( &GetDataWindow() ) );
162 SetMode( BrowserMode::COLUMNSELECTION
|
163 BrowserMode::HLINES
|
164 BrowserMode::VLINES
|
165 BrowserMode::HIDECURSOR
|
166 BrowserMode::HIDESELECT
|
167 BrowserMode::AUTO_HSCROLL
|
168 BrowserMode::AUTO_VSCROLL
);
171 // not the first call
172 RowRemoved(0, GetRowCount());
174 RowInserted(0, m_pConnData
->GetConnLineDataList().size() + 1); // add one extra row
177 void ORelationControl::Resize()
179 EditBrowseBox::Resize();
180 tools::Long nOutputWidth
= GetOutputSizePixel().Width() - 1;
181 SetColumnWidth(1, (nOutputWidth
/ 2));
182 SetColumnWidth(2, (nOutputWidth
/ 2));
185 bool ORelationControl::PreNotify(NotifyEvent
& rNEvt
)
187 if (rNEvt
.GetType() == NotifyEventType::LOSEFOCUS
&& !HasChildPathFocus() && !ControlHasFocus())
188 PostUserEvent(LINK(this, ORelationControl
, AsynchDeactivate
), nullptr, true);
189 else if (rNEvt
.GetType() == NotifyEventType::GETFOCUS
)
190 PostUserEvent(LINK(this, ORelationControl
, AsynchActivate
), nullptr, true);
192 return EditBrowseBox::PreNotify(rNEvt
);
195 IMPL_LINK_NOARG(ORelationControl
, AsynchActivate
, void*, void)
200 IMPL_LINK_NOARG(ORelationControl
, AsynchDeactivate
, void*, void)
205 bool ORelationControl::IsTabAllowed(bool bForward
) const
207 sal_Int32 nRow
= GetCurRow();
208 sal_uInt16 nCol
= GetCurColumnId();
210 bool bRet
= !( ( bForward
&& (nCol
== DEST_COLUMN
) && (nRow
== GetRowCount() - 1))
211 || (!bForward
&& (nCol
== SOURCE_COLUMN
) && (nRow
== 0)));
213 return bRet
&& EditBrowseBox::IsTabAllowed(bForward
);
216 bool ORelationControl::SaveModified()
218 sal_Int32 nRow
= GetCurRow();
219 if ( nRow
!= BROWSER_ENDOFSELECTION
)
221 weld::ComboBox
& rListBox
= m_pListCell
->get_widget();
222 OUString
sFieldName(rListBox
.get_active_text());
223 OConnectionLineDataVec
& rLines
= m_pConnData
->GetConnLineDataList();
224 if ( rLines
.size() <= o3tl::make_unsigned(nRow
) )
226 rLines
.push_back(new OConnectionLineData());
227 nRow
= rLines
.size() - 1;
228 // add new past-rLines row
229 m_ops
.emplace_back(INSERT
, make_pair(nRow
+1, nRow
+2));
232 OConnectionLineDataRef pConnLineData
= rLines
[nRow
];
234 switch( getColumnIdent( GetCurColumnId() ) )
237 pConnLineData
->SetSourceFieldName( sFieldName
);
240 pConnLineData
->SetDestFieldName( sFieldName
);
243 // the modification we just did does *not* need to be registered in m_ops;
244 // it is already taken into account (by the codepath that called us)
245 //m_ops.push_back(make_pair(MODIFY, make_pair(nRow, nRow+1)));
248 const OConnectionLineDataVec::size_type oldSize
= m_pConnData
->GetConnLineDataList().size();
249 OConnectionLineDataVec::size_type line
= m_pConnData
->normalizeLines();
250 const OConnectionLineDataVec::size_type newSize
= m_pConnData
->GetConnLineDataList().size();
251 assert(newSize
<= oldSize
);
252 m_ops
.emplace_back(MODIFY
, make_pair(line
, newSize
));
253 m_ops
.emplace_back(DELETE
, make_pair(newSize
, oldSize
));
258 sal_uInt16
ORelationControl::getColumnIdent( sal_uInt16 _nColId
) const
260 sal_uInt16 nId
= _nColId
;
261 if ( m_pConnData
->getReferencingTable() != m_pBoxControl
->getReferencingTable() )
262 nId
= ( _nColId
== SOURCE_COLUMN
) ? DEST_COLUMN
: SOURCE_COLUMN
;
266 OUString
ORelationControl::GetCellText( sal_Int32 nRow
, sal_uInt16 nColId
) const
269 if ( m_pConnData
->GetConnLineDataList().size() > o3tl::make_unsigned(nRow
) )
271 OConnectionLineDataRef pConnLineData
= m_pConnData
->GetConnLineDataList()[nRow
];
272 switch( getColumnIdent( nColId
) )
275 sText
= pConnLineData
->GetSourceFieldName();
278 sText
= pConnLineData
->GetDestFieldName();
285 void ORelationControl::InitController( CellControllerRef
& /*rController*/, sal_Int32 nRow
, sal_uInt16 nColumnId
)
288 OUString
sHelpId( HID_RELATIONDIALOG_LEFTFIELDCELL
);
290 Reference
< XPropertySet
> xDef
;
291 switch ( getColumnIdent(nColumnId
) )
295 sHelpId
= HID_RELATIONDIALOG_LEFTFIELDCELL
;
299 sHelpId
= HID_RELATIONDIALOG_RIGHTFIELDCELL
;
310 OUString sName
= GetCellText( nRow
, nColumnId
);
311 weld::ComboBox
& rList
= m_pListCell
->get_widget();
312 rList
.set_active_text(sName
);
313 if (rList
.get_active_text() != sName
)
315 rList
.append_text(sName
);
316 rList
.set_active_text(sName
);
319 rList
.set_help_id(sHelpId
);
322 CellController
* ORelationControl::GetController( sal_Int32
/*nRow*/, sal_uInt16
/*nColumnId*/ )
324 return new ListBoxCellController( m_pListCell
.get() );
327 bool ORelationControl::SeekRow( sal_Int32 nRow
)
333 void ORelationControl::PaintCell( OutputDevice
& rDev
, const tools::Rectangle
& rRect
, sal_uInt16 nColumnId
) const
335 OUString aText
= GetCellText( m_nDataPos
, nColumnId
);
337 Point
aPos( rRect
.TopLeft() );
338 Size
aTextSize( GetDataWindow().GetTextWidth( aText
), GetDataWindow().GetTextHeight() );
340 if( aPos
.X() < rRect
.Left() || aPos
.X() + aTextSize
.Width() > rRect
.Right() ||
341 aPos
.Y() < rRect
.Top() || aPos
.Y() + aTextSize
.Height() > rRect
.Bottom() )
343 rDev
.SetClipRegion(vcl::Region(rRect
));
346 rDev
.DrawText( aPos
, aText
);
348 if( rDev
.IsClipRegion() )
349 rDev
.SetClipRegion();
351 void ORelationControl::fillListBox(const Reference
< XPropertySet
>& _xDest
)
353 weld::ComboBox
& rList
= m_pListCell
->get_widget();
359 //sal_Int32 nRows = GetRowCount();
360 Reference
<XColumnsSupplier
> xSup(_xDest
,UNO_QUERY
);
361 Reference
<XNameAccess
> xColumns
= xSup
->getColumns();
362 Sequence
< OUString
> aNames
= xColumns
->getElementNames();
363 const OUString
* pIter
= aNames
.getConstArray();
364 const OUString
* pEnd
= pIter
+ aNames
.getLength();
365 for(;pIter
!= pEnd
;++pIter
)
367 rList
.append_text(*pIter
);
369 rList
.insert_text(0, OUString());
372 catch( const Exception
& )
374 DBG_UNHANDLED_EXCEPTION("dbaccess");
377 void ORelationControl::setWindowTables(const OTableWindow
* _pSource
,const OTableWindow
* _pDest
)
379 // If I edit here, hide
380 bool bWasEditing
= IsEditing();
384 if ( _pSource
&& _pDest
)
386 m_xSourceDef
= _pSource
->GetTable();
387 SetColumnTitle(1, _pSource
->GetName());
389 m_xDestDef
= _pDest
->GetTable();
390 SetColumnTitle(2, _pDest
->GetName());
392 const OJoinTableView
* pView
= _pSource
->getTableView();
393 OTableConnection
* pConn
= pView
->GetTabConn(_pSource
,_pDest
);
394 if ( pConn
&& !m_pConnData
->GetConnLineDataList().empty() )
396 m_pConnData
->CopyFrom(*pConn
->GetData());
397 m_pBoxControl
->getContainer()->notifyConnectionChange();
401 // no connection found so we clear our data
402 OConnectionLineDataVec
& rLines
= m_pConnData
->GetConnLineDataList();
403 for( const auto& rLine
: rLines
)
408 m_pConnData
->setReferencingTable(_pSource
->GetData());
409 m_pConnData
->setReferencedTable(_pDest
->GetData());
411 m_pConnData
->normalizeLines();
424 void ORelationControl::CellModified()
426 EditBrowseBox::CellModified();
428 assert(m_pBoxControl
);
429 m_pBoxControl
->NotifyCellChange();
432 Size
ORelationControl::GetOptimalSize() const
434 return LogicToPixel(Size(140, 80), MapMode(MapUnit::MapAppFont
));
437 OTableListBoxControl::OTableListBoxControl(weld::Builder
* _pParent
,
438 const OJoinTableView::OTableWindowMap
* _pTableMap
,
439 IRelationControlInterface
* _pParentDialog
)
440 : m_xLeftTable(_pParent
->weld_combo_box("table1"))
441 , m_xRightTable(_pParent
->weld_combo_box("table2"))
442 , m_xTable(_pParent
->weld_container("relations"))
443 , m_xTableCtrlParent(m_xTable
->CreateChildFrame())
444 , m_xRC_Tables(VclPtr
<ORelationControl
>::Create(m_xTableCtrlParent
))
445 , m_pTableMap(_pTableMap
)
446 , m_pParentDialog(_pParentDialog
)
448 Size aPrefSize
= m_xRC_Tables
->GetOptimalSize();
449 m_xTable
->set_size_request(aPrefSize
.Width(), aPrefSize
.Height());
451 m_xRC_Tables
->SetController(this);
452 m_xRC_Tables
->Init();
456 Link
<weld::ComboBox
&,void> aLink(LINK(this, OTableListBoxControl
, OnTableChanged
));
457 m_xLeftTable
->connect_changed(aLink
);
458 m_xRightTable
->connect_changed(aLink
);
461 OTableListBoxControl::~OTableListBoxControl()
463 m_xRC_Tables
.disposeAndClear();
464 m_xTableCtrlParent
->dispose();
465 m_xTableCtrlParent
.clear();
468 void OTableListBoxControl::fillListBoxes()
470 OSL_ENSURE( !m_pTableMap
->empty(), "OTableListBoxControl::fillListBoxes: no table window!");
471 OTableWindow
* pInitialLeft
= nullptr;
472 OTableWindow
* pInitialRight
= nullptr;
474 // Collect the names of all TabWins
475 for (auto const& elem
: *m_pTableMap
)
477 m_xLeftTable
->append_text(elem
.first
);
478 m_xRightTable
->append_text(elem
.first
);
482 pInitialLeft
= elem
.second
;
483 m_strCurrentLeft
= elem
.first
;
485 else if (!pInitialRight
)
487 pInitialRight
= elem
.second
;
488 m_strCurrentRight
= elem
.first
;
492 if ( !pInitialRight
)
494 pInitialRight
= pInitialLeft
;
495 m_strCurrentRight
= m_strCurrentLeft
;
498 // The corresponding Defs for my Controls
499 m_xRC_Tables
->setWindowTables(pInitialLeft
,pInitialRight
);
501 // The table selected in a ComboBox must not be available in the other
503 if ( m_pTableMap
->size() > 2 )
505 m_xLeftTable
->remove_text(m_strCurrentRight
);
506 m_xRightTable
->remove_text(m_strCurrentLeft
);
509 // Select the first one on the left side and on the right side,
510 // select the second one
511 m_xLeftTable
->set_active_text(m_strCurrentLeft
);
512 m_xRightTable
->set_active_text(m_strCurrentRight
);
514 m_xLeftTable
->grab_focus();
517 IMPL_LINK(OTableListBoxControl
, OnTableChanged
, weld::ComboBox
&, rListBox
, void)
519 OUString
strSelected(rListBox
.get_active_text());
520 OTableWindow
* pLeft
= nullptr;
521 OTableWindow
* pRight
= nullptr;
523 // Special treatment: If there are only two tables, we need to switch the other one too when changing in a LB
524 if ( m_pTableMap
->size() == 2 )
526 weld::ComboBox
* pOther
;
527 if (&rListBox
== m_xLeftTable
.get())
528 pOther
= m_xRightTable
.get();
530 pOther
= m_xLeftTable
.get();
531 pOther
->set_active(1 - pOther
->get_active());
533 OJoinTableView::OTableWindowMap::const_iterator aIter
= m_pTableMap
->begin();
534 OTableWindow
* pFirst
= aIter
->second
;
536 OTableWindow
* pSecond
= aIter
->second
;
538 if (m_xLeftTable
->get_active_text() == pFirst
->GetName())
551 // First we need the TableDef to the Table and with it the TabWin
552 OJoinTableView::OTableWindowMap::const_iterator aFind
= m_pTableMap
->find(strSelected
);
553 OTableWindow
* pLoop
= nullptr;
554 if( aFind
!= m_pTableMap
->end() )
555 pLoop
= aFind
->second
;
556 OSL_ENSURE(pLoop
!= nullptr, "ORelationDialog::OnTableChanged: invalid ListBox entry!");
557 // We need to find strSelect, because we filled the ListBoxes with the table names with which we compare now
558 if (&rListBox
== m_xLeftTable
.get())
560 // Insert the previously selected Entry on the left side on the right side
561 m_xRightTable
->append_text(m_strCurrentLeft
);
562 // Remove the currently selected Entry
563 m_xRightTable
->remove_text(strSelected
);
564 m_strCurrentLeft
= strSelected
;
568 OJoinTableView::OTableWindowMap::const_iterator aIter
= m_pTableMap
->find(m_xRightTable
->get_active_text());
569 OSL_ENSURE( aIter
!= m_pTableMap
->end(), "Invalid name");
570 if ( aIter
!= m_pTableMap
->end() )
571 pRight
= aIter
->second
;
573 m_xLeftTable
->grab_focus();
577 // Insert the previously selected Entry on the right side on the left side
578 m_xLeftTable
->append_text(m_strCurrentRight
);
579 // Remove the currently selected Entry
580 m_xLeftTable
->remove_text(strSelected
);
581 m_strCurrentRight
= strSelected
;
584 OJoinTableView::OTableWindowMap::const_iterator aIter
= m_pTableMap
->find(m_xLeftTable
->get_active_text());
585 OSL_ENSURE( aIter
!= m_pTableMap
->end(), "Invalid name");
586 if ( aIter
!= m_pTableMap
->end() )
587 pLeft
= aIter
->second
;
591 rListBox
.grab_focus();
593 m_xRC_Tables
->setWindowTables(pLeft
,pRight
);
598 void OTableListBoxControl::NotifyCellChange()
600 // Enable/disable the OK button, depending on having a valid situation
601 TTableConnectionData::value_type pConnData
= m_xRC_Tables
->getData();
602 const OConnectionLineDataVec
& rLines
= pConnData
->GetConnLineDataList();
603 bool bValid
= !rLines
.empty();
606 for (auto const& line
: rLines
)
608 bValid
= ! (line
->GetSourceFieldName().isEmpty() || line
->GetDestFieldName().isEmpty());
613 m_pParentDialog
->setValid(bValid
);
615 m_xRC_Tables
->DeactivateCell();
616 for (auto const& elem
: m_xRC_Tables
->m_ops
)
620 case ORelationControl::DELETE
:
621 m_xRC_Tables
->RowRemoved(elem
.second
.first
, elem
.second
.second
- elem
.second
.first
);
623 case ORelationControl::INSERT
:
624 m_xRC_Tables
->RowInserted(elem
.second
.first
, elem
.second
.second
- elem
.second
.first
);
626 case ORelationControl::MODIFY
:
627 for(OConnectionLineDataVec::size_type j
= elem
.second
.first
; j
< elem
.second
.second
; ++j
)
628 m_xRC_Tables
->RowModified(j
);
632 m_xRC_Tables
->ActivateCell();
633 m_xRC_Tables
->m_ops
.clear();
636 static void fillEntryAndDisable(weld::ComboBox
& _rListBox
,const OUString
& _sEntry
)
638 _rListBox
.append_text(_sEntry
);
639 _rListBox
.set_active(0);
640 _rListBox
.set_sensitive(false);
643 void OTableListBoxControl::fillAndDisable(const TTableConnectionData::value_type
& _pConnectionData
)
645 fillEntryAndDisable(*m_xLeftTable
, _pConnectionData
->getReferencingTable()->GetWinName());
646 fillEntryAndDisable(*m_xRightTable
, _pConnectionData
->getReferencedTable()->GetWinName());
649 void OTableListBoxControl::Init(const TTableConnectionData::value_type
& _pConnData
)
651 m_xRC_Tables
->Init(_pConnData
);
654 void OTableListBoxControl::lateUIInit()
656 m_xRC_Tables
->Show();
660 void OTableListBoxControl::lateInit()
662 m_xRC_Tables
->lateInit();
665 void OTableListBoxControl::Disable()
667 m_xLeftTable
->set_sensitive(false);
668 m_xRightTable
->set_sensitive(false);
669 m_xRC_Tables
->Disable();
672 void OTableListBoxControl::Invalidate()
674 m_xRC_Tables
->Invalidate();
677 void OTableListBoxControl::SaveModified()
679 m_xRC_Tables
->SaveModified();
682 TTableWindowData::value_type
const & OTableListBoxControl::getReferencingTable() const
684 return m_xRC_Tables
->getData()->getReferencingTable();
687 void OTableListBoxControl::enableRelation(bool _bEnable
)
690 m_xRC_Tables
->PostUserEvent(LINK(m_xRC_Tables
, ORelationControl
, AsynchDeactivate
));
691 m_xRC_Tables
->Enable(_bEnable
);
695 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */