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 <WColumnSelect.hxx>
21 #include <strings.hrc>
22 #include <osl/diagnose.h>
23 #include <WCopyTable.hxx>
24 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
25 #include <core_resource.hxx>
26 #include <com/sun/star/sdb/application/CopyTableOperation.hpp>
28 using namespace ::com::sun::star::uno
;
29 using namespace ::com::sun::star::sdbc
;
30 using namespace dbaui
;
32 namespace CopyTableOperation
= ::com::sun::star::sdb::application::CopyTableOperation
;
34 OUString
OWizColumnSelect::GetTitle() const { return DBA_RES(STR_WIZ_COLUMN_SELECT_TITLE
); }
36 OWizardPage::OWizardPage(weld::Container
* pPage
, OCopyTableWizard
* pWizard
, const OUString
& rUIXMLDescription
, const OUString
& rID
)
37 : ::vcl::OWizardPage(pPage
, pWizard
, rUIXMLDescription
, rID
)
43 OWizardPage::~OWizardPage()
48 OWizColumnSelect::OWizColumnSelect(weld::Container
* pPage
, OCopyTableWizard
* pWizard
)
49 : OWizardPage(pPage
, pWizard
, u
"dbaccess/ui/applycolpage.ui"_ustr
, u
"ApplyColPage"_ustr
)
50 , m_xOrgColumnNames(m_xBuilder
->weld_tree_view(u
"from"_ustr
))
51 , m_xColumn_RH(m_xBuilder
->weld_button(u
"colrh"_ustr
))
52 , m_xColumns_RH(m_xBuilder
->weld_button(u
"colsrh"_ustr
))
53 , m_xColumn_LH(m_xBuilder
->weld_button(u
"collh"_ustr
))
54 , m_xColumns_LH(m_xBuilder
->weld_button(u
"colslh"_ustr
))
55 , m_xNewColumnNames(m_xBuilder
->weld_tree_view(u
"to"_ustr
))
57 m_xColumn_RH
->connect_clicked(LINK(this,OWizColumnSelect
,ButtonClickHdl
));
58 m_xColumn_LH
->connect_clicked(LINK(this,OWizColumnSelect
,ButtonClickHdl
));
59 m_xColumns_RH
->connect_clicked(LINK(this,OWizColumnSelect
,ButtonClickHdl
));
60 m_xColumns_LH
->connect_clicked(LINK(this,OWizColumnSelect
,ButtonClickHdl
));
62 m_xOrgColumnNames
->set_selection_mode(SelectionMode::Multiple
);
63 m_xNewColumnNames
->set_selection_mode(SelectionMode::Multiple
);
65 m_xOrgColumnNames
->connect_row_activated(LINK(this,OWizColumnSelect
,ListDoubleClickHdl
));
66 m_xNewColumnNames
->connect_row_activated(LINK(this,OWizColumnSelect
,ListDoubleClickHdl
));
69 OWizColumnSelect::~OWizColumnSelect()
71 while (m_xNewColumnNames
->n_children())
73 delete weld::fromId
<OFieldDescription
*>(m_xNewColumnNames
->get_id(0));
74 m_xNewColumnNames
->remove(0);
78 void OWizColumnSelect::Reset()
80 // restore original state
81 clearListBox(*m_xOrgColumnNames
);
82 clearListBox(*m_xNewColumnNames
);
83 m_pParent
->m_mNameMapping
.clear();
85 // insert the source columns in the left listbox
86 const ODatabaseExport::TColumnVector
& rSrcColumns
= m_pParent
->getSrcVector();
88 for (auto const& column
: rSrcColumns
)
90 OUString
sId(weld::toId(column
->second
));
91 m_xOrgColumnNames
->append(sId
, column
->first
);
94 if (m_xOrgColumnNames
->n_children())
95 m_xOrgColumnNames
->select(0);
100 void OWizColumnSelect::Activate( )
102 // if there are no dest columns reset the left side with the original columns
103 if(m_pParent
->getDestColumns().empty())
106 clearListBox(*m_xNewColumnNames
);
108 const ODatabaseExport::TColumnVector
& rDestColumns
= m_pParent
->getDestVector();
110 // tdf#113923, the added columns must exist in the table
111 // in the case where:
112 // 1: we enabled the creation of a primary key
113 // 2: we come back here from the "Back" button of the next page,
114 // we want to avoid to list the new column generated in the next page
115 const ODatabaseExport::TColumns
& rSrcColumns
= m_pParent
->getSourceColumns();
117 for (auto const& column
: rDestColumns
)
119 if (rSrcColumns
.find(column
->first
) != rSrcColumns
.end())
121 OUString
sId(weld::toId(new OFieldDescription(*(column
->second
))));
122 m_xNewColumnNames
->append(sId
, column
->first
);
123 int nRemove
= m_xOrgColumnNames
->find_text(column
->first
);
125 m_xOrgColumnNames
->remove(nRemove
);
128 m_pParent
->GetOKButton().set_sensitive(m_xNewColumnNames
->n_children() != 0);
129 m_pParent
->EnableNextButton(m_xNewColumnNames
->n_children() && m_pParent
->getOperation() != CopyTableOperation::AppendData
);
130 m_xColumns_RH
->grab_focus();
133 bool OWizColumnSelect::LeavePage()
136 m_pParent
->clearDestColumns();
138 for(sal_Int32 i
=0 ; i
< m_xNewColumnNames
->n_children();++i
)
140 OFieldDescription
* pField
= weld::fromId
<OFieldDescription
*>(m_xNewColumnNames
->get_id(i
));
141 OSL_ENSURE(pField
,"The field information can not be null!");
142 m_pParent
->insertColumn(i
,pField
);
145 clearListBox(*m_xNewColumnNames
);
147 if ( m_pParent
->GetPressedButton() == OCopyTableWizard::WIZARD_NEXT
148 || m_pParent
->GetPressedButton() == OCopyTableWizard::WIZARD_FINISH
150 return !m_pParent
->getDestColumns().empty();
155 IMPL_LINK(OWizColumnSelect
, ButtonClickHdl
, weld::Button
&, rButton
, void)
157 weld::TreeView
*pLeft
= nullptr;
158 weld::TreeView
*pRight
= nullptr;
161 if (&rButton
== m_xColumn_RH
.get())
163 pLeft
= m_xOrgColumnNames
.get();
164 pRight
= m_xNewColumnNames
.get();
166 else if (&rButton
== m_xColumn_LH
.get())
168 pLeft
= m_xNewColumnNames
.get();
169 pRight
= m_xOrgColumnNames
.get();
171 else if (&rButton
== m_xColumns_RH
.get())
173 pLeft
= m_xOrgColumnNames
.get();
174 pRight
= m_xNewColumnNames
.get();
177 else if (&rButton
== m_xColumns_LH
.get())
179 pLeft
= m_xNewColumnNames
.get();
180 pRight
= m_xOrgColumnNames
.get();
184 if (!pLeft
|| !pRight
)
187 Reference
< XDatabaseMetaData
> xMetaData( m_pParent
->m_xDestConnection
->getMetaData() );
188 OUString sExtraChars
= xMetaData
->getExtraNameCharacters();
189 sal_Int32 nMaxNameLen
= m_pParent
->getMaxColumnNameLength();
191 ::comphelper::UStringMixEqual
aCase(xMetaData
->supportsMixedCaseQuotedIdentifiers());
192 std::vector
< OUString
> aRightColumns
;
193 fillColumns(pRight
,aRightColumns
);
197 auto aRows
= pLeft
->get_selected_rows();
198 std::sort(aRows
.begin(), aRows
.end());
200 for (auto it
= aRows
.begin(); it
!= aRows
.end(); ++it
)
201 moveColumn(pRight
,pLeft
,aRightColumns
,pLeft
->get_text(*it
),sExtraChars
,nMaxNameLen
,aCase
);
203 for (auto it
= aRows
.rbegin(); it
!= aRows
.rend(); ++it
)
208 const sal_Int32 nEntries
= pLeft
->n_children();
209 for(sal_Int32 i
=0; i
< nEntries
; ++i
)
210 moveColumn(pRight
,pLeft
,aRightColumns
,pLeft
->get_text(i
),sExtraChars
,nMaxNameLen
,aCase
);
211 for(sal_Int32 j
=pLeft
->n_children(); j
; )
217 if (m_xOrgColumnNames
->n_children())
218 m_xOrgColumnNames
->select(0);
221 IMPL_LINK( OWizColumnSelect
, ListDoubleClickHdl
, weld::TreeView
&, rListBox
, bool )
223 weld::TreeView
*pLeft
,*pRight
;
224 if (&rListBox
== m_xOrgColumnNames
.get())
226 pLeft
= m_xOrgColumnNames
.get();
227 pRight
= m_xNewColumnNames
.get();
231 pRight
= m_xOrgColumnNames
.get();
232 pLeft
= m_xNewColumnNames
.get();
235 // If database is able to process PrimaryKeys, set PrimaryKey
236 Reference
< XDatabaseMetaData
> xMetaData( m_pParent
->m_xDestConnection
->getMetaData() );
237 OUString sExtraChars
= xMetaData
->getExtraNameCharacters();
238 sal_Int32 nMaxNameLen
= m_pParent
->getMaxColumnNameLength();
240 ::comphelper::UStringMixEqual
aCase(xMetaData
->supportsMixedCaseQuotedIdentifiers());
241 std::vector
< OUString
> aRightColumns
;
242 fillColumns(pRight
,aRightColumns
);
244 auto aRows
= pLeft
->get_selected_rows();
245 std::sort(aRows
.begin(), aRows
.end());
247 for (auto it
= aRows
.begin(); it
!= aRows
.end(); ++it
)
248 moveColumn(pRight
,pLeft
,aRightColumns
,pLeft
->get_text(*it
),sExtraChars
,nMaxNameLen
,aCase
);
250 for (auto it
= aRows
.rbegin(); it
!= aRows
.rend(); ++it
)
258 void OWizColumnSelect::clearListBox(weld::TreeView
& rListBox
)
263 void OWizColumnSelect::fillColumns(weld::TreeView
const * pRight
,std::vector
< OUString
> &_rRightColumns
)
265 const sal_Int32 nCount
= pRight
->n_children();
266 _rRightColumns
.reserve(nCount
);
267 for (sal_Int32 i
=0; i
< nCount
; ++i
)
268 _rRightColumns
.push_back(pRight
->get_text(i
));
271 void OWizColumnSelect::createNewColumn( weld::TreeView
* _pListbox
,
272 OFieldDescription
const * _pSrcField
,
273 std::vector
< OUString
>& _rRightColumns
,
274 const OUString
& _sColumnName
,
275 std::u16string_view _sExtraChars
,
276 sal_Int32 _nMaxNameLen
,
277 const ::comphelper::UStringMixEqual
& _aCase
)
279 OUString sConvertedName
= m_pParent
->convertColumnName(TMultiListBoxEntryFindFunctor(&_rRightColumns
,_aCase
),
283 OFieldDescription
* pNewField
= new OFieldDescription(*_pSrcField
);
284 pNewField
->SetName(sConvertedName
);
285 bool bNotConvert
= true;
286 pNewField
->SetType(m_pParent
->convertType(_pSrcField
->getSpecialTypeInfo(),bNotConvert
));
287 if ( !m_pParent
->supportsPrimaryKey() )
288 pNewField
->SetPrimaryKey(false);
290 _pListbox
->append(weld::toId(pNewField
), sConvertedName
);
291 _rRightColumns
.push_back(sConvertedName
);
294 m_pParent
->showColumnTypeNotSupported(sConvertedName
);
297 void OWizColumnSelect::moveColumn( weld::TreeView
* _pRight
,
298 weld::TreeView
const * _pLeft
,
299 std::vector
< OUString
>& _rRightColumns
,
300 const OUString
& _sColumnName
,
301 std::u16string_view _sExtraChars
,
302 sal_Int32 _nMaxNameLen
,
303 const ::comphelper::UStringMixEqual
& _aCase
)
305 if(_pRight
== m_xNewColumnNames
.get())
307 // we copy the column into the new format for the dest
308 OFieldDescription
* pSrcField
= weld::fromId
<OFieldDescription
*>(_pLeft
->get_id(_pLeft
->find_text(_sColumnName
)));
309 createNewColumn(_pRight
,pSrcField
,_rRightColumns
,_sColumnName
,_sExtraChars
,_nMaxNameLen
,_aCase
);
313 // find the new column in the dest name mapping to obtain the old column
314 OCopyTableWizard::TNameMapping::const_iterator aIter
= std::find_if(m_pParent
->m_mNameMapping
.begin(),m_pParent
->m_mNameMapping
.end(),
315 [&_aCase
, &_sColumnName
] (const OCopyTableWizard::TNameMapping::value_type
& nameMap
) {
316 return _aCase(nameMap
.second
, _sColumnName
);
319 OSL_ENSURE(aIter
!= m_pParent
->m_mNameMapping
.end(),"Column must be defined");
320 if ( aIter
== m_pParent
->m_mNameMapping
.end() )
321 return; // do nothing
322 const ODatabaseExport::TColumns
& rSrcColumns
= m_pParent
->getSourceColumns();
323 ODatabaseExport::TColumns::const_iterator aSrcIter
= rSrcColumns
.find((*aIter
).first
);
324 if ( aSrcIter
!= rSrcColumns
.end() )
326 // we need also the old position of this column to insert it back on that position again
327 const ODatabaseExport::TColumnVector
& rSrcVector
= m_pParent
->getSrcVector();
328 ODatabaseExport::TColumnVector::const_iterator aPos
= std::find(rSrcVector
.begin(), rSrcVector
.end(), aSrcIter
);
329 OSL_ENSURE( aPos
!= rSrcVector
.end(),"Invalid position for the iterator here!");
330 ODatabaseExport::TColumnVector::size_type nPos
= (aPos
- rSrcVector
.begin()) - adjustColumnPosition(_pLeft
, _sColumnName
, (aPos
- rSrcVector
.begin()), _aCase
);
332 OUString
sId(weld::toId(aSrcIter
->second
));
333 const OUString
& rStr
= (*aIter
).first
;
334 _pRight
->insert(nullptr, nPos
, &rStr
, &sId
, nullptr, nullptr, false, nullptr);
335 _rRightColumns
.push_back(rStr
);
336 m_pParent
->removeColumnNameFromNameMap(_sColumnName
);
341 // Simply returning fields back to their original position is
342 // not enough. We need to take into account what fields have
343 // been removed earlier and adjust accordingly. Based on the
344 // algorithm employed in moveColumn().
345 sal_Int32
OWizColumnSelect::adjustColumnPosition(weld::TreeView
const * _pLeft
,
346 std::u16string_view _sColumnName
,
347 ODatabaseExport::TColumnVector::size_type nCurrentPos
,
348 const ::comphelper::UStringMixEqual
& _aCase
)
350 sal_Int32 nAdjustedPos
= 0;
352 // if returning all entries to their original position,
353 // then there is no need to adjust the positions.
354 if (m_xColumns_LH
->has_focus())
357 const sal_Int32 nCount
= _pLeft
->n_children();
358 OUString sColumnString
;
359 for(sal_Int32 i
=0; i
< nCount
; ++i
)
361 sColumnString
= _pLeft
->get_text(i
);
362 if(_sColumnName
!= sColumnString
)
364 // find the new column in the dest name mapping to obtain the old column
365 OCopyTableWizard::TNameMapping::const_iterator aIter
= std::find_if(m_pParent
->m_mNameMapping
.begin(),m_pParent
->m_mNameMapping
.end(),
366 [&_aCase
, &sColumnString
] (const OCopyTableWizard::TNameMapping::value_type
& nameMap
) {
367 return _aCase(nameMap
.second
, sColumnString
);
370 OSL_ENSURE(aIter
!= m_pParent
->m_mNameMapping
.end(),"Column must be defined");
371 const ODatabaseExport::TColumns
& rSrcColumns
= m_pParent
->getSourceColumns();
372 ODatabaseExport::TColumns::const_iterator aSrcIter
= rSrcColumns
.find((*aIter
).first
);
373 if ( aSrcIter
!= rSrcColumns
.end() )
375 // we need also the old position of this column to insert it back on that position again
376 const ODatabaseExport::TColumnVector
& rSrcVector
= m_pParent
->getSrcVector();
377 ODatabaseExport::TColumnVector::const_iterator aPos
= std::find(rSrcVector
.begin(), rSrcVector
.end(), aSrcIter
);
378 ODatabaseExport::TColumnVector::size_type nPos
= aPos
- rSrcVector
.begin();
379 if( nPos
< nCurrentPos
)
390 void OWizColumnSelect::enableButtons()
392 bool bEntries
= m_xNewColumnNames
->n_children() != 0;
394 m_pParent
->m_mNameMapping
.clear();
396 m_pParent
->GetOKButton().set_sensitive(bEntries
);
397 m_pParent
->EnableNextButton(bEntries
&& m_pParent
->getOperation() != CopyTableOperation::AppendData
);
400 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */