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 <tools/diagnose_ex.h>
25 #include <vcl/builderfactory.hxx>
26 #include "TableConnectionData.hxx"
27 #include "TableConnection.hxx"
28 #include "TableWindow.hxx"
29 #include <com/sun/star/sdbc/XDatabaseMetaData.hpp>
30 #include "UITools.hxx"
31 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
32 #include <com/sun/star/container/XNameAccess.hpp>
33 #include "RelControliFace.hxx"
34 #include "dbu_control.hrc"
35 #include "dbaccess_helpid.hrc"
36 #include <osl/diagnose.h>
44 #define SOURCE_COLUMN 1
49 using namespace ::com::sun::star::uno
;
50 using namespace ::com::sun::star::beans
;
51 using namespace ::com::sun::star::sdbc
;
52 using namespace ::com::sun::star::sdbcx
;
53 using namespace ::com::sun::star::container
;
56 typedef ::svt::EditBrowseBox ORelationControl_Base
;
57 class ORelationControl
: public ORelationControl_Base
59 friend class OTableListBoxControl
;
61 VclPtr
< ::svt::ListBoxControl
> m_pListCell
;
62 TTableConnectionData::value_type m_pConnData
;
63 OTableListBoxControl
* m_pBoxControl
;
65 Reference
< XPropertySet
> m_xSourceDef
;
66 Reference
< XPropertySet
> m_xDestDef
;
67 enum opcode
{ DELETE
, INSERT
, MODIFY
};
68 typedef list
< pair
< opcode
, pair
< OConnectionLineDataVec::size_type
, OConnectionLineDataVec::size_type
> > > ops_type
;
71 void fillListBox(const Reference
< XPropertySet
>& _xDest
,long nRow
,sal_uInt16 nColumnId
);
72 /** returns the column id for the editbrowsebox
74 the column id SOURCE_COLUMN or DEST_COLUMN
76 @return the current column id either SOURCE_COLUMN or DEST_COLUMN depends on the connection data
78 sal_uInt16
getColumnIdent( sal_uInt16 _nColId
) const;
80 explicit ORelationControl(vcl::Window
*pParent
);
81 void SetController(OTableListBoxControl
* pController
)
83 m_pBoxControl
= pController
;
86 /** searches for a connection between these two tables
92 void setWindowTables(const OTableWindow
* _pSource
,const OTableWindow
* _pDest
);
94 /** allows to access the connection data from outside
96 @return the connection data
98 const TTableConnectionData::value_type
& getData() const { return m_pConnData
; }
103 virtual ~ORelationControl() { disposeOnce(); }
104 virtual void dispose() override
{ m_pListCell
.disposeAndClear(); ORelationControl_Base::dispose(); }
105 virtual void Resize() override
;
106 virtual Size
GetOptimalSize() const override
;
107 virtual bool PreNotify(NotifyEvent
& rNEvt
) override
;
109 virtual bool IsTabAllowed(bool bForward
) const override
;
111 void Init(const TTableConnectionData::value_type
& _pConnData
);
112 virtual void Init() override
{ ORelationControl_Base::Init(); }
113 virtual void InitController( ::svt::CellControllerRef
& rController
, long nRow
, sal_uInt16 nCol
) override
;
114 virtual ::svt::CellController
* GetController( long nRow
, sal_uInt16 nCol
) override
;
115 virtual void PaintCell( OutputDevice
& rDev
, const Rectangle
& rRect
, sal_uInt16 nColId
) const override
;
116 virtual bool SeekRow( long nRow
) override
;
117 virtual bool SaveModified() override
;
118 virtual OUString
GetCellText( long nRow
, sal_uInt16 nColId
) const override
;
120 virtual void CellModified() override
;
122 DECL_LINK_TYPED( AsynchDeactivate
, void*, void );
125 DECL_LINK_TYPED( AsynchActivate
, void*, void );
129 // class ORelationControl
130 ORelationControl::ORelationControl(vcl::Window
*pParent
)
131 : EditBrowseBox(pParent
,
132 EditBrowseBoxFlags::SMART_TAB_TRAVEL
| EditBrowseBoxFlags::NO_HANDLE_COLUMN_CONTENT
,
133 WB_TABSTOP
| WB_BORDER
,
134 BrowserMode::AUTOSIZE_LASTCOL
)
135 , m_pBoxControl(nullptr)
137 , m_xSourceDef(nullptr)
138 , m_xDestDef(nullptr)
142 VCL_BUILDER_FACTORY(ORelationControl
)
144 void ORelationControl::Init(const TTableConnectionData::value_type
& _pConnData
)
147 m_pConnData
= _pConnData
;
148 OSL_ENSURE(m_pConnData
, "No data supplied!");
150 m_pConnData
->normalizeLines();
153 void ORelationControl::lateInit()
155 if ( !m_pConnData
.get() )
157 m_xSourceDef
= m_pConnData
->getReferencingTable()->getTable();
158 m_xDestDef
= m_pConnData
->getReferencedTable()->getTable();
160 if ( ColCount() == 0 )
162 InsertDataColumn( SOURCE_COLUMN
, m_pConnData
->getReferencingTable()->GetWinName(), 100);
163 InsertDataColumn( DEST_COLUMN
, m_pConnData
->getReferencedTable()->GetWinName(), 100);
164 // If the Defs do not yet exits, we need to set them with SetSource-/-DestDef
166 m_pListCell
.reset( VclPtr
<ListBoxControl
>::Create( &GetDataWindow() ) );
169 SetMode( BrowserMode::COLUMNSELECTION
|
170 BrowserMode::HLINES
|
171 BrowserMode::VLINES
|
172 BrowserMode::HIDECURSOR
|
173 BrowserMode::HIDESELECT
|
174 BrowserMode::AUTO_HSCROLL
|
175 BrowserMode::AUTO_VSCROLL
);
178 // not the first call
179 RowRemoved(0, GetRowCount());
181 RowInserted(0, m_pConnData
->GetConnLineDataList().size() + 1); // add one extra row
184 void ORelationControl::Resize()
186 EditBrowseBox::Resize();
187 long nOutputWidth
= GetOutputSizePixel().Width() - 1;
188 SetColumnWidth(1, (nOutputWidth
/ 2));
189 SetColumnWidth(2, (nOutputWidth
/ 2));
192 bool ORelationControl::PreNotify(NotifyEvent
& rNEvt
)
194 if (rNEvt
.GetType() == MouseNotifyEvent::LOSEFOCUS
&& !HasChildPathFocus() )
195 PostUserEvent(LINK(this, ORelationControl
, AsynchDeactivate
), nullptr, true);
196 else if (rNEvt
.GetType() == MouseNotifyEvent::GETFOCUS
)
197 PostUserEvent(LINK(this, ORelationControl
, AsynchActivate
), nullptr, true);
199 return EditBrowseBox::PreNotify(rNEvt
);
202 IMPL_LINK_NOARG_TYPED(ORelationControl
, AsynchActivate
, void*, void)
207 IMPL_LINK_NOARG_TYPED(ORelationControl
, AsynchDeactivate
, void*, void)
212 bool ORelationControl::IsTabAllowed(bool bForward
) const
214 long nRow
= GetCurRow();
215 sal_uInt16 nCol
= GetCurColumnId();
217 bool bRet
= !(( ( bForward
&& (nCol
== DEST_COLUMN
) && (nRow
== GetRowCount() - 1)))
218 || (!bForward
&& (nCol
== SOURCE_COLUMN
) && (nRow
== 0)));
220 return bRet
&& EditBrowseBox::IsTabAllowed(bForward
);
223 bool ORelationControl::SaveModified()
225 long nRow
= GetCurRow();
226 if ( nRow
!= BROWSER_ENDOFSELECTION
)
228 OUString
sFieldName(m_pListCell
->GetSelectEntry());
229 OConnectionLineDataVec
& rLines
= m_pConnData
->GetConnLineDataList();
230 if ( rLines
.size() <= static_cast<OConnectionLineDataVec::size_type
>(nRow
) )
232 rLines
.push_back(new OConnectionLineData());
233 nRow
= rLines
.size() - 1;
234 // add new past-rLines row
235 m_ops
.push_back(make_pair(INSERT
, make_pair(nRow
+1, nRow
+2)));
238 OConnectionLineDataRef pConnLineData
= rLines
[nRow
];
240 switch( getColumnIdent( GetCurColumnId() ) )
243 pConnLineData
->SetSourceFieldName( sFieldName
);
246 pConnLineData
->SetDestFieldName( sFieldName
);
249 // the modification we just did does *not* need to be registered in m_ops;
250 // it is already taken into account (by the codepath that called us)
251 //m_ops.push_back(make_pair(MODIFY, make_pair(nRow, nRow+1)));
254 const OConnectionLineDataVec::size_type oldSize
= m_pConnData
->GetConnLineDataList().size();
255 OConnectionLineDataVec::size_type line
= m_pConnData
->normalizeLines();
256 const OConnectionLineDataVec::size_type newSize
= m_pConnData
->GetConnLineDataList().size();
257 assert(newSize
<= oldSize
);
258 m_ops
.push_back(make_pair(MODIFY
, make_pair(line
, newSize
)));
259 m_ops
.push_back(make_pair(DELETE
, make_pair(newSize
, oldSize
)));
264 sal_uInt16
ORelationControl::getColumnIdent( sal_uInt16 _nColId
) const
266 sal_uInt16 nId
= _nColId
;
267 if ( m_pConnData
->getReferencingTable() != m_pBoxControl
->getReferencingTable() )
268 nId
= ( _nColId
== SOURCE_COLUMN
) ? DEST_COLUMN
: SOURCE_COLUMN
;
272 OUString
ORelationControl::GetCellText( long nRow
, sal_uInt16 nColId
) const
275 if ( m_pConnData
->GetConnLineDataList().size() > static_cast<size_t>(nRow
) )
277 OConnectionLineDataRef pConnLineData
= m_pConnData
->GetConnLineDataList()[nRow
];
278 switch( getColumnIdent( nColId
) )
281 sText
= pConnLineData
->GetSourceFieldName();
284 sText
= pConnLineData
->GetDestFieldName();
291 void ORelationControl::InitController( CellControllerRef
& /*rController*/, long nRow
, sal_uInt16 nColumnId
)
294 OString
sHelpId( HID_RELATIONDIALOG_LEFTFIELDCELL
);
296 Reference
< XPropertySet
> xDef
;
297 switch ( getColumnIdent(nColumnId
) )
301 sHelpId
= HID_RELATIONDIALOG_LEFTFIELDCELL
;
305 sHelpId
= HID_RELATIONDIALOG_RIGHTFIELDCELL
;
314 fillListBox(xDef
,nRow
,nColumnId
);
315 OUString sName
= GetCellText( nRow
, nColumnId
);
316 m_pListCell
->SelectEntry( sName
);
317 if ( m_pListCell
->GetSelectEntry() != sName
)
319 m_pListCell
->InsertEntry( sName
);
320 m_pListCell
->SelectEntry( sName
);
323 m_pListCell
->SetHelpId(sHelpId
);
327 CellController
* ORelationControl::GetController( long /*nRow*/, sal_uInt16
/*nColumnId*/ )
329 return new ListBoxCellController( m_pListCell
.get() );
332 bool ORelationControl::SeekRow( long nRow
)
338 void ORelationControl::PaintCell( OutputDevice
& rDev
, const Rectangle
& rRect
, sal_uInt16 nColumnId
) const
340 OUString aText
= GetCellText( m_nDataPos
, nColumnId
);
342 Point
aPos( rRect
.TopLeft() );
343 Size
aTextSize( GetDataWindow().GetTextWidth( aText
), GetDataWindow().GetTextHeight() );
345 if( aPos
.X() < rRect
.Left() || aPos
.X() + aTextSize
.Width() > rRect
.Right() ||
346 aPos
.Y() < rRect
.Top() || aPos
.Y() + aTextSize
.Height() > rRect
.Bottom() )
348 rDev
.SetClipRegion(vcl::Region(rRect
));
351 rDev
.DrawText( aPos
, aText
);
353 if( rDev
.IsClipRegion() )
354 rDev
.SetClipRegion();
356 void ORelationControl::fillListBox(const Reference
< XPropertySet
>& _xDest
,long /*_nRow*/,sal_uInt16
/*nColumnId*/)
358 m_pListCell
->Clear();
363 //sal_Int32 nRows = GetRowCount();
364 Reference
<XColumnsSupplier
> xSup(_xDest
,UNO_QUERY
);
365 Reference
<XNameAccess
> xColumns
= xSup
->getColumns();
366 Sequence
< OUString
> aNames
= xColumns
->getElementNames();
367 const OUString
* pIter
= aNames
.getConstArray();
368 const OUString
* pEnd
= pIter
+ aNames
.getLength();
369 for(;pIter
!= pEnd
;++pIter
)
371 m_pListCell
->InsertEntry( *pIter
);
373 m_pListCell
->InsertEntry(OUString(), 0);
376 catch( const Exception
& )
378 DBG_UNHANDLED_EXCEPTION();
381 void ORelationControl::setWindowTables(const OTableWindow
* _pSource
,const OTableWindow
* _pDest
)
383 // If I edit here, hide
384 bool bWasEditing
= IsEditing();
388 if ( _pSource
&& _pDest
)
390 m_xSourceDef
= _pSource
->GetTable();
391 SetColumnTitle(1, _pSource
->GetName());
393 m_xDestDef
= _pDest
->GetTable();
394 SetColumnTitle(2, _pDest
->GetName());
396 const OJoinTableView
* pView
= _pSource
->getTableView();
397 OTableConnection
* pConn
= pView
->GetTabConn(_pSource
,_pDest
);
398 if ( pConn
&& !m_pConnData
->GetConnLineDataList().empty() )
400 m_pConnData
->CopyFrom(*pConn
->GetData());
401 m_pBoxControl
->getContainer()->notifyConnectionChange();
405 // no connection found so we clear our data
406 OConnectionLineDataVec
& rLines
= m_pConnData
->GetConnLineDataList();
407 for( const auto& rLine
: rLines
)
409 rLine
.get()->Reset();
412 m_pConnData
->setReferencingTable(_pSource
->GetData());
413 m_pConnData
->setReferencedTable(_pDest
->GetData());
415 m_pConnData
->normalizeLines();
428 void ORelationControl::CellModified()
430 EditBrowseBox::CellModified();
432 assert(m_pBoxControl
);
433 m_pBoxControl
->NotifyCellChange();
436 Size
ORelationControl::GetOptimalSize() const
438 return LogicToPixel(Size(140, 80), MAP_APPFONT
);
441 // class OTableListBoxControl
442 OTableListBoxControl::OTableListBoxControl(VclBuilderContainer
* _pParent
,
443 const OJoinTableView::OTableWindowMap
* _pTableMap
,
444 IRelationControlInterface
* _pParentDialog
)
445 : m_pTableMap(_pTableMap
)
446 , m_pParentDialog(_pParentDialog
)
448 _pParent
->get(m_pLeftTable
, "table1");
449 _pParent
->get(m_pRightTable
, "table2");
451 _pParent
->get(m_pRC_Tables
, "relations");
452 m_pRC_Tables
->SetController(this);
453 m_pRC_Tables
->Init();
457 Link
<ListBox
&,void> aLink(LINK(this, OTableListBoxControl
, OnTableChanged
));
458 m_pLeftTable
->SetSelectHdl(aLink
);
459 m_pRightTable
->SetSelectHdl(aLink
);
462 OTableListBoxControl::~OTableListBoxControl()
466 void OTableListBoxControl::fillListBoxes()
468 OSL_ENSURE( !m_pTableMap
->empty(), "OTableListBoxControl::fillListBoxes: no table window!");
469 OTableWindow
* pInitialLeft
= nullptr;
470 OTableWindow
* pInitialRight
= nullptr;
472 // Collect the names of all TabWins
473 OJoinTableView::OTableWindowMap::const_iterator aIter
= m_pTableMap
->begin();
474 OJoinTableView::OTableWindowMap::const_iterator aEnd
= m_pTableMap
->end();
475 for(;aIter
!= aEnd
;++aIter
)
477 m_pLeftTable
->InsertEntry(aIter
->first
);
478 m_pRightTable
->InsertEntry(aIter
->first
);
482 pInitialLeft
= aIter
->second
;
483 m_strCurrentLeft
= aIter
->first
;
485 else if (!pInitialRight
)
487 pInitialRight
= aIter
->second
;
488 m_strCurrentRight
= aIter
->first
;
492 if ( !pInitialRight
)
494 pInitialRight
= pInitialLeft
;
495 m_strCurrentRight
= m_strCurrentLeft
;
498 // The corresponding Defs for my Controls
499 m_pRC_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_pLeftTable
->RemoveEntry(m_strCurrentRight
);
506 m_pRightTable
->RemoveEntry(m_strCurrentLeft
);
509 // Select the first one on the left side and on the right side,
510 // select the second one
511 m_pLeftTable
->SelectEntry(m_strCurrentLeft
);
512 m_pRightTable
->SelectEntry(m_strCurrentRight
);
514 m_pLeftTable
->GrabFocus();
517 IMPL_LINK_TYPED( OTableListBoxControl
, OnTableChanged
, ListBox
&, rListBox
, void )
519 OUString
strSelected(rListBox
.GetSelectEntry());
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 )
527 if (&rListBox
== m_pLeftTable
)
528 pOther
= m_pRightTable
;
530 pOther
= m_pLeftTable
;
531 pOther
->SelectEntryPos(1 - pOther
->GetSelectEntryPos());
533 OJoinTableView::OTableWindowMap::const_iterator aIter
= m_pTableMap
->begin();
534 OTableWindow
* pFirst
= aIter
->second
;
536 OTableWindow
* pSecond
= aIter
->second
;
538 if ( m_pLeftTable
->GetSelectEntry() == 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_pLeftTable
)
560 // Insert the previously selected Entry on the left side on the right side
561 m_pRightTable
->InsertEntry(m_strCurrentLeft
);
562 // Remove the currently selected Entry
563 m_pRightTable
->RemoveEntry(strSelected
);
564 m_strCurrentLeft
= strSelected
;
568 OJoinTableView::OTableWindowMap::const_iterator aIter
= m_pTableMap
->find(m_pRightTable
->GetSelectEntry());
569 OSL_ENSURE( aIter
!= m_pTableMap
->end(), "Invalid name");
570 if ( aIter
!= m_pTableMap
->end() )
571 pRight
= aIter
->second
;
573 m_pLeftTable
->GrabFocus();
577 // Insert the previously selected Entry on the right side on the left side
578 m_pLeftTable
->InsertEntry(m_strCurrentRight
);
579 // Remove the currently selected Entry
580 m_pLeftTable
->RemoveEntry(strSelected
);
581 m_strCurrentRight
= strSelected
;
584 OJoinTableView::OTableWindowMap::const_iterator aIter
= m_pTableMap
->find(m_pLeftTable
->GetSelectEntry());
585 OSL_ENSURE( aIter
!= m_pTableMap
->end(), "Invalid name");
586 if ( aIter
!= m_pTableMap
->end() )
587 pLeft
= aIter
->second
;
591 rListBox
.GrabFocus();
593 m_pRC_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_pRC_Tables
->getData();
602 const OConnectionLineDataVec
& rLines
= pConnData
->GetConnLineDataList();
603 bool bValid
= !rLines
.empty();
606 OConnectionLineDataVec::const_iterator
l(rLines
.begin());
607 const OConnectionLineDataVec::const_iterator
le(rLines
.end());
608 for (; bValid
&& l
!=le
; ++l
)
610 bValid
= ! ((*l
)->GetSourceFieldName().isEmpty() || (*l
)->GetDestFieldName().isEmpty());
613 m_pParentDialog
->setValid(bValid
);
615 ORelationControl::ops_type::const_iterator
i (m_pRC_Tables
->m_ops
.begin());
616 const ORelationControl::ops_type::const_iterator
e (m_pRC_Tables
->m_ops
.end());
617 m_pRC_Tables
->DeactivateCell();
622 case ORelationControl::DELETE
:
623 m_pRC_Tables
->RowRemoved(i
->second
.first
, i
->second
.second
- i
->second
.first
);
625 case ORelationControl::INSERT
:
626 m_pRC_Tables
->RowInserted(i
->second
.first
, i
->second
.second
- i
->second
.first
);
628 case ORelationControl::MODIFY
:
629 for(OConnectionLineDataVec::size_type j
= i
->second
.first
; j
< i
->second
.second
; ++j
)
630 m_pRC_Tables
->RowModified(j
);
634 m_pRC_Tables
->ActivateCell();
635 m_pRC_Tables
->m_ops
.clear();
638 void fillEntryAndDisable(ListBox
& _rListBox
,const OUString
& _sEntry
)
640 _rListBox
.InsertEntry(_sEntry
);
641 _rListBox
.SelectEntryPos(0);
645 void OTableListBoxControl::fillAndDisable(const TTableConnectionData::value_type
& _pConnectionData
)
647 fillEntryAndDisable(*m_pLeftTable
, _pConnectionData
->getReferencingTable()->GetWinName());
648 fillEntryAndDisable(*m_pRightTable
, _pConnectionData
->getReferencedTable()->GetWinName());
651 void OTableListBoxControl::Init(const TTableConnectionData::value_type
& _pConnData
)
653 m_pRC_Tables
->Init(_pConnData
);
656 void OTableListBoxControl::lateUIInit()
658 m_pRC_Tables
->Show();
662 void OTableListBoxControl::lateInit()
664 m_pRC_Tables
->lateInit();
667 void OTableListBoxControl::Disable()
669 m_pLeftTable
->Disable();
670 m_pRightTable
->Disable();
671 m_pRC_Tables
->Disable();
674 void OTableListBoxControl::Invalidate()
676 m_pLeftTable
->Invalidate();
677 m_pRightTable
->Invalidate();
678 m_pRC_Tables
->Invalidate();
681 void OTableListBoxControl::SaveModified()
683 m_pRC_Tables
->SaveModified();
686 TTableWindowData::value_type
OTableListBoxControl::getReferencingTable() const
688 return m_pRC_Tables
->getData()->getReferencingTable();
691 void OTableListBoxControl::enableRelation(bool _bEnable
)
694 m_pRC_Tables
->PostUserEvent(LINK(m_pRC_Tables
, ORelationControl
, AsynchDeactivate
));
695 m_pRC_Tables
->Enable(_bEnable
);
700 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */