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 <swmodule.hxx>
21 #include <wrtsh.hxx>
22 #include <dbfld.hxx>
23 #include <doc.hxx>
24 
25 #include "flddb.hxx"
26 #include <dbconfig.hxx>
27 #include <dbmgr.hxx>
28 
29 #define USER_DATA_VERSION_1     "1"
30 #define USER_DATA_VERSION USER_DATA_VERSION_1
31 
SwFieldDBPage(weld::Container * pPage,weld::DialogController * pController,const SfxItemSet * const pCoreSet)32 SwFieldDBPage::SwFieldDBPage(weld::Container* pPage, weld::DialogController* pController, const SfxItemSet *const pCoreSet)
33     : SwFieldPage(pPage, pController, "modules/swriter/ui/flddbpage.ui", "FieldDbPage", pCoreSet)
34     , m_nOldFormat(0)
35     , m_nOldSubType(0)
36     , m_xTypeLB(m_xBuilder->weld_tree_view("type"))
37     , m_xDatabaseTLB(new SwDBTreeList(m_xBuilder->weld_tree_view("select")))
38     , m_xAddDBPB(m_xBuilder->weld_button("browse"))
39     , m_xCondition(m_xBuilder->weld_widget("condgroup"))
40     , m_xConditionED(new ConditionEdit(m_xBuilder->weld_entry("condition")))
41     , m_xValue(m_xBuilder->weld_widget("recgroup"))
42     , m_xValueED(m_xBuilder->weld_entry("recnumber"))
43     , m_xDBFormatRB(m_xBuilder->weld_radio_button("fromdatabasecb"))
44     , m_xNewFormatRB(m_xBuilder->weld_radio_button("userdefinedcb"))
45     , m_xNumFormatLB(new NumFormatListBox(m_xBuilder->weld_combo_box("numformat")))
46     , m_xFormatLB(m_xBuilder->weld_combo_box("format"))
47     , m_xFormat(m_xBuilder->weld_widget("formatframe"))
48 {
49     SetTypeSel(-1); //TODO
50 
51     m_xTypeLB->make_sorted();
52     m_xFormatLB->make_sorted();
53 
54     auto nWidth = m_xTypeLB->get_approximate_digit_width() * FIELD_COLUMN_WIDTH;
55     auto nHeight = m_xTypeLB->get_height_rows(10);
56     m_xTypeLB->set_size_request(nWidth, nHeight);
57     m_xDatabaseTLB->set_size_request(nWidth*2, nHeight);
58 
59     m_xNumFormatLB->connect_changed(LINK(this, SwFieldDBPage, NumSelectHdl));
60     m_xDatabaseTLB->connect_changed(LINK(this, SwFieldDBPage, TreeSelectHdl));
61     m_xDatabaseTLB->connect_row_activated(LINK(this, SwFieldDBPage, TreeViewInsertHdl));
62 
63     m_xValueED->connect_changed(LINK(this, SwFieldDBPage, ModifyHdl));
64     m_xAddDBPB->connect_clicked(LINK(this, SwFieldDBPage, AddDBHdl));
65 }
66 
~SwFieldDBPage()67 SwFieldDBPage::~SwFieldDBPage()
68 {
69     // If we have no stored SwWrtShell, it means we didn't do anything useful - no need to revoke.
70     if (SwWrtShell* pSh = GetWrtShell())
71     {
72         // This would cleanup in the case of cancelled dialog
73         SwDBManager* pDbManager = pSh->GetDoc()->GetDBManager();
74         if (pDbManager)
75             pDbManager->RevokeLastRegistrations();
76     }
77 }
78 
79 // initialise TabPage
Reset(const SfxItemSet *)80 void SwFieldDBPage::Reset(const SfxItemSet*)
81 {
82     Init(); // general initialization
83 
84     const sal_Int32 nOldPos = m_xTypeLB->get_selected_index();
85     m_xTypeLB->freeze();
86     m_sOldDBName = m_xDatabaseTLB->GetDBName(m_sOldTableName, m_sOldColumnName);
87 
88     m_xTypeLB->clear();
89 
90     if (!IsFieldEdit())
91     {
92         // initialise TypeListBox
93         const SwFieldGroupRgn& rRg = SwFieldMgr::GetGroupRange(IsFieldDlgHtmlMode(), GetGroup());
94 
95         for(sal_uInt16 i = rRg.nStart; i < rRg.nEnd; ++i)
96         {
97             const SwFieldTypesEnum nTypeId = SwFieldMgr::GetTypeId(i);
98             m_xTypeLB->append(OUString::number(static_cast<sal_uInt16>(nTypeId)), SwFieldMgr::GetTypeStr(i));
99         }
100     }
101     else
102     {
103         const SwFieldTypesEnum nTypeId = GetCurField()->GetTypeId();
104         m_xTypeLB->append(OUString::number(static_cast<sal_uInt16>(nTypeId)),
105                           SwFieldMgr::GetTypeStr(SwFieldMgr::GetPos(nTypeId)));
106     }
107 
108     m_xTypeLB->thaw();
109 
110     // select old Pos
111     if (GetTypeSel() != -1)
112         m_xTypeLB->select(GetTypeSel());
113 
114     m_xFormatLB->clear();
115 
116     const sal_uInt16 nSize = GetFieldMgr().GetFormatCount(SwFieldTypesEnum::DatabaseSetNumber, IsFieldDlgHtmlMode());
117     for( sal_uInt16 i = 0; i < nSize; ++i )
118     {
119         const sal_uInt16 nFormatId = GetFieldMgr().GetFormatId( SwFieldTypesEnum::DatabaseSetNumber, i );
120         OUString sId(OUString::number(nFormatId));
121         m_xFormatLB->append(sId, GetFieldMgr().GetFormatStr(SwFieldTypesEnum::DatabaseSetNumber, i));
122         if (SVX_NUM_ARABIC == nFormatId)
123             m_xFormatLB->set_active_id(sId);
124     }
125 
126     if (!IsFieldEdit())
127     {
128         if (nOldPos != -1)
129             m_xTypeLB->select(nOldPos);
130 
131         if (!m_sOldDBName.isEmpty())
132         {
133             m_xDatabaseTLB->Select(m_sOldDBName, m_sOldTableName, m_sOldColumnName);
134         }
135         else
136         {
137             SwWrtShell *pSh = CheckAndGetWrtShell();
138             if(pSh)
139             {
140                 SwDBData aTmp(pSh->GetDBData());
141                 m_xDatabaseTLB->Select(aTmp.sDataSource, aTmp.sCommand, u"");
142             }
143         }
144     }
145 
146     if( !IsRefresh() )
147     {
148         const OUString sUserData = GetUserData();
149         sal_Int32 nIdx{ 0 };
150         if (sUserData.getToken(0, ';', nIdx).equalsIgnoreAsciiCase(USER_DATA_VERSION_1))
151         {
152             const sal_uInt16 nVal = o3tl::narrowing<sal_uInt16>(sUserData.getToken(0, ';', nIdx).toInt32());
153             if (nVal != USHRT_MAX)
154             {
155                 for (sal_Int32 i = 0, nEntryCount = m_xTypeLB->n_children(); i < nEntryCount; ++i)
156                 {
157                     if (nVal == m_xTypeLB->get_id(i).toUInt32())
158                     {
159                         m_xTypeLB->select(i);
160                         break;
161                     }
162                 }
163             }
164         }
165     }
166     TypeHdl(nullptr);
167 
168     m_xTypeLB->connect_changed(LINK(this, SwFieldDBPage, TypeListBoxHdl));
169     m_xTypeLB->connect_row_activated(LINK(this, SwFieldDBPage, TreeViewInsertHdl));
170 
171     if (IsFieldEdit())
172     {
173         m_xConditionED->save_value();
174         m_xValueED->save_value();
175         m_sOldDBName = m_xDatabaseTLB->GetDBName(m_sOldTableName, m_sOldColumnName);
176         m_nOldFormat = GetCurField()->GetFormat();
177         m_nOldSubType = GetCurField()->GetSubType();
178     }
179 }
180 
181 // SwFieldDBPage may ask for password to select current document's data source,
182 // so only do that when activating the page, not when dialog is creating all pages
DeferResetToFirstActivation()183 bool SwFieldDBPage::DeferResetToFirstActivation() { return true; }
184 
FillItemSet(SfxItemSet *)185 bool SwFieldDBPage::FillItemSet(SfxItemSet* )
186 {
187     OUString sTableName;
188     OUString sColumnName;
189     SwDBData aData;
190     sal_Bool bIsTable;
191     aData.sDataSource = m_xDatabaseTLB->GetDBName(sTableName, sColumnName, &bIsTable);
192     aData.sCommand = sTableName;
193     aData.nCommandType = bIsTable ? 0 : 1;
194     SwWrtShell *pSh = CheckAndGetWrtShell();
195     assert(pSh);
196 
197     SwDBManager* pDbManager = pSh->GetDoc()->GetDBManager();
198     if (pDbManager)
199         pDbManager->CommitLastRegistrations();
200 
201     if (aData.sDataSource.isEmpty())
202         aData = pSh->GetDBData();
203 
204     if(!aData.sDataSource.isEmpty())       // without database no new field command
205     {
206         const SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32());
207         sal_uLong nFormat = 0;
208         sal_uInt16 nSubType = 0;
209 
210         OUString sDBName = aData.sDataSource
211             + OUStringChar(DB_DELIM)
212             + aData.sCommand
213             + OUStringChar(DB_DELIM)
214             + OUString::number(aData.nCommandType)
215             + OUStringChar(DB_DELIM);
216         if (!sColumnName.isEmpty())
217         {
218             sDBName += sColumnName + OUStringChar(DB_DELIM);
219         }
220         OUString aName = sDBName + m_xConditionED->get_text();
221 
222         switch (nTypeId)
223         {
224         case SwFieldTypesEnum::Database:
225             nFormat = m_xNumFormatLB->GetFormat();
226             if (m_xNewFormatRB->get_sensitive() && m_xNewFormatRB->get_active())
227                 nSubType = nsSwExtendedSubType::SUB_OWN_FMT;
228             aName = sDBName;
229             break;
230 
231         case SwFieldTypesEnum::DatabaseSetNumber:
232             nFormat = m_xFormatLB->get_active_id().toUInt32();
233             break;
234         default: break;
235         }
236 
237         const OUString aVal(m_xValueED->get_text());
238         OUString sTempTableName;
239         OUString sTempColumnName;
240         OUString sTempDBName = m_xDatabaseTLB->GetDBName(sTempTableName, sTempColumnName);
241         bool bDBListBoxChanged = m_sOldDBName != sTempDBName ||
242             m_sOldTableName != sTempTableName || m_sOldColumnName != sTempColumnName;
243         if (!IsFieldEdit() ||
244             m_xConditionED->get_value_changed_from_saved() ||
245             m_xValueED->get_saved_value() != aVal ||
246              bDBListBoxChanged ||
247              m_nOldFormat != nFormat || m_nOldSubType != nSubType)
248         {
249             InsertField( nTypeId, nSubType, aName, aVal, nFormat);
250         }
251     }
252 
253     return false;
254 }
255 
Create(weld::Container * pPage,weld::DialogController * pController,const SfxItemSet * const pAttrSet)256 std::unique_ptr<SfxTabPage> SwFieldDBPage::Create( weld::Container* pPage, weld::DialogController* pController,
257                                         const SfxItemSet *const pAttrSet )
258 {
259     return std::make_unique<SwFieldDBPage>( pPage, pController, pAttrSet );
260 }
261 
GetGroup()262 sal_uInt16 SwFieldDBPage::GetGroup()
263 {
264     return GRP_DB;
265 }
266 
IMPL_LINK(SwFieldDBPage,TypeListBoxHdl,weld::TreeView &,rBox,void)267 IMPL_LINK( SwFieldDBPage, TypeListBoxHdl, weld::TreeView&, rBox, void )
268 {
269     TypeHdl(&rBox);
270 }
271 
TypeHdl(const weld::TreeView * pBox)272 void SwFieldDBPage::TypeHdl(const weld::TreeView* pBox)
273 {
274     // save old ListBoxPos
275     const sal_Int32 nOld = GetTypeSel();
276 
277     // current ListBoxPos
278     SetTypeSel(m_xTypeLB->get_selected_index());
279 
280     if (GetTypeSel() == -1)
281     {
282         SetTypeSel(0);
283         m_xTypeLB->select(0);
284     }
285 
286     if (nOld == GetTypeSel())
287         return;
288 
289     SwWrtShell *pSh = CheckAndGetWrtShell();
290     assert(pSh);
291     bool bCond = false, bSetNo = false, bFormat = false, bDBFormat = false;
292     const SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32());
293 
294     m_xDatabaseTLB->ShowColumns(nTypeId == SwFieldTypesEnum::Database);
295 
296     if (IsFieldEdit())
297     {
298         SwDBData aData;
299         OUString sColumnName;
300         if (nTypeId == SwFieldTypesEnum::Database)
301         {
302             aData = static_cast<SwDBField*>(GetCurField())->GetDBData();
303             sColumnName = static_cast<SwDBFieldType*>(GetCurField()->GetTyp())->GetColumnName();
304         }
305         else
306         {
307             aData = static_cast<SwDBNameInfField*>(GetCurField())->GetDBData(pSh->GetDoc());
308         }
309         m_xDatabaseTLB->Select(aData.sDataSource, aData.sCommand, sColumnName);
310     }
311 
312     switch (nTypeId)
313     {
314         case SwFieldTypesEnum::Database:
315         {
316             bFormat = true;
317             bDBFormat = true;
318             m_xNumFormatLB->show();
319             m_xFormatLB->hide();
320 
321             weld::Widget& rWidget = m_xNumFormatLB->get_widget();
322             m_xNewFormatRB->set_accessible_relation_label_for(&rWidget);
323             rWidget.set_accessible_relation_labeled_by(m_xNewFormatRB.get());
324             m_xFormatLB->set_accessible_relation_label_for(nullptr);
325 
326             if (pBox)   // type was changed by user
327                 m_xDBFormatRB->set_active(true);
328 
329             if (IsFieldEdit())
330             {
331                 if (GetCurField()->GetFormat() != 0 && GetCurField()->GetFormat() != SAL_MAX_UINT32)
332                     m_xNumFormatLB->SetDefFormat(GetCurField()->GetFormat());
333 
334                 if (GetCurField()->GetSubType() & nsSwExtendedSubType::SUB_OWN_FMT)
335                     m_xNewFormatRB->set_active(true);
336                 else
337                     m_xDBFormatRB->set_active(true);
338             }
339             break;
340         }
341         case SwFieldTypesEnum::DatabaseNumberSet:
342             bSetNo = true;
343             [[fallthrough]];
344         case SwFieldTypesEnum::DatabaseNextSet:
345             bCond = true;
346             if (IsFieldEdit())
347             {
348                 m_xConditionED->set_text(GetCurField()->GetPar1());
349                 m_xValueED->set_text(GetCurField()->GetPar2());
350             }
351             break;
352 
353         case SwFieldTypesEnum::DatabaseName:
354             break;
355 
356         case SwFieldTypesEnum::DatabaseSetNumber:
357         {
358             bFormat = true;
359             m_xNewFormatRB->set_active(true);
360             m_xNumFormatLB->hide();
361             m_xFormatLB->show();
362 
363             m_xNewFormatRB->set_accessible_relation_label_for(m_xFormatLB.get());
364             m_xFormatLB->set_accessible_relation_labeled_by(m_xNewFormatRB.get());
365             weld::Widget& rWidget = m_xNumFormatLB->get_widget();
366             rWidget.set_accessible_relation_label_for(nullptr);
367 
368             if( IsFieldEdit() )
369             {
370                 for (sal_Int32 nI = m_xFormatLB->get_count(); nI;)
371                 {
372                     if (GetCurField()->GetFormat() == m_xFormatLB->get_id(--nI).toUInt32())
373                     {
374                         m_xFormatLB->set_active( nI );
375                         break;
376                     }
377                 }
378             }
379             break;
380         }
381         default: break;
382     }
383 
384     m_xCondition->set_sensitive(bCond);
385     m_xValue->set_sensitive(bSetNo);
386     if (nTypeId != SwFieldTypesEnum::Database)
387     {
388         m_xDBFormatRB->set_sensitive(bDBFormat);
389         m_xNewFormatRB->set_sensitive(bDBFormat || bFormat);
390         m_xNumFormatLB->set_sensitive(bDBFormat);
391         m_xFormatLB->set_sensitive(bFormat);
392     }
393     m_xFormat->set_sensitive(bDBFormat || bFormat);
394 
395     if (!IsFieldEdit())
396     {
397         m_xValueED->set_text(OUString());
398         if (bCond)
399             m_xConditionED->set_text("TRUE");
400         else
401             m_xConditionED->set_text(OUString());
402     }
403 
404     CheckInsert();
405 }
406 
IMPL_LINK_NOARG(SwFieldDBPage,NumSelectHdl,weld::ComboBox &,void)407 IMPL_LINK_NOARG(SwFieldDBPage, NumSelectHdl, weld::ComboBox&, void)
408 {
409     m_xNewFormatRB->set_active(true);
410     m_xNumFormatLB->CallSelectHdl();
411 }
412 
CheckInsert()413 void SwFieldDBPage::CheckInsert()
414 {
415     bool bInsert = true;
416     const SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32());
417 
418     std::unique_ptr<weld::TreeIter> xIter(m_xDatabaseTLB->make_iterator());
419     if (m_xDatabaseTLB->get_selected(xIter.get()))
420     {
421         bool bEntry = m_xDatabaseTLB->iter_parent(*xIter);
422 
423         if (nTypeId == SwFieldTypesEnum::Database && bEntry)
424             bEntry = m_xDatabaseTLB->iter_parent(*xIter);
425 
426         bInsert &= bEntry;
427     }
428     else
429         bInsert = false;
430 
431     if (nTypeId == SwFieldTypesEnum::DatabaseNumberSet)
432     {
433         bool bHasValue = !m_xValueED->get_text().isEmpty();
434 
435         bInsert &= bHasValue;
436     }
437 
438     EnableInsert(bInsert);
439 }
440 
IMPL_LINK(SwFieldDBPage,TreeSelectHdl,weld::TreeView &,rBox,void)441 IMPL_LINK(SwFieldDBPage, TreeSelectHdl, weld::TreeView&, rBox, void)
442 {
443     std::unique_ptr<weld::TreeIter> xIter(rBox.make_iterator());
444     if (!rBox.get_selected(xIter.get()))
445         return;
446 
447     const SwFieldTypesEnum nTypeId = static_cast<SwFieldTypesEnum>(m_xTypeLB->get_id(GetTypeSel()).toUInt32());
448 
449     bool bEntry = m_xDatabaseTLB->iter_parent(*xIter);
450 
451     if (nTypeId == SwFieldTypesEnum::Database && bEntry)
452         bEntry = m_xDatabaseTLB->iter_parent(*xIter);
453 
454     CheckInsert();
455 
456     if (nTypeId != SwFieldTypesEnum::Database)
457         return;
458 
459     bool bNumFormat = false;
460 
461     if (bEntry)
462     {
463         OUString sTableName;
464         OUString sColumnName;
465         sal_Bool bIsTable;
466         OUString sDBName = m_xDatabaseTLB->GetDBName(sTableName, sColumnName, &bIsTable);
467         bNumFormat = GetFieldMgr().IsDBNumeric(sDBName,
468                     sTableName,
469                     bIsTable,
470                     sColumnName);
471         if (!IsFieldEdit())
472             m_xDBFormatRB->set_active(true);
473     }
474 
475     m_xDBFormatRB->set_sensitive(bNumFormat);
476     m_xNewFormatRB->set_sensitive(bNumFormat);
477     m_xNumFormatLB->set_sensitive(bNumFormat);
478     m_xFormat->set_sensitive(bNumFormat);
479 }
480 
IMPL_LINK_NOARG(SwFieldDBPage,AddDBHdl,weld::Button &,void)481 IMPL_LINK_NOARG(SwFieldDBPage, AddDBHdl, weld::Button&, void)
482 {
483     if (SwWrtShell* pSh = CheckAndGetWrtShell())
484     {
485         OUString sNewDB
486             = SwDBManager::LoadAndRegisterDataSource(GetFrameWeld(), pSh->GetDoc()->GetDocShell());
487         if (!sNewDB.isEmpty())
488         {
489             m_xDatabaseTLB->AddDataSource(sNewDB);
490         }
491     }
492 }
493 
494 // Modify
IMPL_LINK_NOARG(SwFieldDBPage,ModifyHdl,weld::Entry &,void)495 IMPL_LINK_NOARG(SwFieldDBPage, ModifyHdl, weld::Entry&, void)
496 {
497     CheckInsert();
498 }
499 
FillUserData()500 void    SwFieldDBPage::FillUserData()
501 {
502     const sal_Int32 nEntryPos = m_xTypeLB->get_selected_index();
503     const sal_uInt16 nTypeSel = ( -1 == nEntryPos )
504         ? USHRT_MAX : m_xTypeLB->get_id(nEntryPos).toUInt32();
505     SetUserData(USER_DATA_VERSION ";" + OUString::number( nTypeSel ));
506 }
507 
ActivateMailMergeAddress()508 void SwFieldDBPage::ActivateMailMergeAddress()
509 {
510     m_xTypeLB->select_id(OUString::number(static_cast<sal_uInt16>(SwFieldTypesEnum::Database)));
511     TypeListBoxHdl(*m_xTypeLB);
512     const SwDBData& rData = SW_MOD()->GetDBConfig()->GetAddressSource();
513     m_xDatabaseTLB->Select(rData.sDataSource, rData.sCommand, u"");
514 }
515 
SetWrtShell(SwWrtShell & rSh)516 void SwFieldDBPage::SetWrtShell(SwWrtShell& rSh)
517 {
518     // We need to remember the shell to be able to call correct SwDBManager
519     SwFieldPage::SetWrtShell(&rSh);
520     m_xDatabaseTLB->SetWrtShell(rSh);
521 }
522 
CheckAndGetWrtShell()523 SwWrtShell* SwFieldDBPage::CheckAndGetWrtShell()
524 {
525     SwWrtShell* pSh = GetWrtShell();
526     if (!pSh)
527     {
528         pSh = ::GetActiveWrtShell();
529         if (pSh) // this is not guaranteed: e.g., activating print preview with dialog active
530             SetWrtShell(*pSh);
531     }
532     return pSh;
533 }
534 
535 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
536