1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 .
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>
30 #include <fmshimp.hxx>
31 #include <fmservs.hxx>
33 #include <fmpgeimp.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 <comphelper/diagnose_ex.hxx>
55 #include <svx/svxids.hrc>
56 #include <bitmaps.hlst>
57 #include <vcl/commandevent.hxx>
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
;
79 typedef ::std::map
< Reference
< XInterface
>, SdrObject
* > MapModelToShape
;
82 static void collectShapeModelMapping( SdrPage
const * _pPage
, MapModelToShape
& _rMapping
)
84 OSL_ENSURE( _pPage
, "collectShapeModelMapping: invalid arg!" );
88 SdrObjListIter
aIter( _pPage
);
89 while ( aIter
.IsMore() )
91 SdrObject
* pSdrObject
= aIter
.Next();
92 FmFormObj
* pFormObject
= FmFormObj::GetFormObject( pSdrObject
);
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);
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_aSynchronizeTimer("svx NavigatorTree m_aSynchronizeTimer")
136 ,m_sdiState(SDI_DIRTY
)
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 )
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());
156 StartListening( *m_pNavModel
);
158 m_aSynchronizeTimer
.SetInvokeHandler(LINK(this, NavigatorTree
, OnSynchronizeTimer
));
159 m_xTreeView
->connect_selection_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()
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
);
181 void NavigatorTree::Clear()
183 m_pNavModel
->Clear();
186 void NavigatorTree::UpdateContent( FmFormShell
* pFormShell
)
188 if (m_bInitialUpdate
)
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();
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
217 std::unique_ptr
<weld::TreeIter
> xFirst(m_xTreeView
->make_iterator(m_xRootEntry
.get()));
218 bool bFirst
= m_xTreeView
->iter_children(*xFirst
);
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);
234 // Information for AcceptDrop and Execute Drop
235 CollectSelectionData(SDI_ALL
);
236 if (m_arrCurrentSelection
.empty())
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
= weld::fromId
<FmEntryData
*>(m_xTreeView
->get_id(*rEntry
));
245 return !IsHiddenControl( pCurrent
);
248 if ( bHasNonHidden
&& ( 0 == ( _nAction
& DND_ACTION_MOVE
) ) )
249 // non-hidden controls need to be moved
252 if ( _pHasNonHidden
)
253 *_pHasNonHidden
= bHasNonHidden
;
258 bool NavigatorTree::implPrepareExchange( sal_Int8 _nAction
)
260 bool bHasNonHidden
= false;
261 if ( !implAllowExchange( _nAction
, &bHasNonHidden
) )
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());
276 Sequence
< Reference
< XInterface
> > seqIFaces(m_arrCurrentSelection
.size());
277 Reference
< XInterface
>* pArray
= seqIFaces
.getArray();
278 for (const auto& rpEntry
: m_arrCurrentSelection
)
280 *pArray
= weld::fromId
<FmEntryData
*>(m_xTreeView
->get_id(*rpEntry
))->GetElement();
283 // and the new format
284 m_aControlExchange
->addHiddenControlsFormat(seqIFaces
);
287 m_bDragDataDirty
= false;
291 IMPL_LINK(NavigatorTree
, DragBeginHdl
, bool&, rUnsetDragIcon
, bool)
293 rUnsetDragIcon
= false;
295 bool bSuccess
= implPrepareExchange(DND_ACTION_COPYMOVE
);
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);
306 IMPL_LINK(NavigatorTree
, PopupMenuHdl
, const CommandEvent
&, rEvt
, bool)
308 bool bHandled
= false;
309 switch( rEvt
.GetCommand() )
311 case CommandEventId::ContextMenu
:
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))
321 if (!m_xTreeView
->is_selected(*xClickedOn
))
323 m_xTreeView
->unselect_all();
324 m_xTreeView
->select(*xClickedOn
);
325 m_xTreeView
->set_cursor(*xClickedOn
);
330 if (m_arrCurrentSelection
.empty()) // only happens with context menu via keyboard
333 std::unique_ptr
<weld::TreeIter
> xCurrent(m_xTreeView
->make_iterator());
334 if (!m_xTreeView
->get_cursor(xCurrent
.get()))
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
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(), u
"svx/ui/formnavimenu.ui"_ustr
));
363 std::unique_ptr
<weld::Menu
> xContextMenu(xBuilder
->weld_menu(u
"menu"_ustr
));
364 std::unique_ptr
<weld::Menu
> xSubMenuNew(xBuilder
->weld_menu(u
"submenu"_ustr
));
366 // menu 'New' only exists, if only the root or only one form is selected
367 bool bShowNew
= bSingleSelection
&& (m_nFormsSelected
|| m_bRootSelected
);
369 xContextMenu
->remove(u
"new"_ustr
);
371 // 'New'\'Form' under the same terms
372 bool bShowForm
= bSingleSelection
&& (m_nFormsSelected
|| m_bRootSelected
);
374 xSubMenuNew
->append(u
"form"_ustr
, SvxResId(RID_STR_FORM
), RID_SVXBMP_FORM
);
376 // 'New'\'hidden...', if exactly one form is selected
377 bool bShowHidden
= bSingleSelection
&& m_nFormsSelected
;
379 xSubMenuNew
->append(u
"hidden"_ustr
, SvxResId(RID_STR_HIDDEN
), RID_SVXBMP_HIDDEN
);
381 // 'Delete': everything which is not root can be removed
383 xContextMenu
->remove(u
"delete"_ustr
);
385 // 'Cut', 'Copy' and 'Paste'
386 bool bShowCut
= !m_bRootSelected
&& implAllowExchange(DND_ACTION_MOVE
);
388 xContextMenu
->remove(u
"cut"_ustr
);
389 bool bShowCopy
= !m_bRootSelected
&& implAllowExchange(DND_ACTION_COPY
);
391 xContextMenu
->remove(u
"copy"_ustr
);
392 if (!implAcceptPaste())
393 xContextMenu
->remove(u
"paste"_ustr
);
395 // TabDialog, if exactly one form
396 bool bShowTabOrder
= bSingleSelection
&& m_nFormsSelected
;
398 xContextMenu
->remove(u
"taborder"_ustr
);
400 bool bShowProps
= true;
401 // in XML forms, we don't allow for the properties of a form
403 if (pFormShell
->GetImpl()->isEnhancedForm_Lock() && !m_nControlsSelected
)
405 // if the property browser is already open, we don't allow for the properties, too
406 if (pFormShell
->GetImpl()->IsPropBrwOpen_Lock())
409 // and finally, if there's a mixed selection of forms and controls, disable the entry, too
410 if (bShowProps
&& !pFormShell
->GetImpl()->IsPropBrwOpen_Lock())
412 (m_nControlsSelected
&& !m_nFormsSelected
) || (!m_nControlsSelected
&& m_nFormsSelected
);
415 xContextMenu
->remove(u
"props"_ustr
);
417 // rename, if one element and no root
418 bool bShowRename
= bSingleSelection
&& !m_bRootSelected
;
420 xContextMenu
->remove(u
"rename"_ustr
);
422 if (!m_bRootSelected
)
424 // Readonly-entry is only for root
425 xContextMenu
->remove(u
"designmode"_ustr
);
426 // the same for automatic control focus
427 xContextMenu
->remove(u
"controlfocus"_ustr
);
430 std::unique_ptr
<weld::Menu
> xConversionMenu(xBuilder
->weld_menu(u
"changemenu"_ustr
));
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
= weld::fromId
<FmControlData
*>(m_xTreeView
->get_id(*rIter
));
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!" );
443 pFormShell
->GetImpl()->checkControlConversionSlotsForCurrentSelection_Lock(*xConversionMenu
);
446 xContextMenu
->remove(u
"change"_ustr
);
451 xContextMenu
->set_active(u
"designmode"_ustr
, pFormModel
->GetOpenInDesignMode());
452 xContextMenu
->set_active(u
"controlfocus"_ustr
, pFormModel
->GetAutoControlFocus());
455 OUString 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();
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")
481 else if (sIdent
== "copy")
483 else if (sIdent
== "paste")
485 else if (sIdent
== "delete")
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
= weld::fromId
<FmFormData
*>(m_xTreeView
->get_id(*rSelectedForm
));
494 const Reference
< XForm
>& xForm( pFormData
->GetFormIface());
496 Reference
< XTabControllerModel
> xTabController(xForm
, UNO_QUERY
);
497 if( !xTabController
.is() )
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
);
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
= weld::fromId
<FmControlData
*>(m_xTreeView
->get_id(*rIter
));
524 if (pFormShell
->GetImpl()->executeControlConversionSlot_Lock(pCurrent
->GetFormComponent(), sIdent
))
525 ShowSelectionProperties();
537 std::unique_ptr
<weld::TreeIter
> NavigatorTree::FindEntry(FmEntryData
* pEntryData
)
539 std::unique_ptr
<weld::TreeIter
> xRet
;
543 m_xTreeView
->all_foreach([this, pEntryData
, &xRet
](weld::TreeIter
& rEntry
){
544 FmEntryData
* pCurEntryData
= weld::fromId
<FmEntryData
*>(m_xTreeView
->get_id(rEntry
));
545 if (pCurEntryData
&& pCurEntryData
->IsEqualWithoutChildren(pEntryData
))
547 xRet
= m_xTreeView
->make_iterator(&rEntry
);
556 void NavigatorTree::Notify( SfxBroadcaster
& /*rBC*/, const SfxHint
& rHint
)
558 if( rHint
.GetId() == SfxHintId::FmNavRemoved
)
560 auto pRemovedHint
= static_cast<const FmNavRemovedHint
*>(&rHint
);
561 FmEntryData
* pEntryData
= pRemovedHint
->GetEntryData();
562 Remove( pEntryData
);
564 else if( rHint
.GetId() == SfxHintId::FmNavInserted
)
566 auto pInsertedHint
= static_cast<const FmNavInsertedHint
*>(&rHint
);
567 FmEntryData
* pEntryData
= pInsertedHint
->GetEntryData();
568 sal_uInt32 nRelPos
= pInsertedHint
->GetRelPos();
569 Insert( pEntryData
, nRelPos
);
571 else if( rHint
.GetId() == SfxHintId::FmNavModelReplaced
)
573 auto pReplacedHint
= static_cast<const FmNavModelReplacedHint
*>(&rHint
);
574 FmEntryData
* pData
= pReplacedHint
->GetEntryData();
575 std::unique_ptr
<weld::TreeIter
> xEntry
= FindEntry(pData
);
579 m_xTreeView
->set_image(*xEntry
, pData
->GetNormalImage());
582 else if( rHint
.GetId() == SfxHintId::FmNavNameChanged
)
584 auto pNameChangedHint
= static_cast<const FmNavNameChangedHint
*>(&rHint
);
585 std::unique_ptr
<weld::TreeIter
> xEntry
= FindEntry(pNameChangedHint
->GetEntryData());
587 m_xTreeView
->set_text(*xEntry
, pNameChangedHint
->GetNewName());
589 else if( rHint
.GetId() == SfxHintId::FmNavCleared
)
591 m_aCutEntries
.clear();
592 if (m_aControlExchange
.isDataExchangeActive())
593 m_aControlExchange
.clear();
594 m_xTreeView
->clear();
596 // default-entry "Forms"
597 OUString
sText(SvxResId(RID_STR_FORMS
));
598 m_xRootEntry
= m_xTreeView
->make_iterator();
599 m_xTreeView
->insert(nullptr, -1, &sText
, nullptr, nullptr, nullptr,
600 false, m_xRootEntry
.get());
601 m_xTreeView
->set_image(*m_xRootEntry
, RID_SVXBMP_FORMS
);
602 m_xTreeView
->set_sensitive(*m_xRootEntry
, true);
604 else if (rHint
.GetId() == SfxHintId::FmNavRequestSelect
)
606 auto pSelectHint
= static_cast<const FmNavRequestSelectHint
*>(&rHint
);
607 FmEntryDataArray
& arredToSelect
= const_cast<FmNavRequestSelectHint
*>(pSelectHint
)->GetItems();
608 SynchronizeSelection(arredToSelect
);
610 if (pSelectHint
->IsMixedSelection())
611 // in this case I deselect all, although the view had a mixed selection
612 // during next selection, I must adapt the navigator to the view
613 m_bPrevSelectionMixed
= true;
617 std::unique_ptr
<weld::TreeIter
> NavigatorTree::Insert(const FmEntryData
* pEntryData
, int nRelPos
)
619 // insert current entry
620 std::unique_ptr
<weld::TreeIter
> xParentEntry
= FindEntry( pEntryData
->GetParent() );
621 std::unique_ptr
<weld::TreeIter
> xNewEntry(m_xTreeView
->make_iterator());
622 OUString
sId(weld::toId(pEntryData
));
626 m_xTreeView
->insert(m_xRootEntry
.get(), nRelPos
, &pEntryData
->GetText(), &sId
,
627 nullptr, nullptr, false, xNewEntry
.get());
631 m_xTreeView
->insert(xParentEntry
.get(), nRelPos
, &pEntryData
->GetText(), &sId
,
632 nullptr, nullptr, false, xNewEntry
.get());
635 m_xTreeView
->set_image(*xNewEntry
, pEntryData
->GetNormalImage());
636 m_xTreeView
->set_sensitive(*xNewEntry
, true);
638 // If root-entry, expand root
640 m_xTreeView
->expand_row(*m_xRootEntry
);
643 FmEntryDataList
* pChildList
= pEntryData
->GetChildList();
644 size_t nChildCount
= pChildList
->size();
645 for( size_t i
= 0; i
< nChildCount
; i
++ )
647 FmEntryData
* pChildData
= pChildList
->at( i
);
648 Insert(pChildData
, -1);
654 void NavigatorTree::Remove( FmEntryData
* pEntryData
)
659 // entry for the data
660 std::unique_ptr
<weld::TreeIter
> xEntry
= FindEntry(pEntryData
);
664 // delete entry from TreeListBox
665 // I'm not allowed, to treat the selection, which I trigger:
666 // select changes the MarkList of the view, if somebody else does this at the same time
667 // and removes a selection, we get a problem
668 // e.g. Group controls with open navigator
669 LockSelectionHandling();
671 // little problem: I remember the selected data, but if somebody deletes one of these entries,
672 // I get inconsistent... this would be bad
673 m_xTreeView
->unselect(*xEntry
);
675 // selection can be modified during deletion,
676 // but because I disabled SelectionHandling, I have to do it later
677 auto nExpectedSelectionCount
= m_xTreeView
->count_selected_rows();
679 ModelHasRemoved(xEntry
.get());
680 m_xTreeView
->remove(*xEntry
);
682 if (nExpectedSelectionCount
!= m_xTreeView
->count_selected_rows())
683 SynchronizeSelection();
685 // by default I treat the selection of course
686 UnlockSelectionHandling();
689 bool NavigatorTree::IsFormEntry(const weld::TreeIter
& rEntry
)
691 FmEntryData
* pEntryData
= weld::fromId
<FmEntryData
*>(m_xTreeView
->get_id(rEntry
));
692 return !pEntryData
|| dynamic_cast<const FmFormData
*>( pEntryData
) != nullptr;
695 bool NavigatorTree::IsFormComponentEntry(const weld::TreeIter
& rEntry
)
697 FmEntryData
* pEntryData
= weld::fromId
<FmEntryData
*>(m_xTreeView
->get_id(rEntry
));
698 return dynamic_cast<const FmControlData
*>( pEntryData
) != nullptr;
701 bool NavigatorTree::implAcceptPaste( )
703 auto nSelectedEntries
= m_xTreeView
->count_selected_rows();
704 if (nSelectedEntries
!= 1)
705 // no selected entry, or at least two selected entries
709 TransferableDataHelper
aClipboardContent(TransferableDataHelper::CreateFromClipboard(m_xTreeView
->get_clipboard()));
711 sal_Int8 nAction
= m_aControlExchange
.isClipboardOwner() && doingKeyboardCut( ) ? DND_ACTION_MOVE
: DND_ACTION_COPY
;
712 std::unique_ptr
<weld::TreeIter
> xSelected(m_xTreeView
->make_iterator());
713 if (!m_xTreeView
->get_selected(xSelected
.get()))
715 return nAction
== implAcceptDataTransfer(aClipboardContent
.GetDataFlavorExVector(), nAction
, xSelected
.get(), false);
718 sal_Int8
NavigatorTree::implAcceptDataTransfer( const DataFlavorExVector
& _rFlavors
, sal_Int8 _nAction
, const weld::TreeIter
* _pTargetEntry
, bool _bDnD
)
720 // no target -> no drop
722 return DND_ACTION_NONE
;
725 bool bHasDefControlFormat
= OControlExchange::hasFieldExchangeFormat( _rFlavors
);
726 bool bHasControlPathFormat
= OControlExchange::hasControlPathFormat( _rFlavors
);
727 bool bHasHiddenControlsFormat
= OControlExchange::hasHiddenControlModelsFormat( _rFlavors
);
728 if (!bHasDefControlFormat
&& !bHasControlPathFormat
&& !bHasHiddenControlsFormat
)
729 return DND_ACTION_NONE
;
731 bool bSelfSource
= _bDnD
? m_aControlExchange
.isDragSource() : m_aControlExchange
.isClipboardOwner();
733 if ( bHasHiddenControlsFormat
)
734 { // bHasHiddenControlsFormat means that only hidden controls are part of the data
736 // hidden controls can be copied to a form only
737 if (m_xTreeView
->iter_compare(*_pTargetEntry
, *m_xRootEntry
) == 0 || !IsFormEntry(*_pTargetEntry
))
738 return DND_ACTION_NONE
;
740 return bSelfSource
? ( DND_ACTION_COPYMOVE
& _nAction
) : DND_ACTION_COPY
;
745 // DnD or CnP crossing navigator boundaries
746 // The main problem here is that the current API does not allow us to sneak into the content which
747 // is to be inserted. So we have to allow it for the moment, but maybe reject later on (in the real drop).
749 // TODO: this smart behaviour later on ... at the moment, we disallow data transfer crossing navigator
752 return DND_ACTION_NONE
;
755 DBG_ASSERT( _bDnD
? m_aControlExchange
.isDragSource() : m_aControlExchange
.isClipboardOwner(),
756 "NavigatorTree::implAcceptDataTransfer: here only with source=dest!" );
757 // somebody changed the logic of this method ...
759 // from here on, I can work with m_aControlExchange instead of _rData!
761 bool bForeignCollection
= m_aControlExchange
->getFormsRoot().get() != GetNavModel()->GetFormPage()->GetForms().get();
762 if ( bForeignCollection
)
764 // crossing shell/page boundaries, we can exchange hidden controls only
765 // But if we survived the checks above, we do not have hidden controls.
766 // -> no data transfer
767 DBG_ASSERT( !bHasHiddenControlsFormat
, "NavigatorTree::implAcceptDataTransfer: still hidden controls format!" );
768 // somebody changed the logic of this method ...
770 return DND_ACTION_COPY
;
773 if (DND_ACTION_MOVE
!= _nAction
) // 'normal' controls within a shell are moved only (never copied)
774 return DND_ACTION_NONE
;
776 if ( m_bDragDataDirty
|| !bHasDefControlFormat
)
778 if (!bHasControlPathFormat
)
779 // I am in the shell/page, which has the controls, but I have no format,
780 // which survived the shell change (SVX_FM_CONTROLS_AS_PATH)
781 return DND_ACTION_NONE
;
783 // I must recreate the list of the ExchangeObjects, because the shell was changed during dragging
784 // (there are SvLBoxEntries in it, and we lost them during change)
785 m_aControlExchange
->buildListFromPath(m_xTreeView
.get(), m_xRootEntry
.get());
786 m_bDragDataDirty
= false;
789 // List of dropped entries from DragServer
790 const ListBoxEntrySet
& rDropped
= m_aControlExchange
->selected();
791 DBG_ASSERT(!rDropped
.empty(), "NavigatorTree::implAcceptDataTransfer: no entries !");
793 bool bDropTargetIsComponent
= IsFormComponentEntry( *_pTargetEntry
);
795 // conditions to disallow the drop
796 // 0) the root entry is part of the list (can't DnD the root!)
797 // 1) one of the dragged entries is to be dropped onto its own parent
798 // 2) - " - is to be dropped onto itself
799 // 3) - " - is a Form and to be dropped onto one of its descendants
800 // 4) one of the entries is a control and to be dropped onto the root
801 // 5) a control or form will be dropped onto a control which is _not_ a sibling (dropping onto a sibling
802 // means moving the control)
804 // collect the ancestors of the drop target (speeds up 3)
805 SvLBoxEntrySortedArray arrDropAncestors
;
806 std::unique_ptr
<weld::TreeIter
> xLoop(m_xTreeView
->make_iterator(_pTargetEntry
));
809 arrDropAncestors
.emplace(m_xTreeView
->make_iterator(xLoop
.get()));
811 while (m_xTreeView
->iter_parent(*xLoop
));
813 for (const auto& rCurrent
: rDropped
)
816 if (m_xTreeView
->iter_compare(*rCurrent
, *m_xRootEntry
) == 0)
817 return DND_ACTION_NONE
;
819 std::unique_ptr
<weld::TreeIter
> xCurrentParent(m_xTreeView
->make_iterator(rCurrent
.get()));
820 m_xTreeView
->iter_parent(*xCurrentParent
);
823 if (m_xTreeView
->iter_compare(*_pTargetEntry
, *xCurrentParent
) == 0)
824 return DND_ACTION_NONE
;
827 if (m_xTreeView
->iter_compare(*rCurrent
, *_pTargetEntry
) == 0)
828 return DND_ACTION_NONE
;
831 if (bDropTargetIsComponent
)
832 return DND_ACTION_NONE
;
835 if (IsFormEntry(*rCurrent
))
837 auto aIter
= std::find_if(arrDropAncestors
.begin(), arrDropAncestors
.end(),
838 [this, &rCurrent
](const auto& rElem
) {
839 return m_xTreeView
->iter_compare(*rElem
, *rCurrent
) == 0;
842 if ( aIter
!= arrDropAncestors
.end() )
843 return DND_ACTION_NONE
;
845 else if (IsFormComponentEntry(*rCurrent
))
848 if (m_xTreeView
->iter_compare(*_pTargetEntry
, *m_xRootEntry
) == 0)
849 return DND_ACTION_NONE
;
852 return DND_ACTION_MOVE
;
855 sal_Int8
NavigatorTree::AcceptDrop( const AcceptDropEvent
& rEvt
)
857 ::Point aDropPos
= rEvt
.maPosPixel
;
858 std::unique_ptr
<weld::TreeIter
> xDropTarget(m_xTreeView
->make_iterator());
859 // get_dest_row_at_pos with false cause we must drop exactly "on" a form to paste a control into it
860 if (!m_xTreeView
->get_dest_row_at_pos(aDropPos
, xDropTarget
.get(), false))
862 return implAcceptDataTransfer(m_aDropTargetHelper
.GetDataFlavorExVector(), rEvt
.mnAction
, xDropTarget
.get(), true);
865 sal_Int8
NavigatorTree::implExecuteDataTransfer( const OControlTransferData
& _rData
, sal_Int8 _nAction
, const ::Point
& _rDropPos
, bool _bDnD
)
867 std::unique_ptr
<weld::TreeIter
> xDrop(m_xTreeView
->make_iterator());
868 // get_dest_row_at_pos with false cause we must drop exactly "on" a form to paste a control into it
869 if (!m_xTreeView
->get_dest_row_at_pos(_rDropPos
, xDrop
.get(), false))
871 return implExecuteDataTransfer( _rData
, _nAction
, xDrop
.get(), _bDnD
);
874 sal_Int8
NavigatorTree::implExecuteDataTransfer( const OControlTransferData
& _rData
, sal_Int8 _nAction
, const weld::TreeIter
* _pTargetEntry
, bool _bDnD
)
876 const DataFlavorExVector
& rDataFlavors
= _rData
.GetDataFlavorExVector();
878 if ( DND_ACTION_NONE
== implAcceptDataTransfer( rDataFlavors
, _nAction
, _pTargetEntry
, _bDnD
) )
879 // under some platforms, it may happen that ExecuteDrop is called though AcceptDrop returned DND_ACTION_NONE
880 return DND_ACTION_NONE
;
883 // no target -> no drop
884 return DND_ACTION_NONE
;
888 bool bHasHiddenControlsFormat
= OControlExchange::hasHiddenControlModelsFormat( rDataFlavors
);
889 bool bForeignCollection
= _rData
.getFormsRoot().get() != GetNavModel()->GetFormPage()->GetForms().get();
890 DBG_ASSERT(!bForeignCollection
|| bHasHiddenControlsFormat
, "NavigatorTree::implExecuteDataTransfer: invalid format (AcceptDrop shouldn't have let this pass) !");
891 DBG_ASSERT(bForeignCollection
|| !m_bDragDataDirty
, "NavigatorTree::implExecuteDataTransfer: invalid state (shell changed since last exchange resync) !");
892 // this should be done in AcceptDrop: the list of controls is created in _rData
893 // and m_bDragDataDirty is reset
896 if ( DND_ACTION_COPY
== _nAction
)
897 { // bHasHiddenControlsFormat means that only hidden controls are part of the data
899 DBG_ASSERT( bHasHiddenControlsFormat
, "NavigatorTree::implExecuteDataTransfer: copy allowed for hidden controls only!" );
901 DBG_ASSERT( _pTargetEntry
&& m_xTreeView
->iter_compare(*_pTargetEntry
, *m_xRootEntry
) != 0 && IsFormEntry( *_pTargetEntry
),
902 "NavigatorTree::implExecuteDataTransfer: should not be here!" );
903 // implAcceptDataTransfer should have caught both cases
906 DBG_ASSERT(bHasHiddenControlsFormat
, "NavigatorTree::implExecuteDataTransfer: only copying of hidden controls is supported !");
907 // should be caught by AcceptDrop
910 // because i want to select all targets (and only them)
911 m_xTreeView
->unselect_all();
913 const Sequence
< Reference
< XInterface
> >& aControls
= _rData
.hiddenControls();
914 sal_Int32 nCount
= aControls
.getLength();
915 const Reference
< XInterface
>* pControls
= aControls
.getConstArray();
917 FmFormShell
* pFormShell
= GetNavModel()->GetFormShell();
918 FmFormModel
* pFormModel
= pFormShell
? pFormShell
->GetFormModel() : nullptr;
923 OUString
aStr(SvxResId(RID_STR_CONTROL
));
924 OUString aUndoStr
= SvxResId(RID_STR_UNDO_CONTAINER_INSERT
).replaceAll("#", aStr
);
925 pFormModel
->BegUndo(aUndoStr
);
929 for (sal_Int32 i
=0; i
<nCount
; ++i
)
931 // create new control
932 FmControlData
* pNewControlData
= NewControl( FM_COMPONENT_HIDDEN
, *_pTargetEntry
, false);
933 Reference
< XPropertySet
> xNewPropSet( pNewControlData
->GetPropertySet() );
935 // copy properties form old control to new one
936 Reference
< XPropertySet
> xCurrent(pControls
[i
], UNO_QUERY
);
937 #if (OSL_DEBUG_LEVEL > 0)
938 // check whether it is a hidden control
939 sal_Int16 nClassId
= ::comphelper::getINT16(xCurrent
->getPropertyValue(FM_PROP_CLASSID
));
940 OSL_ENSURE(nClassId
== FormComponentType::HIDDENCONTROL
, "NavigatorTree::implExecuteDataTransfer: invalid control in drop list !");
941 // if SVX_FM_HIDDEN_CONTROLS-format exists, the sequence
942 // should only contain hidden controls
943 #endif // (OSL_DEBUG_LEVEL > 0)
944 Reference
< XPropertySetInfo
> xPropInfo( xCurrent
->getPropertySetInfo());
945 const Sequence
< Property
> seqAllCurrentProps
= xPropInfo
->getProperties();
946 for (Property
const & currentProp
: seqAllCurrentProps
)
948 if (((currentProp
.Attributes
& PropertyAttribute::READONLY
) == 0) && (currentProp
.Name
!= FM_PROP_NAME
))
949 { // (read-only attribs aren't set, ditto name,
950 // NewControl defined it uniquely
951 xNewPropSet
->setPropertyValue(currentProp
.Name
, xCurrent
->getPropertyValue(currentProp
.Name
));
955 std::unique_ptr
<weld::TreeIter
> xToSelect
= FindEntry(pNewControlData
);
956 m_xTreeView
->select(*xToSelect
);
958 m_xTreeView
->set_cursor(*xToSelect
);
962 pFormModel
->EndUndo();
967 if ( !OControlExchange::hasFieldExchangeFormat( _rData
.GetDataFlavorExVector() ) )
969 // can't do anything without the internal format here ... usually happens when doing DnD or CnP
970 // over navigator boundaries
971 return DND_ACTION_NONE
;
974 // some data for the target
975 bool bDropTargetIsForm
= IsFormEntry(*_pTargetEntry
);
976 FmFormData
* pTargetData
= bDropTargetIsForm
? weld::fromId
<FmFormData
*>(m_xTreeView
->get_id(*_pTargetEntry
)) : nullptr;
978 DBG_ASSERT( DND_ACTION_COPY
!= _nAction
, "NavigatorTree::implExecuteDataTransfer: somebody changed the logics!" );
980 // list of dragged entries
981 const ListBoxEntrySet
& rDropped
= _rData
.selected();
982 DBG_ASSERT(!rDropped
.empty(), "NavigatorTree::implExecuteDataTransfer: no entries!");
984 // make a copy because rDropped is updated on deleting an entry which we do in the processing loop
985 ListBoxEntrySet aDropped
;
986 for (const auto& rEntry
: rDropped
)
987 aDropped
.emplace(m_xTreeView
->make_iterator(rEntry
.get()));
990 FmFormShell
* pFormShell
= GetNavModel()->GetFormShell();
991 FmFormModel
* pFormModel
= pFormShell
? pFormShell
->GetFormModel() : nullptr;
993 return DND_ACTION_NONE
;
996 const bool bUndo
= pFormModel
->IsUndoEnabled();
1000 OUString
strUndoDescription(SvxResId(RID_STR_UNDO_CONTAINER_REPLACE
));
1001 pFormModel
->BegUndo(strUndoDescription
);
1004 // remove selection before adding an entry, so the mark doesn't flicker
1005 // -> lock action of selection
1006 LockSelectionHandling();
1008 // go through all dropped entries
1009 for ( ListBoxEntrySet::const_iterator dropped
= aDropped
.begin();
1010 dropped
!= aDropped
.end();
1014 bool bFirstEntry
= aDropped
.begin() == dropped
;
1016 // some data of the current element
1017 const auto& rCurrent
= *dropped
;
1018 DBG_ASSERT(rCurrent
, "NavigatorTree::implExecuteDataTransfer: invalid entry");
1019 DBG_ASSERT(m_xTreeView
->get_iter_depth(*rCurrent
) != 0, "NavigatorTree::implExecuteDataTransfer: invalid entry");
1022 FmEntryData
* pCurrentUserData
= weld::fromId
<FmEntryData
*>(m_xTreeView
->get_id(*rCurrent
));
1024 Reference
< XChild
> xCurrentChild
= pCurrentUserData
->GetChildIFace();
1025 Reference
< XIndexContainer
> xContainer(xCurrentChild
->getParent(), UNO_QUERY
);
1027 FmFormData
* pCurrentParentUserData
= static_cast<FmFormData
*>(pCurrentUserData
->GetParent());
1028 DBG_ASSERT(pCurrentParentUserData
== nullptr || dynamic_cast<const FmFormData
*>(pCurrentUserData
->GetParent()) != nullptr, "NavigatorTree::implExecuteDataTransfer: invalid parent");
1030 // remove from parent
1031 if (pCurrentParentUserData
)
1032 pCurrentParentUserData
->GetChildList()->removeNoDelete( pCurrentUserData
);
1034 GetNavModel()->GetRootList()->removeNoDelete( pCurrentUserData
);
1036 // remove from container
1037 sal_Int32 nIndex
= getElementPos(xContainer
, xCurrentChild
);
1038 GetNavModel()->m_pPropChangeList
->Lock();
1039 // UndoAction for removal
1040 if ( bUndo
&& GetNavModel()->m_pPropChangeList
->CanUndo())
1042 pFormModel
->AddUndo(std::make_unique
<FmUndoContainerAction
>(*pFormModel
, FmUndoContainerAction::Removed
,
1043 xContainer
, xCurrentChild
, nIndex
));
1045 else if( !GetNavModel()->m_pPropChangeList
->CanUndo() )
1047 FmUndoContainerAction::DisposeElement( xCurrentChild
);
1051 Reference
< XEventAttacherManager
> xManager(xContainer
, UNO_QUERY
);
1052 Sequence
< ScriptEventDescriptor
> aEvts
;
1054 if (xManager
.is() && nIndex
>= 0)
1055 aEvts
= xManager
->getScriptEvents(nIndex
);
1056 xContainer
->removeByIndex(nIndex
);
1059 m_xTreeView
->unselect(*rCurrent
);
1061 Remove(pCurrentUserData
);
1063 // position in DropParents, where to insert dropped entries
1065 xContainer
.set(pTargetData
->GetElement(), UNO_QUERY
);
1067 xContainer
= GetNavModel()->GetForms();
1069 // always insert at the end
1070 nIndex
= xContainer
->getCount();
1072 // UndoAction for insertion
1073 if ( bUndo
&& GetNavModel()->m_pPropChangeList
->CanUndo())
1074 pFormModel
->AddUndo(std::make_unique
<FmUndoContainerAction
>(*pFormModel
, FmUndoContainerAction::Inserted
,
1075 xContainer
, xCurrentChild
, nIndex
));
1077 // insert in new container
1080 // insert in a form needs a FormComponent
1081 xContainer
->insertByIndex( nIndex
,
1082 Any( Reference
< XFormComponent
>( xCurrentChild
, UNO_QUERY
) ) );
1086 xContainer
->insertByIndex( nIndex
,
1087 Any( Reference
< XForm
>( xCurrentChild
, UNO_QUERY
) ) );
1090 if (aEvts
.hasElements())
1092 xManager
.set(xContainer
, UNO_QUERY
);
1094 xManager
->registerScriptEvents(nIndex
, aEvts
);
1097 GetNavModel()->m_pPropChangeList
->UnLock();
1099 // give an entry the new parent
1100 pCurrentUserData
->SetParent(pTargetData
);
1102 // give parent the new child
1104 pTargetData
->GetChildList()->insert( std::unique_ptr
<FmEntryData
>(pCurrentUserData
), nIndex
);
1106 GetNavModel()->GetRootList()->insert( std::unique_ptr
<FmEntryData
>(pCurrentUserData
), nIndex
);
1108 // announce to myself and reselect
1109 std::unique_ptr
<weld::TreeIter
> xNew
= Insert( pCurrentUserData
, nIndex
);
1110 if (bFirstEntry
&& xNew
)
1112 if (m_xTreeView
->iter_parent(*xNew
))
1113 m_xTreeView
->expand_row(*xNew
);
1117 UnlockSelectionHandling();
1120 pFormModel
->EndUndo();
1122 // During the move, the markings of the underlying view did not change (because the view is not affected by the logical
1123 // hierarchy of the form/control models. But my selection changed - which means I have to adjust it according to the
1124 // view marks, again.
1125 SynchronizeSelection();
1127 // in addition, with the move of controls such things as "the current form" may have changed - force the shell
1128 // to update itself accordingly
1129 if( pFormShell
&& pFormShell
->GetImpl() && pFormShell
->GetFormView() )
1130 pFormShell
->GetImpl()->DetermineSelection_Lock( pFormShell
->GetFormView()->GetMarkedObjectList() );
1132 if ( m_aControlExchange
.isClipboardOwner() && ( DND_ACTION_MOVE
== _nAction
) )
1133 m_aControlExchange
->clear();
1138 sal_Int8
NavigatorTree::ExecuteDrop( const ExecuteDropEvent
& rEvt
)
1140 sal_Int8
nResult( DND_ACTION_NONE
);
1141 if ( m_aControlExchange
.isDragSource() )
1142 nResult
= implExecuteDataTransfer( *m_aControlExchange
, rEvt
.mnAction
, rEvt
.maPosPixel
, true );
1145 OControlTransferData
aDroppedData( rEvt
.maDropEvent
.Transferable
);
1146 nResult
= implExecuteDataTransfer( aDroppedData
, rEvt
.mnAction
, rEvt
.maPosPixel
, true );
1151 void NavigatorTree::doPaste()
1153 std::unique_ptr
<weld::TreeIter
> xSelected(m_xTreeView
->make_iterator());
1154 if (!m_xTreeView
->get_selected(xSelected
.get()))
1159 if ( m_aControlExchange
.isClipboardOwner() )
1161 implExecuteDataTransfer( *m_aControlExchange
, doingKeyboardCut( ) ? DND_ACTION_MOVE
: DND_ACTION_COPY
, xSelected
.get(), false );
1165 // the clipboard content
1166 Reference
< XClipboard
> xClipboard(m_xTreeView
->get_clipboard());
1167 Reference
< XTransferable
> xTransferable
;
1168 if ( xClipboard
.is() )
1169 xTransferable
= xClipboard
->getContents();
1171 OControlTransferData
aClipboardContent( xTransferable
);
1172 implExecuteDataTransfer( aClipboardContent
, DND_ACTION_COPY
, xSelected
.get(), false );
1175 catch( const Exception
& )
1177 TOOLS_WARN_EXCEPTION( "svx", "NavigatorTree::doPaste" );
1181 void NavigatorTree::doCopy()
1183 if ( implPrepareExchange( DND_ACTION_COPY
) )
1185 m_aControlExchange
.setClipboardListener( LINK( this, NavigatorTree
, OnClipboardAction
) );
1186 m_aControlExchange
.copyToClipboard(*m_xTreeView
);
1190 void NavigatorTree::ModelHasRemoved(const weld::TreeIter
* pTypedEntry
)
1192 if (doingKeyboardCut())
1194 auto aIter
= std::find_if(m_aCutEntries
.begin(), m_aCutEntries
.end(),
1195 [this, pTypedEntry
](const auto& rElem
) {
1196 return m_xTreeView
->iter_compare(*rElem
, *pTypedEntry
) == 0;
1198 if (aIter
!= m_aCutEntries
.end())
1199 m_aCutEntries
.erase(aIter
);
1202 if (m_aControlExchange
.isDataExchangeActive())
1204 if (0 == m_aControlExchange
->onEntryRemoved(m_xTreeView
.get(), pTypedEntry
))
1206 // last of the entries which we put into the clipboard has been deleted from the tree.
1207 // Give up the clipboard ownership.
1208 m_aControlExchange
.clear();
1213 void NavigatorTree::doCut()
1215 if ( !implPrepareExchange( DND_ACTION_MOVE
) )
1218 m_aControlExchange
.setClipboardListener( LINK( this, NavigatorTree
, OnClipboardAction
) );
1219 m_aControlExchange
.copyToClipboard(*m_xTreeView
);
1220 m_bKeyboardCut
= true;
1222 // mark all the entries we just "cut" into the clipboard as "nearly moved"
1223 for (const auto& rEntry
: m_arrCurrentSelection
)
1227 m_aCutEntries
.emplace(m_xTreeView
->make_iterator(rEntry
.get()));
1228 m_xTreeView
->set_sensitive(*rEntry
, false);
1232 IMPL_LINK(NavigatorTree
, KeyInputHdl
, const ::KeyEvent
&, rKEvt
, bool)
1234 const vcl::KeyCode
& rCode
= rKEvt
.GetKeyCode();
1237 if (rCode
.GetCode() == KEY_DELETE
&& !rCode
.GetModifier())
1244 switch ( rCode
.GetFunction() )
1246 case KeyFuncType::CUT
:
1250 case KeyFuncType::PASTE
:
1251 if ( implAcceptPaste() )
1255 case KeyFuncType::COPY
:
1266 IMPL_LINK(NavigatorTree
, EditingEntryHdl
, const weld::TreeIter
&, rIter
, bool)
1268 // root, which isn't allowed to be renamed, has UserData=NULL
1269 m_bEditing
= !m_xTreeView
->get_id(rIter
).isEmpty();
1273 void NavigatorTree::NewForm(const weld::TreeIter
& rParentEntry
)
1275 // get ParentFormData
1276 if (!IsFormEntry(rParentEntry
))
1279 FmFormData
* pParentFormData
= weld::fromId
<FmFormData
*>(m_xTreeView
->get_id(rParentEntry
));
1283 const Reference
<XComponentContext
>& xContext
= comphelper::getProcessComponentContext();
1284 Reference
< XForm
> xNewForm(xContext
->getServiceManager()->createInstanceWithContext(FM_SUN_COMPONENT_FORM
, xContext
), UNO_QUERY
);
1288 Reference
< XPropertySet
> xPropertySet(xNewForm
, UNO_QUERY
);
1289 if (!xPropertySet
.is())
1292 FmFormData
* pNewFormData
= new FmFormData(xNewForm
, pParentFormData
);
1296 OUString aName
= GenerateName(*pNewFormData
);
1297 pNewFormData
->SetText(aName
);
1301 xPropertySet
->setPropertyValue( FM_PROP_NAME
, Any(aName
) );
1302 // a form should always have the command type table as default
1303 xPropertySet
->setPropertyValue( FM_PROP_COMMANDTYPE
, Any(sal_Int32(CommandType::TABLE
)));
1305 catch ( const Exception
& )
1307 OSL_FAIL("NavigatorTree::NewForm : could not set essential properties!");
1312 GetNavModel()->Insert(pNewFormData
, SAL_MAX_UINT32
, true);
1315 // set new form as active
1316 FmFormShell
* pFormShell
= GetNavModel()->GetFormShell();
1319 InterfaceBag aSelection
;
1320 aSelection
.insert( Reference
<XInterface
>( xNewForm
, UNO_QUERY
) );
1321 pFormShell
->GetImpl()->setCurrentSelection_Lock(std::move(aSelection
));
1323 pFormShell
->GetViewShell()->GetViewFrame().GetBindings().Invalidate(SID_FM_PROPERTIES
, true, true);
1325 GetNavModel()->SetModified();
1327 // switch to EditMode
1328 std::unique_ptr
<weld::TreeIter
> xNewEntry
= FindEntry(pNewFormData
);
1329 m_xTreeView
->start_editing(*xNewEntry
);
1333 FmControlData
* NavigatorTree::NewControl(const OUString
& rServiceName
, const weld::TreeIter
& rParentEntry
, bool bEditName
)
1336 if (!GetNavModel()->GetFormShell())
1338 if (!IsFormEntry(rParentEntry
))
1341 FmFormData
* pParentFormData
= weld::fromId
<FmFormData
*>(m_xTreeView
->get_id(rParentEntry
));
1342 Reference
<XForm
> xParentForm(pParentFormData
->GetFormIface());
1344 // create new component
1345 const Reference
<XComponentContext
>& xContext
= comphelper::getProcessComponentContext();
1346 Reference
<XFormComponent
> xNewComponent( xContext
->getServiceManager()->createInstanceWithContext(rServiceName
, xContext
), UNO_QUERY
);
1347 if (!xNewComponent
.is())
1350 FmControlData
* pNewFormControlData
= new FmControlData(xNewComponent
, pParentFormData
);
1353 OUString sName
= FmFormPageImpl::setUniqueName( xNewComponent
, xParentForm
);
1355 pNewFormControlData
->SetText( sName
);
1357 // insert FormComponent
1358 GetNavModel()->Insert(pNewFormControlData
, SAL_MAX_UINT32
, true);
1359 GetNavModel()->SetModified();
1363 // switch to EditMode
1364 std::unique_ptr
<weld::TreeIter
> xNewEntry
= FindEntry( pNewFormControlData
);
1365 m_xTreeView
->select(*xNewEntry
);
1367 m_xTreeView
->start_editing(*xNewEntry
);
1371 return pNewFormControlData
;
1374 OUString
NavigatorTree::GenerateName(const FmEntryData
& rEntryData
)
1376 const sal_uInt16 nMaxCount
= 99;
1381 if( dynamic_cast<const FmFormData
*>(&rEntryData
) != nullptr )
1382 aBaseName
= SvxResId( RID_STR_STDFORMNAME
);
1383 else if( dynamic_cast<const FmControlData
*>(&rEntryData
) != nullptr )
1384 aBaseName
= SvxResId( RID_STR_CONTROL
);
1388 FmFormData
* pFormParentData
= static_cast<FmFormData
*>(rEntryData
.GetParent());
1390 for( sal_Int32 i
=0; i
<nMaxCount
; i
++ )
1392 aNewName
= aBaseName
;
1395 aNewName
+= " " + OUString::number(i
);
1398 if( GetNavModel()->FindData(aNewName
, pFormParentData
,false) == nullptr )
1405 IMPL_LINK(NavigatorTree
, EditedEntryHdl
, const IterString
&, rIterString
, bool)
1409 const weld::TreeIter
& rIter
= rIterString
.first
;
1411 FmEntryData
* pEntryData
= weld::fromId
<FmEntryData
*>(m_xTreeView
->get_id(rIter
));
1412 bool bRes
= NavigatorTreeModel::Rename(pEntryData
, rIterString
.second
);
1415 m_xEditEntry
= m_xTreeView
->make_iterator(&rIter
);
1416 nEditEvent
= Application::PostUserEvent(LINK(this, NavigatorTree
, OnEdit
));
1422 IMPL_LINK_NOARG(NavigatorTree
, OnEdit
, void*, void)
1424 nEditEvent
= nullptr;
1425 m_xTreeView
->start_editing(*m_xEditEntry
);
1427 m_xEditEntry
.reset();
1430 IMPL_LINK_NOARG(NavigatorTree
, OnEntrySelDesel
, weld::TreeView
&, void)
1432 m_sdiState
= SDI_DIRTY
;
1434 if (IsSelectionHandlingLocked())
1437 if (m_aSynchronizeTimer
.IsActive())
1438 m_aSynchronizeTimer
.Stop();
1440 m_aSynchronizeTimer
.SetTimeout(EXPLORER_SYNC_DELAY
);
1441 m_aSynchronizeTimer
.Start();
1444 IMPL_LINK_NOARG(NavigatorTree
, OnSynchronizeTimer
, Timer
*, void)
1446 SynchronizeMarkList();
1449 IMPL_LINK_NOARG(NavigatorTree
, OnClipboardAction
, OLocalExchange
&, void)
1451 if ( m_aControlExchange
.isClipboardOwner() )
1454 if ( !doingKeyboardCut() )
1457 for (const auto& rEntry
: m_aCutEntries
)
1461 m_xTreeView
->set_sensitive(*rEntry
, true);
1463 ListBoxEntrySet().swap(m_aCutEntries
);
1465 m_bKeyboardCut
= false;
1468 void NavigatorTree::ShowSelectionProperties(bool bForce
)
1470 // at first i need the FormShell
1471 FmFormShell
* pFormShell
= GetNavModel()->GetFormShell();
1473 // no shell -> impossible to set curObject -> leave
1476 CollectSelectionData(SDI_ALL
);
1477 SAL_WARN_IF(static_cast<size_t>(m_nFormsSelected
+ m_nControlsSelected
1478 + (m_bRootSelected
? 1 : 0)) != m_arrCurrentSelection
.size(),
1480 "NavigatorTree::ShowSelectionProperties : selection meta data invalid !");
1483 InterfaceBag aSelection
;
1484 bool bSetSelectionAsMarkList
= false;
1486 if (m_bRootSelected
)
1487 ; // no properties for the root, neither for single nor for multi selection
1488 else if ( m_nFormsSelected
+ m_nControlsSelected
== 0 ) // none of the two should be less 0
1489 ; // no selection -> no properties
1490 else if ( m_nFormsSelected
* m_nControlsSelected
!= 0 )
1491 ; // mixed selection -> no properties
1493 { // either only forms, or only controls are selected
1494 if (m_arrCurrentSelection
.size() == 1)
1496 const std::unique_ptr
<weld::TreeIter
>& rIter
= *m_arrCurrentSelection
.begin();
1497 if (m_nFormsSelected
> 0)
1498 { // exactly one form is selected
1499 FmFormData
* pFormData
= weld::fromId
<FmFormData
*>(m_xTreeView
->get_id(*rIter
));
1500 aSelection
.insert( Reference
< XInterface
>( pFormData
->GetFormIface(), UNO_QUERY
) );
1503 { // exactly one control is selected (whatever hidden or normal)
1504 FmEntryData
* pEntryData
= weld::fromId
<FmEntryData
*>(m_xTreeView
->get_id(*rIter
));
1506 aSelection
.insert( Reference
< XInterface
>( pEntryData
->GetElement(), UNO_QUERY
) );
1510 { // it's a MultiSelection, so we must build a MultiSet
1511 if (m_nFormsSelected
> 0)
1513 // first of all collect PropertySet-Interfaces of the forms
1514 SvLBoxEntrySortedArray::const_iterator it
= m_arrCurrentSelection
.begin();
1515 for ( sal_Int32 i
= 0; i
< m_nFormsSelected
; ++i
)
1517 const std::unique_ptr
<weld::TreeIter
>& rIter
= *it
;
1518 FmFormData
* pFormData
= weld::fromId
<FmFormData
*>(m_xTreeView
->get_id(*rIter
));
1519 aSelection
.insert( pFormData
->GetPropertySet() );
1524 { // ... only controls
1525 if (m_nHiddenControls
== m_nControlsSelected
)
1526 { // a MultiSet for properties of hidden controls
1527 SvLBoxEntrySortedArray::const_iterator it
= m_arrCurrentSelection
.begin();
1528 for ( sal_Int32 i
= 0; i
< m_nHiddenControls
; ++i
)
1530 const std::unique_ptr
<weld::TreeIter
>& rIter
= *it
;
1531 FmEntryData
* pEntryData
= weld::fromId
<FmEntryData
*>(m_xTreeView
->get_id(*rIter
));
1532 aSelection
.insert( pEntryData
->GetPropertySet() );
1536 else if (m_nHiddenControls
== 0)
1537 { // only normal controls
1538 bSetSelectionAsMarkList
= true;
1545 // and now my form and my SelObject
1546 if ( bSetSelectionAsMarkList
)
1547 pFormShell
->GetImpl()->setCurrentSelectionFromMark_Lock(pFormShell
->GetFormView()->GetMarkedObjectList());
1549 pFormShell
->GetImpl()->setCurrentSelection_Lock(std::move(aSelection
));
1551 if (pFormShell
->GetImpl()->IsPropBrwOpen_Lock() || bForce
)
1553 // and now deliver all to the PropertyBrowser
1554 pFormShell
->GetViewShell()->GetViewFrame().GetDispatcher()->Execute( SID_FM_SHOW_PROPERTY_BROWSER
, SfxCallMode::ASYNCHRON
);
1559 void NavigatorTree::DeleteSelection()
1561 // of course, i can't delete root
1562 bool bRootSelected
= m_xTreeView
->is_selected(*m_xRootEntry
);
1563 auto nSelectedEntries
= m_xTreeView
->count_selected_rows();
1564 if (bRootSelected
&& (nSelectedEntries
> 1)) // root and other elements ?
1565 m_xTreeView
->unselect(*m_xRootEntry
); // yes -> remove root from selection
1567 if ((nSelectedEntries
== 0) || bRootSelected
) // still root ?
1568 return; // -> only selected element -> leave
1570 DBG_ASSERT(!m_bPrevSelectionMixed
, "NavigatorTree::DeleteSelection() : delete permitted if mark and selection are inconsistent");
1572 // i need the FormModel later
1573 FmFormShell
* pFormShell
= GetNavModel()->GetFormShell();
1576 FmFormModel
* pFormModel
= pFormShell
->GetFormModel();
1580 // now I have to safeguard the DeleteList: if you delete a form and a dependent element
1581 // - in this order - than the SvLBoxEntryPtr of the dependent element is already invalid,
1582 // when it should be deleted... you have to prohibit this GPF, that of course would happen,
1583 // so I take the 'normalized' list
1584 CollectSelectionData( SDI_NORMALIZED
);
1586 // see below for why we need this mapping from models to shapes
1587 FmFormView
* pFormView
= pFormShell
->GetFormView();
1588 SdrPageView
* pPageView
= pFormView
? pFormView
->GetSdrPageView() : nullptr;
1589 SdrPage
* pPage
= pPageView
? pPageView
->GetPage() : nullptr;
1590 DBG_ASSERT( pPage
, "NavigatorTree::DeleteSelection: invalid form page!" );
1592 MapModelToShape aModelShapes
;
1594 collectShapeModelMapping( pPage
, aModelShapes
);
1596 // problem: we have to use ExplorerModel::Remove, since only this one properly deletes Form objects.
1597 // But, the controls themself must be deleted via DeleteMarked (else, the Writer has some problems
1598 // somewhere). In case I'd first delete the structure, then the controls, the UNDO would not work
1599 // (since UNDO then would mean to first restore the controls, then the structure, means their parent
1600 // form). The other way round, the EntryDatas would be invalid, if I'd first delete the controls and
1601 // then go on to the structure. This means I have to delete the forms *after* the normal controls, so
1602 // that during UNDO, they're restored in the proper order.
1603 pFormShell
->GetImpl()->EnableTrackProperties_Lock(false);
1604 for (SvLBoxEntrySortedArray::reverse_iterator it
= m_arrCurrentSelection
.rbegin();
1605 it
!= m_arrCurrentSelection
.rend(); )
1607 const std::unique_ptr
<weld::TreeIter
>& rIter
= *it
;
1608 FmEntryData
* pCurrent
= weld::fromId
<FmEntryData
*>(m_xTreeView
->get_id(*rIter
));
1611 auto pFormData
= dynamic_cast<FmFormData
*>(pCurrent
);
1613 // because deletion is done by the view, and i build on its MarkList,
1614 // but normally only direct controls, no indirect ones, are marked in a marked form,
1615 // I have to do it later
1617 MarkViewObj(pFormData
, true/*deep*/);
1619 // a hidden control ?
1620 bool bIsHidden
= IsHiddenControl(pCurrent
);
1622 // keep forms and hidden controls, the rest not
1623 if (!pFormData
&& !bIsHidden
)
1625 // well, no form and no hidden control -> we can remove it from m_arrCurrentSelection, as it will
1626 // be deleted automatically. This is because for every model (except forms and hidden control models)
1627 // there exist a shape, which is marked _if_and_only_if_ the model is selected in our tree.
1628 if ( aModelShapes
.find( pCurrent
->GetElement() ) != aModelShapes
.end() )
1630 // if there's a shape for the current entry, then either it is marked or it is in a
1631 // hidden layer (#i28502#), or something like this.
1632 // In the first case, it will be deleted below, in the second case, we currently don't
1633 // delete it, as there's no real (working!) API for this, neither in UNO nor in non-UNO.
1634 m_arrCurrentSelection
.erase( --(it
.base()) );
1638 // In case there is no shape for the current entry, we keep the entry in m_arrCurrentSelection,
1639 // since then we can definitely remove it.
1644 pFormShell
->GetImpl()->EnableTrackProperties_Lock(true);
1646 // let the view delete the marked controls
1647 pFormShell
->GetFormView()->DeleteMarked();
1649 // start UNDO at this point. Unfortunately, this results in 2 UNDO actions, since DeleteMarked is
1650 // creating an own one. However, if we'd move it before DeleteMarked, Writer does not really like
1657 if ( m_arrCurrentSelection
.size() == 1 )
1659 aUndoStr
= SvxResId(RID_STR_UNDO_CONTAINER_REMOVE
);
1660 if (m_nFormsSelected
)
1661 aUndoStr
= aUndoStr
.replaceFirst( "#", SvxResId( RID_STR_FORM
) );
1663 // it must be a control (else the root would be selected, but it cannot be deleted)
1664 aUndoStr
= aUndoStr
.replaceFirst( "#", SvxResId( RID_STR_CONTROL
) );
1668 aUndoStr
= SvxResId(RID_STR_UNDO_CONTAINER_REMOVE_MULTIPLE
);
1669 aUndoStr
= aUndoStr
.replaceFirst( "#", OUString::number( m_arrCurrentSelection
.size() ) );
1671 pFormModel
->BegUndo(aUndoStr
);
1674 // remove remaining structure
1675 for (const auto& rpSelection
: m_arrCurrentSelection
)
1677 FmEntryData
* pCurrent
= weld::fromId
<FmEntryData
*>(m_xTreeView
->get_id(*rpSelection
));
1679 // if the entry still has children, we skipped deletion of one of those children.
1680 // This may for instance be because the shape is in a hidden layer, where we're unable
1682 if ( pCurrent
->GetChildList()->size() )
1685 // one remaining subtle problem, before deleting it : if it's a form and the shell
1686 // knows it as CurrentObject, I have to tell it something else
1687 if (auto pFormData
= dynamic_cast<FmFormData
*>( pCurrent
))
1689 Reference
< XForm
> xCurrentForm( pFormData
->GetFormIface() );
1690 if (pFormShell
->GetImpl()->getCurrentForm_Lock() == xCurrentForm
) // shell knows form to be deleted ?
1691 pFormShell
->GetImpl()->forgetCurrentForm_Lock(); // -> take away ...
1693 GetNavModel()->Remove(pCurrent
, true);
1695 pFormModel
->EndUndo();
1699 void NavigatorTree::CollectSelectionData(SELDATA_ITEMS sdiHow
)
1701 DBG_ASSERT(sdiHow
!= SDI_DIRTY
, "NavigatorTree::CollectSelectionData : ever thought about your parameter ? DIRTY ?");
1702 if (sdiHow
== m_sdiState
)
1705 m_arrCurrentSelection
.clear();
1706 m_nFormsSelected
= m_nControlsSelected
= m_nHiddenControls
= 0;
1707 m_bRootSelected
= false;
1709 m_xTreeView
->selected_foreach([this, sdiHow
](weld::TreeIter
& rSelectionLoop
){
1710 // count different elements
1711 if (m_xTreeView
->iter_compare(rSelectionLoop
, *m_xRootEntry
) == 0)
1712 m_bRootSelected
= true;
1715 if (IsFormEntry(rSelectionLoop
))
1719 ++m_nControlsSelected
;
1720 if (IsHiddenControl(weld::fromId
<FmEntryData
*>(m_xTreeView
->get_id(rSelectionLoop
))))
1721 ++m_nHiddenControls
;
1725 if (sdiHow
== SDI_NORMALIZED
)
1727 // don't take something with a selected ancestor
1728 if (m_xTreeView
->iter_compare(rSelectionLoop
, *m_xRootEntry
) == 0)
1729 m_arrCurrentSelection
.emplace(m_xTreeView
->make_iterator(&rSelectionLoop
));
1732 std::unique_ptr
<weld::TreeIter
> xParentLoop(m_xTreeView
->make_iterator(&rSelectionLoop
));
1733 bool bParentLoop
= m_xTreeView
->iter_parent(*xParentLoop
);
1736 // actually i would have to test, if parent is part of m_arr_CurrentSelection ...
1737 // but if it's selected, then it's in m_arrCurrentSelection
1738 // or one of its ancestors, which was selected earlier.
1739 // In both cases IsSelected is enough
1740 if (m_xTreeView
->is_selected(*xParentLoop
))
1744 if (m_xTreeView
->iter_compare(*xParentLoop
, *m_xRootEntry
) == 0)
1746 // until root (exclusive), there was no selected parent -> entry belongs to normalized list
1747 m_arrCurrentSelection
.emplace(m_xTreeView
->make_iterator(&rSelectionLoop
));
1751 bParentLoop
= m_xTreeView
->iter_parent(*xParentLoop
);
1756 else if (sdiHow
== SDI_NORMALIZED_FORMARK
)
1758 std::unique_ptr
<weld::TreeIter
> xParent(m_xTreeView
->make_iterator(&rSelectionLoop
));
1759 bool bParent
= m_xTreeView
->iter_parent(*xParent
);
1760 if (!bParent
|| !m_xTreeView
->is_selected(*xParent
) || IsFormEntry(rSelectionLoop
))
1761 m_arrCurrentSelection
.emplace(m_xTreeView
->make_iterator(&rSelectionLoop
));
1764 m_arrCurrentSelection
.emplace(m_xTreeView
->make_iterator(&rSelectionLoop
));
1769 m_sdiState
= sdiHow
;
1772 void NavigatorTree::SynchronizeSelection(FmEntryDataArray
& arredToSelect
)
1774 LockSelectionHandling();
1775 if (arredToSelect
.empty())
1777 m_xTreeView
->unselect_all();
1781 // compare current selection with requested SelectList
1782 m_xTreeView
->selected_foreach([this, &arredToSelect
](weld::TreeIter
& rSelection
) {
1783 FmEntryData
* pCurrent
= weld::fromId
<FmEntryData
*>(m_xTreeView
->get_id(rSelection
));
1784 if (pCurrent
!= nullptr)
1786 FmEntryDataArray::iterator it
= arredToSelect
.find(pCurrent
);
1787 if ( it
!= arredToSelect
.end() )
1788 { // entry already selected, but also in SelectList
1789 // remove it from there
1790 arredToSelect
.erase(it
);
1792 { // entry selected, but not in SelectList -> remove selection
1793 m_xTreeView
->unselect(rSelection
);
1794 // make it visible (maybe it's the only modification i do in this handler
1795 // so you should see it
1796 m_xTreeView
->scroll_to_row(rSelection
);
1800 m_xTreeView
->unselect(rSelection
);
1805 // now SelectList contains only entries, which have to be selected
1806 // two possibilities : 1) run through SelectList, get SvTreeListEntry for every entry and select it (is more intuitive)
1807 // 2) run through my SvLBoxEntries and select those, i can find in the SelectList
1808 // 1) needs =(k*n) (k=length of SelectList, n=number of entries),
1809 // plus the fact, that FindEntry uses extensive IsEqualWithoutChilden instead of comparing pointer to UserData
1810 // 2) needs =(n*log k), duplicates some code from FindEntry
1811 // This may be a frequently used code ( at every change in mark of the view!),
1812 // so i use latter one
1813 m_xTreeView
->all_foreach([this, &arredToSelect
](weld::TreeIter
& rLoop
){
1814 FmEntryData
* pCurEntryData
= weld::fromId
<FmEntryData
*>(m_xTreeView
->get_id(rLoop
));
1815 FmEntryDataArray::iterator it
= arredToSelect
.find(pCurEntryData
);
1816 if (it
!= arredToSelect
.end())
1818 m_xTreeView
->select(rLoop
);
1819 m_xTreeView
->scroll_to_row(rLoop
);
1825 UnlockSelectionHandling();
1829 void NavigatorTree::SynchronizeSelection()
1832 FmFormShell
* pFormShell
= GetNavModel()->GetFormShell();
1833 if(!pFormShell
) return;
1835 FmFormView
* pFormView
= pFormShell
->GetFormView();
1836 if (!pFormView
) return;
1838 GetNavModel()->BroadcastMarkedObjects(pFormView
->GetMarkedObjectList());
1842 void NavigatorTree::SynchronizeMarkList()
1844 // i'll need this shell
1845 FmFormShell
* pFormShell
= GetNavModel()->GetFormShell();
1846 if (!pFormShell
) return;
1848 CollectSelectionData(SDI_NORMALIZED_FORMARK
);
1850 // the view shouldn't notify now if MarkList changed
1851 pFormShell
->GetImpl()->EnableTrackProperties_Lock(false);
1855 for (auto& rSelectionLoop
: m_arrCurrentSelection
)
1857 // When form selection, mark all controls of form
1858 if (IsFormEntry(*rSelectionLoop
) && m_xTreeView
->iter_compare(*rSelectionLoop
, *m_xRootEntry
) != 0)
1859 MarkViewObj(weld::fromId
<FmFormData
*>(m_xTreeView
->get_id(*rSelectionLoop
)), false/*deep*/);
1861 // When control selection, mark Control-SdrObjects
1862 else if (IsFormComponentEntry(*rSelectionLoop
))
1864 FmControlData
* pControlData
= weld::fromId
<FmControlData
*>(m_xTreeView
->get_id(*rSelectionLoop
));
1868 // When HiddenControl no object can be selected
1869 Reference
< XFormComponent
> xFormComponent( pControlData
->GetFormComponent());
1870 if (!xFormComponent
.is())
1872 Reference
< XPropertySet
> xSet(xFormComponent
, UNO_QUERY
);
1876 sal_uInt16 nClassId
= ::comphelper::getINT16(xSet
->getPropertyValue(FM_PROP_CLASSID
));
1877 if (nClassId
!= FormComponentType::HIDDENCONTROL
)
1878 MarkViewObj(pControlData
);
1883 // if PropertyBrowser is open, I have to adopt it according to my selection
1884 // (Not as MarkList of view : if a form is selected, all belonging controls are selected in the view
1885 // but of course i want to see the form-properties
1886 ShowSelectionProperties();
1888 // reset flag at view
1889 pFormShell
->GetImpl()->EnableTrackProperties_Lock(true);
1891 // if exactly one form is selected now, shell should notice it as CurrentForm
1892 // (if selection handling isn't locked, view cares about it in MarkListHasChanged
1893 // but mechanism doesn't work, if form is empty for example
1894 if ((m_arrCurrentSelection
.size() != 1) || (m_nFormsSelected
!= 1))
1897 std::unique_ptr
<weld::TreeIter
> xSelected(m_xTreeView
->make_iterator());
1898 if (!m_xTreeView
->get_selected(xSelected
.get()))
1900 FmFormData
* pSingleSelectionData
= xSelected
? dynamic_cast<FmFormData
*>(weld::fromId
<FmEntryData
*>(m_xTreeView
->get_id(*xSelected
)))
1902 DBG_ASSERT( pSingleSelectionData
, "NavigatorTree::SynchronizeMarkList: invalid selected form!" );
1903 if ( pSingleSelectionData
)
1905 InterfaceBag aSelection
;
1906 aSelection
.insert( Reference
< XInterface
>( pSingleSelectionData
->GetFormIface(), UNO_QUERY
) );
1907 pFormShell
->GetImpl()->setCurrentSelection_Lock(std::move(aSelection
));
1911 bool NavigatorTree::IsHiddenControl(FmEntryData
const * pEntryData
)
1913 if (pEntryData
== nullptr) return false;
1915 const Reference
< XPropertySet
>& xProperties( pEntryData
->GetPropertySet() );
1916 if (::comphelper::hasProperty(FM_PROP_CLASSID
, xProperties
))
1918 Any aClassID
= xProperties
->getPropertyValue( FM_PROP_CLASSID
);
1919 return (::comphelper::getINT16(aClassID
) == FormComponentType::HIDDENCONTROL
);
1924 void NavigatorTree::UnmarkAllViewObj()
1926 FmFormShell
* pFormShell
= GetNavModel()->GetFormShell();
1929 FmFormView
* pFormView
= pFormShell
->GetFormView();
1930 pFormView
->UnMarkAll();
1933 void NavigatorTree::MarkViewObj(FmFormData
const * pFormData
, bool bDeep
)
1935 FmFormShell
* pFormShell
= GetNavModel()->GetFormShell();
1939 // first collect all sdrobjects
1940 ::std::set
< Reference
< XFormComponent
> > aObjects
;
1941 CollectObjects(pFormData
,bDeep
,aObjects
);
1944 // find and select appropriate SdrObj in page
1945 FmFormView
* pFormView
= pFormShell
->GetFormView();
1946 SdrPageView
* pPageView
= pFormView
->GetSdrPageView();
1947 SdrPage
* pPage
= pPageView
->GetPage();
1948 //FmFormPage* pFormPage = dynamic_cast< FmFormPage* >( pPage );
1950 SdrObjListIter
aIter( pPage
);
1951 while ( aIter
.IsMore() )
1953 SdrObject
* pSdrObject
= aIter
.Next();
1954 FmFormObj
* pFormObject
= FmFormObj::GetFormObject( pSdrObject
);
1958 Reference
< XFormComponent
> xControlModel( pFormObject
->GetUnoControlModel(),UNO_QUERY
);
1959 if ( xControlModel
.is() && aObjects
.find(xControlModel
) != aObjects
.end() && !pFormView
->IsObjMarked( pSdrObject
) )
1961 // unfortunately, the writer doesn't like marking an already-marked object, again, so reset the mark first
1962 pFormView
->MarkObj( pSdrObject
, pPageView
);
1964 } // while ( aIter.IsMore() )
1965 // make the mark visible
1966 ::tools::Rectangle
aMarkRect( pFormView
->GetAllMarkedRect());
1967 for ( sal_uInt32 i
= 0; i
< pFormView
->PaintWindowCount(); ++i
)
1969 SdrPaintWindow
* pPaintWindow
= pFormView
->GetPaintWindow( i
);
1970 OutputDevice
& rOutDev
= pPaintWindow
->GetOutputDevice();
1971 if ( ( OUTDEV_WINDOW
== rOutDev
.GetOutDevType() ) && !aMarkRect
.IsEmpty() )
1973 pFormView
->MakeVisible( aMarkRect
, *rOutDev
.GetOwnerWindow() );
1975 } // for ( sal_uInt32 i = 0; i < pFormView->PaintWindowCount(); ++i )
1978 void NavigatorTree::CollectObjects(FmFormData
const * pFormData
, bool bDeep
, ::std::set
< Reference
< XFormComponent
> >& _rObjects
)
1980 FmEntryDataList
* pChildList
= pFormData
->GetChildList();
1981 for( size_t i
= 0; i
< pChildList
->size(); ++i
)
1983 FmEntryData
* pEntryData
= pChildList
->at( i
);
1984 if( auto pControlData
= dynamic_cast<FmControlData
*>( pEntryData
) )
1986 _rObjects
.insert(pControlData
->GetFormComponent());
1987 } // if( dynamic_cast<const FmControlData*>( pEntryData) != nullptr )
1989 if (auto pEntryFormData
= dynamic_cast<FmFormData
*>( pEntryData
))
1990 CollectObjects(pEntryFormData
, bDeep
, _rObjects
);
1991 } // for( sal_uInt32 i=0; i<pChildList->Count(); i++ )
1994 void NavigatorTree::MarkViewObj( FmControlData
const * pControlData
)
1998 FmFormShell
* pFormShell
= GetNavModel()->GetFormShell();
2003 // find and select appropriate SdrObj
2004 FmFormView
* pFormView
= pFormShell
->GetFormView();
2005 const Reference
< XFormComponent
>& xFormComponent( pControlData
->GetFormComponent());
2006 SdrPageView
* pPageView
= pFormView
->GetSdrPageView();
2007 SdrPage
* pPage
= pPageView
->GetPage();
2009 bool bPaint
= false;
2010 SdrObjListIter
aIter( pPage
);
2011 while ( aIter
.IsMore() )
2013 SdrObject
* pSdrObject
= aIter
.Next();
2014 FmFormObj
* pFormObject
= FmFormObj::GetFormObject( pSdrObject
);
2018 Reference
< XInterface
> xControlModel( pFormObject
->GetUnoControlModel() );
2019 if ( xControlModel
!= xFormComponent
)
2023 if ( !pFormView
->IsObjMarked( pSdrObject
) )
2024 // unfortunately, the writer doesn't like marking an already-marked object, again, so reset the mark first
2025 pFormView
->MarkObj( pSdrObject
, pPageView
);
2029 } // while ( aIter.IsMore() )
2033 // make the mark visible
2034 ::tools::Rectangle
aMarkRect( pFormView
->GetAllMarkedRect());
2035 for ( sal_uInt32 i
= 0; i
< pFormView
->PaintWindowCount(); ++i
)
2037 SdrPaintWindow
* pPaintWindow
= pFormView
->GetPaintWindow( i
);
2038 OutputDevice
& rOutDev
= pPaintWindow
->GetOutputDevice();
2039 if ( OUTDEV_WINDOW
== rOutDev
.GetOutDevType() )
2041 pFormView
->MakeVisible( aMarkRect
, *rOutDev
.GetOwnerWindow() );
2043 } // for ( sal_uInt32 i = 0; i < pFormView->PaintWindowCount(); ++i )
2050 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */