bump product version to 5.0.4.1
[LibreOffice.git] / svx / source / form / navigatortree.cxx
blob95152e42cfb98c17a68aefe3c93478c54e113d5c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <svx/dialmgr.hxx>
21 #include <svx/fmshell.hxx>
22 #include <svx/fmmodel.hxx>
23 #include <svx/fmpage.hxx>
24 #include <svx/svdpagv.hxx>
25 #include "svx/svditer.hxx"
27 #include "fmhelp.hrc"
28 #include "fmexpl.hxx"
29 #include "svx/fmresids.hrc"
30 #include "fmshimp.hxx"
31 #include "fmservs.hxx"
32 #include "fmundo.hxx"
33 #include "fmpgeimp.hxx"
34 #include "fmitems.hxx"
35 #include "fmobj.hxx"
36 #include "fmprop.hrc"
37 #include <vcl/wrkwin.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 <com/sun/star/form/FormComponentType.hpp>
44 #include <com/sun/star/sdb/CommandType.hpp>
45 #include <com/sun/star/beans/PropertyAttribute.hpp>
46 #include <com/sun/star/script/XEventAttacherManager.hpp>
47 #include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
48 #include <com/sun/star/datatransfer/XTransferable.hpp>
49 #include <svx/sdrpaintwindow.hxx>
51 #include <svx/svxdlg.hxx>
52 #include <svx/dialogs.hrc>
53 #include "svtools/treelistentry.hxx"
55 namespace svxform
59 #define DROP_ACTION_TIMER_INITIAL_TICKS 10
60 // Time until scroll starts
61 #define DROP_ACTION_TIMER_SCROLL_TICKS 3
62 // Time to scroll one line
63 #define DROP_ACTION_TIMER_TICK_BASE 10
64 // factor for both declarations (in ms)
66 #define EXPLORER_SYNC_DELAY 200
67 // Time (in ms) until explorer synchronizes the view after select or deselect
69 using namespace ::com::sun::star::uno;
70 using namespace ::com::sun::star::lang;
71 using namespace ::com::sun::star::beans;
72 using namespace ::com::sun::star::form;
73 using namespace ::com::sun::star::awt;
74 using namespace ::com::sun::star::container;
75 using namespace ::com::sun::star::script;
76 using namespace ::com::sun::star::datatransfer;
77 using namespace ::com::sun::star::datatransfer::clipboard;
78 using namespace ::com::sun::star::sdb;
81 // helper
84 typedef ::std::map< Reference< XInterface >, SdrObject*, ::comphelper::OInterfaceCompare< XInterface > >
85 MapModelToShape;
86 typedef MapModelToShape::value_type ModelShapePair;
89 void collectShapeModelMapping( SdrPage* _pPage, MapModelToShape& _rMapping )
91 OSL_ENSURE( _pPage, "collectShapeModelMapping: invalid arg!" );
93 _rMapping.clear();
95 SdrObjListIter aIter( *_pPage );
96 while ( aIter.IsMore() )
98 SdrObject* pSdrObject = aIter.Next();
99 FmFormObj* pFormObject = FmFormObj::GetFormObject( pSdrObject );
100 if ( !pFormObject )
101 continue;
103 Reference< XInterface > xNormalizedModel( pFormObject->GetUnoControlModel(), UNO_QUERY );
104 // note that this is normalized (i.e. queried for XInterface explicitly)
106 #ifdef DBG_UTIL
107 ::std::pair< MapModelToShape::iterator, bool > aPos =
108 #endif
109 _rMapping.insert( ModelShapePair( xNormalizedModel, pSdrObject ) );
110 DBG_ASSERT( aPos.second, "collectShapeModelMapping: model was already existent!" );
111 // if this asserts, this would mean we have 2 shapes pointing to the same model
115 NavigatorTree::NavigatorTree( vcl::Window* pParent )
116 :SvTreeListBox( pParent, WB_HASBUTTONS|WB_HASLINES|WB_BORDER|WB_HSCROLL ) // #100258# OJ WB_HSCROLL added
117 ,m_aControlExchange(this)
118 ,m_pNavModel( NULL )
119 ,m_pRootEntry(NULL)
120 ,m_pEditEntry(NULL)
121 ,nEditEvent(0)
122 ,m_sdiState(SDI_DIRTY)
123 ,m_aTimerTriggered(-1,-1)
124 ,m_aDropActionType( DA_SCROLLUP )
125 ,m_nSelectLock(0)
126 ,m_nFormsSelected(0)
127 ,m_nControlsSelected(0)
128 ,m_nHiddenControls(0)
129 ,m_aTimerCounter( DROP_ACTION_TIMER_INITIAL_TICKS )
130 ,m_bDragDataDirty(false)
131 ,m_bPrevSelectionMixed(false)
132 ,m_bMarkingObjects(false)
133 ,m_bRootSelected(false)
134 ,m_bInitialUpdate(true)
135 ,m_bKeyboardCut( false )
137 SetHelpId( HID_FORM_NAVIGATOR );
139 m_aNavigatorImages = ImageList( SVX_RES( RID_SVXIMGLIST_FMEXPL ) );
141 SetNodeBitmaps(
142 m_aNavigatorImages.GetImage( RID_SVXIMG_COLLAPSEDNODE ),
143 m_aNavigatorImages.GetImage( RID_SVXIMG_EXPANDEDNODE )
146 SetDragDropMode(DragDropMode::ALL);
147 EnableInplaceEditing( true );
148 SetSelectionMode(MULTIPLE_SELECTION);
150 m_pNavModel = new NavigatorTreeModel( m_aNavigatorImages );
151 Clear();
153 StartListening( *m_pNavModel );
155 m_aDropActionTimer.SetTimeoutHdl(LINK(this, NavigatorTree, OnDropActionTimer));
157 m_aSynchronizeTimer.SetTimeoutHdl(LINK(this, NavigatorTree, OnSynchronizeTimer));
158 SetSelectHdl(LINK(this, NavigatorTree, OnEntrySelDesel));
159 SetDeselectHdl(LINK(this, NavigatorTree, OnEntrySelDesel));
163 NavigatorTree::~NavigatorTree()
165 disposeOnce();
168 void NavigatorTree::dispose()
170 if( nEditEvent )
171 Application::RemoveUserEvent( nEditEvent );
173 if (m_aSynchronizeTimer.IsActive())
174 m_aSynchronizeTimer.Stop();
176 DBG_ASSERT(GetNavModel() != NULL, "NavigatorTree::~NavigatorTree : unexpected : no ExplorerModel");
177 EndListening( *m_pNavModel );
178 Clear();
179 delete m_pNavModel;
180 SvTreeListBox::dispose();
184 void NavigatorTree::Clear()
186 m_pNavModel->Clear();
190 void NavigatorTree::UpdateContent( FmFormShell* pFormShell )
192 if (m_bInitialUpdate)
194 GrabFocus();
195 m_bInitialUpdate = false;
198 FmFormShell* pOldShell = GetNavModel()->GetFormShell();
199 FmFormPage* pOldPage = GetNavModel()->GetFormPage();
200 FmFormPage* pNewPage = pFormShell ? pFormShell->GetCurPage() : NULL;
202 if ((pOldShell != pFormShell) || (pOldPage != pNewPage))
204 // new shell during editing
205 if (IsEditingActive())
206 CancelTextEditing();
208 m_bDragDataDirty = true; // as a precaution, although i dont drag
210 GetNavModel()->UpdateContent( pFormShell );
212 // if there is a form, expand root
213 if (m_pRootEntry && !IsExpanded(m_pRootEntry))
214 Expand(m_pRootEntry);
215 // if there is EXACTLY ONE form, expand it too
216 if (m_pRootEntry)
218 SvTreeListEntry* pFirst = FirstChild(m_pRootEntry);
219 if (pFirst && !NextSibling(pFirst))
220 Expand(pFirst);
225 bool NavigatorTree::implAllowExchange( sal_Int8 _nAction, bool* _pHasNonHidden )
227 SvTreeListEntry* pCurEntry = GetCurEntry();
228 if (!pCurEntry)
229 return false;
231 // Information for AcceptDrop and Execute Drop
232 CollectSelectionData(SDI_ALL);
233 if (m_arrCurrentSelection.empty())
234 // nothing to do
235 return false;
237 // check whether there are only hidden controls
238 // I may add a format to pCtrlExch
239 bool bHasNonHidden = false;
240 for ( SvLBoxEntrySortedArray::const_iterator it = m_arrCurrentSelection.begin();
241 it != m_arrCurrentSelection.end(); ++it )
243 FmEntryData* pCurrent = static_cast< FmEntryData* >( (*it)->GetUserData() );
244 if ( IsHiddenControl( pCurrent ) )
245 continue;
246 bHasNonHidden = true;
247 break;
250 if ( bHasNonHidden && ( 0 == ( _nAction & DND_ACTION_MOVE ) ) )
251 // non-hidden controls need to be moved
252 return false;
254 if ( _pHasNonHidden )
255 *_pHasNonHidden = bHasNonHidden;
257 return true;
261 bool NavigatorTree::implPrepareExchange( sal_Int8 _nAction )
263 EndSelection();
265 bool bHasNonHidden = false;
266 if ( !implAllowExchange( _nAction, &bHasNonHidden ) )
267 return false;
269 m_aControlExchange.prepareDrag();
270 m_aControlExchange->setFocusEntry( GetCurEntry() );
272 for ( SvLBoxEntrySortedArray::const_iterator it = m_arrCurrentSelection.begin();
273 it != m_arrCurrentSelection.end(); ++it )
274 m_aControlExchange->addSelectedEntry(*it);
276 m_aControlExchange->setFormsRoot( GetNavModel()->GetFormPage()->GetForms() );
277 m_aControlExchange->buildPathFormat( this, m_pRootEntry );
279 if (!bHasNonHidden)
281 // create a sequence
282 Sequence< Reference< XInterface > > seqIFaces(m_arrCurrentSelection.size());
283 Reference< XInterface >* pArray = seqIFaces.getArray();
284 for ( SvLBoxEntrySortedArray::const_iterator it = m_arrCurrentSelection.begin();
285 it != m_arrCurrentSelection.end(); ++it, ++pArray )
286 *pArray = static_cast< FmEntryData* >( (*it)->GetUserData() )->GetElement();
288 // and the new format
289 m_aControlExchange->addHiddenControlsFormat(seqIFaces);
292 m_bDragDataDirty = false;
293 return true;
297 void NavigatorTree::StartDrag( sal_Int8 /*nAction*/, const ::Point& /*rPosPixel*/ )
299 EndSelection();
301 if ( !implPrepareExchange( DND_ACTION_COPYMOVE ) )
302 // nothing to do or something went wrong
303 return;
305 // collected all possible formats for current situation, we can start now
306 m_aControlExchange.startDrag( DND_ACTION_COPYMOVE );
310 void NavigatorTree::Command( const CommandEvent& rEvt )
312 bool bHandled = false;
313 switch( rEvt.GetCommand() )
315 case CommandEventId::ContextMenu:
317 // Position of click
318 ::Point ptWhere;
319 if (rEvt.IsMouseEvent())
321 ptWhere = rEvt.GetMousePosPixel();
322 SvTreeListEntry* ptClickedOn = GetEntry(ptWhere);
323 if (ptClickedOn == NULL)
324 break;
325 if ( !IsSelected(ptClickedOn) )
327 SelectAll(false);
328 Select(ptClickedOn, true);
329 SetCurEntry(ptClickedOn);
332 else
334 if (m_arrCurrentSelection.empty()) // only happens with context menu via keyboard
335 break;
337 SvTreeListEntry* pCurrent = GetCurEntry();
338 if (!pCurrent)
339 break;
340 ptWhere = GetEntryPosition(pCurrent);
343 // update my selection data
344 CollectSelectionData(SDI_ALL);
346 // if there is at least one no-root-entry and the root selected, i deselect root
347 if ( (m_arrCurrentSelection.size() > 1) && m_bRootSelected )
349 Select( m_pRootEntry, false );
350 SetCursor( *m_arrCurrentSelection.begin(), true);
352 bool bSingleSelection = (m_arrCurrentSelection.size() == 1);
355 DBG_ASSERT( (m_arrCurrentSelection.size() > 0) || m_bRootSelected, "no entries selected" );
356 // shouldn't happen, because i would have selected one during call to IsSelected,
357 // if there was none before
360 // create menu
361 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
362 FmFormModel* pFormModel = pFormShell ? pFormShell->GetFormModel() : NULL;
363 if( pFormShell && pFormModel )
365 PopupMenu aContextMenu(SVX_RES(RID_FMEXPLORER_POPUPMENU));
366 PopupMenu* pSubMenuNew = aContextMenu.GetPopupMenu( SID_FM_NEW );
368 // menu 'New' only exists, if only the root or only one form is selected
369 aContextMenu.EnableItem( SID_FM_NEW, bSingleSelection && (m_nFormsSelected || m_bRootSelected) );
371 // 'New'\'Form' under the same terms
372 pSubMenuNew->EnableItem( SID_FM_NEW_FORM, bSingleSelection && (m_nFormsSelected || m_bRootSelected) );
373 pSubMenuNew->SetItemImage(SID_FM_NEW_FORM, m_aNavigatorImages.GetImage(RID_SVXIMG_FORM));
374 pSubMenuNew->SetItemImage(SID_FM_NEW_HIDDEN, m_aNavigatorImages.GetImage(RID_SVXIMG_HIDDEN));
376 // 'New'\'hidden...', if exactly one form is selected
377 pSubMenuNew->EnableItem( SID_FM_NEW_HIDDEN, bSingleSelection && m_nFormsSelected );
379 // 'Delete': everything which is not root can be removed
380 aContextMenu.EnableItem( SID_FM_DELETE, !m_bRootSelected );
382 // 'Cut', 'Copy' and 'Paste'
383 aContextMenu.EnableItem( SID_CUT, !m_bRootSelected && implAllowExchange( DND_ACTION_MOVE ) );
384 aContextMenu.EnableItem( SID_COPY, !m_bRootSelected && implAllowExchange( DND_ACTION_COPY ) );
385 aContextMenu.EnableItem( SID_PASTE, implAcceptPaste( ) );
387 // TabDialog, if exaclty one form
388 aContextMenu.EnableItem( SID_FM_TAB_DIALOG, bSingleSelection && m_nFormsSelected );
390 // in XML forms, we don't allow for the properties of a form
391 // #i36484#
392 if ( pFormShell->GetImpl()->isEnhancedForm() && !m_nControlsSelected )
393 aContextMenu.RemoveItem( aContextMenu.GetItemPos( SID_FM_SHOW_PROPERTY_BROWSER ) );
395 // if the property browser is already open, we don't allow for the properties, too
396 if( pFormShell->GetImpl()->IsPropBrwOpen() )
397 aContextMenu.RemoveItem( aContextMenu.GetItemPos( SID_FM_SHOW_PROPERTY_BROWSER ) );
398 // and finally, if there's a mixed selection of forms and controls, disable the entry, too
399 else
400 aContextMenu.EnableItem( SID_FM_SHOW_PROPERTY_BROWSER,
401 (m_nControlsSelected && !m_nFormsSelected) || (!m_nControlsSelected && m_nFormsSelected) );
403 // rename, if one element and no root
404 aContextMenu.EnableItem( SID_FM_RENAME_OBJECT, bSingleSelection && !m_bRootSelected );
406 // Readonly-entry is only for root
407 aContextMenu.EnableItem( SID_FM_OPEN_READONLY, m_bRootSelected );
408 // the same for automatic control focus
409 aContextMenu.EnableItem( SID_FM_AUTOCONTROLFOCUS, m_bRootSelected );
411 // ConvertTo-Slots are enabled, if one control is selected
412 // the corresponding slot is disabled
413 if (!m_bRootSelected && !m_nFormsSelected && (m_nControlsSelected == 1))
415 aContextMenu.SetPopupMenu( SID_FM_CHANGECONTROLTYPE, FmXFormShell::GetConversionMenu() );
416 #if OSL_DEBUG_LEVEL > 0
417 FmControlData* pCurrent = static_cast<FmControlData*>((*m_arrCurrentSelection.begin())->GetUserData());
418 OSL_ENSURE( pFormShell->GetImpl()->isSolelySelected( pCurrent->GetFormComponent() ),
419 "NavigatorTree::Command: inconsistency between the navigator selection, and the selection as the shell knows it!" );
420 #endif
422 pFormShell->GetImpl()->checkControlConversionSlotsForCurrentSelection( *aContextMenu.GetPopupMenu( SID_FM_CHANGECONTROLTYPE ) );
424 else
425 aContextMenu.EnableItem( SID_FM_CHANGECONTROLTYPE, false );
427 // remove all disabled entries
428 aContextMenu.RemoveDisabledEntries(true, true);
430 // set OpenReadOnly
432 aContextMenu.CheckItem( SID_FM_OPEN_READONLY, pFormModel->GetOpenInDesignMode() );
433 aContextMenu.CheckItem( SID_FM_AUTOCONTROLFOCUS, pFormModel->GetAutoControlFocus() );
435 sal_uInt16 nSlotId = aContextMenu.Execute( this, ptWhere );
436 switch( nSlotId )
438 case SID_FM_NEW_FORM:
440 OUString aStr(SVX_RESSTR(RID_STR_FORM));
441 OUString aUndoStr = SVX_RESSTR(RID_STR_UNDO_CONTAINER_INSERT).replaceAll(OUString('#'), aStr);
443 pFormModel->BegUndo(aUndoStr);
444 // slot was only available, if there is only one selected entry,
445 // which is a root or a form
446 NewForm( *m_arrCurrentSelection.begin() );
447 pFormModel->EndUndo();
449 } break;
450 case SID_FM_NEW_HIDDEN:
452 OUString aStr(SVX_RESSTR(RID_STR_CONTROL));
453 OUString aUndoStr = SVX_RESSTR(RID_STR_UNDO_CONTAINER_INSERT).replaceAll(OUString('#'), aStr);
455 pFormModel->BegUndo(aUndoStr);
456 // slot was valid for (exactly) one selected form
457 OUString fControlName = FM_COMPONENT_HIDDEN;
458 NewControl( fControlName, *m_arrCurrentSelection.begin() );
459 pFormModel->EndUndo();
461 } break;
463 case SID_CUT:
464 doCut();
465 break;
467 case SID_COPY:
468 doCopy();
469 break;
471 case SID_PASTE:
472 doPaste();
473 break;
475 case SID_FM_DELETE:
477 DeleteSelection();
479 break;
480 case SID_FM_TAB_DIALOG:
482 // this slot was effective for exactly one selected form
483 SvTreeListEntry* pSelectedForm = *m_arrCurrentSelection.begin();
484 DBG_ASSERT( IsFormEntry(pSelectedForm), "NavigatorTree::Command: This entry must be a FormEntry." );
486 FmFormData* pFormData = static_cast<FmFormData*>(pSelectedForm->GetUserData());
487 Reference< XForm > xForm( pFormData->GetFormIface());
489 Reference< XTabControllerModel > xTabController(xForm, UNO_QUERY);
490 if( !xTabController.is() )
491 break;
492 GetNavModel()->GetFormShell()->GetImpl()->ExecuteTabOrderDialog( xTabController );
494 break;
496 case SID_FM_SHOW_PROPERTY_BROWSER:
498 ShowSelectionProperties(true);
500 break;
501 case SID_FM_RENAME_OBJECT:
503 // only allowed for one no-root-entry
504 EditEntry( *m_arrCurrentSelection.begin() );
506 break;
507 case SID_FM_OPEN_READONLY:
509 pFormModel->SetOpenInDesignMode( !pFormModel->GetOpenInDesignMode() );
510 pFormShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(SID_FM_OPEN_READONLY);
512 break;
513 case SID_FM_AUTOCONTROLFOCUS:
515 pFormModel->SetAutoControlFocus( !pFormModel->GetAutoControlFocus() );
516 pFormShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(SID_FM_AUTOCONTROLFOCUS);
518 break;
519 default:
520 if (FmXFormShell::isControlConversionSlot(nSlotId))
522 FmControlData* pCurrent = static_cast<FmControlData*>((*m_arrCurrentSelection.begin())->GetUserData());
523 if ( pFormShell->GetImpl()->executeControlConversionSlot( pCurrent->GetFormComponent(), nSlotId ) )
524 ShowSelectionProperties();
528 bHandled = true;
530 break;
531 default: break;
534 if (!bHandled)
535 SvTreeListBox::Command( rEvt );
539 SvTreeListEntry* NavigatorTree::FindEntry( FmEntryData* pEntryData )
541 if( !pEntryData ) return NULL;
542 SvTreeListEntry* pCurEntry = First();
543 while( pCurEntry )
545 FmEntryData* pCurEntryData = static_cast<FmEntryData*>(pCurEntry->GetUserData());
546 if( pCurEntryData && pCurEntryData->IsEqualWithoutChildren(pEntryData) )
547 return pCurEntry;
549 pCurEntry = Next( pCurEntry );
552 return NULL;
556 void NavigatorTree::Notify( SfxBroadcaster& /*rBC*/, const SfxHint& rHint )
558 if( dynamic_cast<const FmNavRemovedHint*>(&rHint) )
560 const FmNavRemovedHint* pRemovedHint = static_cast<const FmNavRemovedHint*>(&rHint);
561 FmEntryData* pEntryData = pRemovedHint->GetEntryData();
562 Remove( pEntryData );
565 else if( dynamic_cast<const FmNavInsertedHint*>(&rHint) )
567 const FmNavInsertedHint* pInsertedHint = static_cast<const FmNavInsertedHint*>(&rHint);
568 FmEntryData* pEntryData = pInsertedHint->GetEntryData();
569 sal_uInt32 nRelPos = pInsertedHint->GetRelPos();
570 Insert( pEntryData, nRelPos );
573 else if( dynamic_cast<const FmNavModelReplacedHint*>(&rHint) )
575 FmEntryData* pData = static_cast<const FmNavModelReplacedHint*>(&rHint)->GetEntryData();
576 SvTreeListEntry* pEntry = FindEntry( pData );
577 if (pEntry)
578 { // reset image
579 SetCollapsedEntryBmp( pEntry, pData->GetNormalImage() );
580 SetExpandedEntryBmp( pEntry, pData->GetNormalImage() );
584 else if( dynamic_cast<const FmNavNameChangedHint*>(&rHint) )
586 const FmNavNameChangedHint* pNameChangedHint = static_cast<const FmNavNameChangedHint*>(&rHint);
587 SvTreeListEntry* pEntry = FindEntry( pNameChangedHint->GetEntryData() );
588 SetEntryText( pEntry, pNameChangedHint->GetNewName() );
591 else if( dynamic_cast<const FmNavClearedHint*>(&rHint) )
593 SvTreeListBox::Clear();
596 // default-entry "Forms"
597 Image aRootImage( m_aNavigatorImages.GetImage( RID_SVXIMG_FORMS ) );
598 m_pRootEntry = InsertEntry( SVX_RESSTR(RID_STR_FORMS), aRootImage, aRootImage,
599 NULL, false, 0, NULL );
601 else if (!m_bMarkingObjects && dynamic_cast<const FmNavRequestSelectHint*>(&rHint))
602 { // if m_bMarkingObjects is sal_True, i mark objects myself
603 // and because of the synchronous mechanism, its exactly the hint,
604 // which was triggered by myself, and thus can be ignored
605 FmNavRequestSelectHint* pershHint = const_cast<FmNavRequestSelectHint*>(static_cast<const FmNavRequestSelectHint*>(&rHint));
606 FmEntryDataArray& arredToSelect = pershHint->GetItems();
607 SynchronizeSelection(arredToSelect);
609 if (pershHint->IsMixedSelection())
610 // in this case i deselect all, although the view had a mixed selection
611 // during next selection, i must adapt the navigator to the view
612 m_bPrevSelectionMixed = true;
617 SvTreeListEntry* NavigatorTree::Insert( FmEntryData* pEntryData, sal_uIntPtr nRelPos )
620 // insert current entry
621 SvTreeListEntry* pParentEntry = FindEntry( pEntryData->GetParent() );
622 SvTreeListEntry* pNewEntry;
624 if( !pParentEntry )
625 pNewEntry = InsertEntry( pEntryData->GetText(),
626 pEntryData->GetNormalImage(), pEntryData->GetNormalImage(),
627 m_pRootEntry, false, nRelPos, pEntryData );
629 else
630 pNewEntry = InsertEntry( pEntryData->GetText(),
631 pEntryData->GetNormalImage(), pEntryData->GetNormalImage(),
632 pParentEntry, false, nRelPos, pEntryData );
635 // If root-entry, expand root
636 if( !pParentEntry )
637 Expand( m_pRootEntry );
640 // insert children
641 FmEntryDataList* pChildList = pEntryData->GetChildList();
642 size_t nChildCount = pChildList->size();
643 for( size_t i = 0; i < nChildCount; i++ )
645 FmEntryData* pChildData = pChildList->at( i );
646 Insert( pChildData, TREELIST_APPEND );
649 return pNewEntry;
653 void NavigatorTree::Remove( FmEntryData* pEntryData )
655 if( !pEntryData )
656 return;
658 // entry for the data
659 SvTreeListEntry* pEntry = FindEntry( pEntryData );
660 if (!pEntry)
661 return;
663 // delete entry from TreeListBox
664 // i'm not allowed, to treat the selection, which i trigger:
665 // select changes the MarkList of the view, if somebody else does this at the same time
666 // and removes a selection, we get a problem
667 // e.g. Group controls with open navigator
668 LockSelectionHandling();
670 // little problem : i remember the selected data, but if somebody deletes one of these entries,
671 // i get inconsistent ... this would be bad
672 Select(pEntry, false);
674 // selection can be modified during deletion,
675 // but because i disabled SelectionHandling, i have to do it later
676 sal_uIntPtr nExpectedSelectionCount = GetSelectionCount();
678 if( pEntry )
679 GetModel()->Remove( pEntry );
681 if (nExpectedSelectionCount != GetSelectionCount())
682 SynchronizeSelection();
684 // by default i treat the selection of course
685 UnlockSelectionHandling();
689 bool NavigatorTree::IsFormEntry( SvTreeListEntry* pEntry )
691 FmEntryData* pEntryData = static_cast<FmEntryData*>(pEntry->GetUserData());
692 return !pEntryData || pEntryData->ISA(FmFormData);
696 bool NavigatorTree::IsFormComponentEntry( SvTreeListEntry* pEntry )
698 FmEntryData* pEntryData = static_cast<FmEntryData*>(pEntry->GetUserData());
699 return pEntryData && pEntryData->ISA(FmControlData);
703 bool NavigatorTree::implAcceptPaste( )
705 SvTreeListEntry* pFirstSelected = FirstSelected();
706 if ( !pFirstSelected || NextSelected( pFirstSelected ) )
707 // no selected entry, or at least two selected entries
708 return false;
710 // get the clipboard
711 TransferableDataHelper aClipboardContent( TransferableDataHelper::CreateFromSystemClipboard( this ) );
713 sal_Int8 nAction = m_aControlExchange.isClipboardOwner() && doingKeyboardCut( ) ? DND_ACTION_MOVE : DND_ACTION_COPY;
714 return ( nAction == implAcceptDataTransfer( aClipboardContent.GetDataFlavorExVector(), nAction, pFirstSelected, false ) );
718 sal_Int8 NavigatorTree::implAcceptDataTransfer( const DataFlavorExVector& _rFlavors, sal_Int8 _nAction, const ::Point& _rDropPos, bool _bDnD )
720 return implAcceptDataTransfer( _rFlavors, _nAction, GetEntry( _rDropPos ), _bDnD );
724 sal_Int8 NavigatorTree::implAcceptDataTransfer( const DataFlavorExVector& _rFlavors, sal_Int8 _nAction, SvTreeListEntry* _pTargetEntry, bool _bDnD )
726 // no target -> no drop
727 if (!_pTargetEntry)
728 return DND_ACTION_NONE;
730 // format check
731 bool bHasDefControlFormat = OControlExchange::hasFieldExchangeFormat( _rFlavors );
732 bool bHasControlPathFormat = OControlExchange::hasControlPathFormat( _rFlavors );
733 bool bHasHiddenControlsFormat = OControlExchange::hasHiddenControlModelsFormat( _rFlavors );
734 if (!bHasDefControlFormat && !bHasControlPathFormat && !bHasHiddenControlsFormat)
735 return DND_ACTION_NONE;
737 bool bSelfSource = _bDnD ? m_aControlExchange.isDragSource() : m_aControlExchange.isClipboardOwner();
739 if ( bHasHiddenControlsFormat )
740 { // bHasHiddenControlsFormat means that only hidden controls are part of the data
742 // hidden controls can be copied to a form only
743 if ( !_pTargetEntry || ( _pTargetEntry == m_pRootEntry ) || !IsFormEntry( _pTargetEntry ) )
744 return DND_ACTION_NONE;
746 return bSelfSource ? ( DND_ACTION_COPYMOVE & _nAction ) : DND_ACTION_COPY;
749 if ( !bSelfSource )
751 // DnD or CnP crossing navigator boundaries
752 // The main problem here is that the current API does not allow us to sneak into the content which
753 // is to be inserted. So we have to allow it for the moment, but maybe reject later on (in the real drop).
755 // TODO: this smart behaviour later on ... at the moment, we disallow data transfer crossing navigator
756 // boundaries.
758 return DND_ACTION_NONE;
761 DBG_ASSERT( _bDnD ? m_aControlExchange.isDragSource() : m_aControlExchange.isClipboardOwner(),
762 "NavigatorTree::implAcceptDataTransfer: here only with source=dest!" );
763 // somebody changed the logic of this method ...
765 // from here on, I can work with m_aControlExchange instead of _rData!
767 bool bForeignCollection = m_aControlExchange->getFormsRoot().get() != GetNavModel()->GetFormPage()->GetForms().get();
768 if ( bForeignCollection )
770 // crossing shell/page boundaries, we can exchange hidden controls only
771 // But if we survived the checks above, we do not have hidden controls.
772 // -> no data transfer
773 DBG_ASSERT( !bHasHiddenControlsFormat, "NavigatorTree::implAcceptDataTransfer: still hidden controls format!" );
774 // somebody changed the logic of this method ...
776 return DND_ACTION_COPY;
779 if (DND_ACTION_MOVE != _nAction) // 'normal' controls within a shell are moved only (never copied)
780 return DND_ACTION_NONE;
782 if ( m_bDragDataDirty || !bHasDefControlFormat )
784 if (!bHasControlPathFormat)
785 // i am in the shell/page, which has the contorls, but i have no format,
786 // which survived the shell change (SVX_FM_CONTROLS_AS_PATH)
787 return DND_ACTION_NONE;
789 // i must recreate the list of the ExchangeObjects, because the shell was changed during dragging
790 // (there are SvLBoxEntries in it, and we lost them during change)
791 m_aControlExchange->buildListFromPath(this, m_pRootEntry);
792 m_bDragDataDirty = false;
795 // List of dropped entries from DragServer
796 const ListBoxEntrySet& aDropped = m_aControlExchange->selected();
797 DBG_ASSERT(aDropped.size() >= 1, "NavigatorTree::implAcceptDataTransfer: no entries !");
799 bool bDropTargetIsComponent = IsFormComponentEntry( _pTargetEntry );
800 //SvTreeListEntry* pDropTargetParent = GetParent( _pTargetEntry );
802 // conditions to disallow the drop
803 // 0) the root entry is part of the list (can't DnD the root!)
804 // 1) one of the draged entries is to be dropped onto it's own parent
805 // 2) - " - is to be dropped onto itself
806 // 3) - " - is a Form and to be dropped onto one of it's descendants
807 // 4) one of the entries is a control and to be dropped onto the root
808 // 5) a control or form will be dropped onto a control which is _not_ a sibling (dropping onto a sibling
809 // means moving the control)
811 // collect the ancestors of the drop targte (speeds up 3)
812 SvLBoxEntrySortedArray arrDropAnchestors;
813 SvTreeListEntry* pLoop = _pTargetEntry;
814 while (pLoop)
816 arrDropAnchestors.insert(pLoop);
817 pLoop = GetParent(pLoop);
820 for ( ListBoxEntrySet::const_iterator dropped = aDropped.begin();
821 dropped != aDropped.end();
822 ++dropped
825 SvTreeListEntry* pCurrent = *dropped;
826 SvTreeListEntry* pCurrentParent = GetParent(pCurrent);
828 // test for 0)
829 if (pCurrent == m_pRootEntry)
830 return DND_ACTION_NONE;
832 // test for 1)
833 if ( _pTargetEntry == pCurrentParent )
834 return DND_ACTION_NONE;
836 // test for 2)
837 if (pCurrent == _pTargetEntry)
838 return DND_ACTION_NONE;
840 // test for 5)
841 // if ( bDropTargetIsComponent && (pDropTargetParent != pCurrentParent) )
842 if ( bDropTargetIsComponent ) // TODO : the line above can be inserted, if ExecuteDrop can handle inversion
843 return DND_ACTION_NONE;
845 // test for 3)
846 if ( IsFormEntry(pCurrent) )
848 if ( arrDropAnchestors.find(pCurrent) != arrDropAnchestors.end() )
849 return DND_ACTION_NONE;
850 } else if ( IsFormComponentEntry(pCurrent) )
852 // test for 4)
853 if (_pTargetEntry == m_pRootEntry)
854 return DND_ACTION_NONE;
858 return DND_ACTION_MOVE;
862 sal_Int8 NavigatorTree::AcceptDrop( const AcceptDropEvent& rEvt )
864 ::Point aDropPos = rEvt.maPosPixel;
866 // first handle possible DropActions (Scroll and swing open)
867 if (rEvt.mbLeaving)
869 if (m_aDropActionTimer.IsActive())
870 m_aDropActionTimer.Stop();
871 } else
873 bool bNeedTrigger = false;
874 // on the first entry ?
875 if ((aDropPos.Y() >= 0) && (aDropPos.Y() < GetEntryHeight()))
877 m_aDropActionType = DA_SCROLLUP;
878 bNeedTrigger = true;
879 } else
880 // on the last one (respectively the area, an entry would tale, if it flush with the bottom ?
881 if ((aDropPos.Y() < GetSizePixel().Height()) && (aDropPos.Y() >= GetSizePixel().Height() - GetEntryHeight()))
883 m_aDropActionType = DA_SCROLLDOWN;
884 bNeedTrigger = true;
885 } else
886 { // on an entry with children, not swang open
887 SvTreeListEntry* pDropppedOn = GetEntry(aDropPos);
888 if (pDropppedOn && (GetChildCount(pDropppedOn) > 0) && !IsExpanded(pDropppedOn))
890 // -> swing open
891 m_aDropActionType = DA_EXPANDNODE;
892 bNeedTrigger = true;
896 if (bNeedTrigger && (m_aTimerTriggered != aDropPos))
898 // restart counting
899 m_aTimerCounter = DROP_ACTION_TIMER_INITIAL_TICKS;
900 // remember pos, because i get AcceptDrops, although mouse hasn't moved
901 m_aTimerTriggered = aDropPos;
902 // start Timer
903 if (!m_aDropActionTimer.IsActive()) // exist Timer?
905 m_aDropActionTimer.SetTimeout(DROP_ACTION_TIMER_TICK_BASE);
906 m_aDropActionTimer.Start();
908 } else if (!bNeedTrigger)
909 m_aDropActionTimer.Stop();
912 return implAcceptDataTransfer( GetDataFlavorExVector(), rEvt.mnAction, aDropPos, true );
916 sal_Int8 NavigatorTree::implExecuteDataTransfer( const OControlTransferData& _rData, sal_Int8 _nAction, const ::Point& _rDropPos, bool _bDnD )
918 return implExecuteDataTransfer( _rData, _nAction, GetEntry( _rDropPos ), _bDnD );
922 sal_Int8 NavigatorTree::implExecuteDataTransfer( const OControlTransferData& _rData, sal_Int8 _nAction, SvTreeListEntry* _pTargetEntry, bool _bDnD )
924 const DataFlavorExVector& rDataFlavors = _rData.GetDataFlavorExVector();
926 if ( DND_ACTION_NONE == implAcceptDataTransfer( rDataFlavors, _nAction, _pTargetEntry, _bDnD ) )
927 // under some platforms, it may happen that ExecuteDrop is called though AcceptDrop returned DND_ACTION_NONE
928 return DND_ACTION_NONE;
930 // would be bad, if we scroll after drop
931 if (m_aDropActionTimer.IsActive())
932 m_aDropActionTimer.Stop();
934 if (!_pTargetEntry)
935 // no target -> no drop
936 return DND_ACTION_NONE;
938 // format checks
939 #ifdef DBG_UTIL
940 bool bHasHiddenControlsFormat = OControlExchange::hasHiddenControlModelsFormat( rDataFlavors );
941 bool bForeignCollection = _rData.getFormsRoot().get() != GetNavModel()->GetFormPage()->GetForms().get();
942 DBG_ASSERT(!bForeignCollection || bHasHiddenControlsFormat, "NavigatorTree::implExecuteDataTransfer: invalid format (AcceptDrop shouldn't have let this pass) !");
943 DBG_ASSERT(bForeignCollection || !m_bDragDataDirty, "NavigatorTree::implExecuteDataTransfer: invalid state (shell changed since last exchange resync) !");
944 // this should be done in AcceptDrop : the list of conrtols is created in _rData
945 // and m_bDragDataDirty is reseted
946 #endif
948 if ( DND_ACTION_COPY == _nAction )
949 { // bHasHiddenControlsFormat means that only hidden controls are part of the data
950 DBG_ASSERT( bHasHiddenControlsFormat, "NavigatorTree::implExecuteDataTransfer: copy allowed for hidden controls only!" );
951 DBG_ASSERT( _pTargetEntry && ( _pTargetEntry != m_pRootEntry ) && IsFormEntry( _pTargetEntry ),
952 "NavigatorTree::implExecuteDataTransfer: should not be here!" );
953 // implAcceptDataTransfer should have caught both cases
955 DBG_ASSERT(bHasHiddenControlsFormat, "NavigatorTree::implExecuteDataTransfer: only copying of hidden controls is supported !");
956 // should be catched by AcceptDrop
958 // because i want to select all targets (and only them)
959 SelectAll(false);
961 Sequence< Reference< XInterface > > aControls = _rData.hiddenControls();
962 sal_Int32 nCount = aControls.getLength();
963 const Reference< XInterface >* pControls = aControls.getConstArray();
965 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
966 FmFormModel* pFormModel = pFormShell ? pFormShell->GetFormModel() : NULL;
968 // within undo
969 if (pFormModel)
971 OUString aStr(SVX_RESSTR(RID_STR_CONTROL));
972 OUString aUndoStr = SVX_RESSTR(RID_STR_UNDO_CONTAINER_INSERT).replaceAll(OUString('#'), aStr);
973 pFormModel->BegUndo(aUndoStr);
976 // copy controls
977 for (sal_Int32 i=0; i<nCount; ++i)
979 // create new control
980 OUString fControlName = FM_COMPONENT_HIDDEN;
981 FmControlData* pNewControlData = NewControl( fControlName, _pTargetEntry, false);
982 Reference< XPropertySet > xNewPropSet( pNewControlData->GetPropertySet() );
984 // copy properties form old control to new one
985 Reference< XPropertySet > xCurrent(pControls[i], UNO_QUERY);
986 #if (OSL_DEBUG_LEVEL > 1)
987 // check whether it is a hidden control
988 sal_Int16 nClassId = ::comphelper::getINT16(xCurrent->getPropertyValue(FM_PROP_CLASSID));
989 OSL_ENSURE(nClassId == FormComponentType::HIDDENCONTROL, "NavigatorTree::implExecuteDataTransfer: invalid control in drop list !");
990 // if SVX_FM_HIDDEN_CONTROLS-format exists, the sequence
991 // should only contain hidden controls
992 #endif // (OSL_DEBUG_LEVEL > 1)
993 Reference< XPropertySetInfo > xPropInfo( xCurrent->getPropertySetInfo());
994 Sequence< Property> seqAllCurrentProps = xPropInfo->getProperties();
995 Property* pAllCurrentProps = seqAllCurrentProps.getArray();
996 for (sal_Int32 j=0; j<seqAllCurrentProps.getLength(); ++j)
998 OUString sCurrentProp = pAllCurrentProps[j].Name;
999 if (((pAllCurrentProps[j].Attributes & PropertyAttribute::READONLY) == 0) && (sCurrentProp != FM_PROP_NAME))
1000 { // (read-only attribs aren't set, ditto name,
1001 // NewControl defined it uniquely
1002 xNewPropSet->setPropertyValue(sCurrentProp, xCurrent->getPropertyValue(sCurrentProp));
1006 SvTreeListEntry* pToSelect = FindEntry(pNewControlData);
1007 Select(pToSelect, true);
1008 if (i == 0)
1009 SetCurEntry(pToSelect);
1012 if (pFormModel)
1013 pFormModel->EndUndo();
1015 return _nAction;
1018 if ( !OControlExchange::hasFieldExchangeFormat( _rData.GetDataFlavorExVector() ) )
1020 // can't do anything without the internal format here ... usually happens when doing DnD or CnP
1021 // over navigator boundaries
1022 return DND_ACTION_NONE;
1025 // some data for the target
1026 bool bDropTargetIsForm = IsFormEntry(_pTargetEntry);
1027 FmFormData* pTargetData = bDropTargetIsForm ? static_cast<FmFormData*>(_pTargetEntry->GetUserData()) : NULL;
1029 DBG_ASSERT( DND_ACTION_COPY != _nAction, "NavigatorTree::implExecuteDataTransfer: somebody changed the logics!" );
1031 // list of dragged entries
1032 ListBoxEntrySet aDropped = _rData.selected();
1033 DBG_ASSERT(aDropped.size() >= 1, "NavigatorTree::implExecuteDataTransfer: no entries!");
1035 // shell and model
1036 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
1037 FmFormModel* pFormModel = pFormShell ? pFormShell->GetFormModel() : NULL;
1038 if (!pFormModel)
1039 return DND_ACTION_NONE;
1041 // for Undo
1042 const bool bUndo = pFormModel->IsUndoEnabled();
1044 if( bUndo )
1046 OUString strUndoDescription(SVX_RESSTR(RID_STR_UNDO_CONTAINER_REPLACE));
1047 pFormModel->BegUndo(strUndoDescription);
1050 // remove selection before adding an entry, so the mark doesn't flicker
1051 // -> lock action of selection
1052 LockSelectionHandling();
1054 // go through all dropped entries
1055 for ( ListBoxEntrySet::const_iterator dropped = aDropped.begin();
1056 dropped != aDropped.end();
1057 ++dropped
1060 // some data of the current element
1061 SvTreeListEntry* pCurrent = *dropped;
1062 DBG_ASSERT(pCurrent != NULL, "NavigatorTree::implExecuteDataTransfer: invalid entry");
1063 DBG_ASSERT(GetParent(pCurrent) != NULL, "NavigatorTree::implExecuteDataTransfer: invalid entry");
1064 // don't drag root
1066 FmEntryData* pCurrentUserData = static_cast<FmEntryData*>(pCurrent->GetUserData());
1068 Reference< XChild > xCurrentChild(pCurrentUserData->GetChildIFace(), UNO_QUERY);
1069 Reference< XIndexContainer > xContainer(xCurrentChild->getParent(), UNO_QUERY);
1071 FmFormData* pCurrentParentUserData = static_cast<FmFormData*>(pCurrentUserData->GetParent());
1072 DBG_ASSERT(pCurrentParentUserData == NULL || pCurrentParentUserData->ISA(FmFormData), "NavigatorTree::implExecuteDataTransfer: invalid parent");
1074 // remove from parent
1075 if (pCurrentParentUserData)
1076 pCurrentParentUserData->GetChildList()->remove( pCurrentUserData );
1077 else
1078 GetNavModel()->GetRootList()->remove( pCurrentUserData );
1080 // remove from container
1081 sal_Int32 nIndex = getElementPos(xContainer, xCurrentChild);
1082 GetNavModel()->m_pPropChangeList->Lock();
1083 // UndoAction for removal
1084 if ( bUndo && GetNavModel()->m_pPropChangeList->CanUndo())
1086 pFormModel->AddUndo(new FmUndoContainerAction(*pFormModel, FmUndoContainerAction::Removed,
1087 xContainer, xCurrentChild, nIndex));
1089 else if( !GetNavModel()->m_pPropChangeList->CanUndo() )
1091 FmUndoContainerAction::DisposeElement( xCurrentChild );
1094 // copy events
1095 Reference< XEventAttacherManager > xManager(xContainer, UNO_QUERY);
1096 Sequence< ScriptEventDescriptor > aEvts;
1098 if (xManager.is() && nIndex >= 0)
1099 aEvts = xManager->getScriptEvents(nIndex);
1100 xContainer->removeByIndex(nIndex);
1102 // remove selection
1103 Select(pCurrent, false);
1104 // and delete it
1105 Remove(pCurrentUserData);
1107 // position in DropParents, where to insert dropped entries
1108 if (pTargetData)
1109 xContainer = Reference< XIndexContainer > (pTargetData->GetElement(), UNO_QUERY);
1110 else
1111 xContainer = Reference< XIndexContainer > (GetNavModel()->GetForms(), UNO_QUERY);
1113 // allways insert at the end
1114 nIndex = xContainer->getCount();
1116 // UndoAction for insertion
1117 if ( bUndo && GetNavModel()->m_pPropChangeList->CanUndo())
1118 pFormModel->AddUndo(new FmUndoContainerAction(*pFormModel, FmUndoContainerAction::Inserted,
1119 xContainer, xCurrentChild, nIndex));
1121 // insert in new container
1122 if (pTargetData)
1124 // insert in a form needs a FormComponent
1125 xContainer->insertByIndex( nIndex,
1126 makeAny( Reference< XFormComponent >( xCurrentChild, UNO_QUERY ) ) );
1128 else
1130 xContainer->insertByIndex( nIndex,
1131 makeAny( Reference< XForm >( xCurrentChild, UNO_QUERY ) ) );
1134 if (aEvts.getLength())
1136 xManager = Reference< XEventAttacherManager > (xContainer, UNO_QUERY);
1137 if (xManager.is())
1138 xManager->registerScriptEvents(nIndex, aEvts);
1141 GetNavModel()->m_pPropChangeList->UnLock();
1143 // give an entry the new parent
1144 pCurrentUserData->SetParent(pTargetData);
1146 // give parent the new child
1147 if (pTargetData)
1148 pTargetData->GetChildList()->insert( pCurrentUserData, nIndex );
1149 else
1150 GetNavModel()->GetRootList()->insert( pCurrentUserData, nIndex );
1152 // announce to myself and reselect
1153 SvTreeListEntry* pNew = Insert( pCurrentUserData, nIndex );
1154 if ( ( aDropped.begin() == dropped ) && pNew )
1156 SvTreeListEntry* pParent = GetParent( pNew );
1157 if ( pParent )
1158 Expand( pParent );
1162 UnlockSelectionHandling();
1164 if( bUndo )
1165 pFormModel->EndUndo();
1167 // During the move, the markings of the underlying view did not change (because the view is not affected by the logical
1168 // hierarchy of the form/control models. But my selection changed - which means I have to adjust it according to the
1169 // view marks, again.
1170 SynchronizeSelection();
1172 // in addition, with the move of controls such things as "the current form" may have changed - force the shell
1173 // to update itself accordingly
1174 if( pFormShell && pFormShell->GetImpl() && pFormShell->GetFormView() )
1175 pFormShell->GetImpl()->DetermineSelection( pFormShell->GetFormView()->GetMarkedObjectList() );
1177 if ( m_aControlExchange.isClipboardOwner() && ( DND_ACTION_MOVE == _nAction ) )
1178 m_aControlExchange->clear();
1180 return _nAction;
1184 sal_Int8 NavigatorTree::ExecuteDrop( const ExecuteDropEvent& rEvt )
1186 sal_Int8 nResult( DND_ACTION_NONE );
1188 if ( m_aControlExchange.isDragSource() )
1189 nResult = implExecuteDataTransfer( *m_aControlExchange, rEvt.mnAction, rEvt.maPosPixel, true );
1190 else
1192 OControlTransferData aDroppedData( rEvt.maDropEvent.Transferable );
1193 nResult = implExecuteDataTransfer( aDroppedData, rEvt.mnAction, rEvt.maPosPixel, true );
1196 return nResult;
1200 void NavigatorTree::doPaste()
1204 if ( m_aControlExchange.isClipboardOwner() )
1206 implExecuteDataTransfer( *m_aControlExchange, doingKeyboardCut( ) ? DND_ACTION_MOVE : DND_ACTION_COPY, FirstSelected(), false );
1208 else
1210 // the clipboard content
1211 Reference< XClipboard > xClipboard( GetClipboard() );
1212 Reference< XTransferable > xTransferable;
1213 if ( xClipboard.is() )
1214 xTransferable = xClipboard->getContents();
1216 OControlTransferData aClipboardContent( xTransferable );
1217 implExecuteDataTransfer( aClipboardContent, DND_ACTION_COPY, FirstSelected(), false );
1220 catch( const Exception& )
1222 OSL_FAIL( "NavigatorTree::doPaste: caught an exception!" );
1227 void NavigatorTree::doCopy()
1229 if ( implPrepareExchange( DND_ACTION_COPY ) )
1231 m_aControlExchange.setClipboardListener( LINK( this, NavigatorTree, OnClipboardAction ) );
1232 m_aControlExchange.copyToClipboard( );
1237 void NavigatorTree::ModelHasRemoved( SvTreeListEntry* _pEntry )
1239 SvTreeListEntry* pTypedEntry = static_cast< SvTreeListEntry* >( _pEntry );
1240 if ( doingKeyboardCut() )
1241 m_aCutEntries.erase( pTypedEntry );
1243 if ( m_aControlExchange.isDataExchangeActive() )
1245 if ( 0 == m_aControlExchange->onEntryRemoved( pTypedEntry ) )
1247 // last of the entries which we put into the clipboard has been deleted from the tree.
1248 // Give up the clipboard ownership.
1249 m_aControlExchange.clear();
1255 void NavigatorTree::doCut()
1257 if ( implPrepareExchange( DND_ACTION_MOVE ) )
1259 m_aControlExchange.setClipboardListener( LINK( this, NavigatorTree, OnClipboardAction ) );
1260 m_aControlExchange.copyToClipboard( );
1261 m_bKeyboardCut = true;
1263 // mark all the entries we just "cut" into the clipboard as "nearly moved"
1264 for ( SvLBoxEntrySortedArray::const_iterator it = m_arrCurrentSelection.begin();
1265 it != m_arrCurrentSelection.end(); ++it )
1267 SvTreeListEntry* pEntry = *it;
1268 if ( pEntry )
1270 m_aCutEntries.insert( pEntry );
1271 pEntry->SetFlags( pEntry->GetFlags() | SvTLEntryFlags::SEMITRANSPARENT );
1272 InvalidateEntry( pEntry );
1279 void NavigatorTree::KeyInput(const ::KeyEvent& rKEvt)
1281 const vcl::KeyCode& rCode = rKEvt.GetKeyCode();
1283 // delete?
1284 if (rKEvt.GetKeyCode().GetCode() == KEY_DELETE && !rKEvt.GetKeyCode().GetModifier())
1286 DeleteSelection();
1287 return;
1290 // copy'n'paste?
1291 switch ( rCode.GetFunction() )
1293 case KeyFuncType::CUT:
1294 doCut();
1295 break;
1297 case KeyFuncType::PASTE:
1298 if ( implAcceptPaste() )
1299 doPaste();
1300 break;
1302 case KeyFuncType::COPY:
1303 doCopy();
1304 break;
1306 default:
1307 break;
1310 SvTreeListBox::KeyInput(rKEvt);
1314 bool NavigatorTree::EditingEntry( SvTreeListEntry* pEntry, ::Selection& rSelection )
1316 if (!SvTreeListBox::EditingEntry( pEntry, rSelection ))
1317 return false;
1319 return (pEntry && (pEntry->GetUserData() != NULL));
1320 // root, which isn't allowed to be renamed, has UserData=NULL
1324 void NavigatorTree::NewForm( SvTreeListEntry* pParentEntry )
1327 // get ParentFormData
1328 if( !IsFormEntry(pParentEntry) )
1329 return;
1331 FmFormData* pParentFormData = static_cast<FmFormData*>(pParentEntry->GetUserData());
1334 // create new form
1335 Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
1336 Reference< XForm > xNewForm(xContext->getServiceManager()->createInstanceWithContext(FM_SUN_COMPONENT_FORM, xContext), UNO_QUERY);
1337 if (!xNewForm.is())
1338 return;
1340 FmFormData* pNewFormData = new FmFormData( xNewForm, m_aNavigatorImages, pParentFormData );
1343 // set name
1344 OUString aName = GenerateName(pNewFormData);
1345 pNewFormData->SetText(aName);
1347 Reference< XPropertySet > xPropertySet(xNewForm, UNO_QUERY);
1348 if (!xPropertySet.is())
1349 return;
1352 xPropertySet->setPropertyValue( FM_PROP_NAME, makeAny(aName) );
1353 // a form should always have the command type table as default
1354 xPropertySet->setPropertyValue( FM_PROP_COMMANDTYPE, makeAny(sal_Int32(CommandType::TABLE)));
1356 catch ( const Exception& )
1358 OSL_FAIL("NavigatorTree::NewForm : could not set esssential properties !");
1363 // insert form
1364 GetNavModel()->Insert( pNewFormData, TREELIST_APPEND, true );
1367 // set new form as active
1368 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
1369 if( pFormShell )
1371 InterfaceBag aSelection;
1372 aSelection.insert( Reference<XInterface>( xNewForm, UNO_QUERY ) );
1373 pFormShell->GetImpl()->setCurrentSelection( aSelection );
1375 pFormShell->GetViewShell()->GetViewFrame()->GetBindings().Invalidate(SID_FM_PROPERTIES, true, true);
1377 GetNavModel()->SetModified();
1380 // switch to EditMode
1381 SvTreeListEntry* pNewEntry = FindEntry( pNewFormData );
1382 EditEntry( pNewEntry );
1386 FmControlData* NavigatorTree::NewControl( const OUString& rServiceName, SvTreeListEntry* pParentEntry, bool bEditName )
1389 // get ParentForm
1390 if (!GetNavModel()->GetFormShell())
1391 return NULL;
1392 if (!IsFormEntry(pParentEntry))
1393 return NULL;
1395 FmFormData* pParentFormData = static_cast<FmFormData*>(pParentEntry->GetUserData());
1396 Reference< XForm > xParentForm( pParentFormData->GetFormIface());
1399 // create new component
1400 Reference<XComponentContext> xContext = comphelper::getProcessComponentContext();
1401 Reference<XFormComponent> xNewComponent( xContext->getServiceManager()->createInstanceWithContext(rServiceName, xContext), UNO_QUERY);
1402 if (!xNewComponent.is())
1403 return NULL;
1405 FmControlData* pNewFormControlData = new FmControlData( xNewComponent, m_aNavigatorImages, pParentFormData );
1408 // set name
1409 OUString sName = FmFormPageImpl::setUniqueName( xNewComponent, xParentForm );
1411 pNewFormControlData->SetText( sName );
1414 // insert FormComponent
1415 GetNavModel()->Insert( pNewFormControlData, TREELIST_APPEND, true );
1416 GetNavModel()->SetModified();
1418 if (bEditName)
1421 // switch to EditMode
1422 SvTreeListEntry* pNewEntry = FindEntry( pNewFormControlData );
1423 Select( pNewEntry, true );
1424 EditEntry( pNewEntry );
1427 return pNewFormControlData;
1431 OUString NavigatorTree::GenerateName( FmEntryData* pEntryData )
1433 const sal_uInt16 nMaxCount = 99;
1434 OUString aNewName;
1437 // create base name
1438 OUString aBaseName;
1439 if( pEntryData->ISA(FmFormData) )
1440 aBaseName = SVX_RESSTR( RID_STR_STDFORMNAME );
1441 else if( pEntryData->ISA(FmControlData) )
1442 aBaseName = SVX_RESSTR( RID_STR_CONTROL );
1445 // create new name
1446 FmFormData* pFormParentData = static_cast<FmFormData*>(pEntryData->GetParent());
1448 for( sal_Int32 i=0; i<nMaxCount; i++ )
1450 aNewName = aBaseName;
1451 if( i>0 )
1453 aNewName += " ";
1454 aNewName += OUString::number(i).getStr();
1457 if( GetNavModel()->FindData(aNewName, pFormParentData,false) == NULL )
1458 break;
1461 return aNewName;
1465 bool NavigatorTree::EditedEntry( SvTreeListEntry* pEntry, const OUString& rNewText )
1467 if (EditingCanceled())
1468 return true;
1470 GrabFocus();
1471 FmEntryData* pEntryData = static_cast<FmEntryData*>(pEntry->GetUserData());
1472 bool bRes = NavigatorTreeModel::Rename( pEntryData, rNewText);
1473 if( !bRes )
1475 m_pEditEntry = pEntry;
1476 nEditEvent = Application::PostUserEvent( LINK(this, NavigatorTree, OnEdit), NULL, true );
1477 } else
1478 SetCursor(pEntry, true);
1480 return bRes;
1484 IMPL_LINK_NOARG(NavigatorTree, OnEdit)
1486 nEditEvent = 0;
1487 EditEntry( m_pEditEntry );
1488 m_pEditEntry = NULL;
1490 return 0L;
1494 IMPL_LINK_NOARG_TYPED(NavigatorTree, OnDropActionTimer, Timer *, void)
1496 if (--m_aTimerCounter > 0)
1497 return;
1499 switch ( m_aDropActionType )
1501 case DA_EXPANDNODE:
1503 SvTreeListEntry* pToExpand = GetEntry(m_aTimerTriggered);
1504 if (pToExpand && (GetChildCount(pToExpand) > 0) && !IsExpanded(pToExpand))
1505 // normaly, we have to test, if the node is expanded,
1506 // but there is no method for this either in base class nor the model
1507 // the base class should tolerate it anyway
1508 Expand(pToExpand);
1510 // After expansion there is nothing to do like after scrolling
1511 m_aDropActionTimer.Stop();
1513 break;
1515 case DA_SCROLLUP :
1516 ScrollOutputArea( 1 );
1517 m_aTimerCounter = DROP_ACTION_TIMER_SCROLL_TICKS;
1518 break;
1520 case DA_SCROLLDOWN :
1521 ScrollOutputArea( -1 );
1522 m_aTimerCounter = DROP_ACTION_TIMER_SCROLL_TICKS;
1523 break;
1529 IMPL_LINK(NavigatorTree, OnEntrySelDesel, NavigatorTree*, /*pThis*/)
1531 m_sdiState = SDI_DIRTY;
1533 if (IsSelectionHandlingLocked())
1534 return 0L;
1536 if (m_aSynchronizeTimer.IsActive())
1537 m_aSynchronizeTimer.Stop();
1539 m_aSynchronizeTimer.SetTimeout(EXPLORER_SYNC_DELAY);
1540 m_aSynchronizeTimer.Start();
1542 return 0L;
1546 IMPL_LINK_NOARG_TYPED(NavigatorTree, OnSynchronizeTimer, Timer *, void)
1548 SynchronizeMarkList();
1553 IMPL_LINK_NOARG(NavigatorTree, OnClipboardAction)
1555 if ( !m_aControlExchange.isClipboardOwner() )
1557 if ( doingKeyboardCut() )
1559 for ( ListBoxEntrySet::const_iterator i = m_aCutEntries.begin();
1560 i != m_aCutEntries.end();
1564 SvTreeListEntry* pEntry = *i;
1565 if ( !pEntry )
1566 continue;
1568 pEntry->SetFlags( pEntry->GetFlags() & ~SvTLEntryFlags::SEMITRANSPARENT );
1569 InvalidateEntry( pEntry );
1571 ListBoxEntrySet aEmpty;
1572 m_aCutEntries.swap( aEmpty );
1574 m_bKeyboardCut = false;
1577 return 0L;
1581 void NavigatorTree::ShowSelectionProperties(bool bForce)
1583 // at first i need the FormShell
1584 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
1585 if (!pFormShell)
1586 // no shell -> impossible to set curObject -> leave
1587 return;
1589 CollectSelectionData(SDI_ALL);
1590 SAL_WARN_IF(static_cast<size_t>(m_nFormsSelected + m_nControlsSelected
1591 + (m_bRootSelected ? 1 : 0)) != m_arrCurrentSelection.size(),
1592 "svx.form",
1593 "NavigatorTree::ShowSelectionProperties : selection meta data invalid !");
1596 InterfaceBag aSelection;
1597 bool bSetSelectionAsMarkList = false;
1599 if (m_bRootSelected)
1600 ; // no properties for the root, neither for single nor for multi selection
1601 else if ( m_nFormsSelected + m_nControlsSelected == 0 ) // none of the two should be less 0
1602 ; // no selection -> no properties
1603 else if ( m_nFormsSelected * m_nControlsSelected != 0 )
1604 ; // mixed selection -> no properties
1605 else
1606 { // either only forms, or only controls are selected
1607 if (m_arrCurrentSelection.size() == 1)
1609 if (m_nFormsSelected > 0)
1610 { // exactly one form is selected
1611 FmFormData* pFormData = static_cast<FmFormData*>((*m_arrCurrentSelection.begin())->GetUserData());
1612 aSelection.insert( Reference< XInterface >( pFormData->GetFormIface(), UNO_QUERY ) );
1614 else
1615 { // exactly one control is selected (whatever hidden or normal)
1616 FmEntryData* pEntryData = static_cast<FmEntryData*>((*m_arrCurrentSelection.begin())->GetUserData());
1618 aSelection.insert( Reference< XInterface >( pEntryData->GetElement(), UNO_QUERY ) );
1621 else
1622 { // it's a MultiSelection, so we must build a MultiSet
1623 if (m_nFormsSelected > 0)
1624 { // ... only forms
1625 // first of all collect PropertySet-Interfaces of the forms
1626 SvLBoxEntrySortedArray::const_iterator it = m_arrCurrentSelection.begin();
1627 for ( sal_Int32 i = 0; i < m_nFormsSelected; ++i )
1629 FmFormData* pFormData = static_cast<FmFormData*>((*it)->GetUserData());
1630 aSelection.insert( pFormData->GetPropertySet().get() );
1631 ++it;
1634 else
1635 { // ... only controls
1636 if (m_nHiddenControls == m_nControlsSelected)
1637 { // a MultiSet for properties of hidden controls
1638 SvLBoxEntrySortedArray::const_iterator it = m_arrCurrentSelection.begin();
1639 for ( sal_Int32 i = 0; i < m_nHiddenControls; ++i )
1641 FmEntryData* pEntryData = static_cast<FmEntryData*>((*it)->GetUserData());
1642 aSelection.insert( pEntryData->GetPropertySet().get() );
1643 ++it;
1646 else if (m_nHiddenControls == 0)
1647 { // only normal controls
1648 bSetSelectionAsMarkList = true;
1655 // and now my form and my SelObject
1656 if ( bSetSelectionAsMarkList )
1657 pFormShell->GetImpl()->setCurrentSelectionFromMark( pFormShell->GetFormView()->GetMarkedObjectList() );
1658 else
1659 pFormShell->GetImpl()->setCurrentSelection( aSelection );
1661 if ( pFormShell->GetImpl()->IsPropBrwOpen() || bForce )
1663 // and now deliver all to the PropertyBrowser
1664 pFormShell->GetViewShell()->GetViewFrame()->GetDispatcher()->Execute( SID_FM_SHOW_PROPERTY_BROWSER, SfxCallMode::ASYNCHRON );
1669 void NavigatorTree::DeleteSelection()
1671 // of course, i can't delete root
1672 bool bRootSelected = IsSelected(m_pRootEntry);
1673 sal_uIntPtr nSelectedEntries = GetSelectionCount();
1674 if (bRootSelected && (nSelectedEntries > 1)) // root and other elements ?
1675 Select(m_pRootEntry, false); // yes -> remove root from selection
1677 if ((nSelectedEntries == 0) || bRootSelected) // still root ?
1678 return; // -> only selected element -> leave
1680 DBG_ASSERT(!m_bPrevSelectionMixed, "NavigatorTree::DeleteSelection() : delete permitted if mark and selection are inconsistent");
1682 // i need the FormModel later
1683 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
1684 if (!pFormShell)
1685 return;
1686 FmFormModel* pFormModel = pFormShell->GetFormModel();
1687 if (!pFormModel)
1688 return;
1690 // now I have to safeguard the DeleteList: if you delete a form and a dependent element
1691 // - in this order - than the SvLBoxEntryPtr of the dependent element is already invalid,
1692 // when it should be deleted... you have to prohibit this GPF, that of course would happen,
1693 // so I take the 'normalized' list
1694 CollectSelectionData( SDI_NORMALIZED );
1696 // see below for why we need this mapping from models to shapes
1697 FmFormView* pFormView = pFormShell->GetFormView();
1698 SdrPageView* pPageView = pFormView ? pFormView->GetSdrPageView() : NULL;
1699 SdrPage* pPage = pPageView ? pPageView->GetPage() : NULL;
1700 DBG_ASSERT( pPage, "NavigatorTree::DeleteSelection: invalid form page!" );
1702 MapModelToShape aModelShapes;
1703 if ( pPage )
1704 collectShapeModelMapping( pPage, aModelShapes );
1706 // problem: we have to use ExplorerModel::Remove, since only this one properly deletes Form objects.
1707 // But, the controls themself must be deleted via DeleteMarked (else, the Writer has some problems
1708 // somewhere). In case I'd first delete the structure, then the controls, the UNDO would not work
1709 // (since UNDO then would mean to first restore the controls, then the structure, means their parent
1710 // form). The other way round, the EntryDatas would be invalid, if I'd first delete the controls and
1711 // then go on to the strucure. This means I have to delete the forms *after* the normal controls, so
1712 // that during UNDO, they're restored in the proper order.
1713 pFormShell->GetImpl()->EnableTrackProperties(false);
1714 for (SvLBoxEntrySortedArray::reverse_iterator it = m_arrCurrentSelection.rbegin();
1715 it != m_arrCurrentSelection.rend(); )
1717 FmEntryData* pCurrent = static_cast<FmEntryData*>((*it)->GetUserData());
1719 // a form ?
1720 bool bIsForm = pCurrent->ISA(FmFormData);
1722 // because deletion is done by the view, and i build on its MarkList,
1723 // but normally only direct controls, no indirect ones, are marked in a marked form,
1724 // i have to do it later
1725 if (bIsForm)
1726 MarkViewObj(static_cast<FmFormData*>(pCurrent), true, true); // second sal_True means "deep"
1728 // a hidden control ?
1729 bool bIsHidden = IsHiddenControl(pCurrent);
1731 // keep forms and hidden controls, the rest not
1732 if (!bIsForm && !bIsHidden)
1734 // well, no form and no hidden control -> we can remove it from m_arrCurrentSelection, as it will
1735 // be deleted automatically. This is because for every model (except forms and hidden control models)
1736 // there exist a shape, which is marked _if_and_only_if_ the model is selected in our tree.
1737 if ( aModelShapes.find( pCurrent->GetElement() ) != aModelShapes.end() )
1739 // if there's a shape for the current entry, then either it is marked or it is in a
1740 // hidden layer (#i28502#), or something like this.
1741 // In the first case, it will be deleted below, in the second case, we currently don't
1742 // delete it, as there's no real (working!) API for this, neither in UNO nor in non-UNO.
1743 m_arrCurrentSelection.erase( --(it.base()) );
1745 else
1746 ++it;
1747 // In case there is no shape for the current entry, we keep the entry in m_arrCurrentSelection,
1748 // since then we can definitely remove it.
1749 // #103597#
1751 else
1752 ++it;
1754 pFormShell->GetImpl()->EnableTrackProperties(true);
1756 // let the view delete the marked controls
1757 pFormShell->GetFormView()->DeleteMarked();
1759 // start UNDO at this point. Unfortunately, this results in 2 UNDO actions, since DeleteMarked is
1760 // creating an own one. However, if we'd move it before DeleteMarked, Writer doesi not really like
1761 // this ... :(
1762 // #i31038#
1765 // initialize UNDO
1766 OUString aUndoStr;
1767 if ( m_arrCurrentSelection.size() == 1 )
1769 aUndoStr = SVX_RESSTR(RID_STR_UNDO_CONTAINER_REMOVE);
1770 if (m_nFormsSelected)
1771 aUndoStr = aUndoStr.replaceFirst( "#", SVX_RESSTR( RID_STR_FORM ) );
1772 else
1773 // it must be a control (else the root would be selected, but it cannot be deleted)
1774 aUndoStr = aUndoStr.replaceFirst( "#", SVX_RESSTR( RID_STR_CONTROL ) );
1776 else
1778 aUndoStr = SVX_RESSTR(RID_STR_UNDO_CONTAINER_REMOVE_MULTIPLE);
1779 aUndoStr = aUndoStr.replaceFirst( "#", OUString::number( m_arrCurrentSelection.size() ) );
1781 pFormModel->BegUndo(aUndoStr);
1784 // remove remaining structure
1785 for (SvLBoxEntrySortedArray::const_iterator it = m_arrCurrentSelection.begin();
1786 it != m_arrCurrentSelection.end(); ++it)
1788 FmEntryData* pCurrent = static_cast<FmEntryData*>((*it)->GetUserData());
1790 // if the entry still has children, we skipped deletion of one of those children.
1791 // This may for instance be because the shape is in a hidden layer, where we're unable
1792 // to remove it
1793 if ( pCurrent->GetChildList()->size() )
1794 continue;
1796 // one remaining subtile problem, before deleting it : if it's a form and the shell
1797 // knows it as CurrentObject, i have to tell it something else
1798 if (pCurrent->ISA(FmFormData))
1800 Reference< XForm > xCurrentForm( static_cast< FmFormData* >( pCurrent )->GetFormIface() );
1801 if ( pFormShell->GetImpl()->getCurrentForm() == xCurrentForm ) // shell knows form to be deleted ?
1802 pFormShell->GetImpl()->forgetCurrentForm(); // -> take away ...
1804 GetNavModel()->Remove(pCurrent, true);
1806 pFormModel->EndUndo();
1810 void NavigatorTree::CollectSelectionData(SELDATA_ITEMS sdiHow)
1812 DBG_ASSERT(sdiHow != SDI_DIRTY, "NavigatorTree::CollectSelectionData : ever thought about your parameter ? DIRTY ?");
1813 if (sdiHow == m_sdiState)
1814 return;
1816 m_arrCurrentSelection.clear();
1817 m_nFormsSelected = m_nControlsSelected = m_nHiddenControls = 0;
1818 m_bRootSelected = false;
1820 SvTreeListEntry* pSelectionLoop = FirstSelected();
1821 while (pSelectionLoop)
1823 // count different elements
1824 if (pSelectionLoop == m_pRootEntry)
1825 m_bRootSelected = true;
1826 else
1828 if (IsFormEntry(pSelectionLoop))
1829 ++m_nFormsSelected;
1830 else
1832 ++m_nControlsSelected;
1833 if (IsHiddenControl(static_cast<FmEntryData*>(pSelectionLoop->GetUserData())))
1834 ++m_nHiddenControls;
1838 if (sdiHow == SDI_NORMALIZED)
1840 // don't take something with a selected ancestor
1841 if (pSelectionLoop == m_pRootEntry)
1842 m_arrCurrentSelection.insert(pSelectionLoop);
1843 else
1845 SvTreeListEntry* pParentLoop = GetParent(pSelectionLoop);
1846 while (pParentLoop)
1848 // actually i would have to test, if parent is part of m_arr_CurrentSelection ...
1849 // but if it's selected, than it's in m_arrCurrentSelection
1850 // or one of it's ancestors, which was selected earlier.
1851 // In both cases IsSelected is enough
1852 if (IsSelected(pParentLoop))
1853 break;
1854 else
1856 if (m_pRootEntry == pParentLoop)
1858 // until root (exclusive), there was no selected parent -> entry belongs to normalized list
1859 m_arrCurrentSelection.insert(pSelectionLoop);
1860 break;
1862 else
1863 pParentLoop = GetParent(pParentLoop);
1868 else if (sdiHow == SDI_NORMALIZED_FORMARK)
1870 SvTreeListEntry* pParent = GetParent(pSelectionLoop);
1871 if (!pParent || !IsSelected(pParent) || IsFormEntry(pSelectionLoop))
1872 m_arrCurrentSelection.insert(pSelectionLoop);
1874 else
1875 m_arrCurrentSelection.insert(pSelectionLoop);
1878 pSelectionLoop = NextSelected(pSelectionLoop);
1881 m_sdiState = sdiHow;
1885 void NavigatorTree::SynchronizeSelection(FmEntryDataArray& arredToSelect)
1887 LockSelectionHandling();
1888 if (arredToSelect.empty())
1890 SelectAll(false);
1892 else
1894 // compare current selection with requested SelectList
1895 SvTreeListEntry* pSelection = FirstSelected();
1896 while (pSelection)
1898 FmEntryData* pCurrent = static_cast<FmEntryData*>(pSelection->GetUserData());
1899 if (pCurrent != NULL)
1901 FmEntryDataArray::iterator it = arredToSelect.find(pCurrent);
1902 if ( it != arredToSelect.end() )
1903 { // entry already selected, but also in SelectList
1904 // remove it from there
1905 arredToSelect.erase(it);
1906 } else
1907 { // entry selected, but not in SelectList -> remove selection
1908 Select(pSelection, false);
1909 // make it visible (maybe it's the only modification i do in this handler
1910 // so you should see it
1911 MakeVisible(pSelection);
1914 else
1915 Select(pSelection, false);
1917 pSelection = NextSelected(pSelection);
1920 // now SelectList contains only entries, which have to be selected
1921 // two possabilities : 1) run through SelectList, get SvTreeListEntry for every entry and select it (is more intuitive)
1922 // 2) run through my SvLBoxEntries and select those, i can find in the SelectList
1923 // 1) needs =(k*n) (k=length of SelectList, n=number of entries),
1924 // plus the fact, that FindEntry uses extensive IsEqualWithoutChilden instead of comparing pointer to UserData
1925 // 2) needs =(n*log k), dublicates some code from FindEntry
1926 // This may be a frequently used code ( at every change in mark of the view!),
1927 // so i use latter one
1928 SvTreeListEntry* pLoop = First();
1929 while( pLoop )
1931 FmEntryData* pCurEntryData = static_cast<FmEntryData*>(pLoop->GetUserData());
1932 FmEntryDataArray::iterator it = arredToSelect.find(pCurEntryData);
1933 if ( it != arredToSelect.end() )
1935 Select(pLoop, true);
1936 MakeVisible(pLoop);
1937 SetCursor(pLoop, true);
1940 pLoop = Next( pLoop );
1943 UnlockSelectionHandling();
1947 void NavigatorTree::SynchronizeSelection()
1949 // shell and view
1950 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
1951 if(!pFormShell) return;
1953 FmFormView* pFormView = pFormShell->GetFormView();
1954 if (!pFormView) return;
1956 GetNavModel()->BroadcastMarkedObjects(pFormView->GetMarkedObjectList());
1960 void NavigatorTree::SynchronizeMarkList()
1962 // i'll need this shell
1963 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
1964 if (!pFormShell) return;
1966 CollectSelectionData(SDI_NORMALIZED_FORMARK);
1968 // the view shouldn't notify now if MarkList changed
1969 pFormShell->GetImpl()->EnableTrackProperties(false);
1971 UnmarkAllViewObj();
1973 for (SvLBoxEntrySortedArray::const_iterator it = m_arrCurrentSelection.begin();
1974 it != m_arrCurrentSelection.end(); ++it)
1976 SvTreeListEntry* pSelectionLoop = *it;
1977 // When form selection, mark all controls of form
1978 if (IsFormEntry(pSelectionLoop) && (pSelectionLoop != m_pRootEntry))
1979 MarkViewObj(static_cast<FmFormData*>(pSelectionLoop->GetUserData()), true, false);
1981 // When control selection, mark Control-SdrObjects
1982 else if (IsFormComponentEntry(pSelectionLoop))
1984 FmControlData* pControlData = static_cast<FmControlData*>(pSelectionLoop->GetUserData());
1985 if (pControlData)
1988 // When HiddenControl no object can be selected
1989 Reference< XFormComponent > xFormComponent( pControlData->GetFormComponent());
1990 if (!xFormComponent.is())
1991 continue;
1992 Reference< XPropertySet > xSet(xFormComponent, UNO_QUERY);
1993 if (!xSet.is())
1994 continue;
1996 sal_uInt16 nClassId = ::comphelper::getINT16(xSet->getPropertyValue(FM_PROP_CLASSID));
1997 if (nClassId != FormComponentType::HIDDENCONTROL)
1998 MarkViewObj(pControlData, true, true);
2003 // if PropertyBrowser is open, i have to adopt it according to my selection
2004 // (Not as MarkList of view : if a form is selected, all belonging controls are selected in the view
2005 // but of course i want to see the form-properties
2006 ShowSelectionProperties(false);
2008 // reset flag at view
2009 pFormShell->GetImpl()->EnableTrackProperties(true);
2011 // if exactly one form is selected now, shell should notice it as CurrentForm
2012 // (if selection handling isn't locked, view cares about it in MarkListHasChanged
2013 // but mechanism doesn't work, if form is empty for example
2014 if ((m_arrCurrentSelection.size() == 1) && (m_nFormsSelected == 1))
2016 FmFormData* pSingleSelectionData = PTR_CAST( FmFormData, static_cast< FmEntryData* >( FirstSelected()->GetUserData() ) );
2017 DBG_ASSERT( pSingleSelectionData, "NavigatorTree::SynchronizeMarkList: invalid selected form!" );
2018 if ( pSingleSelectionData )
2020 InterfaceBag aSelection;
2021 aSelection.insert( Reference< XInterface >( pSingleSelectionData->GetFormIface(), UNO_QUERY ) );
2022 pFormShell->GetImpl()->setCurrentSelection( aSelection );
2028 bool NavigatorTree::IsHiddenControl(FmEntryData* pEntryData)
2030 if (pEntryData == NULL) return false;
2032 Reference< XPropertySet > xProperties( pEntryData->GetPropertySet() );
2033 if (::comphelper::hasProperty(FM_PROP_CLASSID, xProperties))
2035 Any aClassID = xProperties->getPropertyValue( FM_PROP_CLASSID );
2036 return (::comphelper::getINT16(aClassID) == FormComponentType::HIDDENCONTROL);
2038 return false;
2042 bool NavigatorTree::Select( SvTreeListEntry* pEntry, bool bSelect )
2044 if (bSelect == IsSelected(pEntry)) // this happens sometimes, maybe base class is to exact ;)
2045 return true;
2047 return SvTreeListBox::Select(pEntry, bSelect );
2051 void NavigatorTree::UnmarkAllViewObj()
2053 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
2054 if( !pFormShell )
2055 return;
2056 FmFormView* pFormView = pFormShell->GetFormView();
2057 pFormView->UnMarkAll();
2060 void NavigatorTree::MarkViewObj(FmFormData* pFormData, bool bMark, bool bDeep )
2062 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
2063 if( !pFormShell )
2064 return;
2066 // first collect all sdrobjects
2067 ::std::set< Reference< XFormComponent > > aObjects;
2068 CollectObjects(pFormData,bDeep,aObjects);
2071 // find and select appropriate SdrObj in page
2072 FmFormView* pFormView = pFormShell->GetFormView();
2073 SdrPageView* pPageView = pFormView->GetSdrPageView();
2074 SdrPage* pPage = pPageView->GetPage();
2075 //FmFormPage* pFormPage = dynamic_cast< FmFormPage* >( pPage );
2077 SdrObjListIter aIter( *pPage );
2078 while ( aIter.IsMore() )
2080 SdrObject* pSdrObject = aIter.Next();
2081 FmFormObj* pFormObject = FmFormObj::GetFormObject( pSdrObject );
2082 if ( !pFormObject )
2083 continue;
2085 Reference< XFormComponent > xControlModel( pFormObject->GetUnoControlModel(),UNO_QUERY );
2086 if ( xControlModel.is() && aObjects.find(xControlModel) != aObjects.end() && bMark != pFormView->IsObjMarked( pSdrObject ) )
2088 // unfortunately, the writer doesn't like marking an already-marked object, again, so reset the mark first
2089 pFormView->MarkObj( pSdrObject, pPageView, !bMark, false );
2091 } // while ( aIter.IsMore() )
2092 if ( bMark )
2094 // make the mark visible
2095 ::Rectangle aMarkRect( pFormView->GetAllMarkedRect());
2096 for ( sal_uInt32 i = 0; i < pFormView->PaintWindowCount(); ++i )
2098 SdrPaintWindow* pPaintWindow = pFormView->GetPaintWindow( i );
2099 OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
2100 if ( ( OUTDEV_WINDOW == rOutDev.GetOutDevType() ) && !aMarkRect.IsEmpty() )
2102 pFormView->MakeVisible( aMarkRect, static_cast<vcl::Window&>(rOutDev) );
2104 } // for ( sal_uInt32 i = 0; i < pFormView->PaintWindowCount(); ++i )
2108 void NavigatorTree::CollectObjects(FmFormData* pFormData, bool bDeep, ::std::set< Reference< XFormComponent > >& _rObjects)
2110 FmEntryDataList* pChildList = pFormData->GetChildList();
2111 FmControlData* pControlData;
2112 for( size_t i = 0; i < pChildList->size(); ++i )
2114 FmEntryData* pEntryData = pChildList->at( i );
2115 if( pEntryData->ISA(FmControlData) )
2117 pControlData = static_cast<FmControlData*>(pEntryData);
2118 _rObjects.insert(pControlData->GetFormComponent());
2119 } // if( pEntryData->ISA(FmControlData) )
2120 else if (bDeep && (pEntryData->ISA(FmFormData)))
2121 CollectObjects(static_cast<FmFormData*>(pEntryData), bDeep, _rObjects);
2122 } // for( sal_uInt32 i=0; i<pChildList->Count(); i++ )
2125 void NavigatorTree::MarkViewObj( FmControlData* pControlData, bool bMarkHandles, bool bMark)
2127 if( !pControlData )
2128 return;
2129 FmFormShell* pFormShell = GetNavModel()->GetFormShell();
2130 if( !pFormShell )
2131 return;
2134 // find and select appropriate SdrObj
2135 FmFormView* pFormView = pFormShell->GetFormView();
2136 Reference< XFormComponent > xFormComponent( pControlData->GetFormComponent());
2137 SdrPageView* pPageView = pFormView->GetSdrPageView();
2138 SdrPage* pPage = pPageView->GetPage();
2140 bool bPaint = false;
2141 SdrObjListIter aIter( *pPage );
2142 while ( aIter.IsMore() )
2144 SdrObject* pSdrObject = aIter.Next();
2145 FmFormObj* pFormObject = FmFormObj::GetFormObject( pSdrObject );
2146 if ( !pFormObject )
2147 continue;
2149 Reference< XInterface > xControlModel( pFormObject->GetUnoControlModel() );
2150 if ( xControlModel != xFormComponent )
2151 continue;
2153 // mark the object
2154 if ( bMark != pFormView->IsObjMarked( pSdrObject ) )
2155 // unfortunately, the writer doesn't like marking an already-marked object, again, so reset the mark first
2156 pFormView->MarkObj( pSdrObject, pPageView, !bMark, false );
2158 if ( !bMarkHandles || !bMark )
2159 continue;
2161 bPaint = true;
2163 } // while ( aIter.IsMore() )
2164 if ( bPaint )
2166 // make the mark visible
2167 ::Rectangle aMarkRect( pFormView->GetAllMarkedRect());
2168 for ( sal_uInt32 i = 0; i < pFormView->PaintWindowCount(); ++i )
2170 SdrPaintWindow* pPaintWindow = pFormView->GetPaintWindow( i );
2171 OutputDevice& rOutDev = pPaintWindow->GetOutputDevice();
2172 if ( OUTDEV_WINDOW == rOutDev.GetOutDevType() )
2174 pFormView->MakeVisible( aMarkRect, static_cast<vcl::Window&>(rOutDev) );
2176 } // for ( sal_uInt32 i = 0; i < pFormView->PaintWindowCount(); ++i )
2185 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */