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 
21 #include "macrodlg.hxx"
22 #include <basidesh.hxx>
23 #include <strings.hrc>
24 #include <iderid.hxx>
25 
26 #include <iderdll.hxx>
27 #include "iderdll2.hxx"
28 
29 #include "moduldlg.hxx"
30 #include <basic/basmgr.hxx>
31 #include <basic/sbmeth.hxx>
32 #include <basic/sbmod.hxx>
33 #include <com/sun/star/script/XLibraryContainer2.hpp>
34 
35 #include <sfx2/app.hxx>
36 #include <sfx2/dispatch.hxx>
37 #include <sfx2/frame.hxx>
38 #include <sfx2/minfitem.hxx>
39 #include <sfx2/request.hxx>
40 #include <sfx2/sfxsids.hrc>
41 #include <tools/debug.hxx>
42 #include <vcl/commandevent.hxx>
43 #include <vcl/svapp.hxx>
44 #include <vcl/weld.hxx>
45 #include <osl/diagnose.h>
46 
47 namespace basctl
48 {
49 
50 using namespace ::com::sun::star;
51 using namespace ::com::sun::star::uno;
52 
MacroChooser(weld::Window * pParnt,const Reference<frame::XFrame> & xDocFrame)53 MacroChooser::MacroChooser(weld::Window* pParnt, const Reference< frame::XFrame >& xDocFrame)
54     : SfxDialogController(pParnt, "modules/BasicIDE/ui/basicmacrodialog.ui", "BasicMacroDialog")
55     , m_xDocumentFrame(xDocFrame)
56     // the Sfx doesn't ask the BasicManager whether modified or not
57     // => start saving in case of a change without a into the BasicIDE.
58     , bForceStoreBasic(false)
59     , nMode(All)
60     , m_xMacroNameEdit(m_xBuilder->weld_entry("macronameedit"))
61     , m_xMacroFromTxT(m_xBuilder->weld_label("macrofromft"))
62     , m_xMacrosSaveInTxt(m_xBuilder->weld_label("macrotoft"))
63     , m_xBasicBox(new SbTreeListBox(m_xBuilder->weld_tree_view("libraries"), m_xDialog.get()))
64     , m_xBasicBoxIter(m_xBasicBox->make_iterator())
65     , m_xMacrosInTxt(m_xBuilder->weld_label("existingmacrosft"))
66     , m_xMacroBox(m_xBuilder->weld_tree_view("macros"))
67     , m_xMacroBoxIter(m_xMacroBox->make_iterator())
68     , m_xRunButton(m_xBuilder->weld_button("ok"))
69     , m_xCloseButton(m_xBuilder->weld_button("close"))
70     , m_xAssignButton(m_xBuilder->weld_button("assign"))
71     , m_xEditButton(m_xBuilder->weld_button("edit"))
72     , m_xDelButton(m_xBuilder->weld_button("delete"))
73     , m_xNewButton(m_xBuilder->weld_button("new"))
74     , m_xOrganizeButton(m_xBuilder->weld_button("organize"))
75     , m_xNewLibButton(m_xBuilder->weld_button("newlibrary"))
76     , m_xNewModButton(m_xBuilder->weld_button("newmodule"))
77 {
78     m_xBasicBox->set_size_request(m_xBasicBox->get_approximate_digit_width() * 30, m_xBasicBox->get_height_rows(18));
79     m_xMacroBox->set_size_request(m_xMacroBox->get_approximate_digit_width() * 30, m_xMacroBox->get_height_rows(18));
80 
81     m_aMacrosInTxtBaseStr = m_xMacrosInTxt->get_label();
82 
83     m_xRunButton->connect_clicked( LINK( this, MacroChooser, ButtonHdl ) );
84     m_xCloseButton->connect_clicked( LINK( this, MacroChooser, ButtonHdl ) );
85     m_xAssignButton->connect_clicked( LINK( this, MacroChooser, ButtonHdl ) );
86     m_xEditButton->connect_clicked( LINK( this, MacroChooser, ButtonHdl ) );
87     m_xDelButton->connect_clicked( LINK( this, MacroChooser, ButtonHdl ) );
88     m_xNewButton->connect_clicked( LINK( this, MacroChooser, ButtonHdl ) );
89     m_xOrganizeButton->connect_clicked( LINK( this, MacroChooser, ButtonHdl ) );
90 
91     // Buttons only for MacroChooser::Recording
92     m_xNewLibButton->connect_clicked( LINK( this, MacroChooser, ButtonHdl ) );
93     m_xNewModButton->connect_clicked( LINK( this, MacroChooser, ButtonHdl ) );
94     m_xNewLibButton->hide();       // default
95     m_xNewModButton->hide();       // default
96     m_xMacrosSaveInTxt->hide();    // default
97 
98     m_xMacroNameEdit->connect_changed( LINK( this, MacroChooser, EditModifyHdl ) );
99 
100     m_xBasicBox->connect_changed( LINK( this, MacroChooser, BasicSelectHdl ) );
101 
102     m_xMacroBox->connect_row_activated( LINK( this, MacroChooser, MacroDoubleClickHdl ) );
103     m_xMacroBox->connect_changed( LINK( this, MacroChooser, MacroSelectHdl ) );
104     m_xMacroBox->connect_popup_menu( LINK( this, MacroChooser, ContextMenuHdl ) );
105 
106     m_xBasicBox->SetMode( BrowseMode::Modules );
107 
108     if (SfxDispatcher* pDispatcher = GetDispatcher())
109         pDispatcher->Execute( SID_BASICIDE_STOREALLMODULESOURCES );
110 
111     m_xBasicBox->ScanAllEntries();
112 }
113 
~MacroChooser()114 MacroChooser::~MacroChooser()
115 {
116     if (bForceStoreBasic)
117     {
118         SfxGetpApp()->SaveBasicAndDialogContainer();
119         bForceStoreBasic = false;
120     }
121 }
122 
StoreMacroDescription()123 void MacroChooser::StoreMacroDescription()
124 {
125     m_xBasicBox->get_selected(m_xBasicBoxIter.get());
126     EntryDescriptor aDesc = m_xBasicBox->GetEntryDescriptor(m_xBasicBoxIter.get());
127     OUString aMethodName;
128     if (m_xMacroBox->get_selected(m_xMacroBoxIter.get()))
129         aMethodName = m_xMacroBox->get_text(*m_xMacroBoxIter);
130     else
131         aMethodName = m_xMacroNameEdit->get_text();
132     if ( !aMethodName.isEmpty() )
133     {
134         aDesc.SetMethodName( aMethodName );
135         aDesc.SetType( OBJ_TYPE_METHOD );
136     }
137 
138     if (ExtraData* pData = basctl::GetExtraData())
139         pData->SetLastEntryDescriptor( aDesc );
140 }
141 
RestoreMacroDescription()142 void MacroChooser::RestoreMacroDescription()
143 {
144     EntryDescriptor aDesc;
145     if (Shell* pShell = GetShell())
146     {
147         if (BaseWindow* pCurWin = pShell->GetCurWindow())
148             aDesc = pCurWin->CreateEntryDescriptor();
149     }
150     else
151     {
152         if (ExtraData* pData = basctl::GetExtraData())
153             aDesc = pData->GetLastEntryDescriptor();
154     }
155 
156     m_xBasicBox->SetCurrentEntry(aDesc);
157     BasicSelectHdl(m_xBasicBox->get_widget());
158 
159     OUString aLastMacro( aDesc.GetMethodName() );
160     if (!aLastMacro.isEmpty())
161     {
162         // find entry in macro box
163         auto nIndex = m_xMacroBox->find_text(aLastMacro);
164         if (nIndex != -1)
165             m_xMacroBox->select(nIndex);
166         else
167         {
168             m_xMacroNameEdit->set_text(aLastMacro);
169             m_xMacroNameEdit->select_region(0, 0);
170         }
171     }
172 }
173 
run()174 short MacroChooser::run()
175 {
176     RestoreMacroDescription();
177 
178     // #104198 Check if "wrong" document is active
179     bool bSelectedEntry = m_xBasicBox->get_cursor(m_xBasicBoxIter.get());
180     EntryDescriptor aDesc(m_xBasicBox->GetEntryDescriptor(bSelectedEntry ? m_xBasicBoxIter.get() : nullptr));
181     const ScriptDocument& rSelectedDoc(aDesc.GetDocument());
182 
183     // App Basic is always ok, so only check if shell was found
184     if( rSelectedDoc.isDocument() && !rSelectedDoc.isActive() )
185     {
186         // Search for the right entry
187         bool bValidIter = m_xBasicBox->get_iter_first(*m_xBasicBoxIter);
188         while (bValidIter)
189         {
190             EntryDescriptor aCmpDesc(m_xBasicBox->GetEntryDescriptor(m_xBasicBoxIter.get()));
191             const ScriptDocument& rCmpDoc( aCmpDesc.GetDocument() );
192             if (rCmpDoc.isDocument() && rCmpDoc.isActive())
193             {
194                 std::unique_ptr<weld::TreeIter> xEntry(m_xBasicBox->make_iterator());
195                 m_xBasicBox->copy_iterator(*m_xBasicBoxIter, *xEntry);
196                 std::unique_ptr<weld::TreeIter> xLastValid(m_xBasicBox->make_iterator());
197                 bool bValidEntryIter = true;
198                 do
199                 {
200                     m_xBasicBox->copy_iterator(*xEntry, *xLastValid);
201                     bValidEntryIter = m_xBasicBox->iter_children(*xEntry);
202                 }
203                 while (bValidEntryIter);
204                 m_xBasicBox->set_cursor(*xLastValid);
205             }
206             bValidIter = m_xBasicBox->iter_next_sibling(*m_xBasicBoxIter);
207         }
208     }
209 
210     CheckButtons();
211     UpdateFields();
212 
213     // tdf#62955 - Allow searching a name with typing the first letter
214     m_xBasicBox->get_widget().grab_focus();
215 
216     if ( StarBASIC::IsRunning() )
217         m_xCloseButton->grab_focus();
218 
219     return SfxDialogController::run();
220 }
221 
EnableButton(weld::Button & rButton,bool bEnable)222 void MacroChooser::EnableButton(weld::Button& rButton, bool bEnable)
223 {
224     if ( bEnable )
225     {
226         if (nMode == ChooseOnly || nMode == Recording)
227             rButton.set_sensitive(&rButton == m_xRunButton.get());
228         else
229             rButton.set_sensitive(true);
230     }
231     else
232         rButton.set_sensitive(false);
233 }
234 
GetMacro()235 SbMethod* MacroChooser::GetMacro()
236 {
237     if (!m_xBasicBox->get_cursor(m_xBasicBoxIter.get()))
238         return nullptr;
239     SbModule* pModule = m_xBasicBox->FindModule(m_xBasicBoxIter.get());
240     if (!pModule)
241         return nullptr;
242     if (!m_xMacroBox->get_selected(m_xMacroBoxIter.get()))
243         return nullptr;
244     OUString aMacroName(m_xMacroBox->get_text(*m_xMacroBoxIter));
245     return pModule->FindMethod(aMacroName, SbxClassType::Method);
246 }
247 
DeleteMacro()248 void MacroChooser::DeleteMacro()
249 {
250     SbMethod* pMethod = GetMacro();
251     DBG_ASSERT( pMethod, "DeleteMacro: No Macro !" );
252     if (!(pMethod && QueryDelMacro(pMethod->GetName(), m_xDialog.get())))
253         return;
254 
255     if (SfxDispatcher* pDispatcher = GetDispatcher())
256         pDispatcher->Execute( SID_BASICIDE_STOREALLMODULESOURCES );
257 
258     // mark current doc as modified:
259     StarBASIC* pBasic = FindBasic(pMethod);
260     assert(pBasic && "Basic?!");
261     BasicManager* pBasMgr = FindBasicManager( pBasic );
262     DBG_ASSERT( pBasMgr, "BasMgr?" );
263     ScriptDocument aDocument( ScriptDocument::getDocumentForBasicManager( pBasMgr ) );
264     if ( aDocument.isDocument() )
265     {
266         aDocument.setDocumentModified();
267         if (SfxBindings* pBindings = GetBindingsPtr())
268             pBindings->Invalidate( SID_SAVEDOC );
269     }
270 
271     SbModule* pModule = pMethod->GetModule();
272     assert(pModule && "DeleteMacro: No Module?!");
273     OUString aSource( pModule->GetSource32() );
274     sal_uInt16 nStart, nEnd;
275     pMethod->GetLineRange( nStart, nEnd );
276     pModule->GetMethods()->Remove( pMethod );
277     CutLines( aSource, nStart-1, nEnd-nStart+1 );
278     pModule->SetSource32( aSource );
279 
280     // update module in library
281     OUString aLibName = pBasic->GetName();
282     OUString aModName = pModule->GetName();
283     OSL_VERIFY( aDocument.updateModule( aLibName, aModName, aSource ) );
284 
285     bool bSelected = m_xMacroBox->get_selected(m_xMacroBoxIter.get());
286     DBG_ASSERT(bSelected, "DeleteMacro: Entry ?!");
287     m_xMacroBox->remove(*m_xMacroBoxIter);
288     bForceStoreBasic = true;
289 }
290 
CreateMacro()291 SbMethod* MacroChooser::CreateMacro()
292 {
293     SbMethod* pMethod = nullptr;
294     m_xBasicBox->get_cursor(m_xBasicBoxIter.get());
295     EntryDescriptor aDesc = m_xBasicBox->GetEntryDescriptor(m_xBasicBoxIter.get());
296     const ScriptDocument& aDocument( aDesc.GetDocument() );
297     OSL_ENSURE( aDocument.isAlive(), "MacroChooser::CreateMacro: no document!" );
298     if ( !aDocument.isAlive() )
299         return nullptr;
300 
301     OUString aLibName( aDesc.GetLibName() );
302 
303     if ( aLibName.isEmpty() )
304         aLibName = "Standard" ;
305 
306     aDocument.getOrCreateLibrary( E_SCRIPTS, aLibName );
307 
308     OUString aOULibName( aLibName );
309     Reference< script::XLibraryContainer > xModLibContainer( aDocument.getLibraryContainer( E_SCRIPTS ) );
310     if ( xModLibContainer.is() && xModLibContainer->hasByName( aOULibName ) && !xModLibContainer->isLibraryLoaded( aOULibName ) )
311         xModLibContainer->loadLibrary( aOULibName );
312     Reference< script::XLibraryContainer > xDlgLibContainer( aDocument.getLibraryContainer( E_DIALOGS ) );
313     if ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aOULibName ) && !xDlgLibContainer->isLibraryLoaded( aOULibName ) )
314         xDlgLibContainer->loadLibrary( aOULibName );
315 
316     BasicManager* pBasMgr = aDocument.getBasicManager();
317     StarBASIC* pBasic = pBasMgr ? pBasMgr->GetLib( aLibName ) : nullptr;
318     if ( pBasic )
319     {
320         SbModule* pModule = nullptr;
321         OUString aModName( aDesc.GetName() );
322         if ( !aModName.isEmpty() )
323         {
324             // extract the module name from the string like "Sheet1 (Example1)"
325             if( aDesc.GetLibSubName() == IDEResId(RID_STR_DOCUMENT_OBJECTS) )
326             {
327                 aModName = aModName.getToken( 0, ' ' );
328             }
329             pModule = pBasic->FindModule( aModName );
330         }
331         else if ( !pBasic->GetModules().empty() )
332             pModule = pBasic->GetModules().front().get();
333 
334         // Retain the desired macro name before the macro dialog box is forced to close
335         // by opening the module name dialog window when no module exists in the current library.
336         OUString aSubName = m_xMacroNameEdit->get_text();
337 
338         if ( !pModule )
339         {
340             pModule = createModImpl(m_xDialog.get(), aDocument, *m_xBasicBox, aLibName, aModName, false);
341         }
342 
343         DBG_ASSERT( !pModule || !pModule->FindMethod( aSubName, SbxClassType::Method ), "Macro exists already!" );
344         pMethod = pModule ? basctl::CreateMacro( pModule, aSubName ) : nullptr;
345     }
346 
347     return pMethod;
348 }
349 
SaveSetCurEntry(weld::TreeView & rBox,const weld::TreeIter & rEntry)350 void MacroChooser::SaveSetCurEntry(weld::TreeView& rBox, const weld::TreeIter& rEntry)
351 {
352     // the edit would be killed by the highlight otherwise:
353 
354     OUString aSaveText(m_xMacroNameEdit->get_text());
355     int nStartPos, nEndPos;
356     m_xMacroNameEdit->get_selection_bounds(nStartPos, nEndPos);
357 
358     rBox.set_cursor(rEntry);
359 
360     m_xMacroNameEdit->set_text(aSaveText);
361     m_xMacroNameEdit->select_region(nStartPos, nEndPos);
362 }
363 
CheckButtons()364 void MacroChooser::CheckButtons()
365 {
366     const bool bCurEntry = m_xBasicBox->get_cursor(m_xBasicBoxIter.get());
367     EntryDescriptor aDesc = m_xBasicBox->GetEntryDescriptor(bCurEntry ? m_xBasicBoxIter.get() : nullptr);
368     const bool bMacroEntry = m_xMacroBox->get_selected(nullptr);
369     SbMethod* pMethod = GetMacro();
370 
371     // check, if corresponding libraries are readonly
372     bool bReadOnly = false;
373     sal_uInt16 nDepth = bCurEntry ? m_xBasicBox->get_iter_depth(*m_xBasicBoxIter) : 0;
374     if ( nDepth == 1 || nDepth == 2 )
375     {
376         const ScriptDocument& aDocument( aDesc.GetDocument() );
377         const OUString& aOULibName( aDesc.GetLibName() );
378         Reference< script::XLibraryContainer2 > xModLibContainer( aDocument.getLibraryContainer( E_SCRIPTS ), UNO_QUERY );
379         Reference< script::XLibraryContainer2 > xDlgLibContainer( aDocument.getLibraryContainer( E_DIALOGS ), UNO_QUERY );
380         if ( ( xModLibContainer.is() && xModLibContainer->hasByName( aOULibName ) && xModLibContainer->isLibraryReadOnly( aOULibName ) ) ||
381                 ( xDlgLibContainer.is() && xDlgLibContainer->hasByName( aOULibName ) && xDlgLibContainer->isLibraryReadOnly( aOULibName ) ) )
382         {
383             bReadOnly = true;
384         }
385     }
386 
387     if (nMode != Recording)
388     {
389         // Run...
390         bool bEnable = pMethod != nullptr;
391         if (nMode != ChooseOnly && StarBASIC::IsRunning())
392             bEnable = false;
393         EnableButton(*m_xRunButton, bEnable);
394     }
395 
396     // organising still possible?
397 
398     // Assign...
399     EnableButton(*m_xAssignButton, pMethod != nullptr);
400 
401     // Edit...
402     EnableButton(*m_xEditButton, bMacroEntry);
403 
404     // Organizer...
405     EnableButton(*m_xOrganizeButton, !StarBASIC::IsRunning() && nMode == All);
406 
407     // m_xDelButton/m_xNewButton ->...
408     bool bProtected = bCurEntry && m_xBasicBox->IsEntryProtected(m_xBasicBoxIter.get());
409     bool bShare = ( aDesc.GetLocation() == LIBRARY_LOCATION_SHARE );
410     bool bEnable = !StarBASIC::IsRunning() && nMode == All && !bProtected && !bReadOnly && !bShare;
411     EnableButton(*m_xDelButton, bEnable);
412     EnableButton(*m_xNewButton, bEnable);
413     if (nMode == All)
414     {
415         if (pMethod)
416         {
417             m_xDelButton->show();
418             m_xNewButton->hide();
419         }
420         else
421         {
422             m_xNewButton->show();
423             m_xDelButton->hide();
424         }
425     }
426 
427     if (nMode == Recording)
428     {
429         // save button
430         m_xRunButton->set_sensitive(!bProtected && !bReadOnly && !bShare);
431         // new library button
432         m_xNewLibButton->set_sensitive(!bShare);
433         // new module button
434         m_xNewModButton->set_sensitive(!bProtected && !bReadOnly && !bShare);
435     }
436 }
437 
IMPL_LINK_NOARG(MacroChooser,MacroDoubleClickHdl,weld::TreeView &,bool)438 IMPL_LINK_NOARG(MacroChooser, MacroDoubleClickHdl, weld::TreeView&, bool)
439 {
440     SbMethod* pMethod = GetMacro();
441     SbModule* pModule = pMethod ? pMethod->GetModule() : nullptr;
442     StarBASIC* pBasic = pModule ? static_cast<StarBASIC*>(pModule->GetParent()) : nullptr;
443     BasicManager* pBasMgr = pBasic ? FindBasicManager(pBasic) : nullptr;
444     ScriptDocument aDocument(ScriptDocument::getDocumentForBasicManager(pBasMgr));
445     if (aDocument.isDocument() && !aDocument.allowMacros())
446     {
447         std::unique_ptr<weld::MessageDialog> xError(
448             Application::CreateMessageDialog(m_xDialog.get(), VclMessageType::Warning,
449                                              VclButtonsType::Ok, IDEResId(RID_STR_CANNOTRUNMACRO)));
450         xError->run();
451         return true;
452     }
453 
454     StoreMacroDescription();
455     if (nMode == Recording)
456     {
457         if (pMethod && !QueryReplaceMacro(pMethod->GetName(), m_xDialog.get()))
458             return true;
459     }
460 
461     m_xDialog->response(Macro_OkRun);
462     return true;
463 }
464 
IMPL_LINK_NOARG(MacroChooser,MacroSelectHdl,weld::TreeView &,void)465 IMPL_LINK_NOARG(MacroChooser, MacroSelectHdl, weld::TreeView&, void)
466 {
467     UpdateFields();
468     CheckButtons();
469 }
470 
IMPL_LINK_NOARG(MacroChooser,BasicSelectHdl,weld::TreeView &,void)471 IMPL_LINK_NOARG(MacroChooser, BasicSelectHdl, weld::TreeView&, void)
472 {
473     m_xBasicBox->get_cursor(m_xBasicBoxIter.get());
474     SbModule* pModule = m_xBasicBox->FindModule(m_xBasicBoxIter.get());
475     m_xMacroBox->clear();
476     if (pModule)
477     {
478         m_xMacrosInTxt->set_label(m_aMacrosInTxtBaseStr + " " + pModule->GetName());
479 
480         m_xMacroBox->freeze();
481 
482         sal_uInt32 nMacroCount = pModule->GetMethods()->Count();
483         for ( sal_uInt32 iMeth = 0; iMeth  < nMacroCount; iMeth++ )
484         {
485             SbMethod* pMethod = static_cast<SbMethod*>(pModule->GetMethods()->Get(iMeth));
486             assert(pMethod && "Method not found!");
487             if (pMethod->IsHidden())
488                 continue;
489             m_xMacroBox->append_text(pMethod->GetName());
490         }
491 
492         m_xMacroBox->thaw();
493 
494         if (m_xMacroBox->get_iter_first(*m_xMacroBoxIter))
495             m_xMacroBox->set_cursor(*m_xMacroBoxIter);
496     }
497 
498     UpdateFields();
499     CheckButtons();
500 }
501 
IMPL_LINK_NOARG(MacroChooser,EditModifyHdl,weld::Entry &,void)502 IMPL_LINK_NOARG(MacroChooser, EditModifyHdl, weld::Entry&, void)
503 {
504     // select the module in which the macro is put at "new",
505     // if BasicManager or Lib is selecting
506     if (m_xBasicBox->get_cursor(m_xBasicBoxIter.get()))
507     {
508         sal_uInt16 nDepth = m_xBasicBox->get_iter_depth(*m_xBasicBoxIter);
509         if (nDepth == 1 && m_xBasicBox->IsEntryProtected(m_xBasicBoxIter.get()))
510         {
511             // then put to the respective Std-Lib...
512             m_xBasicBox->iter_parent(*m_xBasicBoxIter);
513             m_xBasicBox->iter_children(*m_xBasicBoxIter);
514         }
515         if (nDepth < 2)
516         {
517             std::unique_ptr<weld::TreeIter> xNewEntry(m_xBasicBox->make_iterator());
518             m_xBasicBox->copy_iterator(*m_xBasicBoxIter, *xNewEntry);
519             bool bCurEntry = true;
520             do
521             {
522                 bCurEntry = m_xBasicBox->iter_children(*m_xBasicBoxIter);
523                 if (bCurEntry)
524                 {
525                     m_xBasicBox->copy_iterator(*m_xBasicBoxIter, *xNewEntry);
526                     nDepth = m_xBasicBox->get_iter_depth(*m_xBasicBoxIter);
527                 }
528             }
529             while (bCurEntry && (nDepth < 2));
530             SaveSetCurEntry(m_xBasicBox->get_widget(), *xNewEntry);
531         }
532         auto nCount = m_xMacroBox->n_children();
533         if (nCount)
534         {
535             OUString aEdtText(m_xMacroNameEdit->get_text());
536             bool bFound = false;
537             bool bValidIter = m_xMacroBox->get_iter_first(*m_xMacroBoxIter);
538             while (bValidIter)
539             {
540                 if (m_xMacroBox->get_text(*m_xMacroBoxIter).equalsIgnoreAsciiCase(aEdtText))
541                 {
542                     SaveSetCurEntry(*m_xMacroBox, *m_xMacroBoxIter);
543                     bFound = true;
544                     break;
545                 }
546                 bValidIter = m_xMacroBox->iter_next_sibling(*m_xMacroBoxIter);
547             }
548             if (!bFound)
549             {
550                 bValidIter = m_xMacroBox->get_selected(m_xMacroBoxIter.get());
551                 // if the entry exists ->Select ->Description...
552                 if (bValidIter)
553                     m_xMacroBox->unselect(*m_xMacroBoxIter);
554             }
555         }
556     }
557 
558     CheckButtons();
559 }
560 
IMPL_LINK(MacroChooser,ButtonHdl,weld::Button &,rButton,void)561 IMPL_LINK(MacroChooser, ButtonHdl, weld::Button&, rButton, void)
562 {
563     // apart from New/Record the Description is done by LoseFocus
564     if (&rButton == m_xRunButton.get())
565     {
566         StoreMacroDescription();
567 
568         // #116444# check security settings before macro execution
569         if (nMode == All)
570         {
571             SbMethod* pMethod = GetMacro();
572             SbModule* pModule = pMethod ? pMethod->GetModule() : nullptr;
573             StarBASIC* pBasic = pModule ? static_cast<StarBASIC*>(pModule->GetParent()) : nullptr;
574             BasicManager* pBasMgr = pBasic ? FindBasicManager(pBasic) : nullptr;
575             if ( pBasMgr )
576             {
577                 ScriptDocument aDocument( ScriptDocument::getDocumentForBasicManager( pBasMgr ) );
578                 if ( aDocument.isDocument() && !aDocument.allowMacros() )
579                 {
580                     std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(m_xDialog.get(),
581                                                                 VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_CANNOTRUNMACRO)));
582                     xError->run();
583                     return;
584                 }
585             }
586         }
587         else if (nMode == Recording )
588         {
589             if ( !IsValidSbxName(m_xMacroNameEdit->get_text()) )
590             {
591                 std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(m_xDialog.get(),
592                                                             VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_BADSBXNAME)));
593                 xError->run();
594                 m_xMacroNameEdit->select_region(0, -1);
595                 m_xMacroNameEdit->grab_focus();
596                 return;
597             }
598 
599             SbMethod* pMethod = GetMacro();
600             if (pMethod && !QueryReplaceMacro(pMethod->GetName(), m_xDialog.get()))
601                 return;
602         }
603 
604         m_xDialog->response(Macro_OkRun);
605     }
606     else if (&rButton == m_xCloseButton.get())
607     {
608         StoreMacroDescription();
609         m_xDialog->response(Macro_Close);
610     }
611     else if (&rButton == m_xEditButton.get() || &rButton == m_xDelButton.get() || &rButton == m_xNewButton.get())
612     {
613         m_xBasicBox->get_cursor(m_xBasicBoxIter.get());
614         EntryDescriptor aDesc = m_xBasicBox->GetEntryDescriptor(m_xBasicBoxIter.get());
615         const ScriptDocument& aDocument( aDesc.GetDocument() );
616         DBG_ASSERT( aDocument.isAlive(), "MacroChooser::ButtonHdl: no document, or document is dead!" );
617         if ( !aDocument.isAlive() )
618             return;
619         BasicManager* pBasMgr = aDocument.getBasicManager();
620         const OUString& aLib( aDesc.GetLibName() );
621         OUString aMod( aDesc.GetName() );
622         // extract the module name from the string like "Sheet1 (Example1)"
623         if( aDesc.GetLibSubName() == IDEResId(RID_STR_DOCUMENT_OBJECTS) )
624         {
625             aMod = aMod.getToken( 0, ' ' );
626         }
627         const OUString& aSub( aDesc.GetMethodName() );
628         SfxMacroInfoItem aInfoItem( SID_BASICIDE_ARG_MACROINFO, pBasMgr, aLib, aMod, aSub, OUString() );
629         if (&rButton == m_xEditButton.get())
630         {
631             if (m_xMacroBox->get_selected(m_xMacroBoxIter.get()))
632                 aInfoItem.SetMethod(m_xMacroBox->get_text(*m_xMacroBoxIter));
633             StoreMacroDescription();
634             m_xDialog->hide(); // tdf#126828 dismiss dialog before opening new window
635 
636             SfxAllItemSet aArgs( SfxGetpApp()->GetPool() );
637             SfxRequest aRequest( SID_BASICIDE_APPEAR, SfxCallMode::SYNCHRON, aArgs );
638             SfxGetpApp()->ExecuteSlot( aRequest );
639 
640             if (SfxDispatcher* pDispatcher = GetDispatcher())
641             {
642                 pDispatcher->ExecuteList(SID_BASICIDE_EDITMACRO,
643                         SfxCallMode::ASYNCHRON, { &aInfoItem });
644             }
645             m_xDialog->response(Macro_Edit);
646         }
647         else
648         {
649             if (&rButton == m_xDelButton.get())
650             {
651                 DeleteMacro();
652                 if (SfxDispatcher* pDispatcher = GetDispatcher())
653                 {
654                     pDispatcher->ExecuteList( SID_BASICIDE_UPDATEMODULESOURCE,
655                                   SfxCallMode::SYNCHRON, { &aInfoItem });
656                 }
657                 CheckButtons();
658                 UpdateFields();
659                 //if ( m_xMacroBox->GetCurEntry() )    // OV-Bug ?
660                 //  m_xMacroBox->Select( m_xMacroBox->GetCurEntry() );
661             }
662             else
663             {
664                 if ( !IsValidSbxName(m_xMacroNameEdit->get_text()) )
665                 {
666                     std::unique_ptr<weld::MessageDialog> xError(Application::CreateMessageDialog(m_xDialog.get(),
667                                                                 VclMessageType::Warning, VclButtonsType::Ok, IDEResId(RID_STR_BADSBXNAME)));
668                     xError->run();
669                     m_xMacroNameEdit->select_region(0, -1);
670                     m_xMacroNameEdit->grab_focus();
671                     return;
672                 }
673                 SbMethod* pMethod = CreateMacro();
674                 if ( pMethod )
675                 {
676                     aInfoItem.SetMethod( pMethod->GetName() );
677                     aInfoItem.SetModule( pMethod->GetModule()->GetName() );
678                     aInfoItem.SetLib( pMethod->GetModule()->GetParent()->GetName() );
679                     SfxAllItemSet aArgs( SfxGetpApp()->GetPool() );
680                     SfxRequest aRequest( SID_BASICIDE_APPEAR, SfxCallMode::SYNCHRON, aArgs );
681                     SfxGetpApp()->ExecuteSlot( aRequest );
682 
683                     if (SfxDispatcher* pDispatcher = GetDispatcher())
684                     {
685                         pDispatcher->ExecuteList(SID_BASICIDE_EDITMACRO,
686                                 SfxCallMode::ASYNCHRON, { &aInfoItem });
687                     }
688                     StoreMacroDescription();
689                     m_xDialog->response(Macro_New);
690                 }
691             }
692         }
693     }
694     else if (&rButton == m_xAssignButton.get())
695     {
696         m_xBasicBox->get_cursor(m_xBasicBoxIter.get());
697         EntryDescriptor aDesc = m_xBasicBox->GetEntryDescriptor(m_xBasicBoxIter.get());
698         const ScriptDocument& aDocument( aDesc.GetDocument() );
699         DBG_ASSERT( aDocument.isAlive(), "MacroChooser::ButtonHdl: no document, or document is dead!" );
700         if ( !aDocument.isAlive() )
701             return;
702         BasicManager* pBasMgr = aDocument.getBasicManager();
703         const OUString& aLib( aDesc.GetLibName() );
704         const OUString& aMod( aDesc.GetName() );
705         OUString aSub( m_xMacroNameEdit->get_text() );
706         SbMethod* pMethod = GetMacro();
707         DBG_ASSERT( pBasMgr, "BasMgr?" );
708         DBG_ASSERT( pMethod, "Method?" );
709         OUString aComment( GetInfo( pMethod ) );
710         SfxMacroInfoItem aItem( SID_MACROINFO, pBasMgr, aLib, aMod, aSub, aComment );
711         SfxAllItemSet Args( SfxGetpApp()->GetPool() );
712 
713         SfxAllItemSet aInternalSet(SfxGetpApp()->GetPool());
714         if (m_xDocumentFrame.is())
715             aInternalSet.Put(SfxUnoFrameItem(SID_FILLFRAME, m_xDocumentFrame));
716 
717         SfxRequest aRequest(SID_CONFIG, SfxCallMode::SYNCHRON, Args, aInternalSet);
718         aRequest.AppendItem( aItem );
719         SfxGetpApp()->ExecuteSlot( aRequest );
720     }
721     else if (&rButton == m_xNewLibButton.get())
722     {
723         m_xBasicBox->get_cursor(m_xBasicBoxIter.get());
724         EntryDescriptor aDesc = m_xBasicBox->GetEntryDescriptor(m_xBasicBoxIter.get());
725         const ScriptDocument& aDocument( aDesc.GetDocument() );
726         createLibImpl(m_xDialog.get(), aDocument, nullptr, m_xBasicBox.get());
727     }
728     else if (&rButton == m_xNewModButton.get())
729     {
730         m_xBasicBox->get_cursor(m_xBasicBoxIter.get());
731         EntryDescriptor aDesc = m_xBasicBox->GetEntryDescriptor(m_xBasicBoxIter.get());
732         const ScriptDocument& aDocument( aDesc.GetDocument() );
733         const OUString& aLibName( aDesc.GetLibName() );
734         createModImpl(m_xDialog.get(), aDocument, *m_xBasicBox, aLibName, OUString(), true);
735     }
736     else if (&rButton == m_xOrganizeButton.get())
737     {
738         StoreMacroDescription();
739 
740         m_xBasicBox->get_selected(m_xBasicBoxIter.get());
741         auto xDlg(std::make_shared<OrganizeDialog>(m_xDialog.get(), 0));
742         weld::DialogController::runAsync(xDlg, [this](sal_Int32 nRet) {
743             if (nRet == RET_OK) // not only closed
744             {
745                 m_xDialog->response(Macro_Edit);
746                 return;
747             }
748 
749             Shell* pShell = GetShell();
750             if ( pShell && pShell->IsAppBasicModified() )
751                 bForceStoreBasic = true;
752 
753             m_xBasicBox->UpdateEntries();
754         });
755     }
756 }
757 
IMPL_LINK(MacroChooser,ContextMenuHdl,const CommandEvent &,rCEvt,bool)758 IMPL_LINK(MacroChooser, ContextMenuHdl, const CommandEvent&, rCEvt, bool)
759 {
760     if (rCEvt.GetCommand() != CommandEventId::ContextMenu || !m_xMacroBox->n_children())
761          return false;
762 
763     std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xMacroBox.get(), "modules/BasicIDE/ui/sortmenu.ui"));
764     std::unique_ptr<weld::Menu> xPopup(xBuilder->weld_menu("sortmenu"));
765     std::unique_ptr<weld::Menu> xDropMenu(xBuilder->weld_menu("sortsubmenu"));
766     xDropMenu->set_active("alphabetically", m_xMacroBox->get_sort_order());
767     xDropMenu->set_active("properorder", !m_xMacroBox->get_sort_order());
768 
769     OString sCommand(xPopup->popup_at_rect(m_xMacroBox.get(), tools::Rectangle(rCEvt.GetMousePosPixel(), Size(1,1))));
770     if (sCommand == "alphabetically")
771     {
772         m_xMacroBox->make_sorted();
773     }
774     else if (sCommand == "properorder")
775     {
776         m_xMacroBox->make_unsorted();
777         BasicSelectHdl(m_xBasicBox->get_widget());
778     }
779     else if (!sCommand.isEmpty())
780     {
781          SAL_WARN("basctl.basicide", "Unknown context menu action: " << sCommand );
782     }
783 
784     return true;
785 }
786 
UpdateFields()787 void MacroChooser::UpdateFields()
788 {
789     auto nMacroEntry = m_xMacroBox->get_selected_index();
790     m_xMacroNameEdit->set_text("");
791     if (nMacroEntry != -1)
792         m_xMacroNameEdit->set_text(m_xMacroBox->get_text(nMacroEntry));
793 }
794 
SetMode(Mode nM)795 void MacroChooser::SetMode (Mode nM)
796 {
797     nMode = nM;
798     switch (nMode)
799     {
800         case All:
801         {
802             m_xRunButton->set_label(IDEResId(RID_STR_RUN));
803             EnableButton(*m_xDelButton, true);
804             EnableButton(*m_xNewButton, true);
805             EnableButton(*m_xOrganizeButton, true);
806             break;
807         }
808 
809         case ChooseOnly:
810         {
811             m_xRunButton->set_label(IDEResId(RID_STR_CHOOSE));
812             EnableButton(*m_xDelButton, false);
813             EnableButton(*m_xNewButton, false);
814             EnableButton(*m_xOrganizeButton, false);
815             break;
816         }
817 
818         case Recording:
819         {
820             m_xRunButton->set_label(IDEResId(RID_STR_RECORD));
821             EnableButton(*m_xDelButton, false);
822             EnableButton(*m_xNewButton, false);
823             EnableButton(*m_xOrganizeButton, false);
824 
825             m_xAssignButton->hide();
826             m_xEditButton->hide();
827             m_xDelButton->hide();
828             m_xNewButton->hide();
829             m_xOrganizeButton->hide();
830             m_xMacroFromTxT->hide();
831 
832             m_xNewLibButton->show();
833             m_xNewModButton->show();
834             m_xMacrosSaveInTxt->show();
835 
836             break;
837         }
838     }
839     CheckButtons();
840 }
841 
GetInfo(SbxVariable * pVar)842 OUString MacroChooser::GetInfo( SbxVariable* pVar )
843 {
844     OUString aComment;
845     SbxInfoRef xInfo = pVar->GetInfo();
846     if ( xInfo.is() )
847         aComment = xInfo->GetComment();
848     return aComment;
849 }
850 
851 
852 } // namespace basctl
853 
854 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
855