/* -*- 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: */
↑ V1048 The 'bAskInterceptors' variable was assigned the same value.