513 lines
17 KiB
C++
513 lines
17 KiB
C++
/* -*- 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 <utility>
|
|
#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, u"dbaccess/ui/dbtreelist.ui"_ustr, u"DBTreeList"_ustr)
|
|
, TreeListBox(m_xBuilder->weld_tree_view(u"treeview"_ustr), true)
|
|
, m_xStatusBar(m_xBuilder->weld_label(u"statusbar"_ustr))
|
|
{
|
|
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( Any _aSelection )
|
|
:m_aSelection(std::move( _aSelection ))
|
|
{
|
|
}
|
|
|
|
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( u"Value"_ustr, aResourceName )),
|
|
css::uno::Any(comphelper::makePropertyValue( u"Frame"_ustr, m_pContextMenuProvider->getCommandController().getXController()->getFrame() )),
|
|
css::uno::Any(comphelper::makePropertyValue( u"IsContextMenu"_ustr, true ))
|
|
};
|
|
|
|
const css::uno::Reference< css::uno::XComponentContext >& xContext = comphelper::getProcessComponentContext();
|
|
css::uno::Reference<css::frame::XPopupMenuController> xMenuController
|
|
(xContext->getServiceManager()->createInstanceWithArgumentsAndContext(
|
|
u"com.sun.star.comp.framework.ResourceMenuController"_ustr, 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, u"dbaccess/ui/dbtreelist.ui"_ustr))
|
|
, m_xContainer(m_xBuilder->weld_container(u"DBTreeList"_ustr))
|
|
{
|
|
}
|
|
|
|
DBTreeViewBase::~DBTreeViewBase()
|
|
{
|
|
}
|
|
|
|
DBTreeView::DBTreeView(weld::Container* pContainer, bool bSQLType)
|
|
: DBTreeViewBase(pContainer)
|
|
{
|
|
m_xTreeListBox.reset(new TreeListBox(m_xBuilder->weld_tree_view(u"treeview"_ustr), bSQLType));
|
|
}
|
|
|
|
} // namespace dbaui
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|