1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include <WColumnSelect.hxx>
21 #include <strings.hrc>
22 #include <osl/diagnose.h>
23 #include <WCopyTable.hxx>
24 #include <com/sun/star/sdbcx/XDataDescriptorFactory.hpp>
25 #include <com/sun/star/sdbcx/XColumnsSupplier.hpp>
26 #include <com/sun/star/sdbcx/XAppend.hpp>
27 #include <core_resource.hxx>
28 #include <com/sun/star/sdbc/DataType.hpp>
29 #include <com/sun/star/sdbc/ColumnValue.hpp>
30 #include <com/sun/star/sdb/application/CopyTableOperation.hpp>
31 #include <vcl/svapp.hxx>
32 #include <stringconstants.hxx>
33 #include <functional>
34
35 using namespace ::com::sun::star::uno;
36 using namespace ::com::sun::star::beans;
37 using namespace ::com::sun::star::container;
38 using namespace ::com::sun::star::sdbc;
39 using namespace ::com::sun::star::sdbcx;
40 using namespace dbaui;
41
42 namespace CopyTableOperation = ::com::sun::star::sdb::application::CopyTableOperation;
43
GetTitle() const44 OUString OWizColumnSelect::GetTitle() const { return DBA_RES(STR_WIZ_COLUMN_SELECT_TITEL); }
45
OWizardPage(weld::Container * pPage,OCopyTableWizard * pWizard,const OUString & rUIXMLDescription,const OString & rID)46 OWizardPage::OWizardPage(weld::Container* pPage, OCopyTableWizard* pWizard, const OUString& rUIXMLDescription, const OString& rID)
47 : ::vcl::OWizardPage(pPage, pWizard, rUIXMLDescription, rID)
48 , m_pParent(pWizard)
49 , m_bFirstTime(true)
50 {
51 }
52
~OWizardPage()53 OWizardPage::~OWizardPage()
54 {
55 }
56
57 // OWizColumnSelect
OWizColumnSelect(weld::Container * pPage,OCopyTableWizard * pWizard)58 OWizColumnSelect::OWizColumnSelect(weld::Container* pPage, OCopyTableWizard* pWizard)
59 : OWizardPage(pPage, pWizard, "dbaccess/ui/applycolpage.ui", "ApplyColPage")
60 , m_xOrgColumnNames(m_xBuilder->weld_tree_view("from"))
61 , m_xColumn_RH(m_xBuilder->weld_button("colrh"))
62 , m_xColumns_RH(m_xBuilder->weld_button("colsrh"))
63 , m_xColumn_LH(m_xBuilder->weld_button("collh"))
64 , m_xColumns_LH(m_xBuilder->weld_button("colslh"))
65 , m_xNewColumnNames(m_xBuilder->weld_tree_view("to"))
66 {
67 m_xColumn_RH->connect_clicked(LINK(this,OWizColumnSelect,ButtonClickHdl));
68 m_xColumn_LH->connect_clicked(LINK(this,OWizColumnSelect,ButtonClickHdl));
69 m_xColumns_RH->connect_clicked(LINK(this,OWizColumnSelect,ButtonClickHdl));
70 m_xColumns_LH->connect_clicked(LINK(this,OWizColumnSelect,ButtonClickHdl));
71
72 m_xOrgColumnNames->set_selection_mode(SelectionMode::Multiple);
73 m_xNewColumnNames->set_selection_mode(SelectionMode::Multiple);
74
75 m_xOrgColumnNames->connect_row_activated(LINK(this,OWizColumnSelect,ListDoubleClickHdl));
76 m_xNewColumnNames->connect_row_activated(LINK(this,OWizColumnSelect,ListDoubleClickHdl));
77 }
78
~OWizColumnSelect()79 OWizColumnSelect::~OWizColumnSelect()
80 {
81 while (m_xNewColumnNames->n_children())
82 {
83 delete reinterpret_cast<OFieldDescription*>(m_xNewColumnNames->get_id(0).toInt64());
84 m_xNewColumnNames->remove(0);
85 }
86 }
87
Reset()88 void OWizColumnSelect::Reset()
89 {
90 // restore original state
91 clearListBox(*m_xOrgColumnNames);
92 clearListBox(*m_xNewColumnNames);
93 m_pParent->m_mNameMapping.clear();
94
95 // insert the source columns in the left listbox
96 const ODatabaseExport::TColumnVector& rSrcColumns = m_pParent->getSrcVector();
97
98 for (auto const& column : rSrcColumns)
99 {
100 OUString sId(OUString::number(reinterpret_cast<sal_Int64>(column->second)));
101 m_xOrgColumnNames->append(sId, column->first);
102 }
103
104 if (m_xOrgColumnNames->n_children())
105 m_xOrgColumnNames->select(0);
106
107 m_bFirstTime = false;
108 }
109
Activate()110 void OWizColumnSelect::Activate( )
111 {
112 // if there are no dest columns reset the left side with the original columns
113 if(m_pParent->getDestColumns().empty())
114 Reset();
115
116 clearListBox(*m_xNewColumnNames);
117
118 const ODatabaseExport::TColumnVector& rDestColumns = m_pParent->getDestVector();
119
120 // tdf#113923, the added columns must exist in the table
121 // in the case where:
122 // 1: we enabled the creation of a primary key
123 // 2: we come back here from the "Back" button of the next page,
124 // we want to avoid to list the new column generated in the next page
125 const ODatabaseExport::TColumns& rSrcColumns = m_pParent->getSourceColumns();
126
127 for (auto const& column : rDestColumns)
128 {
129 if (rSrcColumns.find(column->first) != rSrcColumns.end())
130 {
131 OUString sId(OUString::number(reinterpret_cast<sal_Int64>(new OFieldDescription(*(column->second)))));
132 m_xNewColumnNames->append(sId, column->first);
133 int nRemove = m_xOrgColumnNames->find_text(column->first);
134 if (nRemove != -1)
135 m_xOrgColumnNames->remove(nRemove);
136 }
137 }
138 m_pParent->GetOKButton().set_sensitive(m_xNewColumnNames->n_children() != 0);
139 m_pParent->EnableNextButton(m_xNewColumnNames->n_children() && m_pParent->getOperation() != CopyTableOperation::AppendData);
140 m_xColumns_RH->grab_focus();
141 }
142
LeavePage()143 bool OWizColumnSelect::LeavePage()
144 {
145
146 m_pParent->clearDestColumns();
147
148 for(sal_Int32 i=0 ; i< m_xNewColumnNames->n_children();++i)
149 {
150 OFieldDescription* pField = reinterpret_cast<OFieldDescription*>(m_xNewColumnNames->get_id(i).toInt64());
151 OSL_ENSURE(pField,"The field information can not be null!");
152 m_pParent->insertColumn(i,pField);
153 }
154
155 clearListBox(*m_xNewColumnNames);
156
157 if ( m_pParent->GetPressedButton() == OCopyTableWizard::WIZARD_NEXT
158 || m_pParent->GetPressedButton() == OCopyTableWizard::WIZARD_FINISH
159 )
160 return !m_pParent->getDestColumns().empty();
161 else
162 return true;
163 }
164
IMPL_LINK(OWizColumnSelect,ButtonClickHdl,weld::Button &,rButton,void)165 IMPL_LINK(OWizColumnSelect, ButtonClickHdl, weld::Button&, rButton, void)
166 {
167 weld::TreeView *pLeft = nullptr;
168 weld::TreeView *pRight = nullptr;
169 bool bAll = false;
170
171 if (&rButton == m_xColumn_RH.get())
172 {
173 pLeft = m_xOrgColumnNames.get();
174 pRight = m_xNewColumnNames.get();
175 }
176 else if (&rButton == m_xColumn_LH.get())
177 {
178 pLeft = m_xNewColumnNames.get();
179 pRight = m_xOrgColumnNames.get();
180 }
181 else if (&rButton == m_xColumns_RH.get())
182 {
183 pLeft = m_xOrgColumnNames.get();
184 pRight = m_xNewColumnNames.get();
185 bAll = true;
186 }
187 else if (&rButton == m_xColumns_LH.get())
188 {
189 pLeft = m_xNewColumnNames.get();
190 pRight = m_xOrgColumnNames.get();
191 bAll = true;
192 }
193
194 if (!pLeft || !pRight)
195 return;
196
197 Reference< XDatabaseMetaData > xMetaData( m_pParent->m_xDestConnection->getMetaData() );
198 OUString sExtraChars = xMetaData->getExtraNameCharacters();
199 sal_Int32 nMaxNameLen = m_pParent->getMaxColumnNameLength();
200
201 ::comphelper::UStringMixEqual aCase(xMetaData->supportsMixedCaseQuotedIdentifiers());
202 std::vector< OUString> aRightColumns;
203 fillColumns(pRight,aRightColumns);
204
205 if(!bAll)
206 {
207 auto aRows = pLeft->get_selected_rows();
208 std::sort(aRows.begin(), aRows.end());
209
210 for (auto it = aRows.begin(); it != aRows.end(); ++it)
211 moveColumn(pRight,pLeft,aRightColumns,pLeft->get_text(*it),sExtraChars,nMaxNameLen,aCase);
212
213 for (auto it = aRows.rbegin(); it != aRows.rend(); ++it)
214 pLeft->remove(*it);
215 }
216 else
217 {
218 const sal_Int32 nEntries = pLeft->n_children();
219 for(sal_Int32 i=0; i < nEntries; ++i)
220 moveColumn(pRight,pLeft,aRightColumns,pLeft->get_text(i),sExtraChars,nMaxNameLen,aCase);
221 for(sal_Int32 j=pLeft->n_children(); j ; )
222 pLeft->remove(--j);
223 }
224
225 enableButtons();
226
227 if (m_xOrgColumnNames->n_children())
228 m_xOrgColumnNames->select(0);
229 }
230
IMPL_LINK(OWizColumnSelect,ListDoubleClickHdl,weld::TreeView &,rListBox,bool)231 IMPL_LINK( OWizColumnSelect, ListDoubleClickHdl, weld::TreeView&, rListBox, bool )
232 {
233 weld::TreeView *pLeft,*pRight;
234 if (&rListBox == m_xOrgColumnNames.get())
235 {
236 pLeft = m_xOrgColumnNames.get();
237 pRight = m_xNewColumnNames.get();
238 }
239 else
240 {
241 pRight = m_xOrgColumnNames.get();
242 pLeft = m_xNewColumnNames.get();
243 }
244
245 // If database is able to process PrimaryKeys, set PrimaryKey
246 Reference< XDatabaseMetaData > xMetaData( m_pParent->m_xDestConnection->getMetaData() );
247 OUString sExtraChars = xMetaData->getExtraNameCharacters();
248 sal_Int32 nMaxNameLen = m_pParent->getMaxColumnNameLength();
249
250 ::comphelper::UStringMixEqual aCase(xMetaData->supportsMixedCaseQuotedIdentifiers());
251 std::vector< OUString> aRightColumns;
252 fillColumns(pRight,aRightColumns);
253
254 auto aRows = pLeft->get_selected_rows();
255 std::sort(aRows.begin(), aRows.end());
256
257 for (auto it = aRows.begin(); it != aRows.end(); ++it)
258 moveColumn(pRight,pLeft,aRightColumns,pLeft->get_text(*it),sExtraChars,nMaxNameLen,aCase);
259
260 for (auto it = aRows.rbegin(); it != aRows.rend(); ++it)
261 pLeft->remove(*it);
262
263 enableButtons();
264
265 return true;
266 }
267
clearListBox(weld::TreeView & rListBox)268 void OWizColumnSelect::clearListBox(weld::TreeView& rListBox)
269 {
270 rListBox.clear();
271 }
272
fillColumns(weld::TreeView const * pRight,std::vector<OUString> & _rRightColumns)273 void OWizColumnSelect::fillColumns(weld::TreeView const * pRight,std::vector< OUString> &_rRightColumns)
274 {
275 const sal_Int32 nCount = pRight->n_children();
276 _rRightColumns.reserve(nCount);
277 for (sal_Int32 i=0; i < nCount; ++i)
278 _rRightColumns.push_back(pRight->get_text(i));
279 }
280
createNewColumn(weld::TreeView * _pListbox,OFieldDescription const * _pSrcField,std::vector<OUString> & _rRightColumns,const OUString & _sColumnName,const OUString & _sExtraChars,sal_Int32 _nMaxNameLen,const::comphelper::UStringMixEqual & _aCase)281 void OWizColumnSelect::createNewColumn( weld::TreeView* _pListbox,
282 OFieldDescription const * _pSrcField,
283 std::vector< OUString>& _rRightColumns,
284 const OUString& _sColumnName,
285 const OUString& _sExtraChars,
286 sal_Int32 _nMaxNameLen,
287 const ::comphelper::UStringMixEqual& _aCase)
288 {
289 OUString sConvertedName = m_pParent->convertColumnName(TMultiListBoxEntryFindFunctor(&_rRightColumns,_aCase),
290 _sColumnName,
291 _sExtraChars,
292 _nMaxNameLen);
293 OFieldDescription* pNewField = new OFieldDescription(*_pSrcField);
294 pNewField->SetName(sConvertedName);
295 bool bNotConvert = true;
296 pNewField->SetType(m_pParent->convertType(_pSrcField->getSpecialTypeInfo(),bNotConvert));
297 if ( !m_pParent->supportsPrimaryKey() )
298 pNewField->SetPrimaryKey(false);
299
300 _pListbox->append(OUString::number(reinterpret_cast<sal_Int64>(pNewField)), sConvertedName);
301 _rRightColumns.push_back(sConvertedName);
302
303 if ( !bNotConvert )
304 m_pParent->showColumnTypeNotSupported(sConvertedName);
305 }
306
moveColumn(weld::TreeView * _pRight,weld::TreeView const * _pLeft,std::vector<OUString> & _rRightColumns,const OUString & _sColumnName,const OUString & _sExtraChars,sal_Int32 _nMaxNameLen,const::comphelper::UStringMixEqual & _aCase)307 void OWizColumnSelect::moveColumn( weld::TreeView* _pRight,
308 weld::TreeView const * _pLeft,
309 std::vector< OUString>& _rRightColumns,
310 const OUString& _sColumnName,
311 const OUString& _sExtraChars,
312 sal_Int32 _nMaxNameLen,
313 const ::comphelper::UStringMixEqual& _aCase)
314 {
315 if(_pRight == m_xNewColumnNames.get())
316 {
317 // we copy the column into the new format for the dest
318 OFieldDescription* pSrcField = reinterpret_cast<OFieldDescription*>(_pLeft->get_id(_pLeft->find_text(_sColumnName)).toInt64());
319 createNewColumn(_pRight,pSrcField,_rRightColumns,_sColumnName,_sExtraChars,_nMaxNameLen,_aCase);
320 }
321 else
322 {
323 // find the new column in the dest name mapping to obtain the old column
324 OCopyTableWizard::TNameMapping::const_iterator aIter = std::find_if(m_pParent->m_mNameMapping.begin(),m_pParent->m_mNameMapping.end(),
325 [&_aCase, &_sColumnName] (const OCopyTableWizard::TNameMapping::value_type& nameMap) {
326 return _aCase(nameMap.second, _sColumnName);
327 });
328
329 OSL_ENSURE(aIter != m_pParent->m_mNameMapping.end(),"Column must be defined");
330 if ( aIter == m_pParent->m_mNameMapping.end() )
331 return; // do nothing
332 const ODatabaseExport::TColumns& rSrcColumns = m_pParent->getSourceColumns();
333 ODatabaseExport::TColumns::const_iterator aSrcIter = rSrcColumns.find((*aIter).first);
334 if ( aSrcIter != rSrcColumns.end() )
335 {
336 // we need also the old position of this column to insert it back on that position again
337 const ODatabaseExport::TColumnVector& rSrcVector = m_pParent->getSrcVector();
338 ODatabaseExport::TColumnVector::const_iterator aPos = std::find(rSrcVector.begin(), rSrcVector.end(), aSrcIter);
339 OSL_ENSURE( aPos != rSrcVector.end(),"Invalid position for the iterator here!");
340 ODatabaseExport::TColumnVector::size_type nPos = (aPos - rSrcVector.begin()) - adjustColumnPosition(_pLeft, _sColumnName, (aPos - rSrcVector.begin()), _aCase);
341
342 OUString sId(OUString::number(reinterpret_cast<sal_Int64>(aSrcIter->second)));
343 const OUString& rStr = (*aIter).first;
344 _pRight->insert(nullptr, nPos, &rStr, &sId, nullptr, nullptr, nullptr, false, nullptr);
345 _rRightColumns.push_back(rStr);
346 m_pParent->removeColumnNameFromNameMap(_sColumnName);
347 }
348 }
349 }
350
351 // Simply returning fields back to their original position is
352 // not enough. We need to take into account what fields have
353 // been removed earlier and adjust accordingly. Based on the
354 // algorithm employed in moveColumn().
adjustColumnPosition(weld::TreeView const * _pLeft,const OUString & _sColumnName,ODatabaseExport::TColumnVector::size_type nCurrentPos,const::comphelper::UStringMixEqual & _aCase)355 sal_Int32 OWizColumnSelect::adjustColumnPosition(weld::TreeView const * _pLeft,
356 const OUString& _sColumnName,
357 ODatabaseExport::TColumnVector::size_type nCurrentPos,
358 const ::comphelper::UStringMixEqual& _aCase)
359 {
360 sal_Int32 nAdjustedPos = 0;
361
362 // if returning all entries to their original position,
363 // then there is no need to adjust the positions.
364 if (m_xColumns_LH->has_focus())
365 return nAdjustedPos;
366
367 const sal_Int32 nCount = _pLeft->n_children();
368 OUString sColumnString;
369 for(sal_Int32 i=0; i < nCount; ++i)
370 {
371 sColumnString = _pLeft->get_text(i);
372 if(_sColumnName != sColumnString)
373 {
374 // find the new column in the dest name mapping to obtain the old column
375 OCopyTableWizard::TNameMapping::const_iterator aIter = std::find_if(m_pParent->m_mNameMapping.begin(),m_pParent->m_mNameMapping.end(),
376 [&_aCase, &sColumnString] (const OCopyTableWizard::TNameMapping::value_type& nameMap) {
377 return _aCase(nameMap.second, sColumnString);
378 });
379
380 OSL_ENSURE(aIter != m_pParent->m_mNameMapping.end(),"Column must be defined");
381 const ODatabaseExport::TColumns& rSrcColumns = m_pParent->getSourceColumns();
382 ODatabaseExport::TColumns::const_iterator aSrcIter = rSrcColumns.find((*aIter).first);
383 if ( aSrcIter != rSrcColumns.end() )
384 {
385 // we need also the old position of this column to insert it back on that position again
386 const ODatabaseExport::TColumnVector& rSrcVector = m_pParent->getSrcVector();
387 ODatabaseExport::TColumnVector::const_iterator aPos = std::find(rSrcVector.begin(), rSrcVector.end(), aSrcIter);
388 ODatabaseExport::TColumnVector::size_type nPos = aPos - rSrcVector.begin();
389 if( nPos < nCurrentPos)
390 {
391 nAdjustedPos++;
392 }
393 }
394 }
395 }
396
397 return nAdjustedPos;
398 }
399
enableButtons()400 void OWizColumnSelect::enableButtons()
401 {
402 bool bEntries = m_xNewColumnNames->n_children() != 0;
403 if (!bEntries)
404 m_pParent->m_mNameMapping.clear();
405
406 m_pParent->GetOKButton().set_sensitive(bEntries);
407 m_pParent->EnableNextButton(bEntries && m_pParent->getOperation() != CopyTableOperation::AppendData);
408 }
409
410 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
411