tdf#130857 qt weld: Implement QtInstanceWidget::get_text_height
[LibreOffice.git] / dbaccess / source / ui / control / RelationControl.cxx
blob4856b0c3b188e34941f0d1152644d42f1938f460
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
33 #include <helpids.h>
34 #include <o3tl/safeint.hxx>
35 #include <osl/diagnose.h>
37 #include <vector>
38 #include <utility>
39 using std::pair;
40 using std::make_pair;
42 #define SOURCE_COLUMN 1
43 #define DEST_COLUMN 2
45 namespace dbaui
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;
51 using namespace svt;
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;
66 ops_type m_ops;
68 void fillListBox(const Reference< XPropertySet>& _xDest);
69 /** returns the column id for the editbrowsebox
70 @param _nColId
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;
76 public:
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
84 @param _pSource
85 the left table
86 @param _pDest
87 the right window
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; }
97 void lateInit();
99 protected:
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 );
120 private:
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)
132 , m_nDataPos(0)
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()
147 if ( !m_pConnData )
148 return;
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() ) );
160 // set browse mode
161 SetMode( BrowserMode::COLUMNSELECTION |
162 BrowserMode::HLINES |
163 BrowserMode::VLINES |
164 BrowserMode::HIDECURSOR |
165 BrowserMode::HIDESELECT |
166 BrowserMode::AUTO_HSCROLL |
167 BrowserMode::AUTO_VSCROLL);
169 else
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)
196 ActivateCell();
199 IMPL_LINK_NOARG(ORelationControl, AsynchDeactivate, void*, void)
201 DeactivateCell();
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() ) )
235 case SOURCE_COLUMN:
236 pConnLineData->SetSourceFieldName( sFieldName );
237 break;
238 case DEST_COLUMN:
239 pConnLineData->SetDestFieldName( sFieldName );
240 break;
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));
254 return true;
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;
262 return nId;
265 OUString ORelationControl::GetCellText( sal_Int32 nRow, sal_uInt16 nColId ) const
267 OUString sText;
268 if ( m_pConnData->GetConnLineDataList().size() > o3tl::make_unsigned(nRow) )
270 OConnectionLineDataRef pConnLineData = m_pConnData->GetConnLineDataList()[nRow];
271 switch( getColumnIdent( nColId ) )
273 case SOURCE_COLUMN:
274 sText = pConnLineData->GetSourceFieldName();
275 break;
276 case DEST_COLUMN:
277 sText = pConnLineData->GetDestFieldName();
278 break;
281 return sText;
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) )
292 case SOURCE_COLUMN:
293 xDef = m_xSourceDef;
294 sHelpId = HID_RELATIONDIALOG_LEFTFIELDCELL;
295 break;
296 case DEST_COLUMN:
297 xDef = m_xDestDef;
298 sHelpId = HID_RELATIONDIALOG_RIGHTFIELDCELL;
299 break;
300 default:
301 // ?????????
302 break;
305 if ( !xDef.is() )
306 return;
308 fillListBox(xDef);
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 )
328 m_nDataPos = nRow;
329 return true;
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();
353 rList.clear();
356 if ( _xDest.is() )
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();
377 if ( bWasEditing )
378 DeactivateCell();
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();
395 else
397 // no connection found so we clear our data
398 OConnectionLineDataVec& rLines = m_pConnData->GetConnLineDataList();
399 for( const auto& rLine : rLines )
401 rLine->Reset();
404 m_pConnData->setReferencingTable(_pSource->GetData());
405 m_pConnData->setReferencedTable(_pDest->GetData());
407 m_pConnData->normalizeLines();
410 // Repaint
411 Invalidate();
413 if ( bWasEditing )
415 GoToRow(0);
416 ActivateCell();
420 void ORelationControl::CellModified()
422 EditBrowseBox::CellModified();
423 SaveModified();
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();
450 lateUIInit();
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);
476 if (!pInitialLeft)
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();
525 else
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;
531 ++aIter;
532 OTableWindow* pSecond = aIter->second;
534 if (m_xLeftTable->get_active_text() == pFirst->GetName())
536 pLeft = pFirst;
537 pRight = pSecond;
539 else
541 pLeft = pSecond;
542 pRight = pFirst;
545 else
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;
562 pLeft = pLoop;
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();
571 else
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;
579 pRight = pLoop;
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);
591 NotifyCellChange();
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();
600 if (bValid)
602 for (auto const& line : rLines)
604 bValid = ! (line->GetSourceFieldName().isEmpty() || line->GetDestFieldName().isEmpty());
605 if (!bValid)
606 break;
609 m_pParentDialog->setValid(bValid);
611 m_xRC_Tables->DeactivateCell();
612 for (auto const& elem : m_xRC_Tables->m_ops)
614 switch(elem.first)
616 case ORelationControl::DELETE:
617 m_xRC_Tables->RowRemoved(elem.second.first, elem.second.second - elem.second.first);
618 break;
619 case ORelationControl::INSERT:
620 m_xRC_Tables->RowInserted(elem.second.first, elem.second.second - elem.second.first);
621 break;
622 case ORelationControl::MODIFY:
623 for(OConnectionLineDataVec::size_type j = elem.second.first; j < elem.second.second; ++j)
624 m_xRC_Tables->RowModified(j);
625 break;
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();
653 lateInit();
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)
685 if ( !_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: */