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 <tools/diagnose_ex.h>
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_aControlExchange()
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_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
= 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
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
= reinterpret_cast<FmEntryData
*>(m_xTreeView
->get_id(*rpEntry
).toInt64())->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(), "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
);
369 xContextMenu
->remove("new");
371 // 'New'\'Form' under the same terms
372 bool bShowForm
= bSingleSelection
&& (m_nFormsSelected
|| m_bRootSelected
);
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
;
379 xSubMenuNew
->append("hidden", SvxResId(RID_STR_HIDDEN
), RID_SVXBMP_HIDDEN
);
381 // 'Delete': everything which is not root can be removed
383 xContextMenu
->remove("delete");
385 // 'Cut', 'Copy' and 'Paste'
386 bool bShowCut
= !m_bRootSelected
&& implAllowExchange(DND_ACTION_MOVE
);
388 xContextMenu
->remove("cut");
389 bool bShowCopy
= !m_bRootSelected
&& implAllowExchange(DND_ACTION_COPY
);
391 xContextMenu
->remove("copy");
392 if (!implAcceptPaste())
393 xContextMenu
->remove("paste");
395 // TabDialog, if exactly one form
396 bool bShowTabOrder
= bSingleSelection
&& m_nFormsSelected
;
398 xContextMenu
->remove("taborder");
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("props");
417 // rename, if one element and no root
418 bool bShowRename
= bSingleSelection
&& !m_bRootSelected
;
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!" );
443 pFormShell
->GetImpl()->checkControlConversionSlotsForCurrentSelection_Lock(*xConversionMenu
);
446 xContextMenu
->remove("change");
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();
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
= 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() )
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
= reinterpret_cast<FmControlData
*>(m_xTreeView
->get_id(*rIter
).toInt64());
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
= reinterpret_cast<FmEntryData
*>(m_xTreeView
->get_id(rEntry
).toInt64());
545 if (pCurEntryData
&& pCurEntryData
->IsEqualWithoutChildren(pEntryData
))
547 xRet
= m_xTreeView
->make_iterator(&rEntry
);
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
);
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
)));
621 m_xTreeView
->insert(m_xRootEntry
.get(), nRelPos
, &pEntryData
->GetText(), &sId
,
622 nullptr, nullptr, false, xNewEntry
.get());
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
635 m_xTreeView
->expand_row(*m_xRootEntry
);
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);
649 void NavigatorTree::Remove( FmEntryData
* pEntryData
)
654 // entry for the data
655 std::unique_ptr
<weld::TreeIter
> xEntry
= FindEntry(pEntryData
);
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
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()))
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
717 return DND_ACTION_NONE
;
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
;
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
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
)
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
);
818 if (m_xTreeView
->iter_compare(*_pTargetEntry
, *xCurrentParent
) == 0)
819 return DND_ACTION_NONE
;
822 if (m_xTreeView
->iter_compare(*rCurrent
, *_pTargetEntry
) == 0)
823 return DND_ACTION_NONE
;
826 if (bDropTargetIsComponent
)
827 return DND_ACTION_NONE
;
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
))
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))
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))
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
;
878 // no target -> no drop
879 return DND_ACTION_NONE
;
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
891 if ( DND_ACTION_COPY
== _nAction
)
892 { // bHasHiddenControlsFormat means that only hidden controls are part of the data
894 DBG_ASSERT( bHasHiddenControlsFormat
, "NavigatorTree::implExecuteDataTransfer: copy allowed for hidden controls only!" );
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
901 DBG_ASSERT(bHasHiddenControlsFormat
, "NavigatorTree::implExecuteDataTransfer: only copying of hidden controls is supported !");
902 // should be caught by AcceptDrop
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;
918 OUString
aStr(SvxResId(RID_STR_CONTROL
));
919 OUString aUndoStr
= SvxResId(RID_STR_UNDO_CONTAINER_INSERT
).replaceAll("#", aStr
);
920 pFormModel
->BegUndo(aUndoStr
);
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
);
953 m_xTreeView
->set_cursor(*xToSelect
);
957 pFormModel
->EndUndo();
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()));
985 FmFormShell
* pFormShell
= GetNavModel()->GetFormShell();
986 FmFormModel
* pFormModel
= pFormShell
? pFormShell
->GetFormModel() : nullptr;
988 return DND_ACTION_NONE
;
991 const bool bUndo
= pFormModel
->IsUndoEnabled();
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();
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");
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
);
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
);
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
);
1054 m_xTreeView
->unselect(*rCurrent
);
1056 Remove(pCurrentUserData
);
1058 // position in DropParents, where to insert dropped entries
1060 xContainer
.set(pTargetData
->GetElement(), UNO_QUERY
);
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
1075 // insert in a form needs a FormComponent
1076 xContainer
->insertByIndex( nIndex
,
1077 makeAny( Reference
< XFormComponent
>( xCurrentChild
, UNO_QUERY
) ) );
1081 xContainer
->insertByIndex( nIndex
,
1082 makeAny( Reference
< XForm
>( xCurrentChild
, UNO_QUERY
) ) );
1085 if (aEvts
.hasElements())
1087 xManager
.set(xContainer
, UNO_QUERY
);
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
1099 pTargetData
->GetChildList()->insert( std::unique_ptr
<FmEntryData
>(pCurrentUserData
), nIndex
);
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();
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();
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 );
1140 OControlTransferData
aDroppedData( rEvt
.maDropEvent
.Transferable
);
1141 nResult
= implExecuteDataTransfer( aDroppedData
, rEvt
.mnAction
, rEvt
.maPosPixel
, true );
1146 void NavigatorTree::doPaste()
1148 std::unique_ptr
<weld::TreeIter
> xSelected(m_xTreeView
->make_iterator());
1149 if (!m_xTreeView
->get_selected(xSelected
.get()))
1154 if ( m_aControlExchange
.isClipboardOwner() )
1156 implExecuteDataTransfer( *m_aControlExchange
, doingKeyboardCut( ) ? DND_ACTION_MOVE
: DND_ACTION_COPY
, xSelected
.get(), false );
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
) )
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
)
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();
1232 if (rCode
.GetCode() == KEY_DELETE
&& !rCode
.GetModifier())
1239 switch ( rCode
.GetFunction() )
1241 case KeyFuncType::CUT
:
1245 case KeyFuncType::PASTE
:
1246 if ( implAcceptPaste() )
1250 case KeyFuncType::COPY
:
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();
1268 void NavigatorTree::NewForm(const weld::TreeIter
& rParentEntry
)
1270 // get ParentFormData
1271 if (!IsFormEntry(rParentEntry
))
1274 FmFormData
* pParentFormData
= reinterpret_cast<FmFormData
*>(m_xTreeView
->get_id(rParentEntry
).toInt64());
1278 Reference
<XComponentContext
> xContext
= comphelper::getProcessComponentContext();
1279 Reference
< XForm
> xNewForm(xContext
->getServiceManager()->createInstanceWithContext(FM_SUN_COMPONENT_FORM
, xContext
), UNO_QUERY
);
1283 Reference
< XPropertySet
> xPropertySet(xNewForm
, UNO_QUERY
);
1284 if (!xPropertySet
.is())
1287 FmFormData
* pNewFormData
= new FmFormData(xNewForm
, pParentFormData
);
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!");
1307 GetNavModel()->Insert(pNewFormData
, SAL_MAX_UINT32
, true);
1310 // set new form as active
1311 FmFormShell
* pFormShell
= GetNavModel()->GetFormShell();
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
);
1328 FmControlData
* NavigatorTree::NewControl(const OUString
& rServiceName
, const weld::TreeIter
& rParentEntry
, bool bEditName
)
1331 if (!GetNavModel()->GetFormShell())
1333 if (!IsFormEntry(rParentEntry
))
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())
1345 FmControlData
* pNewFormControlData
= new FmControlData(xNewComponent
, pParentFormData
);
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();
1358 // switch to EditMode
1359 std::unique_ptr
<weld::TreeIter
> xNewEntry
= FindEntry( pNewFormControlData
);
1360 m_xTreeView
->select(*xNewEntry
);
1362 m_xTreeView
->start_editing(*xNewEntry
);
1366 return pNewFormControlData
;
1369 OUString
NavigatorTree::GenerateName( FmEntryData
const * pEntryData
)
1371 const sal_uInt16 nMaxCount
= 99;
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
);
1383 FmFormData
* pFormParentData
= static_cast<FmFormData
*>(pEntryData
->GetParent());
1385 for( sal_Int32 i
=0; i
<nMaxCount
; i
++ )
1387 aNewName
= aBaseName
;
1390 aNewName
+= " " + OUString::number(i
);
1393 if( GetNavModel()->FindData(aNewName
, pFormParentData
,false) == nullptr )
1400 IMPL_LINK(NavigatorTree
, EditedEntryHdl
, const IterString
&, rIterString
, bool)
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
);
1410 m_xEditEntry
= m_xTreeView
->make_iterator(&rIter
);
1411 nEditEvent
= Application::PostUserEvent(LINK(this, NavigatorTree
, OnEdit
));
1417 IMPL_LINK_NOARG(NavigatorTree
, OnEdit
, void*, void)
1419 nEditEvent
= nullptr;
1420 m_xTreeView
->start_editing(*m_xEditEntry
);
1422 m_xEditEntry
.reset();
1425 IMPL_LINK_NOARG(NavigatorTree
, OnEntrySelDesel
, weld::TreeView
&, void)
1427 m_sdiState
= SDI_DIRTY
;
1429 if (IsSelectionHandlingLocked())
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() )
1449 if ( !doingKeyboardCut() )
1452 for (const auto& rEntry
: m_aCutEntries
)
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();
1469 // no shell -> impossible to set curObject -> leave
1472 CollectSelectionData(SDI_ALL
);
1473 SAL_WARN_IF(static_cast<size_t>(m_nFormsSelected
+ m_nControlsSelected
1474 + (m_bRootSelected
? 1 : 0)) != m_arrCurrentSelection
.size(),
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
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
) );
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
) );
1506 { // it's a MultiSelection, so we must build a MultiSet
1507 if (m_nFormsSelected
> 0)
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() );
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() );
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());
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();
1572 FmFormModel
* pFormModel
= pFormShell
->GetFormModel();
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
;
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());
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
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()) );
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.
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
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
) );
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
) );
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
1678 if ( pCurrent
->GetChildList()->size() )
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
)
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;
1711 if (IsFormEntry(rSelectionLoop
))
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
));
1728 std::unique_ptr
<weld::TreeIter
> xParentLoop(m_xTreeView
->make_iterator(&rSelectionLoop
));
1729 bool bParentLoop
= m_xTreeView
->iter_parent(*xParentLoop
);
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
))
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
));
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
));
1760 m_arrCurrentSelection
.emplace(m_xTreeView
->make_iterator(&rSelectionLoop
));
1765 m_sdiState
= sdiHow
;
1768 void NavigatorTree::SynchronizeSelection(FmEntryDataArray
& arredToSelect
)
1770 LockSelectionHandling();
1771 if (arredToSelect
.empty())
1773 m_xTreeView
->unselect_all();
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
);
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
);
1796 m_xTreeView
->unselect(rSelection
);
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
);
1821 UnlockSelectionHandling();
1825 void NavigatorTree::SynchronizeSelection()
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);
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());
1864 // When HiddenControl no object can be selected
1865 Reference
< XFormComponent
> xFormComponent( pControlData
->GetFormComponent());
1866 if (!xFormComponent
.is())
1868 Reference
< XPropertySet
> xSet(xFormComponent
, UNO_QUERY
);
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))
1893 std::unique_ptr
<weld::TreeIter
> xSelected(m_xTreeView
->make_iterator());
1894 if (!m_xTreeView
->get_selected(xSelected
.get()))
1896 FmFormData
* pSingleSelectionData
= xSelected
? dynamic_cast<FmFormData
*>(reinterpret_cast<FmEntryData
*>(m_xTreeView
->get_id(*xSelected
).toInt64()))
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
);
1920 void NavigatorTree::UnmarkAllViewObj()
1922 FmFormShell
* pFormShell
= GetNavModel()->GetFormShell();
1925 FmFormView
* pFormView
= pFormShell
->GetFormView();
1926 pFormView
->UnMarkAll();
1929 void NavigatorTree::MarkViewObj(FmFormData
const * pFormData
, bool bDeep
)
1931 FmFormShell
* pFormShell
= GetNavModel()->GetFormShell();
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
);
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 )
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
)
1994 FmFormShell
* pFormShell
= GetNavModel()->GetFormShell();
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
);
2014 Reference
< XInterface
> xControlModel( pFormObject
->GetUnoControlModel() );
2015 if ( xControlModel
!= xFormComponent
)
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
);
2025 } // while ( aIter.IsMore() )
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: */