LibreOffice Module dbaccess (master) 1
dbtreelistbox.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20#include <dbtreelistbox.hxx>
21#include <dbexchange.hxx>
22#include <callbacks.hxx>
23
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>
38#include <svx/dbaobjectex.hxx>
39#include <utility>
40#include <vcl/commandevent.hxx>
41#include <vcl/event.hxx>
42#include <vcl/svapp.hxx>
43
44#include <memory>
45
46namespace dbaui
47{
48
49using namespace ::com::sun::star;
50using namespace ::com::sun::star::uno;
51using namespace ::com::sun::star::beans;
52using namespace ::com::sun::star::lang;
53using namespace ::com::sun::star::datatransfer;
54using namespace ::com::sun::star::ui;
55using namespace ::com::sun::star::view;
56
58 : InterimItemWindow(pParent, "dbaccess/ui/dbtreelist.ui", "DBTreeList")
59 , TreeListBox(m_xBuilder->weld_tree_view("treeview"), true)
60 , m_xStatusBar(m_xBuilder->weld_label("statusbar"))
61{
63}
64
66{
68}
69
71{
73 m_xStatusBar.reset();
74 m_xTreeView.reset();
76}
77
79{
80 return ChildKeyInput(rKEvt);
81}
82
84 : DropTargetHelper(rTreeView.GetWidget().get_drop_target())
85 , m_rTreeView(rTreeView)
86{
87}
88
90{
91 sal_Int8 nAccept = m_rTreeView.AcceptDrop(rEvt);
92
93 if (nAccept != DND_ACTION_NONE)
94 {
95 // to enable the autoscroll when we're close to the edges
97 rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true);
98 }
99
100 return nAccept;
101}
102
104{
105 return m_rTreeView.ExecuteDrop(rEvt);
106}
107
108TreeListBox::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")
114{
115 m_xTreeView->connect_key_press(LINK(this, TreeListBox, KeyInputHdl));
116 m_xTreeView->connect_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));
119
120 if (bSQLType)
121 m_xHelper.set(new ODataClipboard);
122 else
124 m_xTreeView->enable_drag_source(m_xHelper, DND_ACTION_COPY);
125 m_xTreeView->connect_drag_begin(LINK(this, TreeListBox, DragBeginHdl));
126
127 m_aTimer.SetTimeout(900);
128 m_aTimer.SetInvokeHandler(LINK(this, TreeListBox, OnTimeOut));
129}
130
132{
133 // nothing by default
134 return false;
135}
136
137IMPL_LINK(TreeListBox, KeyInputHdl, const KeyEvent&, rKEvt, bool)
138{
139 KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
140 bool bHandled = false;
141
142 switch (eFunc)
143 {
144 case KeyFuncType::COPY:
145 bHandled = m_aCopyHandler.IsSet() && !m_xTreeView->get_selected(nullptr);
146 if (bHandled)
147 m_aCopyHandler.Call(nullptr);
148 break;
149 case KeyFuncType::PASTE:
150 bHandled = m_aPasteHandler.IsSet() && !m_xTreeView->get_selected(nullptr);
151 if (bHandled)
152 m_aPasteHandler.Call(nullptr);
153 break;
154 case KeyFuncType::DELETE:
155 bHandled = m_aDeleteHandler.IsSet() && !m_xTreeView->get_selected(nullptr);
156 if (bHandled)
157 m_aDeleteHandler.Call(nullptr);
158 break;
159 default:
160 break;
161 }
162
163 return bHandled || DoChildKeyInput(rKEvt);
164}
165
167{
168 if ( m_aTimer.IsActive() )
169 m_aTimer.Stop();
170}
171
173{
175 m_aTimer.Start();
176}
177
179{
180 implStartSelectionTimer();
181}
182
184{
185}
186
187std::unique_ptr<weld::TreeIter> TreeListBox::GetEntryPosByName(std::u16string_view aName, const weld::TreeIter* pStart, const IEntryFilter* _pFilter) const
188{
189 auto xEntry(m_xTreeView->make_iterator(pStart));
190 if (pStart)
191 {
192 if (!m_xTreeView->iter_children(*xEntry))
193 return nullptr;
194 }
195 else
196 {
197 if (!m_xTreeView->get_iter_first(*xEntry))
198 return nullptr;
199 }
200
201 do
202 {
203 if (m_xTreeView->get_text(*xEntry) == aName)
204 {
205 if (!_pFilter || _pFilter->includeEntry(weld::fromId<void*>(m_xTreeView->get_id(*xEntry))))
206 {
207 // found
208 return xEntry;
209 }
210 }
211 } while (m_xTreeView->iter_next_sibling(*xEntry));
212
213 return nullptr;
214}
215
216IMPL_LINK(TreeListBox, DragBeginHdl, bool&, rUnsetDragIcon, bool)
217{
218 rUnsetDragIcon = false;
219
220 if (m_pActionListener)
221 {
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))
226 {
227 // if the (asynchronous) drag started, stop the selection timer
228 implStopSelectionTimer();
229 return false;
230 }
231 }
232
233 return true;
234}
235
237{
238 sal_Int8 nDropOption = DND_ACTION_NONE;
239 if ( m_pActionListener )
240 {
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))
244 xDropTarget.reset();
245
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)
249 {
250 if (!m_xDragedEntry) // no entry to move
251 return m_pActionListener->queryDrop(rEvt, m_aDropTargetHelper.GetDataFlavorExVector());
252
253 if (xDropTarget)
254 {
255 xParent = m_xTreeView->make_iterator(xDropTarget.get());
256 if (!m_xTreeView->iter_parent(*xParent))
257 xParent.reset();
258 }
259 while (xParent && m_xTreeView->iter_compare(*xParent, *m_xDragedEntry) != 0)
260 {
261 if (!m_xTreeView->iter_parent(*xParent))
262 xParent.reset();
263 }
264 }
265
266 if (!xParent)
267 {
268 nDropOption = m_pActionListener->queryDrop(rEvt, m_aDropTargetHelper.GetDataFlavorExVector());
269 // check if move is allowed
270 if ( nDropOption & DND_ACTION_MOVE )
271 {
272 if (!m_xDragedEntry || !xDropTarget ||
273 m_xTreeView->iter_compare(*m_xDragedEntry, *xDropTarget) == 0 ||
274 GetEntryPosByName(m_xTreeView->get_text(*m_xDragedEntry), xDropTarget.get()))
275 {
276 nDropOption = nDropOption & ~DND_ACTION_MOVE;//DND_ACTION_NONE;
277 }
278 }
279 }
280 }
281
282 return nDropOption;
283}
284
286{
289 m_xTreeView->unset_drag_dest_row();
290 return DND_ACTION_NONE;
291}
292
293IMPL_LINK(TreeListBox, QueryTooltipHdl, const weld::TreeIter&, rIter, OUString)
294{
295 OUString sQuickHelpText;
296 if (m_pActionListener &&
297 m_pActionListener->requestQuickHelp(weld::fromId<void*>(m_xTreeView->get_id(rIter)), sQuickHelpText))
298 {
299 return sQuickHelpText;
300 }
301 return m_xTreeView->get_tooltip_text();
302}
303
304namespace
305{
306 // SelectionSupplier
307 typedef ::cppu::WeakImplHelper< XSelectionSupplier
308 > SelectionSupplier_Base;
309 class SelectionSupplier : public SelectionSupplier_Base
310 {
311 public:
312 explicit SelectionSupplier( Any _aSelection )
313 :m_aSelection(std::move( _aSelection ))
314 {
315 }
316
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;
321
322 protected:
323 virtual ~SelectionSupplier() override
324 {
325 }
326
327 private:
329 };
330
331 sal_Bool SAL_CALL SelectionSupplier::select( const Any& /*_Selection*/ )
332 {
333 throw IllegalArgumentException();
334 // API bug: this should be a NoSupportException
335 }
336
337 Any SAL_CALL SelectionSupplier::getSelection( )
338 {
339 return m_aSelection;
340 }
341
342 void SAL_CALL SelectionSupplier::addSelectionChangeListener( const Reference< XSelectionChangeListener >& /*_Listener*/ )
343 {
344 OSL_FAIL( "SelectionSupplier::removeSelectionChangeListener: no support!" );
345 // API bug: this should be a NoSupportException
346 }
347
348 void SAL_CALL SelectionSupplier::removeSelectionChangeListener( const Reference< XSelectionChangeListener >& /*_Listener*/ )
349 {
350 OSL_FAIL( "SelectionSupplier::removeSelectionChangeListener: no support!" );
351 // API bug: this should be a NoSupportException
352 }
353}
354
355IMPL_LINK(TreeListBox, CommandHdl, const CommandEvent&, rCEvt, bool)
356{
357 if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
358 return false;
359
360 ::Point aPos = rCEvt.GetMousePosPixel();
361
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))
364 {
365 m_xTreeView->unselect_all();
366 m_xTreeView->set_cursor(*xIter);
367 m_xTreeView->select(*xIter);
368 SelectHdl(*m_xTreeView);
369 }
370
371 if (!m_pContextMenuProvider)
372 return false;
373
374 OUString aResourceName(m_pContextMenuProvider->getContextMenuResourceName());
375 if (aResourceName.isEmpty())
376 return false;
377
378 css::uno::Sequence< css::uno::Any > aArgs{
379 css::uno::Any(comphelper::makePropertyValue( "Value", aResourceName )),
380 css::uno::Any(comphelper::makePropertyValue( "Frame", m_pContextMenuProvider->getCommandController().getXController()->getFrame() )),
381 css::uno::Any(comphelper::makePropertyValue( "IsContextMenu", true ))
382 };
383
384 css::uno::Reference< css::uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
385 css::uno::Reference<css::frame::XPopupMenuController> xMenuController
386 (xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
387 "com.sun.star.comp.framework.ResourceMenuController", aArgs, xContext), css::uno::UNO_QUERY);
388
389 if (!xMenuController.is())
390 return false;
391
392 VclPtr<vcl::Window> xMenuParent = m_pContextMenuProvider->getMenuParent();
393
394 css::uno::Reference< css::awt::XWindow> xSourceWindow = VCLUnoHelper::GetInterface(xMenuParent);
395
396 rtl::Reference xPopupMenu( new VCLXPopupMenu );
397 xMenuController->setPopupMenu( xPopupMenu );
398
399 // allow context menu interception
400 ::comphelper::OInterfaceContainerHelper2* pInterceptors = m_pContextMenuProvider->getContextMenuInterceptors();
401 if (pInterceptors && pInterceptors->getLength())
402 {
403 OUString aMenuIdentifier( "private:resource/popupmenu/" + aResourceName );
404
405 ContextMenuExecuteEvent aEvent;
406 aEvent.SourceWindow = xSourceWindow;
407 aEvent.ExecutePosition.X = -1;
408 aEvent.ExecutePosition.Y = -1;
410 xPopupMenu, &aMenuIdentifier );
411 aEvent.Selection = new SelectionSupplier(m_pContextMenuProvider->getCurrentSelection(*m_xTreeView));
412
413 ::comphelper::OInterfaceIteratorHelper2 aIter( *pInterceptors );
414 bool bModifiedMenu = false;
415 bool bAskInterceptors = true;
416 while ( aIter.hasMoreElements() && bAskInterceptors )
417 {
418 Reference< XContextMenuInterceptor > xInterceptor( aIter.next(), UNO_QUERY );
419 if ( !xInterceptor.is() )
420 continue;
421
422 try
423 {
424 ContextMenuInterceptorAction eAction = xInterceptor->notifyContextMenuExecute( aEvent );
425 switch ( eAction )
426 {
427 case ContextMenuInterceptorAction_CANCELLED:
428 return false;
429
430 case ContextMenuInterceptorAction_EXECUTE_MODIFIED:
431 bModifiedMenu = true;
432 bAskInterceptors = false;
433 break;
434
435 case ContextMenuInterceptorAction_CONTINUE_MODIFIED:
436 bModifiedMenu = true;
437 bAskInterceptors = true;
438 break;
439
440 default:
441 OSL_FAIL( "DBTreeListBox::CreateContextMenu: unexpected return value of the interceptor call!" );
442 [[fallthrough]];
443 case ContextMenuInterceptorAction_IGNORED:
444 break;
445 }
446 }
447 catch( const DisposedException& e )
448 {
449 if ( e.Context == xInterceptor )
450 aIter.remove();
451 }
452 }
453
454 if ( bModifiedMenu )
455 {
456 xPopupMenu->clear();
458 xPopupMenu, aEvent.ActionTriggerContainer );
459 aEvent.ActionTriggerContainer.clear();
460 }
461 }
462
463 // adjust pos relative to m_xTreeView to relative to xMenuParent
464 m_pContextMenuProvider->adjustMenuPosition(*m_xTreeView, aPos);
465
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);
469
470 css::uno::Reference<css::lang::XComponent> xComponent(xMenuController, css::uno::UNO_QUERY);
471 if (xComponent.is())
472 xComponent->dispose();
473 xMenuController.clear();
474
475 return true;
476}
477
479{
480 implStopSelectionTimer();
481
482 m_aSelChangeHdl.Call( nullptr );
483}
484
485std::unique_ptr<weld::TreeIter> TreeListBox::GetRootLevelParent(const weld::TreeIter* pEntry) const
486{
487 if (!pEntry)
488 return nullptr;
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);
492 return xEntry;
493}
494
496 : m_xBuilder(Application::CreateBuilder(pContainer, "dbaccess/ui/dbtreelist.ui"))
497 , m_xContainer(m_xBuilder->weld_container("DBTreeList"))
498{
499}
500
502{
503}
504
505DBTreeView::DBTreeView(weld::Container* pContainer, bool bSQLType)
506 : DBTreeViewBase(pContainer)
507{
508 m_xTreeListBox.reset(new TreeListBox(m_xBuilder->weld_tree_view("treeview"), bSQLType));
509}
510
511} // namespace dbaui
512
513/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
AnyEventRef aEvent
bool ChildKeyInput(const KeyEvent &rKEvt)
virtual void dispose() override
void InitControlBase(weld::Widget *pWidget)
constexpr tools::Long Y() const
constexpr tools::Long X() const
bool IsActive() const
void Stop()
void SetTimeout(sal_uInt64 nTimeoutMs)
void SetInvokeHandler(const Link< Timer *, void > &rLink)
virtual void Start(bool bStartTimer=true) override
static css::uno::Reference< css::awt::XWindow > GetInterface(vcl::Window *pWindow)
css::uno::XInterface * next()
std::unique_ptr< weld::Builder > m_xBuilder
DBTreeViewBase(weld::Container *pContainer)
std::unique_ptr< TreeListBox > m_xTreeListBox
DBTreeView(weld::Container *pContainer, bool bSQLType)
virtual sal_Int8 queryDrop(const AcceptDropEvent &_rEvt, const DataFlavorExVector &_rFlavors)=0
check whether or not a drop request should be accepted
virtual sal_Int8 executeDrop(const ExecuteDropEvent &_rEvt)=0
execute a drop request
virtual bool includeEntry(const void *pUserData) const =0
virtual bool DoChildKeyInput(const KeyEvent &rKEvt) override
virtual ~InterimDBTreeListBox() override
std::unique_ptr< weld::Label > m_xStatusBar
virtual void dispose() override
InterimDBTreeListBox(vcl::Window *pParent)
virtual sal_Int8 ExecuteDrop(const ExecuteDropEvent &rEvt) override
TreeListBoxDropTarget(TreeListBox &rTreeView)
virtual sal_Int8 AcceptDrop(const AcceptDropEvent &rEvt) override
sal_Int8 AcceptDrop(const AcceptDropEvent &rEvt)
std::unique_ptr< weld::TreeIter > GetEntryPosByName(std::u16string_view rName, const weld::TreeIter *pStart=nullptr, const IEntryFilter *pFilter=nullptr) const
std::unique_ptr< weld::TreeIter > GetRootLevelParent(const weld::TreeIter *pEntry) const
std::unique_ptr< weld::TreeIter > m_xDragedEntry
sal_Int8 ExecuteDrop(const ExecuteDropEvent &rEvt)
virtual bool DoChildKeyInput(const KeyEvent &rKEvt)
TreeListBoxDropTarget m_aDropTargetHelper
IControlActionListener * m_pActionListener
weld::TreeView & GetWidget()
rtl::Reference< TransferDataContainer > m_xHelper
std::unique_ptr< weld::TreeView > m_xTreeView
TreeListBox(std::unique_ptr< weld::TreeView > xTreeView, bool bSQLType)
static void CreateMenuFromActionTriggerContainer(const css::uno::Reference< css::awt::XPopupMenu > &rNewMenu, const css::uno::Reference< css::container::XIndexContainer > &rActionTriggerContainer)
static css::uno::Reference< css::container::XIndexContainer > CreateActionTriggerContainerFromMenu(const css::uno::Reference< css::awt::XPopupMenu > &rMenu, const OUString *pMenuIdentifier)
virtual bool get_dest_row_at_pos(const Point &rPos, weld::TreeIter *pResult, bool bDnDMode, bool bAutoScroll=true)=0
Any m_aSelection
OUString aName
KeyFuncType
Reference< XComponentContext > getProcessComponentContext()
css::beans::PropertyValue makePropertyValue(const OUString &rName, T &&rValue)
IMPL_LINK_NOARG(OApplicationController, OnClipboardChanged, TransferableDataHelper *, void)
IMPL_LINK(OApplicationController, OnSelectContainer, void *, _pType, void)
Reference< XNameAccess > m_xContainer
Definition: objectnames.cxx:80
sal_Int8 mnAction
#define DND_ACTION_MOVE
#define DND_ACTION_COPY
#define DND_ACTION_NONE
unsigned char sal_Bool
signed char sal_Int8