nss: upgrade to release 3.73
[LibreOffice.git] / sw / source / ui / dbui / createaddresslistdialog.cxx
blob969bff35d7addc73d79ad7e9e6c32695c32ae50c
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 <sal/config.h>
22 #include <cstddef>
24 #include <osl/diagnose.h>
25 #include <swtypes.hxx>
26 #include "createaddresslistdialog.hxx"
27 #include "customizeaddresslistdialog.hxx"
28 #include <mmconfigitem.hxx>
29 #include <vcl/svapp.hxx>
30 #include <unotools/pathoptions.hxx>
31 #include <sfx2/filedlghelper.hxx>
32 #include <sfx2/docfile.hxx>
33 #include <rtl/textenc.h>
34 #include <com/sun/star/ui/dialogs/TemplateDescription.hpp>
35 #include <com/sun/star/ui/dialogs/XFilePicker3.hpp>
36 #include <tools/urlobj.hxx>
37 #include <strings.hrc>
38 #include <map>
40 using namespace ::com::sun::star;
41 using namespace ::com::sun::star::ui::dialogs;
43 namespace {
45 struct SwAddressFragment
47 std::unique_ptr<weld::Builder> m_xBuilder;
48 std::unique_ptr<weld::Label> m_xLabel;
49 std::unique_ptr<weld::Entry> m_xEntry;
50 weld::Container* m_pGrid;
52 SwAddressFragment(weld::Container* pGrid, int nLine)
53 : m_xBuilder(Application::CreateBuilder(pGrid, "modules/swriter/ui/addressfragment.ui"))
54 , m_xLabel(m_xBuilder->weld_label("label"))
55 , m_xEntry(m_xBuilder->weld_entry("entry"))
56 , m_pGrid(pGrid)
58 m_xLabel->set_grid_left_attach(0);
59 m_xLabel->set_grid_top_attach(nLine);
61 m_xEntry->set_grid_left_attach(1);
62 m_xEntry->set_grid_top_attach(nLine);
65 ~SwAddressFragment()
67 m_pGrid->move(m_xEntry.get(), nullptr);
68 m_pGrid->move(m_xLabel.get(), nullptr);
74 class SwAddressControl_Impl
76 std::map<weld::Entry*, sal_Int32> m_aEditLines;
78 SwCSVData* m_pData;
79 sal_uInt32 m_nCurrentDataSet;
81 bool m_bNoDataSet;
83 std::unique_ptr<weld::ScrolledWindow> m_xScrollBar;
84 std::unique_ptr<weld::Container> m_xWindow;
85 std::vector<std::unique_ptr<SwAddressFragment>> m_aLines;
87 DECL_LINK(GotFocusHdl_Impl, weld::Widget&, void);
88 DECL_LINK(EditModifyHdl_Impl, weld::Entry&, void);
90 void MakeVisible(const tools::Rectangle& aRect);
92 public:
93 SwAddressControl_Impl(weld::Builder& rBuilder);
95 void SetData(SwCSVData& rDBData);
97 void SetCurrentDataSet(sal_uInt32 nSet);
98 void CurrentDataSetInvalidated() { m_nCurrentDataSet = std::numeric_limits<sal_uInt32>::max(); }
99 sal_uInt32 GetCurrentDataSet() const { return m_nCurrentDataSet; }
100 void SetCursorTo(std::size_t nElement);
103 SwAddressControl_Impl::SwAddressControl_Impl(weld::Builder& rBuilder)
104 : m_pData(nullptr)
105 , m_nCurrentDataSet(0)
106 , m_bNoDataSet(true)
107 , m_xScrollBar(rBuilder.weld_scrolled_window("scrollwin"))
108 , m_xWindow(rBuilder.weld_container("CONTAINER"))
112 void SwAddressControl_Impl::SetData(SwCSVData& rDBData)
114 m_pData = &rDBData;
115 //when the address data is updated then remove the controls and build again
116 if (!m_aLines.empty())
118 m_aLines.clear();
119 m_bNoDataSet = true;
122 Link<weld::Widget&,void> aFocusLink = LINK(this, SwAddressControl_Impl, GotFocusHdl_Impl);
123 Link<weld::Entry&,void> aEditModifyLink = LINK(this, SwAddressControl_Impl, EditModifyHdl_Impl);
124 sal_Int32 nLines = 0;
125 for (const auto& rHeader : m_pData->aDBColumnHeaders)
127 m_aLines.emplace_back(new SwAddressFragment(m_xWindow.get(), nLines));
129 // when we have one line, measure it to get the line height to use as
130 // the basis for overall size request
131 if (nLines == 0)
133 auto nLineHeight = m_xWindow->get_preferred_size().Height();
134 m_xScrollBar->set_size_request(m_xScrollBar->get_approximate_digit_width() * 65,
135 nLineHeight * 10);
138 weld::Label* pNewFT = m_aLines.back()->m_xLabel.get();
139 weld::Entry* pNewED = m_aLines.back()->m_xEntry.get();
140 //set nLines a position identifier - used in the ModifyHdl
141 m_aEditLines[pNewED] = nLines;
142 pNewED->connect_focus_in(aFocusLink);
143 pNewED->connect_changed(aEditModifyLink);
145 pNewFT->set_label(rHeader);
147 nLines++;
151 void SwAddressControl_Impl::SetCurrentDataSet(sal_uInt32 nSet)
153 if(!(m_bNoDataSet || m_nCurrentDataSet != nSet))
154 return;
156 m_bNoDataSet = false;
157 m_nCurrentDataSet = nSet;
158 OSL_ENSURE(m_pData->aDBData.size() > m_nCurrentDataSet, "wrong data set index");
159 if(m_pData->aDBData.size() > m_nCurrentDataSet)
161 sal_uInt32 nIndex = 0;
162 for(auto& rLine : m_aLines)
164 OSL_ENSURE(nIndex < m_pData->aDBData[m_nCurrentDataSet].size(),
165 "number of columns doesn't match number of Edits");
166 rLine->m_xEntry->set_text(m_pData->aDBData[m_nCurrentDataSet][nIndex]);
167 ++nIndex;
172 IMPL_LINK(SwAddressControl_Impl, GotFocusHdl_Impl, weld::Widget&, rEdit, void)
174 int x, y, width, height;
175 rEdit.get_extents_relative_to(*m_xWindow, x, y, width, height);
176 // the container has a border of 3 in the .ui
177 tools::Rectangle aRect(Point(x - 3, y - 3), Size(width + 6, height + 6));
178 MakeVisible(aRect);
181 void SwAddressControl_Impl::MakeVisible(const tools::Rectangle & rRect)
183 //determine range of visible positions
184 auto nMinVisiblePos = m_xScrollBar->vadjustment_get_value();
185 auto nMaxVisiblePos = nMinVisiblePos + m_xScrollBar->vadjustment_get_page_size();
186 if (rRect.Top() < nMinVisiblePos || rRect.Bottom() > nMaxVisiblePos)
187 m_xScrollBar->vadjustment_set_value(rRect.Top());
190 // copy data changes into database
191 IMPL_LINK(SwAddressControl_Impl, EditModifyHdl_Impl, weld::Entry&, rEdit, void)
193 //get the data element number of the current set
194 sal_Int32 nIndex = m_aEditLines[&rEdit];
195 //get the index of the set
196 OSL_ENSURE(m_pData->aDBData.size() > m_nCurrentDataSet, "wrong data set index" );
197 if (m_pData->aDBData.size() > m_nCurrentDataSet)
199 m_pData->aDBData[m_nCurrentDataSet][nIndex] = rEdit.get_text();
203 void SwAddressControl_Impl::SetCursorTo(std::size_t nElement)
205 if (nElement < m_aLines.size())
207 weld::Entry* pEdit = m_aLines[nElement]->m_xEntry.get();
208 pEdit->grab_focus();
213 SwCreateAddressListDialog::SwCreateAddressListDialog(
214 weld::Window* pParent, const OUString& rURL, SwMailMergeConfigItem const & rConfig)
215 : SfxDialogController(pParent, "modules/swriter/ui/createaddresslist.ui", "CreateAddressList")
216 , m_sAddressListFilterName(SwResId(ST_FILTERNAME))
217 , m_sURL(rURL)
218 , m_pCSVData(new SwCSVData)
219 , m_xAddressControl(new SwAddressControl_Impl(*m_xBuilder))
220 , m_xNewPB(m_xBuilder->weld_button("NEW"))
221 , m_xDeletePB(m_xBuilder->weld_button("DELETE"))
222 , m_xFindPB(m_xBuilder->weld_button("FIND"))
223 , m_xCustomizePB(m_xBuilder->weld_button("CUSTOMIZE"))
224 , m_xStartPB(m_xBuilder->weld_button("START"))
225 , m_xPrevPB(m_xBuilder->weld_button("PREV"))
226 , m_xSetNoED(m_xBuilder->weld_entry("SETNOED"))
227 , m_xSetNoNF(m_xBuilder->weld_spin_button("SETNOSB"))
228 , m_xNextPB(m_xBuilder->weld_button("NEXT"))
229 , m_xEndPB(m_xBuilder->weld_button("END"))
230 , m_xOK(m_xBuilder->weld_button("ok"))
232 m_xSetNoNF->set_min(1);
234 m_xNewPB->connect_clicked(LINK(this, SwCreateAddressListDialog, NewHdl_Impl));
235 m_xDeletePB->connect_clicked(LINK(this, SwCreateAddressListDialog, DeleteHdl_Impl));
236 m_xFindPB->connect_clicked(LINK(this, SwCreateAddressListDialog, FindHdl_Impl));
237 m_xCustomizePB->connect_clicked(LINK(this, SwCreateAddressListDialog, CustomizeHdl_Impl));
238 m_xOK->connect_clicked(LINK(this, SwCreateAddressListDialog, OkHdl_Impl));
240 Link<weld::Button&,void> aLk = LINK(this, SwCreateAddressListDialog, DBCursorHdl_Impl);
241 m_xStartPB->connect_clicked(aLk);
242 m_xPrevPB->connect_clicked(aLk);
243 m_xSetNoED->connect_changed(LINK(this, SwCreateAddressListDialog, DBNumCursorHdl_Impl));
244 m_xSetNoED->connect_focus_out(LINK(this, SwCreateAddressListDialog, RefreshNum_Impl));
245 m_xNextPB->connect_clicked(aLk);
246 m_xEndPB->connect_clicked(aLk);
248 if (!m_sURL.isEmpty())
250 //file exists, has to be loaded here
251 SfxMedium aMedium( m_sURL, StreamMode::READ );
252 SvStream* pStream = aMedium.GetInStream();
253 if(pStream)
255 pStream->SetLineDelimiter( LINEEND_LF );
256 pStream->SetStreamCharSet(RTL_TEXTENCODING_UTF8);
258 OUString sLine;
259 bool bRead = pStream->ReadByteStringLine( sLine, RTL_TEXTENCODING_UTF8 );
261 if(bRead && !sLine.isEmpty())
263 sal_Int32 nIndex = 0;
266 const OUString sHeader = sLine.getToken( 0, '\t', nIndex );
267 OSL_ENSURE(sHeader.getLength() > 2 &&
268 sHeader.startsWith("\"") && sHeader.endsWith("\""),
269 "Wrong format of header");
270 if(sHeader.getLength() > 2)
272 m_pCSVData->aDBColumnHeaders.push_back( sHeader.copy(1, sHeader.getLength() -2));
275 while (nIndex > 0);
277 while(pStream->ReadByteStringLine( sLine, RTL_TEXTENCODING_UTF8 ))
279 std::vector<OUString> aNewData;
280 //analyze data line
281 sal_Int32 nIndex = { sLine.isEmpty() ? -1 : 0 };
282 while (nIndex >= 0)
284 const OUString sData = sLine.getToken( 0, '\t', nIndex );
285 OSL_ENSURE( sData.startsWith("\"") && sData.endsWith("\""),
286 "Wrong format of line");
287 if(sData.getLength() >= 2)
288 aNewData.push_back(sData.copy(1, sData.getLength() - 2));
289 else
290 aNewData.push_back(sData);
292 m_pCSVData->aDBData.push_back( aNewData );
296 else
298 //database has to be created
299 const std::vector<std::pair<OUString, int>>& rAddressHeader = rConfig.GetDefaultAddressHeaders();
300 const sal_uInt32 nCount = rAddressHeader.size();
301 for(sal_uInt32 nHeader = 0; nHeader < nCount; ++nHeader)
302 m_pCSVData->aDBColumnHeaders.push_back(rAddressHeader[nHeader].first);
303 std::vector<OUString> aNewData;
304 aNewData.insert(aNewData.begin(), nCount, OUString());
305 m_pCSVData->aDBData.push_back(aNewData);
307 //now fill the address control
308 m_xAddressControl->SetData(*m_pCSVData);
309 m_xAddressControl->SetCurrentDataSet(0);
310 m_xSetNoNF->set_max(m_pCSVData->aDBData.size());
312 m_xSetNoNF->set_value(1);
313 RefreshNum_Impl(*m_xSetNoED);
315 UpdateButtons();
318 SwCreateAddressListDialog::~SwCreateAddressListDialog()
322 IMPL_LINK_NOARG(SwCreateAddressListDialog, NewHdl_Impl, weld::Button&, void)
324 sal_uInt32 nCurrent = m_xAddressControl->GetCurrentDataSet();
325 std::vector<OUString> aNewData;
326 aNewData.insert(aNewData.begin(), m_pCSVData->aDBColumnHeaders.size(), OUString());
327 m_pCSVData->aDBData.insert(m_pCSVData->aDBData.begin() + ++nCurrent, aNewData);
328 m_xSetNoNF->set_max(m_pCSVData->aDBData.size());
329 //the NumericField start at 1
330 m_xSetNoNF->set_value(nCurrent + 1);
331 RefreshNum_Impl(*m_xSetNoED);
332 //the address control starts at 0
333 m_xAddressControl->SetCurrentDataSet(nCurrent);
334 UpdateButtons();
337 IMPL_LINK_NOARG(SwCreateAddressListDialog, DeleteHdl_Impl, weld::Button&, void)
339 sal_uInt32 nCurrent = m_xAddressControl->GetCurrentDataSet();
340 if (m_pCSVData->aDBData.size() > 1)
342 m_pCSVData->aDBData.erase(m_pCSVData->aDBData.begin() + nCurrent);
343 if (nCurrent)
344 --nCurrent;
346 else
348 // if only one set is available then clear the data
349 m_pCSVData->aDBData[0].assign(m_pCSVData->aDBData[0].size(), OUString());
350 m_xDeletePB->set_sensitive(false);
352 m_xAddressControl->CurrentDataSetInvalidated();
353 m_xAddressControl->SetCurrentDataSet(nCurrent);
354 m_xSetNoNF->set_max(m_pCSVData->aDBData.size());
355 UpdateButtons();
358 IMPL_LINK_NOARG(SwCreateAddressListDialog, FindHdl_Impl, weld::Button&, void)
360 if (!m_xFindDlg)
362 m_xFindDlg.reset(new SwFindEntryDialog(this));
363 weld::ComboBox& rColumnBox = m_xFindDlg->GetFieldsListBox();
364 for(const auto& rHeader : m_pCSVData->aDBColumnHeaders)
365 rColumnBox.append_text(rHeader);
366 rColumnBox.set_active(0);
367 m_xFindDlg->show();
369 else
370 m_xFindDlg->set_visible(!m_xFindDlg->get_visible());
373 IMPL_LINK_NOARG(SwCreateAddressListDialog, CustomizeHdl_Impl, weld::Button&, void)
375 SwCustomizeAddressListDialog aDlg(m_xDialog.get(), *m_pCSVData);
376 if (aDlg.run() == RET_OK)
378 m_pCSVData = aDlg.ReleaseNewData();
379 m_xAddressControl->SetData(*m_pCSVData);
380 m_xAddressControl->SetCurrentDataSet(m_xAddressControl->GetCurrentDataSet());
383 //update find dialog
384 if (m_xFindDlg)
386 weld::ComboBox& rColumnBox = m_xFindDlg->GetFieldsListBox();
387 rColumnBox.clear();
388 for(const auto& rHeader : m_pCSVData->aDBColumnHeaders)
389 rColumnBox.append_text(rHeader);
393 namespace
396 void lcl_WriteValues(const std::vector<OUString> *pFields, SvStream* pStream)
398 OUStringBuffer sLine;
399 const std::vector< OUString >::const_iterator aBegin = pFields->begin();
400 const std::vector< OUString >::const_iterator aEnd = pFields->end();
401 for(std::vector< OUString >::const_iterator aIter = aBegin; aIter != aEnd; ++aIter)
403 if (aIter==aBegin)
405 sLine.append("\"").append(*aIter).append("\"");
407 else
409 sLine.append("\t\"").append(*aIter).append("\"");
412 pStream->WriteByteStringLine( sLine.makeStringAndClear(), RTL_TEXTENCODING_UTF8 );
417 IMPL_LINK_NOARG(SwCreateAddressListDialog, OkHdl_Impl, weld::Button&, void)
419 if(m_sURL.isEmpty())
421 sfx2::FileDialogHelper aDlgHelper(TemplateDescription::FILESAVE_SIMPLE,
422 FileDialogFlags::NONE, m_xDialog.get());
423 uno::Reference < XFilePicker3 > xFP = aDlgHelper.GetFilePicker();
425 const OUString sPath( SvtPathOptions().SubstituteVariable("$(userurl)/database") );
426 aDlgHelper.SetDisplayDirectory( sPath );
427 xFP->appendFilter( m_sAddressListFilterName, "*.csv" );
428 xFP->setCurrentFilter( m_sAddressListFilterName ) ;
430 if( ERRCODE_NONE == aDlgHelper.Execute() )
432 m_sURL = xFP->getSelectedFiles().getConstArray()[0];
433 INetURLObject aResult( m_sURL );
434 aResult.setExtension("csv");
435 m_sURL = aResult.GetMainURL(INetURLObject::DecodeMechanism::NONE);
438 if(m_sURL.isEmpty())
439 return;
441 SfxMedium aMedium( m_sURL, StreamMode::READWRITE|StreamMode::TRUNC );
442 SvStream* pStream = aMedium.GetOutStream();
443 pStream->SetLineDelimiter( LINEEND_LF );
444 pStream->SetStreamCharSet(RTL_TEXTENCODING_UTF8);
446 lcl_WriteValues(&(m_pCSVData->aDBColumnHeaders), pStream);
448 for(const auto& rData : m_pCSVData->aDBData)
450 lcl_WriteValues(&rData, pStream);
452 aMedium.Commit();
453 m_xDialog->response(RET_OK);
456 IMPL_LINK(SwCreateAddressListDialog, DBCursorHdl_Impl, weld::Button&, rButton, void)
458 int nValue = m_xSetNoNF->get_value();
460 if (&rButton == m_xStartPB.get())
461 nValue = 1;
462 else if (&rButton == m_xPrevPB.get())
464 if (nValue > 1)
465 --nValue;
467 else if (&rButton == m_xNextPB.get())
469 if (nValue < m_xSetNoNF->get_max())
470 ++nValue;
472 else //m_aEndPB
473 nValue = m_xSetNoNF->get_max();
474 if (nValue != m_xSetNoNF->get_value())
476 m_xSetNoNF->set_value(nValue);
477 RefreshNum_Impl(*m_xSetNoED);
478 DBNumCursor();
482 IMPL_LINK_NOARG(SwCreateAddressListDialog, DBNumCursorHdl_Impl, weld::Entry&, void)
484 m_xSetNoNF->set_text(m_xSetNoED->get_text());
485 DBNumCursor();
488 IMPL_LINK_NOARG(SwCreateAddressListDialog, RefreshNum_Impl, weld::Widget&, void)
490 m_xSetNoED->set_text(OUString::number(m_xSetNoNF->get_value()));
493 void SwCreateAddressListDialog::DBNumCursor()
495 m_xAddressControl->SetCurrentDataSet(m_xSetNoNF->get_value() - 1);
496 UpdateButtons();
499 void SwCreateAddressListDialog::UpdateButtons()
501 sal_uInt32 nCurrent = static_cast< sal_uInt32 >(m_xSetNoNF->get_value() );
502 sal_uInt32 nSize = static_cast<sal_uInt32>(m_pCSVData->aDBData.size());
503 m_xStartPB->set_sensitive(nCurrent != 1);
504 m_xPrevPB->set_sensitive(nCurrent != 1);
505 m_xNextPB->set_sensitive(nCurrent != nSize);
506 m_xEndPB->set_sensitive(nCurrent != nSize);
507 m_xDeletePB->set_sensitive(nSize > 0);
510 void SwCreateAddressListDialog::Find(const OUString& rSearch, sal_Int32 nColumn)
512 const OUString sSearch = rSearch.toAsciiLowerCase();
513 sal_uInt32 nCurrent = m_xAddressControl->GetCurrentDataSet();
514 //search forward
515 bool bFound = false;
516 sal_uInt32 nStart = nCurrent + 1;
517 sal_uInt32 nEnd = m_pCSVData->aDBData.size();
518 std::size_t nElement = 0;
519 sal_uInt32 nPos = 0;
520 for(short nTemp = 0; nTemp < 2 && !bFound; nTemp++)
522 for(nPos = nStart; nPos < nEnd; ++nPos)
524 std::vector< OUString> const & aData = m_pCSVData->aDBData[nPos];
525 if(nColumn >=0)
526 bFound = -1 != aData[static_cast<sal_uInt32>(nColumn)].toAsciiLowerCase().indexOf(sSearch);
527 else
529 for( nElement = 0; nElement < aData.size(); ++nElement)
531 bFound = -1 != aData[nElement].toAsciiLowerCase().indexOf(sSearch);
532 if(bFound)
534 nColumn = nElement; //TODO: std::size_t -> sal_Int32!
535 break;
539 if(bFound)
540 break;
542 nStart = 0;
543 nEnd = nCurrent + 1;
545 if(bFound)
547 m_xAddressControl->SetCurrentDataSet(nPos);
548 m_xSetNoNF->set_value( nPos + 1 );
549 RefreshNum_Impl(*m_xSetNoED);
550 UpdateButtons();
551 m_xAddressControl->SetCursorTo(nElement);
555 SwFindEntryDialog::SwFindEntryDialog(SwCreateAddressListDialog* pParent)
556 : GenericDialogController(pParent->getDialog(), "modules/swriter/ui/findentrydialog.ui", "FindEntryDialog")
557 , m_pParent(pParent)
558 , m_xFindED(m_xBuilder->weld_entry("entry"))
559 , m_xFindOnlyCB(m_xBuilder->weld_check_button("findin"))
560 , m_xFindOnlyLB(m_xBuilder->weld_combo_box("area"))
561 , m_xFindPB(m_xBuilder->weld_button("find"))
562 , m_xCancel(m_xBuilder->weld_button("cancel"))
564 m_xFindPB->connect_clicked(LINK(this, SwFindEntryDialog, FindHdl_Impl));
565 m_xFindED->connect_changed(LINK(this, SwFindEntryDialog, FindEnableHdl_Impl));
566 m_xCancel->connect_clicked(LINK(this, SwFindEntryDialog, CloseHdl_Impl));
569 SwFindEntryDialog::~SwFindEntryDialog()
573 IMPL_LINK_NOARG(SwFindEntryDialog, FindHdl_Impl, weld::Button&, void)
575 sal_Int32 nColumn = -1;
576 if (m_xFindOnlyCB->get_active())
577 nColumn = m_xFindOnlyLB->get_active();
578 m_pParent->Find(m_xFindED->get_text(), nColumn);
581 IMPL_LINK_NOARG(SwFindEntryDialog, FindEnableHdl_Impl, weld::Entry&, void)
583 m_xFindPB->set_sensitive(!m_xFindED->get_text().isEmpty());
586 IMPL_LINK_NOARG(SwFindEntryDialog, CloseHdl_Impl, weld::Button&, void)
588 m_xDialog->hide();
591 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */