Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / svx / source / form / navigatortree.cxx
blob6dee1b1dc0000850acdf14565dae2cb2c7be43f5
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 <memory>
21 #include <svx/dialmgr.hxx>
22 #include <svx/fmshell.hxx>
23 #include <svx/fmmodel.hxx>
24 #include <svx/fmpage.hxx>
25 #include <svx/svdpagv.hxx>
26 #include <svx/svditer.hxx>
28 #include <helpids.h>
29 #include <fmexpl.hxx>
30 #include <fmshimp.hxx>
31 #include <fmservs.hxx>
32 #include <fmundo.hxx>
33 #include <fmpgeimp.hxx>
34 #include <fmobj.hxx>
35 #include <fmprop.hxx>
36 #include <sal/log.hxx>
37 #include <vcl/svapp.hxx>
38 #include <sfx2/viewsh.hxx>
39 #include <sfx2/dispatch.hxx>
40 #include <sfx2/viewfrm.hxx>
41 #include <comphelper/processfactory.hxx>
42 #include <comphelper/property.hxx>
43 #include <comphelper/types.hxx>
44 #include <com/sun/star/form/FormComponentType.hpp>
45 #include <com/sun/star/sdb/CommandType.hpp>
46 #include <com/sun/star/beans/PropertyAttribute.hpp>
47 #include <com/sun/star/script/XEventAttacherManager.hpp>
48 #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
49 #include <com/sun/star/datatransfer/XTransferable.hpp>
50 #include <com/sun/star/uno/XComponentContext.hpp>
51 #include <svx/sdrpaintwindow.hxx>
53 #include <svx/strings.hrc>
54 #include <tools/diagnose_ex.h>
55 #include <svx/svxids.hrc>
56 #include <bitmaps.hlst>
57 #include <vcl/commandevent.hxx>
59 namespace svxform
61 #define EXPLORER_SYNC_DELAY 200
62 // Time (in ms) until explorer synchronizes the view after select or deselect
64 using namespace ::com::sun::star::uno;
65 using namespace ::com::sun::star::lang;
66 using namespace ::com::sun::star::beans;
67 using namespace ::com::sun::star::form;
68 using namespace ::com::sun::star::awt;
69 using namespace ::com::sun::star::container;
70 using namespace ::com::sun::star::script;
71 using namespace ::com::sun::star::datatransfer;
72 using namespace ::com::sun::star::datatransfer::clipboard;
73 using namespace ::com::sun::star::sdb;
76 // helper
79 typedef ::std::map< Reference< XInterface >, SdrObject* > MapModelToShape;
82 static void collectShapeModelMapping( SdrPage const * _pPage, MapModelToShape& _rMapping )
84 OSL_ENSURE( _pPage, "collectShapeModelMapping: invalid arg!" );
86 _rMapping.clear();
88 SdrObjListIter aIter( _pPage );
89 while ( aIter.IsMore() )
91 SdrObject* pSdrObject = aIter.Next();
92 FmFormObj* pFormObject = FmFormObj::GetFormObject( pSdrObject );
93 if ( !pFormObject )
94 continue;
96 Reference< XInterface > xNormalizedModel( pFormObject->GetUnoControlModel(), UNO_QUERY );
97 // note that this is normalized (i.e. queried for XInterface explicitly)
99 ::std::pair< MapModelToShape::iterator, bool > aPos =
100 _rMapping.emplace( xNormalizedModel, pSdrObject );
101 DBG_ASSERT( aPos.second, "collectShapeModelMapping: model was already existent!" );
102 // if this asserts, this would mean we have 2 shapes pointing to the same model
106 NavigatorTreeDropTarget::NavigatorTreeDropTarget(NavigatorTree& rTreeView)
107 : DropTargetHelper(rTreeView.get_widget().get_drop_target())
108 , m_rTreeView(rTreeView)
112 sal_Int8 NavigatorTreeDropTarget::AcceptDrop(const AcceptDropEvent& rEvt)
114 sal_Int8 nAccept = m_rTreeView.AcceptDrop(rEvt);
116 if (nAccept != DND_ACTION_NONE)
118 // to enable the autoscroll when we're close to the edges
119 weld::TreeView& rWidget = m_rTreeView.get_widget();
120 rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true);
123 return nAccept;
126 sal_Int8 NavigatorTreeDropTarget::ExecuteDrop(const ExecuteDropEvent& rEvt)
128 return m_rTreeView.ExecuteDrop(rEvt);
131 NavigatorTree::NavigatorTree(std::unique_ptr<weld::TreeView> xTreeView)
132 :m_xTreeView(std::move(xTreeView))
133 ,m_aDropTargetHelper(*this)
134 ,m_aControlExchange()
135 ,nEditEvent(nullptr)
136 ,m_sdiState(SDI_DIRTY)
137 ,m_nSelectLock(0)
138 ,m_nFormsSelected(0)
139 ,m_nControlsSelected(0)
140 ,m_nHiddenControls(0)
141 ,m_bDragDataDirty(false)
142 ,m_bPrevSelectionMixed(false)
143 ,m_bRootSelected(false)
144 ,m_bInitialUpdate(true)
145 ,m_bKeyboardCut( false )
146 ,m_bEditing( false )
148 m_xTreeView->set_help_id(HID_FORM_NAVIGATOR);
149 m_xTreeView->set_size_request(200, 200);
151 m_xTreeView->set_selection_mode(SelectionMode::Multiple);
153 m_pNavModel.reset(new NavigatorTreeModel());
154 Clear();
156 StartListening( *m_pNavModel );
158 m_aSynchronizeTimer.SetInvokeHandler(LINK(this, NavigatorTree, OnSynchronizeTimer));
159 m_xTreeView->connect_changed(LINK(this, NavigatorTree, OnEntrySelDesel));
160 m_xTreeView->connect_key_press(LINK(this, NavigatorTree, KeyInputHdl));
161 m_xTreeView->connect_popup_menu(LINK(this, NavigatorTree, PopupMenuHdl));
162 m_xTreeView->connect_editing(LINK(this, NavigatorTree, EditingEntryHdl),
163 LINK(this, NavigatorTree, EditedEntryHdl));
164 m_xTreeView->connect_drag_begin(LINK(this, NavigatorTree, DragBeginHdl));
167 NavigatorTree::~NavigatorTree()
169 if( nEditEvent )
170 Application::RemoveUserEvent( nEditEvent );
172 if (m_aSynchronizeTimer.IsActive())
173 m_aSynchronizeTimer.Stop();
175 DBG_ASSERT(GetNavModel() != nullptr, "NavigatorTree::~NavigatorTree : unexpected : no ExplorerModel");
176 EndListening( *m_pNavModel );
177 Clear();
178 m_pNavModel.reset();
181 void NavigatorTree::Clear()
183 m_pNavModel->Clear();
186 void NavigatorTree::UpdateContent( FmFormShell* pFormShell )
188 if (m_bInitialUpdate)
190 GrabFocus();
191 m_bInitialUpdate = false;
194 FmFormShell* pOldShell = GetNavModel()->GetFormShell();
195 FmFormPage* pOldPage = GetNavModel()->GetFormPage();
196 FmFormPage* pNewPage = pFormShell ? pFormShell->GetCurPage() : nullptr;
198 if ((pOldShell != pFormShell) || (pOldPage != pNewPage))
200 // new shell during editing
201 if (IsEditingActive())
203 m_xTreeView->end_editing();
204 m_bEditing = false;
207 m_bDragDataDirty = true; // as a precaution, although I don't drag
209 GetNavModel()->UpdateContent( pFormShell );
211 // if there is a form, expand root
212 if (m_xRootEntry && !m_xTreeView->get_row_expanded(*m_xRootEntry))
213 m_xTreeView->expand_row(*m_xRootEntry);
214 // if there is EXACTLY ONE form, expand it too
215 if (m_xRootEntry)
217 std::unique_ptr<weld::TreeIter> xFirst(m_xTreeView->make_iterator(m_xRootEntry.get()));
218 bool bFirst = m_xTreeView->iter_children(*xFirst);
219 if (bFirst)
221 std::unique_ptr<weld::TreeIter> xSibling(m_xTreeView->make_iterator(xFirst.get()));
222 if (!m_xTreeView->iter_next_sibling(*xSibling))
223 m_xTreeView->expand_row(*xFirst);
228 bool NavigatorTree::implAllowExchange( sal_Int8 _nAction, bool* _pHasNonHidden )
230 bool bCurEntry = m_xTreeView->get_cursor(nullptr);
231 if (!bCurEntry)
232 return false;
234 // Information for AcceptDrop and Execute Drop
235 CollectSelectionData(SDI_ALL);
236 if (m_arrCurrentSelection.empty())
237 // nothing to do
238 return false;
240 // check whether there are only hidden controls
241 // I may add a format to pCtrlExch
242 bool bHasNonHidden = std::any_of(m_arrCurrentSelection.begin(), m_arrCurrentSelection.end(),
243 [this](const auto& rEntry) {
244 FmEntryData* pCurrent = reinterpret_cast<FmEntryData*>(m_xTreeView->get_id(*rEntry).toInt64());
245 return !IsHiddenControl( pCurrent );
248 if ( bHasNonHidden && ( 0 == ( _nAction & DND_ACTION_MOVE ) ) )
249 // non-hidden controls need to be moved
250 return false;
252 if ( _pHasNonHidden )
253 *_pHasNonHidden = bHasNonHidden;
255 return true;
258 bool NavigatorTree::implPrepareExchange( sal_Int8 _nAction )
260 bool bHasNonHidden = false;
261 if ( !implAllowExchange( _nAction, &bHasNonHidden ) )
262 return false;
264 m_aControlExchange.prepareDrag();
265 m_aControlExchange->setFocusEntry(m_xTreeView->get_cursor(nullptr));
267 for (const auto& rpEntry : m_arrCurrentSelection)
268 m_aControlExchange->addSelectedEntry(m_xTreeView->make_iterator(rpEntry.get()));
270 m_aControlExchange->setFormsRoot( GetNavModel()->GetFormPage()->GetForms() );
271 m_aControlExchange->buildPathFormat(m_xTreeView.get(), m_xRootEntry.get());
273 if (!bHasNonHidden)
275 // create a sequence
276 Sequence< Reference< XInterface > > seqIFaces(m_arrCurrentSelection.size());
277 Reference< XInterface >* pArray = seqIFaces.getArray();
278 for (const auto& rpEntry : m_arrCurrentSelection)
280 *pArray = reinterpret_cast<FmEntryData*>(m_xTreeView->get_id(*rpEntry).toInt64())->GetElement();
281 ++pArray;
283 // and the new format
284 m_aControlExchange->addHiddenControlsFormat(seqIFaces);
287 m_bDragDataDirty = false;
288 return true;
291 IMPL_LINK(NavigatorTree, DragBeginHdl, bool&, rUnsetDragIcon, bool)
293 rUnsetDragIcon = false;
295 bool bSuccess = implPrepareExchange(DND_ACTION_COPYMOVE);
296 if (bSuccess)
298 OControlExchange& rExchange = *m_aControlExchange;
299 rtl::Reference<TransferDataContainer> xHelper(&rExchange);
300 m_xTreeView->enable_drag_source(xHelper, DND_ACTION_COPYMOVE);
301 rExchange.setDragging(true);
303 return !bSuccess;
306 IMPL_LINK(NavigatorTree, PopupMenuHdl, const CommandEvent&, rEvt, bool)
308 bool bHandled = false;
309 switch( rEvt.GetCommand() )
311 case CommandEventId::ContextMenu:
313 // Position of click
314 ::Point ptWhere;
315 if (rEvt.IsMouseEvent())
317 ptWhere = rEvt.GetMousePosPixel();
318 std::unique_ptr<weld::TreeIter> xClickedOn(m_xTreeView->make_iterator());
319 if (!m_xTreeView->get_dest_row_at_pos(ptWhere, xClickedOn.get(), false))
320 break;
321 if (!m_xTreeView->is_selected(*xClickedOn))
323 m_xTreeView->unselect_all();
324 m_xTreeView->select(*xClickedOn);
325 m_xTreeView->set_cursor(*xClickedOn);
328 else
330 if (m_arrCurrentSelection.empty()) // only happens with context menu via keyboard
331 break;
333 std::unique_ptr<weld::TreeIter> xCurrent(m_xTreeView->make_iterator());
334 if (!m_xTreeView->get_cursor(xCurrent.get()))
335 break;
336 ptWhere = m_xTreeView->get_row_area(*xCurrent).Center();
339 // update my selection data
340 CollectSelectionData(SDI_ALL);
342 // if there is at least one no-root-entry and the root selected, I deselect root
343 if ( (m_arrCurrentSelection.size() > 1) && m_bRootSelected )
345 const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
346 m_xTreeView->set_cursor(*rIter);
347 m_xTreeView->unselect(*m_xRootEntry);
349 bool bSingleSelection = (m_arrCurrentSelection.size() == 1);
352 DBG_ASSERT( (!m_arrCurrentSelection.empty()) || m_bRootSelected, "no entries selected" );
353 // shouldn't happen, because I would have selected one during call to IsSelected,
354 // if there was none before
357 // create menu
358 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
359 FmFormModel* pFormModel = pFormShell ? pFormShell->GetFormModel() : nullptr;
360 if( pFormShell && pFormModel )
362 std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), "svx/ui/formnavimenu.ui"));
363 std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu("menu"));
364 std::unique_ptr<weld::Menu> xSubMenuNew(xBuilder->weld_menu("submenu"));
366 // menu 'New' only exists, if only the root or only one form is selected
367 bool bShowNew = bSingleSelection && (m_nFormsSelected || m_bRootSelected);
368 if (!bShowNew)
369 xContextMenu->remove("new");
371 // 'New'\'Form' under the same terms
372 bool bShowForm = bSingleSelection && (m_nFormsSelected || m_bRootSelected);
373 if (bShowForm)
374 xSubMenuNew->append("form", SvxResId(RID_STR_FORM), RID_SVXBMP_FORM);
376 // 'New'\'hidden...', if exactly one form is selected
377 bool bShowHidden = bSingleSelection && m_nFormsSelected;
378 if (bShowHidden)
379 xSubMenuNew->append("hidden", SvxResId(RID_STR_HIDDEN), RID_SVXBMP_HIDDEN);
381 // 'Delete': everything which is not root can be removed
382 if (m_bRootSelected)
383 xContextMenu->remove("delete");
385 // 'Cut', 'Copy' and 'Paste'
386 bool bShowCut = !m_bRootSelected && implAllowExchange(DND_ACTION_MOVE);
387 if (!bShowCut)
388 xContextMenu->remove("cut");
389 bool bShowCopy = !m_bRootSelected && implAllowExchange(DND_ACTION_COPY);
390 if (!bShowCopy)
391 xContextMenu->remove("copy");
392 if (!implAcceptPaste())
393 xContextMenu->remove("paste");
395 // TabDialog, if exactly one form
396 bool bShowTabOrder = bSingleSelection && m_nFormsSelected;
397 if (!bShowTabOrder)
398 xContextMenu->remove("taborder");
400 bool bShowProps = true;
401 // in XML forms, we don't allow for the properties of a form
402 // #i36484#
403 if (pFormShell->GetImpl()->isEnhancedForm_Lock() && !m_nControlsSelected)
404 bShowProps = false;
405 // if the property browser is already open, we don't allow for the properties, too
406 if (pFormShell->GetImpl()->IsPropBrwOpen_Lock())
407 bShowProps = false;
409 // and finally, if there's a mixed selection of forms and controls, disable the entry, too
410 if (bShowProps && !pFormShell->GetImpl()->IsPropBrwOpen_Lock())
411 bShowProps =
412 (m_nControlsSelected && !m_nFormsSelected) || (!m_nControlsSelected && m_nFormsSelected);
414 if (!bShowProps)
415 xContextMenu->remove("props");
417 // rename, if one element and no root
418 bool bShowRename = bSingleSelection && !m_bRootSelected;
419 if (!bShowRename)
420 xContextMenu->remove("rename");
422 if (!m_bRootSelected)
424 // Readonly-entry is only for root
425 xContextMenu->remove("designmode");
426 // the same for automatic control focus
427 xContextMenu->remove("controlfocus");
430 std::unique_ptr<weld::Menu> xConversionMenu(xBuilder->weld_menu("changemenu"));
431 // ConvertTo-Slots are enabled, if one control is selected
432 // the corresponding slot is disabled
433 if (!m_bRootSelected && !m_nFormsSelected && (m_nControlsSelected == 1))
435 FmXFormShell::GetConversionMenu_Lock(*xConversionMenu);
436 #if OSL_DEBUG_LEVEL > 0
437 const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
438 FmControlData* pCurrent = reinterpret_cast<FmControlData*>(m_xTreeView->get_id(*rIter).toInt64());
439 OSL_ENSURE( pFormShell->GetImpl()->isSolelySelected_Lock( pCurrent->GetFormComponent() ),
440 "NavigatorTree::Command: inconsistency between the navigator selection, and the selection as the shell knows it!" );
441 #endif
443 pFormShell->GetImpl()->checkControlConversionSlotsForCurrentSelection_Lock(*xConversionMenu);
445 else
446 xContextMenu->remove("change");
448 if (m_bRootSelected)
450 // set OpenReadOnly
451 xContextMenu->set_active("designmode", pFormModel->GetOpenInDesignMode());
452 xContextMenu->set_active("controlfocus", pFormModel->GetAutoControlFocus());
455 OString sIdent = xContextMenu->popup_at_rect(m_xTreeView.get(), tools::Rectangle(ptWhere, ::Size(1, 1)));
456 if (sIdent == "form")
458 OUString aStr(SvxResId(RID_STR_FORM));
459 OUString aUndoStr = SvxResId(RID_STR_UNDO_CONTAINER_INSERT).replaceAll("#", aStr);
461 pFormModel->BegUndo(aUndoStr);
462 // slot was only available, if there is only one selected entry,
463 // which is a root or a form
464 const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
465 NewForm(*rIter);
466 pFormModel->EndUndo();
468 else if (sIdent == "hidden")
470 OUString aStr(SvxResId(RID_STR_CONTROL));
471 OUString aUndoStr = SvxResId(RID_STR_UNDO_CONTAINER_INSERT).replaceAll("#", aStr);
473 pFormModel->BegUndo(aUndoStr);
474 // slot was valid for (exactly) one selected form
475 const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
476 NewControl(FM_COMPONENT_HIDDEN, *rIter, true);
477 pFormModel->EndUndo();
479 else if (sIdent == "cut")
480 doCut();
481 else if (sIdent == "copy")
482 doCopy();
483 else if (sIdent == "paste")
484 doPaste();
485 else if (sIdent == "delete")
486 DeleteSelection();
487 else if (sIdent == "taborder")
489 // this slot was effective for exactly one selected form
490 const std::unique_ptr<weld::TreeIter>& rSelectedForm = *m_arrCurrentSelection.begin();
491 DBG_ASSERT( IsFormEntry(*rSelectedForm), "NavigatorTree::Command: This entry must be a FormEntry." );
493 FmFormData* pFormData = reinterpret_cast<FmFormData*>(m_xTreeView->get_id(*rSelectedForm).toInt64());
494 const Reference< XForm >& xForm( pFormData->GetFormIface());
496 Reference< XTabControllerModel > xTabController(xForm, UNO_QUERY);
497 if( !xTabController.is() )
498 break;
499 GetNavModel()->GetFormShell()->GetImpl()->ExecuteTabOrderDialog_Lock(xTabController);
501 else if (sIdent == "props")
502 ShowSelectionProperties(true);
503 else if (sIdent == "rename")
505 // only allowed for one no-root-entry
506 const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
507 m_xTreeView->start_editing(*rIter);
508 m_bEditing = true;
510 else if (sIdent == "designmode")
512 pFormModel->SetOpenInDesignMode( !pFormModel->GetOpenInDesignMode() );
513 pFormShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(SID_FM_OPEN_READONLY);
515 else if (sIdent == "controlfocus")
517 pFormModel->SetAutoControlFocus( !pFormModel->GetAutoControlFocus() );
518 pFormShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(SID_FM_AUTOCONTROLFOCUS);
520 else if (FmXFormShell::isControlConversionSlot(sIdent))
522 const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
523 FmControlData* pCurrent = reinterpret_cast<FmControlData*>(m_xTreeView->get_id(*rIter).toInt64());
524 if (pFormShell->GetImpl()->executeControlConversionSlot_Lock(pCurrent->GetFormComponent(), sIdent))
525 ShowSelectionProperties();
528 bHandled = true;
530 break;
531 default: break;
534 return bHandled;
537 std::unique_ptr<weld::TreeIter> NavigatorTree::FindEntry(FmEntryData* pEntryData)
539 std::unique_ptr<weld::TreeIter> xRet;
540 if(!pEntryData)
541 return xRet;
543 m_xTreeView->all_foreach([this, pEntryData, &xRet](weld::TreeIter& rEntry){
544 FmEntryData* pCurEntryData = reinterpret_cast<FmEntryData*>(m_xTreeView->get_id(rEntry).toInt64());
545 if (pCurEntryData && pCurEntryData->IsEqualWithoutChildren(pEntryData))
547 xRet = m_xTreeView->make_iterator(&rEntry);
548 return true;
550 return false;
553 return xRet;
556 void NavigatorTree::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
558 if( auto pRemovedHint = dynamic_cast<const FmNavRemovedHint*>(&rHint) )
560 FmEntryData* pEntryData = pRemovedHint->GetEntryData();
561 Remove( pEntryData );
563 else if( auto pInsertedHint = dynamic_cast<const FmNavInsertedHint*>(&rHint) )
565 FmEntryData* pEntryData = pInsertedHint->GetEntryData();
566 sal_uInt32 nRelPos = pInsertedHint->GetRelPos();
567 Insert( pEntryData, nRelPos );
569 else if( auto pReplacedHint = dynamic_cast<const FmNavModelReplacedHint*>(&rHint) )
571 FmEntryData* pData = pReplacedHint->GetEntryData();
572 std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pData);
573 if (xEntry)
575 // reset image
576 m_xTreeView->set_image(*xEntry, pData->GetNormalImage());
579 else if( auto pNameChangedHint = dynamic_cast<const FmNavNameChangedHint*>(&rHint) )
581 std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pNameChangedHint->GetEntryData());
582 m_xTreeView->set_text(*xEntry, pNameChangedHint->GetNewName());
584 else if( dynamic_cast<const FmNavClearedHint*>(&rHint) )
586 m_aCutEntries.clear();
587 if (m_aControlExchange.isDataExchangeActive())
588 m_aControlExchange.clear();
589 m_xTreeView->clear();
591 // default-entry "Forms"
592 OUString sText(SvxResId(RID_STR_FORMS));
593 m_xRootEntry = m_xTreeView->make_iterator();
594 m_xTreeView->insert(nullptr, -1, &sText, nullptr, nullptr, nullptr,
595 false, m_xRootEntry.get());
596 m_xTreeView->set_image(*m_xRootEntry, RID_SVXBMP_FORMS);
597 m_xTreeView->set_sensitive(*m_xRootEntry, true);
599 else if (dynamic_cast<const FmNavRequestSelectHint*>(&rHint))
601 FmNavRequestSelectHint* pershHint = const_cast<FmNavRequestSelectHint*>(static_cast<const FmNavRequestSelectHint*>(&rHint));
602 FmEntryDataArray& arredToSelect = pershHint->GetItems();
603 SynchronizeSelection(arredToSelect);
605 if (pershHint->IsMixedSelection())
606 // in this case I deselect all, although the view had a mixed selection
607 // during next selection, I must adapt the navigator to the view
608 m_bPrevSelectionMixed = true;
612 std::unique_ptr<weld::TreeIter> NavigatorTree::Insert(FmEntryData* pEntryData, int nRelPos)
614 // insert current entry
615 std::unique_ptr<weld::TreeIter> xParentEntry = FindEntry( pEntryData->GetParent() );
616 std::unique_ptr<weld::TreeIter> xNewEntry(m_xTreeView->make_iterator());
617 OUString sId(OUString::number(reinterpret_cast<sal_Int64>(pEntryData)));
619 if(!xParentEntry)
621 m_xTreeView->insert(m_xRootEntry.get(), nRelPos, &pEntryData->GetText(), &sId,
622 nullptr, nullptr, false, xNewEntry.get());
624 else
626 m_xTreeView->insert(xParentEntry.get(), nRelPos, &pEntryData->GetText(), &sId,
627 nullptr, nullptr, false, xNewEntry.get());
630 m_xTreeView->set_image(*xNewEntry, pEntryData->GetNormalImage());
631 m_xTreeView->set_sensitive(*xNewEntry, true);
633 // If root-entry, expand root
634 if (!xParentEntry)
635 m_xTreeView->expand_row(*m_xRootEntry);
637 // insert children
638 FmEntryDataList* pChildList = pEntryData->GetChildList();
639 size_t nChildCount = pChildList->size();
640 for( size_t i = 0; i < nChildCount; i++ )
642 FmEntryData* pChildData = pChildList->at( i );
643 Insert(pChildData, -1);
646 return xNewEntry;
649 void NavigatorTree::Remove( FmEntryData* pEntryData )
651 if( !pEntryData )
652 return;
654 // entry for the data
655 std::unique_ptr<weld::TreeIter> xEntry = FindEntry(pEntryData);
656 if (!xEntry)
657 return;
659 // delete entry from TreeListBox
660 // I'm not allowed, to treat the selection, which I trigger:
661 // select changes the MarkList of the view, if somebody else does this at the same time
662 // and removes a selection, we get a problem
663 // e.g. Group controls with open navigator
664 LockSelectionHandling();
666 // little problem: I remember the selected data, but if somebody deletes one of these entries,
667 // I get inconsistent... this would be bad
668 m_xTreeView->unselect(*xEntry);
670 // selection can be modified during deletion,
671 // but because I disabled SelectionHandling, I have to do it later
672 auto nExpectedSelectionCount = m_xTreeView->count_selected_rows();
674 ModelHasRemoved(xEntry.get());
675 m_xTreeView->remove(*xEntry);
677 if (nExpectedSelectionCount != m_xTreeView->count_selected_rows())
678 SynchronizeSelection();
680 // by default I treat the selection of course
681 UnlockSelectionHandling();
684 bool NavigatorTree::IsFormEntry(const weld::TreeIter& rEntry)
686 FmEntryData* pEntryData = reinterpret_cast<FmEntryData*>(m_xTreeView->get_id(rEntry).toInt64());
687 return !pEntryData || dynamic_cast<const FmFormData*>( pEntryData) != nullptr;
690 bool NavigatorTree::IsFormComponentEntry(const weld::TreeIter& rEntry)
692 FmEntryData* pEntryData = reinterpret_cast<FmEntryData*>(m_xTreeView->get_id(rEntry).toInt64());
693 return dynamic_cast<const FmControlData*>( pEntryData) != nullptr;
696 bool NavigatorTree::implAcceptPaste( )
698 auto nSelectedEntries = m_xTreeView->count_selected_rows();
699 if (nSelectedEntries != 1)
700 // no selected entry, or at least two selected entries
701 return false;
703 // get the clipboard
704 TransferableDataHelper aClipboardContent(TransferableDataHelper::CreateFromClipboard(GetSystemClipboard()));
706 sal_Int8 nAction = m_aControlExchange.isClipboardOwner() && doingKeyboardCut( ) ? DND_ACTION_MOVE : DND_ACTION_COPY;
707 std::unique_ptr<weld::TreeIter> xSelected(m_xTreeView->make_iterator());
708 if (!m_xTreeView->get_selected(xSelected.get()))
709 xSelected.reset();
710 return nAction == implAcceptDataTransfer(aClipboardContent.GetDataFlavorExVector(), nAction, xSelected.get(), false);
713 sal_Int8 NavigatorTree::implAcceptDataTransfer( const DataFlavorExVector& _rFlavors, sal_Int8 _nAction, const weld::TreeIter* _pTargetEntry, bool _bDnD )
715 // no target -> no drop
716 if (!_pTargetEntry)
717 return DND_ACTION_NONE;
719 // format check
720 bool bHasDefControlFormat = OControlExchange::hasFieldExchangeFormat( _rFlavors );
721 bool bHasControlPathFormat = OControlExchange::hasControlPathFormat( _rFlavors );
722 bool bHasHiddenControlsFormat = OControlExchange::hasHiddenControlModelsFormat( _rFlavors );
723 if (!bHasDefControlFormat && !bHasControlPathFormat && !bHasHiddenControlsFormat)
724 return DND_ACTION_NONE;
726 bool bSelfSource = _bDnD ? m_aControlExchange.isDragSource() : m_aControlExchange.isClipboardOwner();
728 if ( bHasHiddenControlsFormat )
729 { // bHasHiddenControlsFormat means that only hidden controls are part of the data
731 // hidden controls can be copied to a form only
732 if (m_xTreeView->iter_compare(*_pTargetEntry, *m_xRootEntry) == 0 || !IsFormEntry(*_pTargetEntry))
733 return DND_ACTION_NONE;
735 return bSelfSource ? ( DND_ACTION_COPYMOVE & _nAction ) : DND_ACTION_COPY;
738 if ( !bSelfSource )
740 // DnD or CnP crossing navigator boundaries
741 // The main problem here is that the current API does not allow us to sneak into the content which
742 // is to be inserted. So we have to allow it for the moment, but maybe reject later on (in the real drop).
744 // TODO: this smart behaviour later on ... at the moment, we disallow data transfer crossing navigator
745 // boundaries.
747 return DND_ACTION_NONE;
750 DBG_ASSERT( _bDnD ? m_aControlExchange.isDragSource() : m_aControlExchange.isClipboardOwner(),
751 "NavigatorTree::implAcceptDataTransfer: here only with source=dest!" );
752 // somebody changed the logic of this method ...
754 // from here on, I can work with m_aControlExchange instead of _rData!
756 bool bForeignCollection = m_aControlExchange->getFormsRoot().get() != GetNavModel()->GetFormPage()->GetForms().get();
757 if ( bForeignCollection )
759 // crossing shell/page boundaries, we can exchange hidden controls only
760 // But if we survived the checks above, we do not have hidden controls.
761 // -> no data transfer
762 DBG_ASSERT( !bHasHiddenControlsFormat, "NavigatorTree::implAcceptDataTransfer: still hidden controls format!" );
763 // somebody changed the logic of this method ...
765 return DND_ACTION_COPY;
768 if (DND_ACTION_MOVE != _nAction) // 'normal' controls within a shell are moved only (never copied)
769 return DND_ACTION_NONE;
771 if ( m_bDragDataDirty || !bHasDefControlFormat )
773 if (!bHasControlPathFormat)
774 // I am in the shell/page, which has the controls, but I have no format,
775 // which survived the shell change (SVX_FM_CONTROLS_AS_PATH)
776 return DND_ACTION_NONE;
778 // I must recreate the list of the ExchangeObjects, because the shell was changed during dragging
779 // (there are SvLBoxEntries in it, and we lost them during change)
780 m_aControlExchange->buildListFromPath(m_xTreeView.get(), m_xRootEntry.get());
781 m_bDragDataDirty = false;
784 // List of dropped entries from DragServer
785 const ListBoxEntrySet& rDropped = m_aControlExchange->selected();
786 DBG_ASSERT(!rDropped.empty(), "NavigatorTree::implAcceptDataTransfer: no entries !");
788 bool bDropTargetIsComponent = IsFormComponentEntry( *_pTargetEntry );
790 // conditions to disallow the drop
791 // 0) the root entry is part of the list (can't DnD the root!)
792 // 1) one of the dragged entries is to be dropped onto its own parent
793 // 2) - " - is to be dropped onto itself
794 // 3) - " - is a Form and to be dropped onto one of its descendants
795 // 4) one of the entries is a control and to be dropped onto the root
796 // 5) a control or form will be dropped onto a control which is _not_ a sibling (dropping onto a sibling
797 // means moving the control)
799 // collect the ancestors of the drop target (speeds up 3)
800 SvLBoxEntrySortedArray arrDropAncestors;
801 std::unique_ptr<weld::TreeIter> xLoop(m_xTreeView->make_iterator(_pTargetEntry));
804 arrDropAncestors.emplace(m_xTreeView->make_iterator(xLoop.get()));
806 while (m_xTreeView->iter_parent(*xLoop));
808 for (const auto& rCurrent : rDropped)
810 // test for 0)
811 if (m_xTreeView->iter_compare(*rCurrent, *m_xRootEntry) == 0)
812 return DND_ACTION_NONE;
814 std::unique_ptr<weld::TreeIter> xCurrentParent(m_xTreeView->make_iterator(rCurrent.get()));
815 m_xTreeView->iter_parent(*xCurrentParent);
817 // test for 1)
818 if (m_xTreeView->iter_compare(*_pTargetEntry, *xCurrentParent) == 0)
819 return DND_ACTION_NONE;
821 // test for 2)
822 if (m_xTreeView->iter_compare(*rCurrent, *_pTargetEntry) == 0)
823 return DND_ACTION_NONE;
825 // test for 5)
826 if (bDropTargetIsComponent)
827 return DND_ACTION_NONE;
829 // test for 3)
830 if (IsFormEntry(*rCurrent))
832 auto aIter = std::find_if(arrDropAncestors.begin(), arrDropAncestors.end(),
833 [this, &rCurrent](const auto& rElem) {
834 return m_xTreeView->iter_compare(*rElem, *rCurrent) == 0;
837 if ( aIter != arrDropAncestors.end() )
838 return DND_ACTION_NONE;
840 else if (IsFormComponentEntry(*rCurrent))
842 // test for 4)
843 if (m_xTreeView->iter_compare(*_pTargetEntry, *m_xRootEntry) == 0)
844 return DND_ACTION_NONE;
847 return DND_ACTION_MOVE;
850 sal_Int8 NavigatorTree::AcceptDrop( const AcceptDropEvent& rEvt )
852 ::Point aDropPos = rEvt.maPosPixel;
853 std::unique_ptr<weld::TreeIter> xDropTarget(m_xTreeView->make_iterator());
854 // get_dest_row_at_pos with false cause we must drop exactly "on" a form to paste a control into it
855 if (!m_xTreeView->get_dest_row_at_pos(aDropPos, xDropTarget.get(), false))
856 xDropTarget.reset();
857 return implAcceptDataTransfer(m_aDropTargetHelper.GetDataFlavorExVector(), rEvt.mnAction, xDropTarget.get(), true);
860 sal_Int8 NavigatorTree::implExecuteDataTransfer( const OControlTransferData& _rData, sal_Int8 _nAction, const ::Point& _rDropPos, bool _bDnD )
862 std::unique_ptr<weld::TreeIter> xDrop(m_xTreeView->make_iterator());
863 // get_dest_row_at_pos with false cause we must drop exactly "on" a form to paste a control into it
864 if (!m_xTreeView->get_dest_row_at_pos(_rDropPos, xDrop.get(), false))
865 xDrop.reset();
866 return implExecuteDataTransfer( _rData, _nAction, xDrop.get(), _bDnD );
869 sal_Int8 NavigatorTree::implExecuteDataTransfer( const OControlTransferData& _rData, sal_Int8 _nAction, const weld::TreeIter* _pTargetEntry, bool _bDnD )
871 const DataFlavorExVector& rDataFlavors = _rData.GetDataFlavorExVector();
873 if ( DND_ACTION_NONE == implAcceptDataTransfer( rDataFlavors, _nAction, _pTargetEntry, _bDnD ) )
874 // under some platforms, it may happen that ExecuteDrop is called though AcceptDrop returned DND_ACTION_NONE
875 return DND_ACTION_NONE;
877 if (!_pTargetEntry)
878 // no target -> no drop
879 return DND_ACTION_NONE;
881 // format checks
882 #ifdef DBG_UTIL
883 bool bHasHiddenControlsFormat = OControlExchange::hasHiddenControlModelsFormat( rDataFlavors );
884 bool bForeignCollection = _rData.getFormsRoot().get() != GetNavModel()->GetFormPage()->GetForms().get();
885 DBG_ASSERT(!bForeignCollection || bHasHiddenControlsFormat, "NavigatorTree::implExecuteDataTransfer: invalid format (AcceptDrop shouldn't have let this pass) !");
886 DBG_ASSERT(bForeignCollection || !m_bDragDataDirty, "NavigatorTree::implExecuteDataTransfer: invalid state (shell changed since last exchange resync) !");
887 // this should be done in AcceptDrop: the list of controls is created in _rData
888 // and m_bDragDataDirty is reset
889 #endif
891 if ( DND_ACTION_COPY == _nAction )
892 { // bHasHiddenControlsFormat means that only hidden controls are part of the data
893 #ifdef DBG_UTIL
894 DBG_ASSERT( bHasHiddenControlsFormat, "NavigatorTree::implExecuteDataTransfer: copy allowed for hidden controls only!" );
895 #endif
896 DBG_ASSERT( _pTargetEntry && m_xTreeView->iter_compare(*_pTargetEntry, *m_xRootEntry) != 0 && IsFormEntry( *_pTargetEntry ),
897 "NavigatorTree::implExecuteDataTransfer: should not be here!" );
898 // implAcceptDataTransfer should have caught both cases
900 #ifdef DBG_UTIL
901 DBG_ASSERT(bHasHiddenControlsFormat, "NavigatorTree::implExecuteDataTransfer: only copying of hidden controls is supported !");
902 // should be caught by AcceptDrop
903 #endif
905 // because i want to select all targets (and only them)
906 m_xTreeView->unselect_all();
908 const Sequence< Reference< XInterface > >& aControls = _rData.hiddenControls();
909 sal_Int32 nCount = aControls.getLength();
910 const Reference< XInterface >* pControls = aControls.getConstArray();
912 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
913 FmFormModel* pFormModel = pFormShell ? pFormShell->GetFormModel() : nullptr;
915 // within undo
916 if (pFormModel)
918 OUString aStr(SvxResId(RID_STR_CONTROL));
919 OUString aUndoStr = SvxResId(RID_STR_UNDO_CONTAINER_INSERT).replaceAll("#", aStr);
920 pFormModel->BegUndo(aUndoStr);
923 // copy controls
924 for (sal_Int32 i=0; i<nCount; ++i)
926 // create new control
927 FmControlData* pNewControlData = NewControl( FM_COMPONENT_HIDDEN, *_pTargetEntry, false);
928 Reference< XPropertySet > xNewPropSet( pNewControlData->GetPropertySet() );
930 // copy properties form old control to new one
931 Reference< XPropertySet > xCurrent(pControls[i], UNO_QUERY);
932 #if (OSL_DEBUG_LEVEL > 0)
933 // check whether it is a hidden control
934 sal_Int16 nClassId = ::comphelper::getINT16(xCurrent->getPropertyValue(FM_PROP_CLASSID));
935 OSL_ENSURE(nClassId == FormComponentType::HIDDENCONTROL, "NavigatorTree::implExecuteDataTransfer: invalid control in drop list !");
936 // if SVX_FM_HIDDEN_CONTROLS-format exists, the sequence
937 // should only contain hidden controls
938 #endif // (OSL_DEBUG_LEVEL > 0)
939 Reference< XPropertySetInfo > xPropInfo( xCurrent->getPropertySetInfo());
940 const Sequence< Property> seqAllCurrentProps = xPropInfo->getProperties();
941 for (Property const & currentProp : seqAllCurrentProps)
943 if (((currentProp.Attributes & PropertyAttribute::READONLY) == 0) && (currentProp.Name != FM_PROP_NAME))
944 { // (read-only attribs aren't set, ditto name,
945 // NewControl defined it uniquely
946 xNewPropSet->setPropertyValue(currentProp.Name, xCurrent->getPropertyValue(currentProp.Name));
950 std::unique_ptr<weld::TreeIter> xToSelect = FindEntry(pNewControlData);
951 m_xTreeView->select(*xToSelect);
952 if (i == 0)
953 m_xTreeView->set_cursor(*xToSelect);
956 if (pFormModel)
957 pFormModel->EndUndo();
959 return _nAction;
962 if ( !OControlExchange::hasFieldExchangeFormat( _rData.GetDataFlavorExVector() ) )
964 // can't do anything without the internal format here ... usually happens when doing DnD or CnP
965 // over navigator boundaries
966 return DND_ACTION_NONE;
969 // some data for the target
970 bool bDropTargetIsForm = IsFormEntry(*_pTargetEntry);
971 FmFormData* pTargetData = bDropTargetIsForm ? reinterpret_cast<FmFormData*>(m_xTreeView->get_id(*_pTargetEntry).toInt64()) : nullptr;
973 DBG_ASSERT( DND_ACTION_COPY != _nAction, "NavigatorTree::implExecuteDataTransfer: somebody changed the logics!" );
975 // list of dragged entries
976 const ListBoxEntrySet& rDropped = _rData.selected();
977 DBG_ASSERT(!rDropped.empty(), "NavigatorTree::implExecuteDataTransfer: no entries!");
979 // make a copy because rDropped is updated on deleting an entry which we do in the processing loop
980 ListBoxEntrySet aDropped;
981 for (const auto& rEntry : rDropped)
982 aDropped.emplace(m_xTreeView->make_iterator(rEntry.get()));
984 // shell and model
985 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
986 FmFormModel* pFormModel = pFormShell ? pFormShell->GetFormModel() : nullptr;
987 if (!pFormModel)
988 return DND_ACTION_NONE;
990 // for Undo
991 const bool bUndo = pFormModel->IsUndoEnabled();
993 if( bUndo )
995 OUString strUndoDescription(SvxResId(RID_STR_UNDO_CONTAINER_REPLACE));
996 pFormModel->BegUndo(strUndoDescription);
999 // remove selection before adding an entry, so the mark doesn't flicker
1000 // -> lock action of selection
1001 LockSelectionHandling();
1003 // go through all dropped entries
1004 for ( ListBoxEntrySet::const_iterator dropped = aDropped.begin();
1005 dropped != aDropped.end();
1006 ++dropped
1009 bool bFirstEntry = aDropped.begin() == dropped;
1011 // some data of the current element
1012 const auto& rCurrent = *dropped;
1013 DBG_ASSERT(rCurrent, "NavigatorTree::implExecuteDataTransfer: invalid entry");
1014 DBG_ASSERT(m_xTreeView->get_iter_depth(*rCurrent) != 0, "NavigatorTree::implExecuteDataTransfer: invalid entry");
1015 // don't drag root
1017 FmEntryData* pCurrentUserData = reinterpret_cast<FmEntryData*>(m_xTreeView->get_id(*rCurrent).toInt64());
1019 Reference< XChild > xCurrentChild = pCurrentUserData->GetChildIFace();
1020 Reference< XIndexContainer > xContainer(xCurrentChild->getParent(), UNO_QUERY);
1022 FmFormData* pCurrentParentUserData = static_cast<FmFormData*>(pCurrentUserData->GetParent());
1023 DBG_ASSERT(pCurrentParentUserData == nullptr || dynamic_cast<const FmFormData*>(pCurrentUserData->GetParent()) != nullptr, "NavigatorTree::implExecuteDataTransfer: invalid parent");
1025 // remove from parent
1026 if (pCurrentParentUserData)
1027 pCurrentParentUserData->GetChildList()->removeNoDelete( pCurrentUserData );
1028 else
1029 GetNavModel()->GetRootList()->removeNoDelete( pCurrentUserData );
1031 // remove from container
1032 sal_Int32 nIndex = getElementPos(xContainer, xCurrentChild);
1033 GetNavModel()->m_pPropChangeList->Lock();
1034 // UndoAction for removal
1035 if ( bUndo && GetNavModel()->m_pPropChangeList->CanUndo())
1037 pFormModel->AddUndo(std::make_unique<FmUndoContainerAction>(*pFormModel, FmUndoContainerAction::Removed,
1038 xContainer, xCurrentChild, nIndex));
1040 else if( !GetNavModel()->m_pPropChangeList->CanUndo() )
1042 FmUndoContainerAction::DisposeElement( xCurrentChild );
1045 // copy events
1046 Reference< XEventAttacherManager > xManager(xContainer, UNO_QUERY);
1047 Sequence< ScriptEventDescriptor > aEvts;
1049 if (xManager.is() && nIndex >= 0)
1050 aEvts = xManager->getScriptEvents(nIndex);
1051 xContainer->removeByIndex(nIndex);
1053 // remove selection
1054 m_xTreeView->unselect(*rCurrent);
1055 // and delete it
1056 Remove(pCurrentUserData);
1058 // position in DropParents, where to insert dropped entries
1059 if (pTargetData)
1060 xContainer.set(pTargetData->GetElement(), UNO_QUERY);
1061 else
1062 xContainer = GetNavModel()->GetForms();
1064 // always insert at the end
1065 nIndex = xContainer->getCount();
1067 // UndoAction for insertion
1068 if ( bUndo && GetNavModel()->m_pPropChangeList->CanUndo())
1069 pFormModel->AddUndo(std::make_unique<FmUndoContainerAction>(*pFormModel, FmUndoContainerAction::Inserted,
1070 xContainer, xCurrentChild, nIndex));
1072 // insert in new container
1073 if (pTargetData)
1075 // insert in a form needs a FormComponent
1076 xContainer->insertByIndex( nIndex,
1077 makeAny( Reference< XFormComponent >( xCurrentChild, UNO_QUERY ) ) );
1079 else
1081 xContainer->insertByIndex( nIndex,
1082 makeAny( Reference< XForm >( xCurrentChild, UNO_QUERY ) ) );
1085 if (aEvts.hasElements())
1087 xManager.set(xContainer, UNO_QUERY);
1088 if (xManager.is())
1089 xManager->registerScriptEvents(nIndex, aEvts);
1092 GetNavModel()->m_pPropChangeList->UnLock();
1094 // give an entry the new parent
1095 pCurrentUserData->SetParent(pTargetData);
1097 // give parent the new child
1098 if (pTargetData)
1099 pTargetData->GetChildList()->insert( std::unique_ptr<FmEntryData>(pCurrentUserData), nIndex );
1100 else
1101 GetNavModel()->GetRootList()->insert( std::unique_ptr<FmEntryData>(pCurrentUserData), nIndex );
1103 // announce to myself and reselect
1104 std::unique_ptr<weld::TreeIter> xNew = Insert( pCurrentUserData, nIndex );
1105 if (bFirstEntry && xNew)
1107 if (m_xTreeView->iter_parent(*xNew))
1108 m_xTreeView->expand_row(*xNew);
1112 UnlockSelectionHandling();
1114 if( bUndo )
1115 pFormModel->EndUndo();
1117 // During the move, the markings of the underlying view did not change (because the view is not affected by the logical
1118 // hierarchy of the form/control models. But my selection changed - which means I have to adjust it according to the
1119 // view marks, again.
1120 SynchronizeSelection();
1122 // in addition, with the move of controls such things as "the current form" may have changed - force the shell
1123 // to update itself accordingly
1124 if( pFormShell && pFormShell->GetImpl() && pFormShell->GetFormView() )
1125 pFormShell->GetImpl()->DetermineSelection_Lock( pFormShell->GetFormView()->GetMarkedObjectList() );
1127 if ( m_aControlExchange.isClipboardOwner() && ( DND_ACTION_MOVE == _nAction ) )
1128 m_aControlExchange->clear();
1130 return _nAction;
1133 sal_Int8 NavigatorTree::ExecuteDrop( const ExecuteDropEvent& rEvt )
1135 sal_Int8 nResult( DND_ACTION_NONE );
1136 if ( m_aControlExchange.isDragSource() )
1137 nResult = implExecuteDataTransfer( *m_aControlExchange, rEvt.mnAction, rEvt.maPosPixel, true );
1138 else
1140 OControlTransferData aDroppedData( rEvt.maDropEvent.Transferable );
1141 nResult = implExecuteDataTransfer( aDroppedData, rEvt.mnAction, rEvt.maPosPixel, true );
1143 return nResult;
1146 void NavigatorTree::doPaste()
1148 std::unique_ptr<weld::TreeIter> xSelected(m_xTreeView->make_iterator());
1149 if (!m_xTreeView->get_selected(xSelected.get()))
1150 xSelected.reset();
1154 if ( m_aControlExchange.isClipboardOwner() )
1156 implExecuteDataTransfer( *m_aControlExchange, doingKeyboardCut( ) ? DND_ACTION_MOVE : DND_ACTION_COPY, xSelected.get(), false );
1158 else
1160 // the clipboard content
1161 Reference< XClipboard > xClipboard( GetSystemClipboard() );
1162 Reference< XTransferable > xTransferable;
1163 if ( xClipboard.is() )
1164 xTransferable = xClipboard->getContents();
1166 OControlTransferData aClipboardContent( xTransferable );
1167 implExecuteDataTransfer( aClipboardContent, DND_ACTION_COPY, xSelected.get(), false );
1170 catch( const Exception& )
1172 TOOLS_WARN_EXCEPTION( "svx", "NavigatorTree::doPaste" );
1176 void NavigatorTree::doCopy()
1178 if ( implPrepareExchange( DND_ACTION_COPY ) )
1180 m_aControlExchange.setClipboardListener( LINK( this, NavigatorTree, OnClipboardAction ) );
1181 m_aControlExchange.copyToClipboard( );
1185 void NavigatorTree::ModelHasRemoved(const weld::TreeIter* pTypedEntry)
1187 if (doingKeyboardCut())
1189 auto aIter = std::find_if(m_aCutEntries.begin(), m_aCutEntries.end(),
1190 [this, pTypedEntry](const auto& rElem) {
1191 return m_xTreeView->iter_compare(*rElem, *pTypedEntry) == 0;
1193 if (aIter != m_aCutEntries.end())
1194 m_aCutEntries.erase(aIter);
1197 if (m_aControlExchange.isDataExchangeActive())
1199 if (0 == m_aControlExchange->onEntryRemoved(m_xTreeView.get(), pTypedEntry))
1201 // last of the entries which we put into the clipboard has been deleted from the tree.
1202 // Give up the clipboard ownership.
1203 m_aControlExchange.clear();
1208 void NavigatorTree::doCut()
1210 if ( !implPrepareExchange( DND_ACTION_MOVE ) )
1211 return;
1213 m_aControlExchange.setClipboardListener( LINK( this, NavigatorTree, OnClipboardAction ) );
1214 m_aControlExchange.copyToClipboard( );
1215 m_bKeyboardCut = true;
1217 // mark all the entries we just "cut" into the clipboard as "nearly moved"
1218 for (const auto& rEntry : m_arrCurrentSelection )
1220 if (!rEntry)
1221 continue;
1222 m_aCutEntries.emplace(m_xTreeView->make_iterator(rEntry.get()));
1223 m_xTreeView->set_sensitive(*rEntry, false);
1227 IMPL_LINK(NavigatorTree, KeyInputHdl, const ::KeyEvent&, rKEvt, bool)
1229 const vcl::KeyCode& rCode = rKEvt.GetKeyCode();
1231 // delete?
1232 if (rCode.GetCode() == KEY_DELETE && !rCode.GetModifier())
1234 DeleteSelection();
1235 return true;
1238 // copy'n'paste?
1239 switch ( rCode.GetFunction() )
1241 case KeyFuncType::CUT:
1242 doCut();
1243 break;
1245 case KeyFuncType::PASTE:
1246 if ( implAcceptPaste() )
1247 doPaste();
1248 break;
1250 case KeyFuncType::COPY:
1251 doCopy();
1252 break;
1254 default:
1255 break;
1258 return false;
1261 IMPL_LINK(NavigatorTree, EditingEntryHdl, const weld::TreeIter&, rIter, bool)
1263 // root, which isn't allowed to be renamed, has UserData=NULL
1264 m_bEditing = !m_xTreeView->get_id(rIter).isEmpty();
1265 return m_bEditing;
1268 void NavigatorTree::NewForm(const weld::TreeIter& rParentEntry)
1270 // get ParentFormData
1271 if (!IsFormEntry(rParentEntry))
1272 return;
1274 FmFormData* pParentFormData = reinterpret_cast<FmFormData*>(m_xTreeView->get_id(rParentEntry).toInt64());
1277 // create new form
1278 Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
1279 Reference< XForm > xNewForm(xContext->getServiceManager()->createInstanceWithContext(FM_SUN_COMPONENT_FORM, xContext), UNO_QUERY);
1280 if (!xNewForm.is())
1281 return;
1283 Reference< XPropertySet > xPropertySet(xNewForm, UNO_QUERY);
1284 if (!xPropertySet.is())
1285 return;
1287 FmFormData* pNewFormData = new FmFormData(xNewForm, pParentFormData);
1290 // set name
1291 OUString aName = GenerateName(pNewFormData);
1292 pNewFormData->SetText(aName);
1296 xPropertySet->setPropertyValue( FM_PROP_NAME, makeAny(aName) );
1297 // a form should always have the command type table as default
1298 xPropertySet->setPropertyValue( FM_PROP_COMMANDTYPE, makeAny(sal_Int32(CommandType::TABLE)));
1300 catch ( const Exception& )
1302 OSL_FAIL("NavigatorTree::NewForm : could not set essential properties!");
1306 // insert form
1307 GetNavModel()->Insert(pNewFormData, SAL_MAX_UINT32, true);
1310 // set new form as active
1311 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
1312 if( pFormShell )
1314 InterfaceBag aSelection;
1315 aSelection.insert( Reference<XInterface>( xNewForm, UNO_QUERY ) );
1316 pFormShell->GetImpl()->setCurrentSelection_Lock(aSelection);
1318 pFormShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(SID_FM_PROPERTIES, true, true);
1320 GetNavModel()->SetModified();
1322 // switch to EditMode
1323 std::unique_ptr<weld::TreeIter> xNewEntry = FindEntry(pNewFormData);
1324 m_xTreeView->start_editing(*xNewEntry);
1325 m_bEditing = true;
1328 FmControlData* NavigatorTree::NewControl(const OUString& rServiceName, const weld::TreeIter& rParentEntry, bool bEditName)
1330 // get ParentForm
1331 if (!GetNavModel()->GetFormShell())
1332 return nullptr;
1333 if (!IsFormEntry(rParentEntry))
1334 return nullptr;
1336 FmFormData* pParentFormData = reinterpret_cast<FmFormData*>(m_xTreeView->get_id(rParentEntry).toInt64());
1337 Reference<XForm> xParentForm(pParentFormData->GetFormIface());
1339 // create new component
1340 Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
1341 Reference<XFormComponent> xNewComponent( xContext->getServiceManager()->createInstanceWithContext(rServiceName, xContext), UNO_QUERY);
1342 if (!xNewComponent.is())
1343 return nullptr;
1345 FmControlData* pNewFormControlData = new FmControlData(xNewComponent, pParentFormData);
1347 // set name
1348 OUString sName = FmFormPageImpl::setUniqueName( xNewComponent, xParentForm );
1350 pNewFormControlData->SetText( sName );
1352 // insert FormComponent
1353 GetNavModel()->Insert(pNewFormControlData, SAL_MAX_UINT32, true);
1354 GetNavModel()->SetModified();
1356 if (bEditName)
1358 // switch to EditMode
1359 std::unique_ptr<weld::TreeIter> xNewEntry = FindEntry( pNewFormControlData );
1360 m_xTreeView->select(*xNewEntry);
1362 m_xTreeView->start_editing(*xNewEntry);
1363 m_bEditing = true;
1366 return pNewFormControlData;
1369 OUString NavigatorTree::GenerateName( FmEntryData const * pEntryData )
1371 const sal_uInt16 nMaxCount = 99;
1372 OUString aNewName;
1374 // create base name
1375 OUString aBaseName;
1376 if( dynamic_cast<const FmFormData*>( pEntryData) != nullptr )
1377 aBaseName = SvxResId( RID_STR_STDFORMNAME );
1378 else if( dynamic_cast<const FmControlData*>( pEntryData) != nullptr )
1379 aBaseName = SvxResId( RID_STR_CONTROL );
1382 // create new name
1383 FmFormData* pFormParentData = static_cast<FmFormData*>(pEntryData->GetParent());
1385 for( sal_Int32 i=0; i<nMaxCount; i++ )
1387 aNewName = aBaseName;
1388 if( i>0 )
1390 aNewName += " " + OUString::number(i);
1393 if( GetNavModel()->FindData(aNewName, pFormParentData,false) == nullptr )
1394 break;
1397 return aNewName;
1400 IMPL_LINK(NavigatorTree, EditedEntryHdl, const IterString&, rIterString, bool)
1402 m_bEditing = false;
1404 const weld::TreeIter& rIter = rIterString.first;
1406 FmEntryData* pEntryData = reinterpret_cast<FmEntryData*>(m_xTreeView->get_id(rIter).toInt64());
1407 bool bRes = NavigatorTreeModel::Rename(pEntryData, rIterString.second);
1408 if (!bRes)
1410 m_xEditEntry = m_xTreeView->make_iterator(&rIter);
1411 nEditEvent = Application::PostUserEvent(LINK(this, NavigatorTree, OnEdit));
1414 return bRes;
1417 IMPL_LINK_NOARG(NavigatorTree, OnEdit, void*, void)
1419 nEditEvent = nullptr;
1420 m_xTreeView->start_editing(*m_xEditEntry);
1421 m_bEditing = true;
1422 m_xEditEntry.reset();
1425 IMPL_LINK_NOARG(NavigatorTree, OnEntrySelDesel, weld::TreeView&, void)
1427 m_sdiState = SDI_DIRTY;
1429 if (IsSelectionHandlingLocked())
1430 return;
1432 if (m_aSynchronizeTimer.IsActive())
1433 m_aSynchronizeTimer.Stop();
1435 m_aSynchronizeTimer.SetTimeout(EXPLORER_SYNC_DELAY);
1436 m_aSynchronizeTimer.Start();
1439 IMPL_LINK_NOARG(NavigatorTree, OnSynchronizeTimer, Timer *, void)
1441 SynchronizeMarkList();
1444 IMPL_LINK_NOARG(NavigatorTree, OnClipboardAction, OLocalExchange&, void)
1446 if ( m_aControlExchange.isClipboardOwner() )
1447 return;
1449 if ( !doingKeyboardCut() )
1450 return;
1452 for (const auto& rEntry : m_aCutEntries)
1454 if (!rEntry)
1455 continue;
1456 m_xTreeView->set_sensitive(*rEntry, true);
1458 ListBoxEntrySet aEmpty;
1459 m_aCutEntries.swap( aEmpty );
1461 m_bKeyboardCut = false;
1464 void NavigatorTree::ShowSelectionProperties(bool bForce)
1466 // at first i need the FormShell
1467 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
1468 if (!pFormShell)
1469 // no shell -> impossible to set curObject -> leave
1470 return;
1472 CollectSelectionData(SDI_ALL);
1473 SAL_WARN_IF(static_cast<size_t>(m_nFormsSelected + m_nControlsSelected
1474 + (m_bRootSelected ? 1 : 0)) != m_arrCurrentSelection.size(),
1475 "svx.form",
1476 "NavigatorTree::ShowSelectionProperties : selection meta data invalid !");
1479 InterfaceBag aSelection;
1480 bool bSetSelectionAsMarkList = false;
1482 if (m_bRootSelected)
1483 ; // no properties for the root, neither for single nor for multi selection
1484 else if ( m_nFormsSelected + m_nControlsSelected == 0 ) // none of the two should be less 0
1485 ; // no selection -> no properties
1486 else if ( m_nFormsSelected * m_nControlsSelected != 0 )
1487 ; // mixed selection -> no properties
1488 else
1489 { // either only forms, or only controls are selected
1490 if (m_arrCurrentSelection.size() == 1)
1492 const std::unique_ptr<weld::TreeIter>& rIter = *m_arrCurrentSelection.begin();
1493 if (m_nFormsSelected > 0)
1494 { // exactly one form is selected
1495 FmFormData* pFormData = reinterpret_cast<FmFormData*>(m_xTreeView->get_id(*rIter).toInt64());
1496 aSelection.insert( Reference< XInterface >( pFormData->GetFormIface(), UNO_QUERY ) );
1498 else
1499 { // exactly one control is selected (whatever hidden or normal)
1500 FmEntryData* pEntryData = reinterpret_cast<FmEntryData*>(m_xTreeView->get_id(*rIter).toInt64());
1502 aSelection.insert( Reference< XInterface >( pEntryData->GetElement(), UNO_QUERY ) );
1505 else
1506 { // it's a MultiSelection, so we must build a MultiSet
1507 if (m_nFormsSelected > 0)
1508 { // ... only forms
1509 // first of all collect PropertySet-Interfaces of the forms
1510 SvLBoxEntrySortedArray::const_iterator it = m_arrCurrentSelection.begin();
1511 for ( sal_Int32 i = 0; i < m_nFormsSelected; ++i )
1513 const std::unique_ptr<weld::TreeIter>& rIter = *it;
1514 FmFormData* pFormData = reinterpret_cast<FmFormData*>(m_xTreeView->get_id(*rIter).toInt64());
1515 aSelection.insert( pFormData->GetPropertySet().get() );
1516 ++it;
1519 else
1520 { // ... only controls
1521 if (m_nHiddenControls == m_nControlsSelected)
1522 { // a MultiSet for properties of hidden controls
1523 SvLBoxEntrySortedArray::const_iterator it = m_arrCurrentSelection.begin();
1524 for ( sal_Int32 i = 0; i < m_nHiddenControls; ++i )
1526 const std::unique_ptr<weld::TreeIter>& rIter = *it;
1527 FmEntryData* pEntryData = reinterpret_cast<FmEntryData*>(m_xTreeView->get_id(*rIter).toInt64());
1528 aSelection.insert( pEntryData->GetPropertySet().get() );
1529 ++it;
1532 else if (m_nHiddenControls == 0)
1533 { // only normal controls
1534 bSetSelectionAsMarkList = true;
1541 // and now my form and my SelObject
1542 if ( bSetSelectionAsMarkList )
1543 pFormShell->GetImpl()->setCurrentSelectionFromMark_Lock(pFormShell->GetFormView()->GetMarkedObjectList());
1544 else
1545 pFormShell->GetImpl()->setCurrentSelection_Lock(aSelection);
1547 if (pFormShell->GetImpl()->IsPropBrwOpen_Lock() || bForce)
1549 // and now deliver all to the PropertyBrowser
1550 pFormShell->GetViewShell()->GetViewFrame()->GetDispatcher()->Execute( SID_FM_SHOW_PROPERTY_BROWSER, SfxCallMode::ASYNCHRON );
1555 void NavigatorTree::DeleteSelection()
1557 // of course, i can't delete root
1558 bool bRootSelected = m_xTreeView->is_selected(*m_xRootEntry);
1559 auto nSelectedEntries = m_xTreeView->count_selected_rows();
1560 if (bRootSelected && (nSelectedEntries > 1)) // root and other elements ?
1561 m_xTreeView->unselect(*m_xRootEntry); // yes -> remove root from selection
1563 if ((nSelectedEntries == 0) || bRootSelected) // still root ?
1564 return; // -> only selected element -> leave
1566 DBG_ASSERT(!m_bPrevSelectionMixed, "NavigatorTree::DeleteSelection() : delete permitted if mark and selection are inconsistent");
1568 // i need the FormModel later
1569 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
1570 if (!pFormShell)
1571 return;
1572 FmFormModel* pFormModel = pFormShell->GetFormModel();
1573 if (!pFormModel)
1574 return;
1576 // now I have to safeguard the DeleteList: if you delete a form and a dependent element
1577 // - in this order - than the SvLBoxEntryPtr of the dependent element is already invalid,
1578 // when it should be deleted... you have to prohibit this GPF, that of course would happen,
1579 // so I take the 'normalized' list
1580 CollectSelectionData( SDI_NORMALIZED );
1582 // see below for why we need this mapping from models to shapes
1583 FmFormView* pFormView = pFormShell->GetFormView();
1584 SdrPageView* pPageView = pFormView ? pFormView->GetSdrPageView() : nullptr;
1585 SdrPage* pPage = pPageView ? pPageView->GetPage() : nullptr;
1586 DBG_ASSERT( pPage, "NavigatorTree::DeleteSelection: invalid form page!" );
1588 MapModelToShape aModelShapes;
1589 if ( pPage )
1590 collectShapeModelMapping( pPage, aModelShapes );
1592 // problem: we have to use ExplorerModel::Remove, since only this one properly deletes Form objects.
1593 // But, the controls themself must be deleted via DeleteMarked (else, the Writer has some problems
1594 // somewhere). In case I'd first delete the structure, then the controls, the UNDO would not work
1595 // (since UNDO then would mean to first restore the controls, then the structure, means their parent
1596 // form). The other way round, the EntryDatas would be invalid, if I'd first delete the controls and
1597 // then go on to the structure. This means I have to delete the forms *after* the normal controls, so
1598 // that during UNDO, they're restored in the proper order.
1599 pFormShell->GetImpl()->EnableTrackProperties_Lock(false);
1600 for (SvLBoxEntrySortedArray::reverse_iterator it = m_arrCurrentSelection.rbegin();
1601 it != m_arrCurrentSelection.rend(); )
1603 const std::unique_ptr<weld::TreeIter>& rIter = *it;
1604 FmEntryData* pCurrent = reinterpret_cast<FmEntryData*>(m_xTreeView->get_id(*rIter).toInt64());
1606 // a form ?
1607 bool bIsForm = dynamic_cast<const FmFormData*>( pCurrent) != nullptr;
1609 // because deletion is done by the view, and i build on its MarkList,
1610 // but normally only direct controls, no indirect ones, are marked in a marked form,
1611 // I have to do it later
1612 if (bIsForm)
1613 MarkViewObj(static_cast<FmFormData*>(pCurrent), true/*deep*/);
1615 // a hidden control ?
1616 bool bIsHidden = IsHiddenControl(pCurrent);
1618 // keep forms and hidden controls, the rest not
1619 if (!bIsForm && !bIsHidden)
1621 // well, no form and no hidden control -> we can remove it from m_arrCurrentSelection, as it will
1622 // be deleted automatically. This is because for every model (except forms and hidden control models)
1623 // there exist a shape, which is marked _if_and_only_if_ the model is selected in our tree.
1624 if ( aModelShapes.find( pCurrent->GetElement() ) != aModelShapes.end() )
1626 // if there's a shape for the current entry, then either it is marked or it is in a
1627 // hidden layer (#i28502#), or something like this.
1628 // In the first case, it will be deleted below, in the second case, we currently don't
1629 // delete it, as there's no real (working!) API for this, neither in UNO nor in non-UNO.
1630 m_arrCurrentSelection.erase( --(it.base()) );
1632 else
1633 ++it;
1634 // In case there is no shape for the current entry, we keep the entry in m_arrCurrentSelection,
1635 // since then we can definitely remove it.
1637 else
1638 ++it;
1640 pFormShell->GetImpl()->EnableTrackProperties_Lock(true);
1642 // let the view delete the marked controls
1643 pFormShell->GetFormView()->DeleteMarked();
1645 // start UNDO at this point. Unfortunately, this results in 2 UNDO actions, since DeleteMarked is
1646 // creating an own one. However, if we'd move it before DeleteMarked, Writer does not really like
1647 // this ... :(
1648 // #i31038#
1651 // initialize UNDO
1652 OUString aUndoStr;
1653 if ( m_arrCurrentSelection.size() == 1 )
1655 aUndoStr = SvxResId(RID_STR_UNDO_CONTAINER_REMOVE);
1656 if (m_nFormsSelected)
1657 aUndoStr = aUndoStr.replaceFirst( "#", SvxResId( RID_STR_FORM ) );
1658 else
1659 // it must be a control (else the root would be selected, but it cannot be deleted)
1660 aUndoStr = aUndoStr.replaceFirst( "#", SvxResId( RID_STR_CONTROL ) );
1662 else
1664 aUndoStr = SvxResId(RID_STR_UNDO_CONTAINER_REMOVE_MULTIPLE);
1665 aUndoStr = aUndoStr.replaceFirst( "#", OUString::number( m_arrCurrentSelection.size() ) );
1667 pFormModel->BegUndo(aUndoStr);
1670 // remove remaining structure
1671 for (const auto& rpSelection : m_arrCurrentSelection)
1673 FmEntryData* pCurrent = reinterpret_cast<FmEntryData*>(m_xTreeView->get_id(*rpSelection).toInt64());
1675 // if the entry still has children, we skipped deletion of one of those children.
1676 // This may for instance be because the shape is in a hidden layer, where we're unable
1677 // to remove it
1678 if ( pCurrent->GetChildList()->size() )
1679 continue;
1681 // one remaining subtile problem, before deleting it : if it's a form and the shell
1682 // knows it as CurrentObject, I have to tell it something else
1683 if (auto pFormData = dynamic_cast<FmFormData*>( pCurrent))
1685 Reference< XForm > xCurrentForm( pFormData->GetFormIface() );
1686 if (pFormShell->GetImpl()->getCurrentForm_Lock() == xCurrentForm) // shell knows form to be deleted ?
1687 pFormShell->GetImpl()->forgetCurrentForm_Lock(); // -> take away ...
1689 GetNavModel()->Remove(pCurrent, true);
1691 pFormModel->EndUndo();
1695 void NavigatorTree::CollectSelectionData(SELDATA_ITEMS sdiHow)
1697 DBG_ASSERT(sdiHow != SDI_DIRTY, "NavigatorTree::CollectSelectionData : ever thought about your parameter ? DIRTY ?");
1698 if (sdiHow == m_sdiState)
1699 return;
1701 m_arrCurrentSelection.clear();
1702 m_nFormsSelected = m_nControlsSelected = m_nHiddenControls = 0;
1703 m_bRootSelected = false;
1705 m_xTreeView->selected_foreach([this, sdiHow](weld::TreeIter& rSelectionLoop){
1706 // count different elements
1707 if (m_xTreeView->iter_compare(rSelectionLoop, *m_xRootEntry) == 0)
1708 m_bRootSelected = true;
1709 else
1711 if (IsFormEntry(rSelectionLoop))
1712 ++m_nFormsSelected;
1713 else
1715 ++m_nControlsSelected;
1716 if (IsHiddenControl(reinterpret_cast<FmEntryData*>(m_xTreeView->get_id(rSelectionLoop).toInt64())))
1717 ++m_nHiddenControls;
1721 if (sdiHow == SDI_NORMALIZED)
1723 // don't take something with a selected ancestor
1724 if (m_xTreeView->iter_compare(rSelectionLoop, *m_xRootEntry) == 0)
1725 m_arrCurrentSelection.emplace(m_xTreeView->make_iterator(&rSelectionLoop));
1726 else
1728 std::unique_ptr<weld::TreeIter> xParentLoop(m_xTreeView->make_iterator(&rSelectionLoop));
1729 bool bParentLoop = m_xTreeView->iter_parent(*xParentLoop);
1730 while (bParentLoop)
1732 // actually i would have to test, if parent is part of m_arr_CurrentSelection ...
1733 // but if it's selected, then it's in m_arrCurrentSelection
1734 // or one of its ancestors, which was selected earlier.
1735 // In both cases IsSelected is enough
1736 if (m_xTreeView->is_selected(*xParentLoop))
1737 break;
1738 else
1740 if (m_xTreeView->iter_compare(*xParentLoop, *m_xRootEntry) == 0)
1742 // until root (exclusive), there was no selected parent -> entry belongs to normalized list
1743 m_arrCurrentSelection.emplace(m_xTreeView->make_iterator(&rSelectionLoop));
1744 break;
1746 else
1747 bParentLoop = m_xTreeView->iter_parent(*xParentLoop);
1752 else if (sdiHow == SDI_NORMALIZED_FORMARK)
1754 std::unique_ptr<weld::TreeIter> xParent(m_xTreeView->make_iterator(&rSelectionLoop));
1755 bool bParent = m_xTreeView->iter_parent(*xParent);
1756 if (!bParent || !m_xTreeView->is_selected(*xParent) || IsFormEntry(rSelectionLoop))
1757 m_arrCurrentSelection.emplace(m_xTreeView->make_iterator(&rSelectionLoop));
1759 else
1760 m_arrCurrentSelection.emplace(m_xTreeView->make_iterator(&rSelectionLoop));
1762 return false;
1765 m_sdiState = sdiHow;
1768 void NavigatorTree::SynchronizeSelection(FmEntryDataArray& arredToSelect)
1770 LockSelectionHandling();
1771 if (arredToSelect.empty())
1773 m_xTreeView->unselect_all();
1775 else
1777 // compare current selection with requested SelectList
1778 m_xTreeView->selected_foreach([this, &arredToSelect](weld::TreeIter& rSelection) {
1779 FmEntryData* pCurrent = reinterpret_cast<FmEntryData*>(m_xTreeView->get_id(rSelection).toInt64());
1780 if (pCurrent != nullptr)
1782 FmEntryDataArray::iterator it = arredToSelect.find(pCurrent);
1783 if ( it != arredToSelect.end() )
1784 { // entry already selected, but also in SelectList
1785 // remove it from there
1786 arredToSelect.erase(it);
1787 } else
1788 { // entry selected, but not in SelectList -> remove selection
1789 m_xTreeView->unselect(rSelection);
1790 // make it visible (maybe it's the only modification i do in this handler
1791 // so you should see it
1792 m_xTreeView->scroll_to_row(rSelection);
1795 else
1796 m_xTreeView->unselect(rSelection);
1798 return false;
1801 // now SelectList contains only entries, which have to be selected
1802 // two possibilities : 1) run through SelectList, get SvTreeListEntry for every entry and select it (is more intuitive)
1803 // 2) run through my SvLBoxEntries and select those, i can find in the SelectList
1804 // 1) needs =(k*n) (k=length of SelectList, n=number of entries),
1805 // plus the fact, that FindEntry uses extensive IsEqualWithoutChilden instead of comparing pointer to UserData
1806 // 2) needs =(n*log k), duplicates some code from FindEntry
1807 // This may be a frequently used code ( at every change in mark of the view!),
1808 // so i use latter one
1809 m_xTreeView->all_foreach([this, &arredToSelect](weld::TreeIter& rLoop){
1810 FmEntryData* pCurEntryData = reinterpret_cast<FmEntryData*>(m_xTreeView->get_id(rLoop).toInt64());
1811 FmEntryDataArray::iterator it = arredToSelect.find(pCurEntryData);
1812 if (it != arredToSelect.end())
1814 m_xTreeView->select(rLoop);
1815 m_xTreeView->scroll_to_row(rLoop);
1818 return false;
1821 UnlockSelectionHandling();
1825 void NavigatorTree::SynchronizeSelection()
1827 // shell and view
1828 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
1829 if(!pFormShell) return;
1831 FmFormView* pFormView = pFormShell->GetFormView();
1832 if (!pFormView) return;
1834 GetNavModel()->BroadcastMarkedObjects(pFormView->GetMarkedObjectList());
1838 void NavigatorTree::SynchronizeMarkList()
1840 // i'll need this shell
1841 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
1842 if (!pFormShell) return;
1844 CollectSelectionData(SDI_NORMALIZED_FORMARK);
1846 // the view shouldn't notify now if MarkList changed
1847 pFormShell->GetImpl()->EnableTrackProperties_Lock(false);
1849 UnmarkAllViewObj();
1851 for (auto& rSelectionLoop : m_arrCurrentSelection)
1853 // When form selection, mark all controls of form
1854 if (IsFormEntry(*rSelectionLoop) && m_xTreeView->iter_compare(*rSelectionLoop, *m_xRootEntry) != 0)
1855 MarkViewObj(reinterpret_cast<FmFormData*>(m_xTreeView->get_id(*rSelectionLoop).toInt64()), false/*deep*/);
1857 // When control selection, mark Control-SdrObjects
1858 else if (IsFormComponentEntry(*rSelectionLoop))
1860 FmControlData* pControlData = reinterpret_cast<FmControlData*>(m_xTreeView->get_id(*rSelectionLoop).toInt64());
1861 if (pControlData)
1864 // When HiddenControl no object can be selected
1865 Reference< XFormComponent > xFormComponent( pControlData->GetFormComponent());
1866 if (!xFormComponent.is())
1867 continue;
1868 Reference< XPropertySet > xSet(xFormComponent, UNO_QUERY);
1869 if (!xSet.is())
1870 continue;
1872 sal_uInt16 nClassId = ::comphelper::getINT16(xSet->getPropertyValue(FM_PROP_CLASSID));
1873 if (nClassId != FormComponentType::HIDDENCONTROL)
1874 MarkViewObj(pControlData);
1879 // if PropertyBrowser is open, I have to adopt it according to my selection
1880 // (Not as MarkList of view : if a form is selected, all belonging controls are selected in the view
1881 // but of course i want to see the form-properties
1882 ShowSelectionProperties();
1884 // reset flag at view
1885 pFormShell->GetImpl()->EnableTrackProperties_Lock(true);
1887 // if exactly one form is selected now, shell should notice it as CurrentForm
1888 // (if selection handling isn't locked, view cares about it in MarkListHasChanged
1889 // but mechanism doesn't work, if form is empty for example
1890 if ((m_arrCurrentSelection.size() != 1) || (m_nFormsSelected != 1))
1891 return;
1893 std::unique_ptr<weld::TreeIter> xSelected(m_xTreeView->make_iterator());
1894 if (!m_xTreeView->get_selected(xSelected.get()))
1895 xSelected.reset();
1896 FmFormData* pSingleSelectionData = xSelected ? dynamic_cast<FmFormData*>(reinterpret_cast<FmEntryData*>(m_xTreeView->get_id(*xSelected).toInt64()))
1897 : nullptr;
1898 DBG_ASSERT( pSingleSelectionData, "NavigatorTree::SynchronizeMarkList: invalid selected form!" );
1899 if ( pSingleSelectionData )
1901 InterfaceBag aSelection;
1902 aSelection.insert( Reference< XInterface >( pSingleSelectionData->GetFormIface(), UNO_QUERY ) );
1903 pFormShell->GetImpl()->setCurrentSelection_Lock(aSelection);
1907 bool NavigatorTree::IsHiddenControl(FmEntryData const * pEntryData)
1909 if (pEntryData == nullptr) return false;
1911 Reference< XPropertySet > xProperties( pEntryData->GetPropertySet() );
1912 if (::comphelper::hasProperty(FM_PROP_CLASSID, xProperties))
1914 Any aClassID = xProperties->getPropertyValue( FM_PROP_CLASSID );
1915 return (::comphelper::getINT16(aClassID) == FormComponentType::HIDDENCONTROL);
1917 return false;
1920 void NavigatorTree::UnmarkAllViewObj()
1922 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
1923 if( !pFormShell )
1924 return;
1925 FmFormView* pFormView = pFormShell->GetFormView();
1926 pFormView->UnMarkAll();
1929 void NavigatorTree::MarkViewObj(FmFormData const * pFormData, bool bDeep )
1931 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
1932 if( !pFormShell )
1933 return;
1935 // first collect all sdrobjects
1936 ::std::set< Reference< XFormComponent > > aObjects;
1937 CollectObjects(pFormData,bDeep,aObjects);
1940 // find and select appropriate SdrObj in page
1941 FmFormView* pFormView = pFormShell->GetFormView();
1942 SdrPageView* pPageView = pFormView->GetSdrPageView();
1943 SdrPage* pPage = pPageView->GetPage();
1944 //FmFormPage* pFormPage = dynamic_cast< FmFormPage* >( pPage );
1946 SdrObjListIter aIter( pPage );
1947 while ( aIter.IsMore() )
1949 SdrObject* pSdrObject = aIter.Next();
1950 FmFormObj* pFormObject = FmFormObj::GetFormObject( pSdrObject );
1951 if ( !pFormObject )
1952 continue;
1954 Reference< XFormComponent > xControlModel( pFormObject->GetUnoControlModel(),UNO_QUERY );
1955 if ( xControlModel.is() && aObjects.find(xControlModel) != aObjects.end() && !pFormView->IsObjMarked( pSdrObject ) )
1957 // unfortunately, the writer doesn't like marking an already-marked object, again, so reset the mark first
1958 pFormView->MarkObj( pSdrObject, pPageView );
1960 } // while ( aIter.IsMore() )
1961 // make the mark visible
1962 ::tools::Rectangle aMarkRect( pFormView->GetAllMarkedRect());
1963 for ( sal_uInt32 i = 0; i < pFormView->PaintWindowCount(); ++i )
1965 SdrPaintWindow* pPaintWindow = pFormView->GetPaintWindow( i );
1966 OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
1967 if ( ( OUTDEV_WINDOW == rOutDev.GetOutDevType() ) && !aMarkRect.IsEmpty() )
1969 pFormView->MakeVisible( aMarkRect, static_cast<vcl::Window&>(rOutDev) );
1971 } // for ( sal_uInt32 i = 0; i < pFormView->PaintWindowCount(); ++i )
1974 void NavigatorTree::CollectObjects(FmFormData const * pFormData, bool bDeep, ::std::set< Reference< XFormComponent > >& _rObjects)
1976 FmEntryDataList* pChildList = pFormData->GetChildList();
1977 for( size_t i = 0; i < pChildList->size(); ++i )
1979 FmEntryData* pEntryData = pChildList->at( i );
1980 if( auto pControlData = dynamic_cast<FmControlData*>( pEntryData) )
1982 _rObjects.insert(pControlData->GetFormComponent());
1983 } // if( dynamic_cast<const FmControlData*>( pEntryData) != nullptr )
1984 else if (bDeep)
1985 if (auto pEntryFormData = dynamic_cast<FmFormData*>( pEntryData))
1986 CollectObjects(pEntryFormData, bDeep, _rObjects);
1987 } // for( sal_uInt32 i=0; i<pChildList->Count(); i++ )
1990 void NavigatorTree::MarkViewObj( FmControlData const * pControlData)
1992 if( !pControlData )
1993 return;
1994 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
1995 if( !pFormShell )
1996 return;
1999 // find and select appropriate SdrObj
2000 FmFormView* pFormView = pFormShell->GetFormView();
2001 Reference< XFormComponent > xFormComponent( pControlData->GetFormComponent());
2002 SdrPageView* pPageView = pFormView->GetSdrPageView();
2003 SdrPage* pPage = pPageView->GetPage();
2005 bool bPaint = false;
2006 SdrObjListIter aIter( pPage );
2007 while ( aIter.IsMore() )
2009 SdrObject* pSdrObject = aIter.Next();
2010 FmFormObj* pFormObject = FmFormObj::GetFormObject( pSdrObject );
2011 if ( !pFormObject )
2012 continue;
2014 Reference< XInterface > xControlModel( pFormObject->GetUnoControlModel() );
2015 if ( xControlModel != xFormComponent )
2016 continue;
2018 // mark the object
2019 if ( !pFormView->IsObjMarked( pSdrObject ) )
2020 // unfortunately, the writer doesn't like marking an already-marked object, again, so reset the mark first
2021 pFormView->MarkObj( pSdrObject, pPageView );
2023 bPaint = true;
2025 } // while ( aIter.IsMore() )
2026 if ( !bPaint )
2027 return;
2029 // make the mark visible
2030 ::tools::Rectangle aMarkRect( pFormView->GetAllMarkedRect());
2031 for ( sal_uInt32 i = 0; i < pFormView->PaintWindowCount(); ++i )
2033 SdrPaintWindow* pPaintWindow = pFormView->GetPaintWindow( i );
2034 OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
2035 if ( OUTDEV_WINDOW == rOutDev.GetOutDevType() )
2037 pFormView->MakeVisible( aMarkRect, static_cast<vcl::Window&>(rOutDev) );
2039 } // for ( sal_uInt32 i = 0; i < pFormView->PaintWindowCount(); ++i )
2046 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */