summaryrefslogtreecommitdiffstats
path: root/dbaccess/source/ui/control/dbtreelistbox.cxx
diff options
context:
space:
mode:
Diffstat (limited to 'dbaccess/source/ui/control/dbtreelistbox.cxx')
-rw-r--r--dbaccess/source/ui/control/dbtreelistbox.cxx512
1 files changed, 512 insertions, 0 deletions
diff --git a/dbaccess/source/ui/control/dbtreelistbox.cxx b/dbaccess/source/ui/control/dbtreelistbox.cxx
new file mode 100644
index 000000000..eb89e1fdb
--- /dev/null
+++ b/dbaccess/source/ui/control/dbtreelistbox.cxx
@@ -0,0 +1,512 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <dbtreelistbox.hxx>
+#include <dbexchange.hxx>
+#include <callbacks.hxx>
+
+#include <com/sun/star/awt/PopupMenuDirection.hpp>
+#include <com/sun/star/ui/XContextMenuInterceptor.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/frame/XPopupMenuController.hpp>
+#include <com/sun/star/lang/IllegalArgumentException.hpp>
+#include <cppuhelper/implbase.hxx>
+#include <comphelper/interfacecontainer2.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <dbaccess/IController.hxx>
+#include <framework/actiontriggerhelper.hxx>
+#include <toolkit/awt/vclxmenu.hxx>
+#include <toolkit/helper/vclunohelper.hxx>
+#include <svx/dbaobjectex.hxx>
+#include <vcl/commandevent.hxx>
+#include <vcl/event.hxx>
+#include <vcl/svapp.hxx>
+
+#include <memory>
+
+namespace dbaui
+{
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::datatransfer;
+using namespace ::com::sun::star::ui;
+using namespace ::com::sun::star::view;
+
+InterimDBTreeListBox::InterimDBTreeListBox(vcl::Window* pParent)
+ : InterimItemWindow(pParent, "dbaccess/ui/dbtreelist.ui", "DBTreeList")
+ , TreeListBox(m_xBuilder->weld_tree_view("treeview"), true)
+ , m_xStatusBar(m_xBuilder->weld_label("statusbar"))
+{
+ InitControlBase(&GetWidget());
+}
+
+InterimDBTreeListBox::~InterimDBTreeListBox()
+{
+ disposeOnce();
+}
+
+void InterimDBTreeListBox::dispose()
+{
+ implStopSelectionTimer();
+ m_xStatusBar.reset();
+ m_xTreeView.reset();
+ InterimItemWindow::dispose();
+}
+
+bool InterimDBTreeListBox::DoChildKeyInput(const KeyEvent& rKEvt)
+{
+ return ChildKeyInput(rKEvt);
+}
+
+TreeListBoxDropTarget::TreeListBoxDropTarget(TreeListBox& rTreeView)
+ : DropTargetHelper(rTreeView.GetWidget().get_drop_target())
+ , m_rTreeView(rTreeView)
+{
+}
+
+sal_Int8 TreeListBoxDropTarget::AcceptDrop(const AcceptDropEvent& rEvt)
+{
+ sal_Int8 nAccept = m_rTreeView.AcceptDrop(rEvt);
+
+ if (nAccept != DND_ACTION_NONE)
+ {
+ // to enable the autoscroll when we're close to the edges
+ weld::TreeView& rWidget = m_rTreeView.GetWidget();
+ rWidget.get_dest_row_at_pos(rEvt.maPosPixel, nullptr, true);
+ }
+
+ return nAccept;
+}
+
+sal_Int8 TreeListBoxDropTarget::ExecuteDrop(const ExecuteDropEvent& rEvt)
+{
+ return m_rTreeView.ExecuteDrop(rEvt);
+}
+
+TreeListBox::TreeListBox(std::unique_ptr<weld::TreeView> xTreeView, bool bSQLType)
+ : m_xTreeView(std::move(xTreeView))
+ , m_aDropTargetHelper(*this)
+ , m_pActionListener(nullptr)
+ , m_pContextMenuProvider(nullptr)
+ , m_aTimer("dbaccess TreeListBox m_aTimer")
+{
+ m_xTreeView->connect_key_press(LINK(this, TreeListBox, KeyInputHdl));
+ m_xTreeView->connect_changed(LINK(this, TreeListBox, SelectHdl));
+ m_xTreeView->connect_query_tooltip(LINK(this, TreeListBox, QueryTooltipHdl));
+ m_xTreeView->connect_popup_menu(LINK(this, TreeListBox, CommandHdl));
+
+ if (bSQLType)
+ m_xHelper.set(new ODataClipboard);
+ else
+ m_xHelper.set(new svx::OComponentTransferable);
+ m_xTreeView->enable_drag_source(m_xHelper, DND_ACTION_COPY);
+ m_xTreeView->connect_drag_begin(LINK(this, TreeListBox, DragBeginHdl));
+
+ m_aTimer.SetTimeout(900);
+ m_aTimer.SetInvokeHandler(LINK(this, TreeListBox, OnTimeOut));
+}
+
+bool TreeListBox::DoChildKeyInput(const KeyEvent& /*rKEvt*/)
+{
+ // nothing by default
+ return false;
+}
+
+IMPL_LINK(TreeListBox, KeyInputHdl, const KeyEvent&, rKEvt, bool)
+{
+ KeyFuncType eFunc = rKEvt.GetKeyCode().GetFunction();
+ bool bHandled = false;
+
+ switch (eFunc)
+ {
+ case KeyFuncType::COPY:
+ bHandled = m_aCopyHandler.IsSet() && !m_xTreeView->get_selected(nullptr);
+ if (bHandled)
+ m_aCopyHandler.Call(nullptr);
+ break;
+ case KeyFuncType::PASTE:
+ bHandled = m_aPasteHandler.IsSet() && !m_xTreeView->get_selected(nullptr);
+ if (bHandled)
+ m_aPasteHandler.Call(nullptr);
+ break;
+ case KeyFuncType::DELETE:
+ bHandled = m_aDeleteHandler.IsSet() && !m_xTreeView->get_selected(nullptr);
+ if (bHandled)
+ m_aDeleteHandler.Call(nullptr);
+ break;
+ default:
+ break;
+ }
+
+ return bHandled || DoChildKeyInput(rKEvt);
+}
+
+void TreeListBox::implStopSelectionTimer()
+{
+ if ( m_aTimer.IsActive() )
+ m_aTimer.Stop();
+}
+
+void TreeListBox::implStartSelectionTimer()
+{
+ implStopSelectionTimer();
+ m_aTimer.Start();
+}
+
+IMPL_LINK_NOARG(TreeListBox, SelectHdl, weld::TreeView&, void)
+{
+ implStartSelectionTimer();
+}
+
+TreeListBox::~TreeListBox()
+{
+}
+
+std::unique_ptr<weld::TreeIter> TreeListBox::GetEntryPosByName(std::u16string_view aName, const weld::TreeIter* pStart, const IEntryFilter* _pFilter) const
+{
+ auto xEntry(m_xTreeView->make_iterator(pStart));
+ if (pStart)
+ {
+ if (!m_xTreeView->iter_children(*xEntry))
+ return nullptr;
+ }
+ else
+ {
+ if (!m_xTreeView->get_iter_first(*xEntry))
+ return nullptr;
+ }
+
+ do
+ {
+ if (m_xTreeView->get_text(*xEntry) == aName)
+ {
+ if (!_pFilter || _pFilter->includeEntry(weld::fromId<void*>(m_xTreeView->get_id(*xEntry))))
+ {
+ // found
+ return xEntry;
+ }
+ }
+ } while (m_xTreeView->iter_next_sibling(*xEntry));
+
+ return nullptr;
+}
+
+IMPL_LINK(TreeListBox, DragBeginHdl, bool&, rUnsetDragIcon, bool)
+{
+ rUnsetDragIcon = false;
+
+ if (m_pActionListener)
+ {
+ m_xDragedEntry = m_xTreeView->make_iterator();
+ if (!m_xTreeView->get_selected(m_xDragedEntry.get()))
+ m_xDragedEntry.reset();
+ if (m_xDragedEntry && m_pActionListener->requestDrag(*m_xDragedEntry))
+ {
+ // if the (asynchronous) drag started, stop the selection timer
+ implStopSelectionTimer();
+ return false;
+ }
+ }
+
+ return true;
+}
+
+sal_Int8 TreeListBox::AcceptDrop(const AcceptDropEvent& rEvt)
+{
+ sal_Int8 nDropOption = DND_ACTION_NONE;
+ if ( m_pActionListener )
+ {
+ ::Point aDropPos = rEvt.maPosPixel;
+ std::unique_ptr<weld::TreeIter> xDropTarget(m_xTreeView->make_iterator());
+ if (!m_xTreeView->get_dest_row_at_pos(aDropPos, xDropTarget.get(), true))
+ xDropTarget.reset();
+
+ // check if drag is on child entry, which is not allowed
+ std::unique_ptr<weld::TreeIter> xParent;
+ if (rEvt.mnAction & DND_ACTION_MOVE)
+ {
+ if (!m_xDragedEntry) // no entry to move
+ return m_pActionListener->queryDrop(rEvt, m_aDropTargetHelper.GetDataFlavorExVector());
+
+ if (xDropTarget)
+ {
+ xParent = m_xTreeView->make_iterator(xDropTarget.get());
+ if (!m_xTreeView->iter_parent(*xParent))
+ xParent.reset();
+ }
+ while (xParent && m_xTreeView->iter_compare(*xParent, *m_xDragedEntry) != 0)
+ {
+ if (!m_xTreeView->iter_parent(*xParent))
+ xParent.reset();
+ }
+ }
+
+ if (!xParent)
+ {
+ nDropOption = m_pActionListener->queryDrop(rEvt, m_aDropTargetHelper.GetDataFlavorExVector());
+ // check if move is allowed
+ if ( nDropOption & DND_ACTION_MOVE )
+ {
+ if (!m_xDragedEntry || !xDropTarget ||
+ m_xTreeView->iter_compare(*m_xDragedEntry, *xDropTarget) == 0 ||
+ GetEntryPosByName(m_xTreeView->get_text(*m_xDragedEntry), xDropTarget.get()))
+ {
+ nDropOption = nDropOption & ~DND_ACTION_MOVE;//DND_ACTION_NONE;
+ }
+ }
+ }
+ }
+
+ return nDropOption;
+}
+
+sal_Int8 TreeListBox::ExecuteDrop(const ExecuteDropEvent& rEvt)
+{
+ if (m_pActionListener)
+ m_pActionListener->executeDrop(rEvt);
+ m_xTreeView->unset_drag_dest_row();
+ return DND_ACTION_NONE;
+}
+
+IMPL_LINK(TreeListBox, QueryTooltipHdl, const weld::TreeIter&, rIter, OUString)
+{
+ OUString sQuickHelpText;
+ if (m_pActionListener &&
+ m_pActionListener->requestQuickHelp(weld::fromId<void*>(m_xTreeView->get_id(rIter)), sQuickHelpText))
+ {
+ return sQuickHelpText;
+ }
+ return m_xTreeView->get_tooltip_text();
+}
+
+namespace
+{
+ // SelectionSupplier
+ typedef ::cppu::WeakImplHelper< XSelectionSupplier
+ > SelectionSupplier_Base;
+ class SelectionSupplier : public SelectionSupplier_Base
+ {
+ public:
+ explicit SelectionSupplier( const Any& _rSelection )
+ :m_aSelection( _rSelection )
+ {
+ }
+
+ virtual sal_Bool SAL_CALL select( const Any& xSelection ) override;
+ virtual Any SAL_CALL getSelection( ) override;
+ virtual void SAL_CALL addSelectionChangeListener( const Reference< XSelectionChangeListener >& xListener ) override;
+ virtual void SAL_CALL removeSelectionChangeListener( const Reference< XSelectionChangeListener >& xListener ) override;
+
+ protected:
+ virtual ~SelectionSupplier() override
+ {
+ }
+
+ private:
+ Any m_aSelection;
+ };
+
+ sal_Bool SAL_CALL SelectionSupplier::select( const Any& /*_Selection*/ )
+ {
+ throw IllegalArgumentException();
+ // API bug: this should be a NoSupportException
+ }
+
+ Any SAL_CALL SelectionSupplier::getSelection( )
+ {
+ return m_aSelection;
+ }
+
+ void SAL_CALL SelectionSupplier::addSelectionChangeListener( const Reference< XSelectionChangeListener >& /*_Listener*/ )
+ {
+ OSL_FAIL( "SelectionSupplier::removeSelectionChangeListener: no support!" );
+ // API bug: this should be a NoSupportException
+ }
+
+ void SAL_CALL SelectionSupplier::removeSelectionChangeListener( const Reference< XSelectionChangeListener >& /*_Listener*/ )
+ {
+ OSL_FAIL( "SelectionSupplier::removeSelectionChangeListener: no support!" );
+ // API bug: this should be a NoSupportException
+ }
+}
+
+IMPL_LINK(TreeListBox, CommandHdl, const CommandEvent&, rCEvt, bool)
+{
+ if (rCEvt.GetCommand() != CommandEventId::ContextMenu)
+ return false;
+
+ ::Point aPos = rCEvt.GetMousePosPixel();
+
+ std::unique_ptr<weld::TreeIter> xIter(m_xTreeView->make_iterator());
+ if (m_xTreeView->get_dest_row_at_pos(aPos, xIter.get(), false) && !m_xTreeView->is_selected(*xIter))
+ {
+ m_xTreeView->unselect_all();
+ m_xTreeView->set_cursor(*xIter);
+ m_xTreeView->select(*xIter);
+ SelectHdl(*m_xTreeView);
+ }
+
+ if (!m_pContextMenuProvider)
+ return false;
+
+ OUString aResourceName(m_pContextMenuProvider->getContextMenuResourceName());
+ if (aResourceName.isEmpty())
+ return false;
+
+ css::uno::Sequence< css::uno::Any > aArgs{
+ css::uno::Any(comphelper::makePropertyValue( "Value", aResourceName )),
+ css::uno::Any(comphelper::makePropertyValue( "Frame", m_pContextMenuProvider->getCommandController().getXController()->getFrame() )),
+ css::uno::Any(comphelper::makePropertyValue( "IsContextMenu", true ))
+ };
+
+ css::uno::Reference< css::uno::XComponentContext > xContext = comphelper::getProcessComponentContext();
+ css::uno::Reference<css::frame::XPopupMenuController> xMenuController
+ (xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
+ "com.sun.star.comp.framework.ResourceMenuController", aArgs, xContext), css::uno::UNO_QUERY);
+
+ if (!xMenuController.is())
+ return false;
+
+ VclPtr<vcl::Window> xMenuParent = m_pContextMenuProvider->getMenuParent();
+
+ css::uno::Reference< css::awt::XWindow> xSourceWindow = VCLUnoHelper::GetInterface(xMenuParent);
+
+ rtl::Reference xPopupMenu( new VCLXPopupMenu );
+ xMenuController->setPopupMenu( xPopupMenu );
+
+ // allow context menu interception
+ ::comphelper::OInterfaceContainerHelper2* pInterceptors = m_pContextMenuProvider->getContextMenuInterceptors();
+ if (pInterceptors && pInterceptors->getLength())
+ {
+ OUString aMenuIdentifier( "private:resource/popupmenu/" + aResourceName );
+
+ ContextMenuExecuteEvent aEvent;
+ aEvent.SourceWindow = xSourceWindow;
+ aEvent.ExecutePosition.X = -1;
+ aEvent.ExecutePosition.Y = -1;
+ aEvent.ActionTriggerContainer = ::framework::ActionTriggerHelper::CreateActionTriggerContainerFromMenu(
+ xPopupMenu, &aMenuIdentifier );
+ aEvent.Selection = new SelectionSupplier(m_pContextMenuProvider->getCurrentSelection(*m_xTreeView));
+
+ ::comphelper::OInterfaceIteratorHelper2 aIter( *pInterceptors );
+ bool bModifiedMenu = false;
+ bool bAskInterceptors = true;
+ while ( aIter.hasMoreElements() && bAskInterceptors )
+ {
+ Reference< XContextMenuInterceptor > xInterceptor( aIter.next(), UNO_QUERY );
+ if ( !xInterceptor.is() )
+ continue;
+
+ try
+ {
+ ContextMenuInterceptorAction eAction = xInterceptor->notifyContextMenuExecute( aEvent );
+ switch ( eAction )
+ {
+ case ContextMenuInterceptorAction_CANCELLED:
+ return false;
+
+ case ContextMenuInterceptorAction_EXECUTE_MODIFIED:
+ bModifiedMenu = true;
+ bAskInterceptors = false;
+ break;
+
+ case ContextMenuInterceptorAction_CONTINUE_MODIFIED:
+ bModifiedMenu = true;
+ bAskInterceptors = true;
+ break;
+
+ default:
+ OSL_FAIL( "DBTreeListBox::CreateContextMenu: unexpected return value of the interceptor call!" );
+ [[fallthrough]];
+ case ContextMenuInterceptorAction_IGNORED:
+ break;
+ }
+ }
+ catch( const DisposedException& e )
+ {
+ if ( e.Context == xInterceptor )
+ aIter.remove();
+ }
+ }
+
+ if ( bModifiedMenu )
+ {
+ xPopupMenu->clear();
+ ::framework::ActionTriggerHelper::CreateMenuFromActionTriggerContainer(
+ xPopupMenu, aEvent.ActionTriggerContainer );
+ aEvent.ActionTriggerContainer.clear();
+ }
+ }
+
+ // adjust pos relative to m_xTreeView to relative to xMenuParent
+ m_pContextMenuProvider->adjustMenuPosition(*m_xTreeView, aPos);
+
+ // do action for selected entry in popup menu
+ css::uno::Reference<css::awt::XWindowPeer> xParent(xSourceWindow, css::uno::UNO_QUERY);
+ xPopupMenu->execute(xParent, css::awt::Rectangle(aPos.X(), aPos.Y(), 1, 1), css::awt::PopupMenuDirection::EXECUTE_DOWN);
+
+ css::uno::Reference<css::lang::XComponent> xComponent(xMenuController, css::uno::UNO_QUERY);
+ if (xComponent.is())
+ xComponent->dispose();
+ xMenuController.clear();
+
+ return true;
+}
+
+IMPL_LINK_NOARG(TreeListBox, OnTimeOut, Timer*, void)
+{
+ implStopSelectionTimer();
+
+ m_aSelChangeHdl.Call( nullptr );
+}
+
+std::unique_ptr<weld::TreeIter> TreeListBox::GetRootLevelParent(const weld::TreeIter* pEntry) const
+{
+ if (!pEntry)
+ return nullptr;
+ std::unique_ptr<weld::TreeIter> xEntry(m_xTreeView->make_iterator(pEntry));
+ while (m_xTreeView->get_iter_depth(*xEntry))
+ m_xTreeView->iter_parent(*xEntry);
+ return xEntry;
+}
+
+DBTreeViewBase::DBTreeViewBase(weld::Container* pContainer)
+ : m_xBuilder(Application::CreateBuilder(pContainer, "dbaccess/ui/dbtreelist.ui"))
+ , m_xContainer(m_xBuilder->weld_container("DBTreeList"))
+{
+}
+
+DBTreeViewBase::~DBTreeViewBase()
+{
+}
+
+DBTreeView::DBTreeView(weld::Container* pContainer, bool bSQLType)
+ : DBTreeViewBase(pContainer)
+{
+ m_xTreeListBox.reset(new TreeListBox(m_xBuilder->weld_tree_view("treeview"), bSQLType));
+}
+
+} // namespace dbaui
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */