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 .
20 #include <dbtreelistbox.hxx>
21 #include <dbexchange.hxx>
22 #include <callbacks.hxx>
24 #include <com/sun/star/awt/PopupMenuDirection.hpp>
25 #include <com/sun/star/ui/XContextMenuInterceptor.hpp>
26 #include <com/sun/star/uno/XComponentContext.hpp>
27 #include <com/sun/star/frame/XController.hpp>
28 #include <com/sun/star/frame/XPopupMenuController.hpp>
29 #include <com/sun/star/lang/IllegalArgumentException.hpp>
30 #include <cppuhelper/implbase.hxx>
31 #include <comphelper/interfacecontainer2.hxx>
32 #include <comphelper/processfactory.hxx>
33 #include <comphelper/propertyvalue.hxx>
34 #include <dbaccess/IController.hxx>
35 #include <framework/actiontriggerhelper.hxx>
36 #include <toolkit/awt/vclxmenu.hxx>
37 #include <toolkit/helper/vclunohelper.hxx>
38 #include <svx/dbaobjectex.hxx>
40 #include <vcl/commandevent.hxx>
41 #include <vcl/event.hxx>
42 #include <vcl/svapp.hxx>
49 using namespace ::com::sun::star
;
50 using namespace ::com::sun::star::uno
;
51 using namespace ::com::sun::star::beans
;
52 using namespace ::com::sun::star::lang
;
53 using namespace ::com::sun::star::datatransfer
;
54 using namespace ::com::sun::star::ui
;
55 using namespace ::com::sun::star::view
;
57 InterimDBTreeListBox::InterimDBTreeListBox(vcl::Window
* pParent
)
58 : InterimItemWindow(pParent
, u
"dbaccess/ui/dbtreelist.ui"_ustr
, u
"DBTreeList"_ustr
)
59 , TreeListBox(m_xBuilder
->weld_tree_view(u
"treeview"_ustr
), true)
60 , m_xStatusBar(m_xBuilder
->weld_label(u
"statusbar"_ustr
))
62 InitControlBase(&GetWidget());
65 InterimDBTreeListBox::~InterimDBTreeListBox()
70 void InterimDBTreeListBox::dispose()
72 implStopSelectionTimer();
75 InterimItemWindow::dispose();
78 bool InterimDBTreeListBox::DoChildKeyInput(const KeyEvent
& rKEvt
)
80 return ChildKeyInput(rKEvt
);
83 TreeListBoxDropTarget::TreeListBoxDropTarget(TreeListBox
& rTreeView
)
84 : DropTargetHelper(rTreeView
.GetWidget().get_drop_target())
85 , m_rTreeView(rTreeView
)
89 sal_Int8
TreeListBoxDropTarget::AcceptDrop(const AcceptDropEvent
& rEvt
)
91 sal_Int8 nAccept
= m_rTreeView
.AcceptDrop(rEvt
);
93 if (nAccept
!= DND_ACTION_NONE
)
95 // to enable the autoscroll when we're close to the edges
96 weld::TreeView
& rWidget
= m_rTreeView
.GetWidget();
97 rWidget
.get_dest_row_at_pos(rEvt
.maPosPixel
, nullptr, true);
103 sal_Int8
TreeListBoxDropTarget::ExecuteDrop(const ExecuteDropEvent
& rEvt
)
105 return m_rTreeView
.ExecuteDrop(rEvt
);
108 TreeListBox::TreeListBox(std::unique_ptr
<weld::TreeView
> xTreeView
, bool bSQLType
)
109 : m_xTreeView(std::move(xTreeView
))
110 , m_aDropTargetHelper(*this)
111 , m_pActionListener(nullptr)
112 , m_pContextMenuProvider(nullptr)
113 , m_aTimer("dbaccess TreeListBox m_aTimer")
115 m_xTreeView
->connect_key_press(LINK(this, TreeListBox
, KeyInputHdl
));
116 m_xTreeView
->connect_selection_changed(LINK(this, TreeListBox
, SelectHdl
));
117 m_xTreeView
->connect_query_tooltip(LINK(this, TreeListBox
, QueryTooltipHdl
));
118 m_xTreeView
->connect_popup_menu(LINK(this, TreeListBox
, CommandHdl
));
121 m_xHelper
.set(new ODataClipboard
);
123 m_xHelper
.set(new svx::OComponentTransferable
);
124 m_xTreeView
->enable_drag_source(m_xHelper
, DND_ACTION_COPY
);
125 m_xTreeView
->connect_drag_begin(LINK(this, TreeListBox
, DragBeginHdl
));
127 m_aTimer
.SetTimeout(900);
128 m_aTimer
.SetInvokeHandler(LINK(this, TreeListBox
, OnTimeOut
));
131 bool TreeListBox::DoChildKeyInput(const KeyEvent
& /*rKEvt*/)
133 // nothing by default
137 IMPL_LINK(TreeListBox
, KeyInputHdl
, const KeyEvent
&, rKEvt
, bool)
139 KeyFuncType eFunc
= rKEvt
.GetKeyCode().GetFunction();
140 bool bHandled
= false;
144 case KeyFuncType::COPY
:
145 bHandled
= m_aCopyHandler
.IsSet() && !m_xTreeView
->get_selected(nullptr);
147 m_aCopyHandler
.Call(nullptr);
149 case KeyFuncType::PASTE
:
150 bHandled
= m_aPasteHandler
.IsSet() && !m_xTreeView
->get_selected(nullptr);
152 m_aPasteHandler
.Call(nullptr);
154 case KeyFuncType::DELETE
:
155 bHandled
= m_aDeleteHandler
.IsSet() && !m_xTreeView
->get_selected(nullptr);
157 m_aDeleteHandler
.Call(nullptr);
163 return bHandled
|| DoChildKeyInput(rKEvt
);
166 void TreeListBox::implStopSelectionTimer()
168 if ( m_aTimer
.IsActive() )
172 void TreeListBox::implStartSelectionTimer()
174 implStopSelectionTimer();
178 IMPL_LINK_NOARG(TreeListBox
, SelectHdl
, weld::TreeView
&, void)
180 implStartSelectionTimer();
183 TreeListBox::~TreeListBox()
187 std::unique_ptr
<weld::TreeIter
> TreeListBox::GetEntryPosByName(std::u16string_view aName
, const weld::TreeIter
* pStart
, const IEntryFilter
* _pFilter
) const
189 auto xEntry(m_xTreeView
->make_iterator(pStart
));
192 if (!m_xTreeView
->iter_children(*xEntry
))
197 if (!m_xTreeView
->get_iter_first(*xEntry
))
203 if (m_xTreeView
->get_text(*xEntry
) == aName
)
205 if (!_pFilter
|| _pFilter
->includeEntry(weld::fromId
<void*>(m_xTreeView
->get_id(*xEntry
))))
211 } while (m_xTreeView
->iter_next_sibling(*xEntry
));
216 IMPL_LINK(TreeListBox
, DragBeginHdl
, bool&, rUnsetDragIcon
, bool)
218 rUnsetDragIcon
= false;
220 if (m_pActionListener
)
222 m_xDragedEntry
= m_xTreeView
->make_iterator();
223 if (!m_xTreeView
->get_selected(m_xDragedEntry
.get()))
224 m_xDragedEntry
.reset();
225 if (m_xDragedEntry
&& m_pActionListener
->requestDrag(*m_xDragedEntry
))
227 // if the (asynchronous) drag started, stop the selection timer
228 implStopSelectionTimer();
236 sal_Int8
TreeListBox::AcceptDrop(const AcceptDropEvent
& rEvt
)
238 sal_Int8 nDropOption
= DND_ACTION_NONE
;
239 if ( m_pActionListener
)
241 ::Point aDropPos
= rEvt
.maPosPixel
;
242 std::unique_ptr
<weld::TreeIter
> xDropTarget(m_xTreeView
->make_iterator());
243 if (!m_xTreeView
->get_dest_row_at_pos(aDropPos
, xDropTarget
.get(), true))
246 // check if drag is on child entry, which is not allowed
247 std::unique_ptr
<weld::TreeIter
> xParent
;
248 if (rEvt
.mnAction
& DND_ACTION_MOVE
)
250 if (!m_xDragedEntry
) // no entry to move
251 return m_pActionListener
->queryDrop(rEvt
, m_aDropTargetHelper
.GetDataFlavorExVector());
255 xParent
= m_xTreeView
->make_iterator(xDropTarget
.get());
256 if (!m_xTreeView
->iter_parent(*xParent
))
259 while (xParent
&& m_xTreeView
->iter_compare(*xParent
, *m_xDragedEntry
) != 0)
261 if (!m_xTreeView
->iter_parent(*xParent
))
268 nDropOption
= m_pActionListener
->queryDrop(rEvt
, m_aDropTargetHelper
.GetDataFlavorExVector());
269 // check if move is allowed
270 if ( nDropOption
& DND_ACTION_MOVE
)
272 if (!m_xDragedEntry
|| !xDropTarget
||
273 m_xTreeView
->iter_compare(*m_xDragedEntry
, *xDropTarget
) == 0 ||
274 GetEntryPosByName(m_xTreeView
->get_text(*m_xDragedEntry
), xDropTarget
.get()))
276 nDropOption
= nDropOption
& ~DND_ACTION_MOVE
;//DND_ACTION_NONE;
285 sal_Int8
TreeListBox::ExecuteDrop(const ExecuteDropEvent
& rEvt
)
287 if (m_pActionListener
)
288 m_pActionListener
->executeDrop(rEvt
);
289 m_xTreeView
->unset_drag_dest_row();
290 return DND_ACTION_NONE
;
293 IMPL_LINK(TreeListBox
, QueryTooltipHdl
, const weld::TreeIter
&, rIter
, OUString
)
295 OUString sQuickHelpText
;
296 if (m_pActionListener
&&
297 m_pActionListener
->requestQuickHelp(weld::fromId
<void*>(m_xTreeView
->get_id(rIter
)), sQuickHelpText
))
299 return sQuickHelpText
;
301 return m_xTreeView
->get_tooltip_text();
307 typedef ::cppu::WeakImplHelper
< XSelectionSupplier
308 > SelectionSupplier_Base
;
309 class SelectionSupplier
: public SelectionSupplier_Base
312 explicit SelectionSupplier( Any _aSelection
)
313 :m_aSelection(std::move( _aSelection
))
317 virtual sal_Bool SAL_CALL
select( const Any
& xSelection
) override
;
318 virtual Any SAL_CALL
getSelection( ) override
;
319 virtual void SAL_CALL
addSelectionChangeListener( const Reference
< XSelectionChangeListener
>& xListener
) override
;
320 virtual void SAL_CALL
removeSelectionChangeListener( const Reference
< XSelectionChangeListener
>& xListener
) override
;
323 virtual ~SelectionSupplier() override
331 sal_Bool SAL_CALL
SelectionSupplier::select( const Any
& /*_Selection*/ )
333 throw IllegalArgumentException();
334 // API bug: this should be a NoSupportException
337 Any SAL_CALL
SelectionSupplier::getSelection( )
342 void SAL_CALL
SelectionSupplier::addSelectionChangeListener( const Reference
< XSelectionChangeListener
>& /*_Listener*/ )
344 OSL_FAIL( "SelectionSupplier::removeSelectionChangeListener: no support!" );
345 // API bug: this should be a NoSupportException
348 void SAL_CALL
SelectionSupplier::removeSelectionChangeListener( const Reference
< XSelectionChangeListener
>& /*_Listener*/ )
350 OSL_FAIL( "SelectionSupplier::removeSelectionChangeListener: no support!" );
351 // API bug: this should be a NoSupportException
355 IMPL_LINK(TreeListBox
, CommandHdl
, const CommandEvent
&, rCEvt
, bool)
357 if (rCEvt
.GetCommand() != CommandEventId::ContextMenu
)
360 ::Point aPos
= rCEvt
.GetMousePosPixel();
362 std::unique_ptr
<weld::TreeIter
> xIter(m_xTreeView
->make_iterator());
363 if (m_xTreeView
->get_dest_row_at_pos(aPos
, xIter
.get(), false) && !m_xTreeView
->is_selected(*xIter
))
365 m_xTreeView
->unselect_all();
366 m_xTreeView
->set_cursor(*xIter
);
367 m_xTreeView
->select(*xIter
);
368 SelectHdl(*m_xTreeView
);
371 if (!m_pContextMenuProvider
)
374 OUString
aResourceName(m_pContextMenuProvider
->getContextMenuResourceName());
375 if (aResourceName
.isEmpty())
378 css::uno::Sequence
< css::uno::Any
> aArgs
{
379 css::uno::Any(comphelper::makePropertyValue( u
"Value"_ustr
, aResourceName
)),
380 css::uno::Any(comphelper::makePropertyValue( u
"Frame"_ustr
, m_pContextMenuProvider
->getCommandController().getXController()->getFrame() )),
381 css::uno::Any(comphelper::makePropertyValue( u
"IsContextMenu"_ustr
, true ))
384 const css::uno::Reference
< css::uno::XComponentContext
>& xContext
= comphelper::getProcessComponentContext();
385 css::uno::Reference
<css::frame::XPopupMenuController
> xMenuController
386 (xContext
->getServiceManager()->createInstanceWithArgumentsAndContext(
387 u
"com.sun.star.comp.framework.ResourceMenuController"_ustr
, aArgs
, xContext
), css::uno::UNO_QUERY
);
389 if (!xMenuController
.is())
392 VclPtr
<vcl::Window
> xMenuParent
= m_pContextMenuProvider
->getMenuParent();
394 css::uno::Reference
< css::awt::XWindow
> xSourceWindow
= VCLUnoHelper::GetInterface(xMenuParent
);
396 rtl::Reference
xPopupMenu( new VCLXPopupMenu
);
397 xMenuController
->setPopupMenu( xPopupMenu
);
399 // allow context menu interception
400 ::comphelper::OInterfaceContainerHelper2
* pInterceptors
= m_pContextMenuProvider
->getContextMenuInterceptors();
401 if (pInterceptors
&& pInterceptors
->getLength())
403 OUString
aMenuIdentifier( "private:resource/popupmenu/" + aResourceName
);
405 ContextMenuExecuteEvent aEvent
;
406 aEvent
.SourceWindow
= xSourceWindow
;
407 aEvent
.ExecutePosition
.X
= -1;
408 aEvent
.ExecutePosition
.Y
= -1;
409 aEvent
.ActionTriggerContainer
= ::framework::ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
410 xPopupMenu
, &aMenuIdentifier
);
411 aEvent
.Selection
= new SelectionSupplier(m_pContextMenuProvider
->getCurrentSelection(*m_xTreeView
));
413 ::comphelper::OInterfaceIteratorHelper2
aIter( *pInterceptors
);
414 bool bModifiedMenu
= false;
415 bool bAskInterceptors
= true;
416 while ( aIter
.hasMoreElements() && bAskInterceptors
)
418 Reference
< XContextMenuInterceptor
> xInterceptor( aIter
.next(), UNO_QUERY
);
419 if ( !xInterceptor
.is() )
424 ContextMenuInterceptorAction eAction
= xInterceptor
->notifyContextMenuExecute( aEvent
);
427 case ContextMenuInterceptorAction_CANCELLED
:
430 case ContextMenuInterceptorAction_EXECUTE_MODIFIED
:
431 bModifiedMenu
= true;
432 bAskInterceptors
= false;
435 case ContextMenuInterceptorAction_CONTINUE_MODIFIED
:
436 bModifiedMenu
= true;
437 bAskInterceptors
= true;
441 OSL_FAIL( "DBTreeListBox::CreateContextMenu: unexpected return value of the interceptor call!" );
443 case ContextMenuInterceptorAction_IGNORED
:
447 catch( const DisposedException
& e
)
449 if ( e
.Context
== xInterceptor
)
457 ::framework::ActionTriggerHelper::CreateMenuFromActionTriggerContainer(
458 xPopupMenu
, aEvent
.ActionTriggerContainer
);
459 aEvent
.ActionTriggerContainer
.clear();
463 // adjust pos relative to m_xTreeView to relative to xMenuParent
464 m_pContextMenuProvider
->adjustMenuPosition(*m_xTreeView
, aPos
);
466 // do action for selected entry in popup menu
467 css::uno::Reference
<css::awt::XWindowPeer
> xParent(xSourceWindow
, css::uno::UNO_QUERY
);
468 xPopupMenu
->execute(xParent
, css::awt::Rectangle(aPos
.X(), aPos
.Y(), 1, 1), css::awt::PopupMenuDirection::EXECUTE_DOWN
);
470 css::uno::Reference
<css::lang::XComponent
> xComponent(xMenuController
, css::uno::UNO_QUERY
);
472 xComponent
->dispose();
473 xMenuController
.clear();
478 IMPL_LINK_NOARG(TreeListBox
, OnTimeOut
, Timer
*, void)
480 implStopSelectionTimer();
482 m_aSelChangeHdl
.Call( nullptr );
485 std::unique_ptr
<weld::TreeIter
> TreeListBox::GetRootLevelParent(const weld::TreeIter
* pEntry
) const
489 std::unique_ptr
<weld::TreeIter
> xEntry(m_xTreeView
->make_iterator(pEntry
));
490 while (m_xTreeView
->get_iter_depth(*xEntry
))
491 m_xTreeView
->iter_parent(*xEntry
);
495 DBTreeViewBase::DBTreeViewBase(weld::Container
* pContainer
)
496 : m_xBuilder(Application::CreateBuilder(pContainer
, u
"dbaccess/ui/dbtreelist.ui"_ustr
))
497 , m_xContainer(m_xBuilder
->weld_container(u
"DBTreeList"_ustr
))
501 DBTreeViewBase::~DBTreeViewBase()
505 DBTreeView::DBTreeView(weld::Container
* pContainer
, bool bSQLType
)
506 : DBTreeViewBase(pContainer
)
508 m_xTreeListBox
.reset(new TreeListBox(m_xBuilder
->weld_tree_view(u
"treeview"_ustr
), bSQLType
));
513 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */