diff options
Diffstat (limited to 'reportdesign/source/ui/dlg')
-rw-r--r-- | reportdesign/source/ui/dlg/AddField.cxx | 352 | ||||
-rw-r--r-- | reportdesign/source/ui/dlg/CondFormat.cxx | 449 | ||||
-rw-r--r-- | reportdesign/source/ui/dlg/Condition.cxx | 379 | ||||
-rw-r--r-- | reportdesign/source/ui/dlg/Condition.hxx | 183 | ||||
-rw-r--r-- | reportdesign/source/ui/dlg/DateTime.cxx | 209 | ||||
-rw-r--r-- | reportdesign/source/ui/dlg/Formula.cxx | 275 | ||||
-rw-r--r-- | reportdesign/source/ui/dlg/GroupExchange.cxx | 68 | ||||
-rw-r--r-- | reportdesign/source/ui/dlg/GroupExchange.hxx | 43 | ||||
-rw-r--r-- | reportdesign/source/ui/dlg/GroupsSorting.cxx | 1181 | ||||
-rw-r--r-- | reportdesign/source/ui/dlg/Navigator.cxx | 814 | ||||
-rw-r--r-- | reportdesign/source/ui/dlg/PageNumber.cxx | 104 | ||||
-rw-r--r-- | reportdesign/source/ui/dlg/dlgpage.cxx | 78 |
12 files changed, 4135 insertions, 0 deletions
diff --git a/reportdesign/source/ui/dlg/AddField.cxx b/reportdesign/source/ui/dlg/AddField.cxx new file mode 100644 index 0000000000..631a7f7d90 --- /dev/null +++ b/reportdesign/source/ui/dlg/AddField.cxx @@ -0,0 +1,352 @@ +/* -*- 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 <AddField.hxx> +#include <UITools.hxx> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdb/XDocumentDataSource.hpp> + +#include <utility> +#include <vcl/svapp.hxx> + +#include <comphelper/diagnose_ex.hxx> + +#include <connectivity/dbtools.hxx> +#include <core_resource.hxx> +#include <helpids.h> +#include <strings.hrc> +#include <strings.hxx> + +#include <comphelper/sequence.hxx> + +namespace rptui +{ + +using namespace ::com::sun::star; +using namespace sdbc; +using namespace sdb; +using namespace uno; +using namespace datatransfer; +using namespace beans; +using namespace lang; +using namespace container; +using namespace ::svx; + +IMPL_LINK(OAddFieldWindow, DragBeginHdl, bool&, rUnsetDragIcon, bool) +{ + rUnsetDragIcon = false; + if (m_xListBox->get_selected_index() == -1) + { + // no drag without a field + return true; + } + + m_xHelper->setDescriptors(getSelectedFieldDescriptors()); + return false; +} + +OAddFieldWindow::OAddFieldWindow(weld::Window* pParent, uno::Reference< beans::XPropertySet > xRowSet) + : GenericDialogController(pParent, "modules/dbreport/ui/floatingfield.ui", "FloatingField") + , ::comphelper::OPropertyChangeListener(m_aMutex) + , ::comphelper::OContainerListener(m_aMutex) + , m_xRowSet(std::move(xRowSet)) + , m_xActions(m_xBuilder->weld_toolbar("toolbox")) + , m_xListBox(m_xBuilder->weld_tree_view("treeview")) + , m_nCommandType(0) + , m_bEscapeProcessing(false) +{ + m_xListBox->set_help_id(HID_RPT_FIELD_SEL); + m_xListBox->set_selection_mode(SelectionMode::Multiple); + m_xHelper.set(new svx::OMultiColumnTransferable); + rtl::Reference<TransferDataContainer> xHelper(m_xHelper); + m_xListBox->enable_drag_source(xHelper, DND_ACTION_COPYMOVE | DND_ACTION_LINK); + m_xListBox->connect_drag_begin(LINK(this, OAddFieldWindow, DragBeginHdl)); + + m_xDialog->connect_container_focus_changed(LINK(this, OAddFieldWindow, FocusChangeHdl)); + + m_xDialog->set_help_id(HID_RPT_FIELD_SEL_WIN); + + m_xActions->connect_clicked(LINK(this, OAddFieldWindow, OnSortAction)); + m_xActions->set_item_active("up", true); + m_xListBox->make_sorted(); + m_xActions->set_item_sensitive("insert", false); + + m_xListBox->connect_row_activated(LINK( this, OAddFieldWindow, OnDoubleClickHdl ) ); + m_xListBox->connect_changed(LINK( this, OAddFieldWindow, OnSelectHdl ) ); + m_xListBox->set_size_request(m_xListBox->get_approximate_digit_width() * 45, m_xListBox->get_height_rows(8)); + + if (!m_xRowSet.is()) + return; + + try + { + // be notified when the settings of report definition change + m_pChangeListener = new ::comphelper::OPropertyChangeMultiplexer( this, m_xRowSet ); + m_pChangeListener->addProperty( PROPERTY_COMMAND ); + m_pChangeListener->addProperty( PROPERTY_COMMANDTYPE ); + m_pChangeListener->addProperty( PROPERTY_ESCAPEPROCESSING ); + m_pChangeListener->addProperty( PROPERTY_FILTER ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("reportdesign"); + } +} + +OAddFieldWindow::~OAddFieldWindow() +{ + m_aListBoxData.clear(); + if (m_pChangeListener.is()) + m_pChangeListener->dispose(); + if ( m_pContainerListener.is() ) + m_pContainerListener->dispose(); +} + +IMPL_LINK_NOARG(OAddFieldWindow, FocusChangeHdl, weld::Container&, void) +{ + if (m_xDialog->has_toplevel_focus()) + m_xListBox->grab_focus(); +} + +uno::Sequence< beans::PropertyValue > OAddFieldWindow::getSelectedFieldDescriptors() +{ + std::vector<beans::PropertyValue> aArgs; + + m_xListBox->selected_foreach([this, &aArgs](weld::TreeIter& rEntry){ + // build a descriptor for the currently selected field + svx::ODataAccessDescriptor aDescriptor; + fillDescriptor(rEntry, aDescriptor); + aArgs.push_back(beans::PropertyValue()); + aArgs.back().Value <<= aDescriptor.createPropertyValueSequence(); + + return false; + }); + + return comphelper::containerToSequence(aArgs); +} + +void OAddFieldWindow::_propertyChanged( const beans::PropertyChangeEvent& _evt ) +{ + OSL_ENSURE( _evt.Source == m_xRowSet, "OAddFieldWindow::_propertyChanged: where did this come from?" ); + Update(); +} + +void OAddFieldWindow::addToList(const uno::Sequence< OUString >& rEntries) +{ + for (const OUString& rEntry : rEntries) + { + m_aListBoxData.emplace_back(new ColumnInfo(rEntry)); + OUString sId(weld::toId(m_aListBoxData.back().get())); + m_xListBox->append(sId, rEntry); + } +} + +void OAddFieldWindow::addToList(const uno::Reference< container::XNameAccess>& i_xColumns) +{ + const uno::Sequence< OUString > aEntries = i_xColumns->getElementNames(); + for ( const OUString& rEntry : aEntries ) + { + uno::Reference< beans::XPropertySet> xColumn(i_xColumns->getByName(rEntry),UNO_QUERY_THROW); + OUString sLabel; + if ( xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_LABEL) ) + xColumn->getPropertyValue(PROPERTY_LABEL) >>= sLabel; + m_aListBoxData.emplace_back(new ColumnInfo(rEntry, sLabel)); + OUString sId(weld::toId(m_aListBoxData.back().get())); + if ( !sLabel.isEmpty() ) + m_xListBox->append(sId, sLabel); + else + m_xListBox->append(sId, rEntry); + } +} + +void OAddFieldWindow::Update() +{ + SolarMutexGuard aSolarGuard; + + if ( m_pContainerListener.is() ) + m_pContainerListener->dispose(); + m_pContainerListener = nullptr; + m_xColumns.clear(); + + try + { + // ListBox loeschen + m_xListBox->clear(); + m_aListBoxData.clear(); + const OUString aIds[] = { "up", "down" }; + for (size_t j = 0; j< std::size(aIds); ++j) + m_xActions->set_item_sensitive(aIds[j], false); + + OUString aTitle(RptResId(RID_STR_FIELDSELECTION)); + m_xDialog->set_title(aTitle); + if ( m_xRowSet.is() ) + { + OUString sCommand( m_aCommandName ); + sal_Int32 nCommandType( m_nCommandType ); + bool bEscapeProcessing( m_bEscapeProcessing ); + OUString sFilter( m_sFilter ); + + OSL_VERIFY( m_xRowSet->getPropertyValue( PROPERTY_COMMAND ) >>= sCommand ); + OSL_VERIFY( m_xRowSet->getPropertyValue( PROPERTY_COMMANDTYPE ) >>= nCommandType ); + OSL_VERIFY( m_xRowSet->getPropertyValue( PROPERTY_ESCAPEPROCESSING ) >>= bEscapeProcessing ); + OSL_VERIFY( m_xRowSet->getPropertyValue( PROPERTY_FILTER ) >>= sFilter ); + + m_aCommandName = sCommand; + m_nCommandType = nCommandType; + m_bEscapeProcessing = bEscapeProcessing; + m_sFilter = sFilter; + + // add the columns to the list + uno::Reference< sdbc::XConnection> xCon = getConnection(); + if ( xCon.is() && !m_aCommandName.isEmpty() ) + m_xColumns = dbtools::getFieldsByCommandDescriptor( xCon, GetCommandType(), GetCommand(), m_xHoldAlive ); + if ( m_xColumns.is() ) + { + addToList(m_xColumns); + uno::Reference< container::XContainer> xContainer(m_xColumns,uno::UNO_QUERY); + if ( xContainer.is() ) + m_pContainerListener = new ::comphelper::OContainerListenerAdapter(this,xContainer); + } + + // add the parameter columns to the list + uno::Reference< css::sdbc::XRowSet > xRowSet(m_xRowSet,uno::UNO_QUERY); + Sequence< OUString > aParamNames( getParameterNames( xRowSet ) ); + addToList(aParamNames); + + // set title + aTitle += " " + m_aCommandName; + m_xDialog->set_title(aTitle); + if ( !m_aCommandName.isEmpty() ) + { + for (size_t i = 0; i < std::size(aIds); ++i) + m_xActions->set_item_sensitive(aIds[i], true); + } + OnSelectHdl(*m_xListBox); + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("reportdesign"); + } +} + +uno::Reference< sdbc::XConnection> OAddFieldWindow::getConnection() const +{ + return uno::Reference< sdbc::XConnection>(m_xRowSet->getPropertyValue( PROPERTY_ACTIVECONNECTION ),uno::UNO_QUERY); +} + +void OAddFieldWindow::fillDescriptor(const weld::TreeIter& rSelected, svx::ODataAccessDescriptor& rDescriptor) +{ + if (!m_xColumns.is()) + return; + + uno::Reference<container::XChild> xChild(getConnection(),uno::UNO_QUERY); + if ( xChild.is( ) ) + { + uno::Reference<sdb::XDocumentDataSource> xDocument( xChild->getParent(), uno::UNO_QUERY ); + if ( xDocument.is() ) + { + uno::Reference<frame::XModel> xModel(xDocument->getDatabaseDocument(),uno::UNO_QUERY); + if ( xModel.is() ) + rDescriptor[ DataAccessDescriptorProperty::DatabaseLocation ] <<= xModel->getURL(); + } + } + + rDescriptor[ svx::DataAccessDescriptorProperty::Command ] <<= GetCommand(); + rDescriptor[ svx::DataAccessDescriptorProperty::CommandType ] <<= GetCommandType(); + rDescriptor[ svx::DataAccessDescriptorProperty::EscapeProcessing ] <<= m_bEscapeProcessing; + rDescriptor[ svx::DataAccessDescriptorProperty::Connection ] <<= getConnection(); + + ColumnInfo* pInfo = weld::fromId<ColumnInfo*>(m_xListBox->get_id(rSelected)); + rDescriptor[ svx::DataAccessDescriptorProperty::ColumnName ] <<= pInfo->sColumnName; + if ( m_xColumns->hasByName( pInfo->sColumnName ) ) + rDescriptor[ svx::DataAccessDescriptorProperty::ColumnObject ] = m_xColumns->getByName(pInfo->sColumnName); +} + +void OAddFieldWindow::_elementInserted( const container::ContainerEvent& _rEvent ) +{ + OUString sName; + if ( !((_rEvent.Accessor >>= sName) && m_xColumns->hasByName(sName)) ) + return; + + uno::Reference< beans::XPropertySet> xColumn(m_xColumns->getByName(sName),UNO_QUERY_THROW); + OUString sLabel; + if ( xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_LABEL) ) + xColumn->getPropertyValue(PROPERTY_LABEL) >>= sLabel; + m_aListBoxData.emplace_back(new ColumnInfo(sName, sLabel)); + OUString sId(weld::toId(m_aListBoxData.back().get())); + if (!sLabel.isEmpty()) + m_xListBox->append(sId, sLabel); + else + m_xListBox->append(sId, sName); +} + +void OAddFieldWindow::_elementRemoved( const container::ContainerEvent& /*_rEvent*/ ) +{ + m_xListBox->clear(); + m_aListBoxData.clear(); + if ( m_xColumns.is() ) + addToList(m_xColumns); +} + +void OAddFieldWindow::_elementReplaced( const container::ContainerEvent& /*_rEvent*/ ) +{ +} + +IMPL_LINK_NOARG( OAddFieldWindow, OnSelectHdl, weld::TreeView&, void ) +{ + m_xActions->set_item_sensitive("insert", m_xListBox->get_selected_index() != -1); +} + +IMPL_LINK_NOARG( OAddFieldWindow, OnDoubleClickHdl, weld::TreeView&, bool ) +{ + m_aCreateLink.Call(*this); + return true; +} + +IMPL_LINK(OAddFieldWindow, OnSortAction, const OUString&, rCurItem, void) +{ + if (rCurItem == "insert") + { + OnDoubleClickHdl(*m_xListBox); + return; + } + + const OUString aIds[] = { "up", "down" }; + + if (rCurItem == "delete") + { + for (size_t j = 0; j< std::size(aIds); ++j) + m_xActions->set_item_active(aIds[j], false); + + m_xListBox->make_unsorted(); + Update(); + return; + } + + for (size_t j = 0; j< std::size(aIds); ++j) + m_xActions->set_item_active(aIds[j], rCurItem == aIds[j]); + + m_xListBox->make_sorted(); + if (m_xActions->get_item_active("down")) + m_xListBox->set_sort_order(false); +} + +} // namespace rptui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/reportdesign/source/ui/dlg/CondFormat.cxx b/reportdesign/source/ui/dlg/CondFormat.cxx new file mode 100644 index 0000000000..41173c9323 --- /dev/null +++ b/reportdesign/source/ui/dlg/CondFormat.cxx @@ -0,0 +1,449 @@ +/* -*- 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 <CondFormat.hxx> + +#include <strings.hxx> +#include <strings.hrc> +#include <core_resource.hxx> +#include <ReportController.hxx> +#include "Condition.hxx" + +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <o3tl/safeint.hxx> +#include <toolkit/helper/vclunohelper.hxx> + +#include <comphelper/diagnose_ex.hxx> + +#include <comphelper/property.hxx> +#include <comphelper/propertyvalue.hxx> + +#include <algorithm> +#include <UndoActions.hxx> + + +namespace rptui +{ + + + using ::com::sun::star::uno::Reference; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::uno::UNO_QUERY; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::lang::IllegalArgumentException; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::beans::PropertyValue; + using ::com::sun::star::uno::Any; + + using namespace ::com::sun::star::report; + + void ConditionalFormattingDialog::impl_setPrefHeight(bool bFirst) + { + if (!m_bConstructed && !bFirst) + return; + + //allow dialog to resize itself + size_t nCount = impl_getConditionCount(); + if (!nCount) + return; + + auto nHeight = m_aConditions[0]->get_preferred_size().Height(); + size_t nVisibleConditions = ::std::min(nCount, MAX_CONDITIONS); + nHeight *= nVisibleConditions; + nHeight += 2; + if (nHeight != m_xScrollWindow->get_size_request().Height()) + { + m_xScrollWindow->set_size_request(-1, nHeight); + if (!bFirst) + m_xDialog->resize_to_request(); + } + } + + ConditionalFormattingDialog::ConditionalFormattingDialog( + weld::Window* _pParent, const Reference< XReportControlModel >& _rxFormatConditions, ::rptui::OReportController& _rController) + : GenericDialogController(_pParent, "modules/dbreport/ui/condformatdialog.ui", "CondFormat") + , m_rController(_rController) + , m_xFormatConditions(_rxFormatConditions) + , m_bConstructed(false) + , m_xScrollWindow(m_xBuilder->weld_scrolled_window("scrolledwindow")) + , m_xConditionPlayground(m_xBuilder->weld_box("condPlaygroundDrawingarea")) + { + OSL_ENSURE( m_xFormatConditions.is(), "ConditionalFormattingDialog::ConditionalFormattingDialog: ReportControlModel is NULL -> Prepare for GPF!" ); + + m_xCopy.set( m_xFormatConditions->createClone(), UNO_QUERY_THROW ); + + m_xScrollWindow->connect_vadjustment_changed(LINK(this, ConditionalFormattingDialog, OnScroll)); + + impl_initializeConditions(); + + impl_setPrefHeight(true); + + m_bConstructed = true; + } + + ConditionalFormattingDialog::~ConditionalFormattingDialog() + { + } + + void ConditionalFormattingDialog::impl_updateConditionIndicies() + { + sal_Int32 nIndex = 0; + for (const auto& rxCondition : m_aConditions) + { + rxCondition->setConditionIndex( nIndex, impl_getConditionCount() ); + m_xConditionPlayground->reorder_child(rxCondition->get_widget(), nIndex); + ++nIndex; + } + } + + void ConditionalFormattingDialog::impl_conditionCountChanged() + { + if ( m_aConditions.empty() ) + impl_addCondition_nothrow( 0 ); + + impl_setPrefHeight(false); + + impl_updateConditionIndicies(); + impl_layoutAll(); + } + + void ConditionalFormattingDialog::addCondition( size_t _nAddAfterIndex ) + { + OSL_PRECOND( _nAddAfterIndex < impl_getConditionCount(), "ConditionalFormattingDialog::addCondition: illegal condition index!" ); + impl_addCondition_nothrow( _nAddAfterIndex + 1 ); + } + + + void ConditionalFormattingDialog::deleteCondition( size_t _nCondIndex ) + { + impl_deleteCondition_nothrow( _nCondIndex ); + } + + + void ConditionalFormattingDialog::impl_addCondition_nothrow( size_t _nNewCondIndex ) + { + try + { + if ( _nNewCondIndex > o3tl::make_unsigned(m_xCopy->getCount()) ) + throw IllegalArgumentException(); + + Reference< XFormatCondition > xCond = m_xCopy->createFormatCondition(); + ::comphelper::copyProperties(m_xCopy, xCond); + m_xCopy->insertByIndex( _nNewCondIndex, Any( xCond ) ); + auto xCon = std::make_unique<Condition>(m_xConditionPlayground.get(), m_xDialog.get(), *this, m_rController); + xCon->setCondition(xCond); + m_xConditionPlayground->reorder_child(xCon->get_widget(), _nNewCondIndex); + m_aConditions.insert(m_aConditions.begin() + _nNewCondIndex, std::move(xCon)); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("reportdesign"); + } + + impl_conditionCountChanged(); + + impl_ensureConditionVisible( _nNewCondIndex ); + } + + void ConditionalFormattingDialog::impl_focusCondition( size_t _nCondIndex ) + { + OSL_PRECOND( _nCondIndex < impl_getConditionCount(), + "ConditionalFormattingDialog::impl_focusCondition: illegal index!" ); + + impl_ensureConditionVisible( _nCondIndex ); + m_aConditions[ _nCondIndex ]->grab_focus(); + } + + void ConditionalFormattingDialog::impl_deleteCondition_nothrow( size_t _nCondIndex ) + { + OSL_PRECOND( _nCondIndex < impl_getConditionCount(), + "ConditionalFormattingDialog::impl_deleteCondition_nothrow: illegal index!" ); + + bool bLastCondition = ( impl_getConditionCount() == 1 ); + + bool bSetNewFocus = false; + size_t nNewFocusIndex( _nCondIndex ); + try + { + if ( !bLastCondition ) + m_xCopy->removeByIndex( _nCondIndex ); + + Conditions::iterator pos = m_aConditions.begin() + _nCondIndex; + if ( bLastCondition ) + { + Reference< XFormatCondition > xFormatCondition( m_xCopy->getByIndex( 0 ), UNO_QUERY_THROW ); + xFormatCondition->setFormula( OUString() ); + (*pos)->setCondition( xFormatCondition ); + } + else + { + bSetNewFocus = (*pos)->has_focus(); + + auto xMovedCondition = std::move(*pos); + m_aConditions.erase(pos); + m_xConditionPlayground->move(xMovedCondition->get_widget(), nullptr); + } + + if ( bSetNewFocus ) + { + if ( nNewFocusIndex >= impl_getConditionCount() ) + nNewFocusIndex = impl_getConditionCount() - 1; + } + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("reportdesign"); + } + + impl_conditionCountChanged(); + if ( bSetNewFocus ) + impl_focusCondition( nNewFocusIndex ); + } + + + void ConditionalFormattingDialog::impl_moveCondition_nothrow( size_t _nCondIndex, bool _bMoveUp ) + { + size_t nOldConditionIndex( _nCondIndex ); + size_t nNewConditionIndex( _bMoveUp ? _nCondIndex - 1 : _nCondIndex + 1 ); + + // do this in two steps, so we don't become inconsistent if any of the UNO actions fails + Any aMovedCondition; + std::unique_ptr<Condition> xMovedCondition; + try + { + aMovedCondition = m_xCopy->getByIndex( static_cast<sal_Int32>(nOldConditionIndex) ); + m_xCopy->removeByIndex( static_cast<sal_Int32>(nOldConditionIndex) ); + + Conditions::iterator aRemovePos( m_aConditions.begin() + nOldConditionIndex ); + xMovedCondition = std::move(*aRemovePos); + m_aConditions.erase( aRemovePos ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("reportdesign"); + return; + } + + try + { + m_xCopy->insertByIndex( static_cast<sal_Int32>(nNewConditionIndex), aMovedCondition ); + m_aConditions.insert(m_aConditions.begin() + nNewConditionIndex, std::move(xMovedCondition)); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("reportdesign"); + } + + // at least the two swapped conditions need to know their new index + impl_updateConditionIndicies(); + + // ensure the moved condition is visible + impl_ensureConditionVisible( nNewConditionIndex ); + } + + IMPL_LINK_NOARG(ConditionalFormattingDialog, OnScroll, weld::ScrolledWindow&, void) + { + size_t nFirstCondIndex( impl_getFirstVisibleConditionIndex() ); + size_t nFocusCondIndex = impl_getFocusedConditionIndex( nFirstCondIndex ); + + if ( nFocusCondIndex < nFirstCondIndex ) + impl_focusCondition( nFirstCondIndex ); + else if ( nFocusCondIndex >= nFirstCondIndex + MAX_CONDITIONS ) + impl_focusCondition( nFirstCondIndex + MAX_CONDITIONS - 1 ); + } + + void ConditionalFormattingDialog::impl_layoutAll() + { + // scrollbar visibility + if ( m_aConditions.size() <= MAX_CONDITIONS ) + // normalize the position, so it can, in all situations, be used as top index + m_xScrollWindow->vadjustment_set_value(0); + } + + void ConditionalFormattingDialog::impl_initializeConditions() + { + try + { + sal_Int32 nCount = m_xCopy->getCount(); + for ( sal_Int32 i = 0; i < nCount ; ++i ) + { + auto xCon = std::make_unique<Condition>(m_xConditionPlayground.get(), m_xDialog.get(), *this, m_rController); + Reference< XFormatCondition > xCond( m_xCopy->getByIndex(i), UNO_QUERY ); + m_xConditionPlayground->reorder_child(xCon->get_widget(), i); + xCon->setCondition(xCond); + xCon->updateToolbar(xCond); + m_aConditions.push_back(std::move(xCon)); + } + } + catch(Exception&) + { + OSL_FAIL("Can not access format condition!"); + } + + impl_conditionCountChanged(); + } + + void ConditionalFormattingDialog::applyCommand(size_t _nCondIndex, sal_uInt16 _nCommandId, const ::Color& rColor) + { + OSL_PRECOND( _nCommandId, "ConditionalFormattingDialog::applyCommand: illegal command id!" ); + try + { + Reference< XReportControlFormat > xReportControlFormat( m_xCopy->getByIndex( _nCondIndex ), UNO_QUERY_THROW ); + + Sequence< PropertyValue > aArgs{ + comphelper::makePropertyValue(REPORTCONTROLFORMAT, xReportControlFormat), + comphelper::makePropertyValue(CURRENT_WINDOW, m_xDialog->GetXWindow()), + comphelper::makePropertyValue(PROPERTY_FONTCOLOR, rColor) + }; + + // we use this way to create undo actions + m_rController.executeUnChecked(_nCommandId,aArgs); + m_aConditions[ _nCondIndex ]->updateToolbar(xReportControlFormat); + } + catch( Exception& ) + { + DBG_UNHANDLED_EXCEPTION("reportdesign"); + } + } + + + void ConditionalFormattingDialog::moveConditionUp( size_t _nCondIndex ) + { + OSL_PRECOND( _nCondIndex > 0, "ConditionalFormattingDialog::moveConditionUp: cannot move up the first condition!" ); + if ( _nCondIndex > 0 ) + impl_moveCondition_nothrow( _nCondIndex, true ); + } + + + void ConditionalFormattingDialog::moveConditionDown( size_t _nCondIndex ) + { + OSL_PRECOND( _nCondIndex < impl_getConditionCount(), "ConditionalFormattingDialog::moveConditionDown: cannot move down the last condition!" ); + if ( _nCondIndex < impl_getConditionCount() ) + impl_moveCondition_nothrow( _nCondIndex, false ); + } + + OUString ConditionalFormattingDialog::getDataField() const + { + OUString sDataField; + try + { + sDataField = m_xFormatConditions->getDataField(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("reportdesign"); + } + return sDataField; + } + + short ConditionalFormattingDialog::run() + { + short nRet = GenericDialogController::run(); + if (nRet == RET_OK) + { + const OUString sUndoAction( RptResId( RID_STR_UNDO_CONDITIONAL_FORMATTING ) ); + const UndoContext aUndoContext( m_rController.getUndoManager(), sUndoAction ); + try + { + sal_Int32 j(0), i(0); + for ( Conditions::const_iterator cond = m_aConditions.begin(); + cond != m_aConditions.end(); + ++cond, ++i + ) + { + Reference< XFormatCondition > xCond( m_xCopy->getByIndex(i), UNO_QUERY_THROW ); + (*cond)->fillFormatCondition( xCond ); + + if ( (*cond)->isEmpty() ) + continue; + + Reference< XFormatCondition > xNewCond; + bool bAppend = j >= m_xFormatConditions->getCount(); + if ( bAppend ) + { + xNewCond = m_xFormatConditions->createFormatCondition(); + m_xFormatConditions->insertByIndex( i, Any( xNewCond ) ); + } + else + xNewCond.set( m_xFormatConditions->getByIndex(j), UNO_QUERY ); + ++j; + + ::comphelper::copyProperties(xCond, xNewCond); + } + + for ( sal_Int32 k = m_xFormatConditions->getCount()-1; k >= j; --k ) + m_xFormatConditions->removeByIndex(k); + + ::comphelper::copyProperties( m_xCopy, m_xFormatConditions ); + } + catch ( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("reportdesign"); + nRet = RET_NO; + } + } + return nRet; + } + + size_t ConditionalFormattingDialog::impl_getFirstVisibleConditionIndex() const + { + auto nHeight = m_aConditions[0]->get_preferred_size().Height(); + return m_xScrollWindow->vadjustment_get_value() / nHeight; + } + + size_t ConditionalFormattingDialog::impl_getLastVisibleConditionIndex() const + { + return ::std::min( impl_getFirstVisibleConditionIndex() + MAX_CONDITIONS, impl_getConditionCount() ) - 1; + } + + size_t ConditionalFormattingDialog::impl_getFocusedConditionIndex( sal_Int32 _nFallBackIfNone ) const + { + auto cond = std::find_if(m_aConditions.begin(), m_aConditions.end(), + [](const std::unique_ptr<Condition>& rxCondition) { return rxCondition->has_focus(); }); + if (cond != m_aConditions.end()) + return static_cast<size_t>(std::distance(m_aConditions.begin(), cond)); + return _nFallBackIfNone; + } + + void ConditionalFormattingDialog::impl_scrollTo( size_t nTopCondIndex ) + { + OSL_PRECOND( nTopCondIndex + MAX_CONDITIONS <= impl_getConditionCount(), + "ConditionalFormattingDialog::impl_scrollTo: illegal index!" ); + + auto nHeight = m_aConditions[0]->get_preferred_size().Height(); + m_xScrollWindow->vadjustment_set_value(nTopCondIndex * nHeight); + OnScroll(*m_xScrollWindow); + } + + void ConditionalFormattingDialog::impl_ensureConditionVisible( size_t _nCondIndex ) + { + OSL_PRECOND( _nCondIndex < impl_getConditionCount(), + "ConditionalFormattingDialog::impl_ensureConditionVisible: illegal index!" ); + + if ( _nCondIndex < impl_getFirstVisibleConditionIndex() ) + impl_scrollTo( _nCondIndex ); + else if ( _nCondIndex > impl_getLastVisibleConditionIndex() ) + impl_scrollTo( _nCondIndex - MAX_CONDITIONS + 1 ); + } + + +} // rptui + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/reportdesign/source/ui/dlg/Condition.cxx b/reportdesign/source/ui/dlg/Condition.cxx new file mode 100644 index 0000000000..c442e4ccfc --- /dev/null +++ b/reportdesign/source/ui/dlg/Condition.cxx @@ -0,0 +1,379 @@ +/* -*- 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 "Condition.hxx" +#include <UITools.hxx> +#include <CondFormat.hxx> +#include <core_resource.hxx> +#include <strings.hrc> +#include <ReportController.hxx> +#include <reportformula.hxx> + +#include <svx/PaletteManager.hxx> +#include <svx/svxids.hrc> +#include <toolkit/helper/vclunohelper.hxx> +#include <vcl/svapp.hxx> +#include <vcl/settings.hxx> + +#include <comphelper/diagnose_ex.hxx> + +namespace rptui +{ +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::beans; + +ConditionField::ConditionField(Condition* pParent, std::unique_ptr<weld::Entry> xSubEdit, + std::unique_ptr<weld::Button> xFormula) + : m_pParent(pParent) + , m_xSubEdit(std::move(xSubEdit)) + , m_xFormula(std::move(xFormula)) +{ + m_xFormula->set_label("..."); + m_xFormula->connect_clicked( LINK( this, ConditionField, OnFormula ) ); +} + +IMPL_LINK_NOARG(ConditionField, OnFormula, weld::Button&, void) +{ + OUString sFormula(m_xSubEdit->get_text()); + const sal_Int32 nLen = sFormula.getLength(); + if ( nLen ) + { + ReportFormula aFormula( sFormula ); + sFormula = aFormula.getCompleteFormula(); + } + uno::Reference< awt::XWindow> xInspectorWindow = m_pParent->GetXWindow(); + uno::Reference< beans::XPropertySet> xProp(m_pParent->getController().getRowSet(),uno::UNO_QUERY); + if ( rptui::openDialogFormula_nothrow( sFormula, m_pParent->getController().getContext(),xInspectorWindow,xProp ) ) + { + ReportFormula aFormula( sFormula ); + m_xSubEdit->set_text(aFormula.getUndecoratedContent()); + } +} + +ConditionColorWrapper::ConditionColorWrapper(Condition* pControl, sal_uInt16 nSlotId) + : mpControl(pControl) + , mnSlotId(nSlotId) +{ +} + +void ConditionColorWrapper::operator()( + [[maybe_unused]] const OUString& /*rCommand*/, const NamedColor& rNamedColor) +{ + mpControl->ApplyCommand(mnSlotId, rNamedColor); +} + +// = Condition +Condition::Condition(weld::Container* pParent, weld::Window* pDialog, IConditionalFormatAction& _rAction, ::rptui::OReportController& _rController) + : m_xPaletteManager(std::make_shared<PaletteManager>()) + , m_aBackColorWrapper(this, SID_BACKGROUND_COLOR) + , m_aForeColorWrapper(this, SID_ATTR_CHAR_COLOR2) + , m_rController(_rController) + , m_rAction(_rAction) + , m_nCondIndex(0) + , m_pDialog(pDialog) + , m_xBuilder(Application::CreateBuilder(pParent, "modules/dbreport/ui/conditionwin.ui")) + , m_xContainer(m_xBuilder->weld_container("ConditionWin")) + , m_xHeader(m_xBuilder->weld_label("headerLabel")) + , m_xConditionType(m_xBuilder->weld_combo_box("typeCombobox")) + , m_xOperationList(m_xBuilder->weld_combo_box("opCombobox")) + , m_xOperandGlue(m_xBuilder->weld_label("andLabel")) + , m_xActions(m_xBuilder->weld_toolbar("formatToolbox")) + , m_xPreview(new weld::CustomWeld(*m_xBuilder, "previewDrawingarea", m_aPreview)) + , m_xMoveUp(m_xBuilder->weld_button("upButton")) + , m_xMoveDown(m_xBuilder->weld_button("downButton")) + , m_xAddCondition(m_xBuilder->weld_button("addButton")) + , m_xRemoveCondition(m_xBuilder->weld_button("removeButton")) +{ + m_xCondLHS.reset(new ConditionField(this, m_xBuilder->weld_entry("lhsEntry"), m_xBuilder->weld_button("lhsButton"))); + m_xCondRHS.reset(new ConditionField(this, m_xBuilder->weld_entry("rhsEntry"), m_xBuilder->weld_button("rhsButton"))); + + m_xCondLHS->grab_focus(); + + m_xConditionType->connect_changed( LINK( this, Condition, OnTypeSelected ) ); + + m_xOperationList->connect_changed( LINK( this, Condition, OnOperationSelected ) ); + + m_xActions->connect_clicked(LINK(this, Condition, OnFormatAction)); + + m_xMoveUp->connect_clicked( LINK( this, Condition, OnConditionAction ) ); + m_xMoveDown->connect_clicked( LINK( this, Condition, OnConditionAction ) ); + m_xAddCondition->connect_clicked( LINK( this, Condition, OnConditionAction ) ); + m_xRemoveCondition->connect_clicked( LINK( this, Condition, OnConditionAction ) ); + + m_xConditionType->set_active(0); + m_xOperationList->set_active(0); + + SetBackgroundDropdownClick(); + SetForegroundDropdownClick(); + + m_xContainer->show(); + + ConditionalExpressionFactory::getKnownConditionalExpressions( m_aConditionalExpressions ); +} + +sal_uInt16 Condition::mapToolbarItemToSlotId(std::u16string_view rItemId) +{ + if (rItemId == u"bold") + return SID_ATTR_CHAR_WEIGHT; + if (rItemId == u"italic") + return SID_ATTR_CHAR_POSTURE; + if (rItemId == u"underline") + return SID_ATTR_CHAR_UNDERLINE; + if (rItemId == u"background") + return SID_BACKGROUND_COLOR; + if (rItemId == u"foreground") + return SID_ATTR_CHAR_COLOR2; + if (rItemId == u"fontdialog") + return SID_CHAR_DLG; + return 0; +} + +Condition::~Condition() +{ +} + +void Condition::SetBackgroundDropdownClick() +{ + m_xBackColorFloat.reset(new ColorWindow( + OUString() /*m_aCommandURL*/, + m_xPaletteManager, + m_aColorStatus, + SID_BACKGROUND_COLOR, + nullptr, + MenuOrToolMenuButton(m_xActions.get(), "background"), + [this]{ return m_pDialog; }, + m_aBackColorWrapper)); + + m_xActions->set_item_popover("background", m_xBackColorFloat->getTopLevel()); +} + +void Condition::SetForegroundDropdownClick() +{ + m_xForeColorFloat.reset(new ColorWindow( + OUString() /*m_aCommandURL*/, + m_xPaletteManager, + m_aColorStatus, + SID_ATTR_CHAR_COLOR2, + nullptr, + MenuOrToolMenuButton(m_xActions.get(), "foreground"), + [this]{ return m_pDialog; }, + m_aForeColorWrapper)); + + m_xActions->set_item_popover("foreground", m_xForeColorFloat->getTopLevel()); +} + + +IMPL_LINK(Condition, OnFormatAction, const OUString&, rIdent, void) +{ + ApplyCommand(mapToolbarItemToSlotId(rIdent), + NamedColor(COL_AUTO, "#" + COL_AUTO.AsRGBHexString())); +} + +IMPL_LINK(Condition, OnConditionAction, weld::Button&, rClickedButton, void) +{ + if ( &rClickedButton == m_xMoveUp.get() ) + m_rAction.moveConditionUp( getConditionIndex() ); + else if ( &rClickedButton == m_xMoveDown.get() ) + m_rAction.moveConditionDown( getConditionIndex() ); + else if ( &rClickedButton == m_xAddCondition.get() ) + m_rAction.addCondition( getConditionIndex() ); + else if ( &rClickedButton == m_xRemoveCondition.get() ) + m_rAction.deleteCondition( getConditionIndex() ); +} + +void Condition::ApplyCommand( sal_uInt16 _nCommandId, const NamedColor& rNamedColor ) +{ + m_rAction.applyCommand(m_nCondIndex, _nCommandId, rNamedColor.m_aColor); +} + +IMPL_LINK_NOARG( Condition, OnTypeSelected, weld::ComboBox&, void ) +{ + impl_layoutOperands(); +} + +IMPL_LINK_NOARG( Condition, OnOperationSelected, weld::ComboBox&, void ) +{ + impl_layoutOperands(); +} + +void Condition::impl_layoutOperands() +{ + const ConditionType eType( impl_getCurrentConditionType() ); + const ComparisonOperation eOperation( impl_getCurrentComparisonOperation() ); + + const bool bIsExpression = ( eType == eExpression ); + const bool bHaveRHS = + ( ( eType == eFieldValueComparison ) + && ( ( eOperation == eBetween ) + || ( eOperation == eNotBetween ) + ) + ); + + // the "condition type" list box + m_xOperationList->set_visible( !bIsExpression ); + m_xOperandGlue->set_visible( bHaveRHS ); + m_xCondRHS->set_visible( bHaveRHS ); +} + +void Condition::impl_setCondition( const OUString& _rConditionFormula ) +{ + // determine the condition's type and comparison operation + ConditionType eType( eFieldValueComparison ); + ComparisonOperation eOperation( eBetween ); + + // LHS and RHS, matched below + OUString sLHS, sRHS; + + if ( !_rConditionFormula.isEmpty() ) + { + // the unprefixed expression which forms the condition + ReportFormula aFormula( _rConditionFormula ); + OSL_ENSURE( aFormula.getType() == ReportFormula::Expression, "Condition::setCondition: illegal formula!" ); + OUString sExpression; + if ( aFormula.getType() == ReportFormula::Expression ) + sExpression = aFormula.getExpression(); + // as fallback, if the below matching does not succeed, assume + // the whole expression is the LHS + eType = eExpression; + sLHS = sExpression; + + // the data field (or expression) to which our control is bound + const ReportFormula aFieldContentFormula( m_rAction.getDataField() ); + const OUString sUnprefixedFieldContent( aFieldContentFormula.getBracketedFieldOrExpression() ); + + // check whether one of the Field Value Expression Factories recognizes the expression + for (const auto& [rOperation, rxConditionalExpression] : m_aConditionalExpressions) + { + if ( rxConditionalExpression->matchExpression( sExpression, sUnprefixedFieldContent, sLHS, sRHS ) ) + { + eType = eFieldValueComparison; + eOperation = rOperation; + break; + } + } + } + + // update UI + m_xConditionType->set_active(static_cast<sal_uInt16>(eType)); + m_xOperationList->set_active(static_cast<sal_uInt16>(eOperation)); + m_xCondLHS->set_text( sLHS ); + m_xCondRHS->set_text( sRHS ); + + // re-layout + impl_layoutOperands(); +} + + +void Condition::setCondition( const uno::Reference< report::XFormatCondition >& _rxCondition ) +{ + OSL_PRECOND( _rxCondition.is(), "Condition::setCondition: empty condition object!" ); + if ( !_rxCondition.is() ) + return; + + OUString sConditionFormula; + try + { + if ( _rxCondition.is() ) + sConditionFormula = _rxCondition->getFormula(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("reportdesign"); + } + impl_setCondition( sConditionFormula ); + updateToolbar( _rxCondition ); +} + + +void Condition::updateToolbar(const uno::Reference< report::XReportControlFormat >& _xReportControlFormat) +{ + OUString aItems[] = { "bold", "italic", "underline", "fontdialog" }; + + OSL_ENSURE(_xReportControlFormat.is(),"XReportControlFormat is NULL!"); + if ( !_xReportControlFormat.is() ) + return; + + for (size_t j = 0; j < SAL_N_ELEMENTS(aItems); ++j) + { + m_xActions->set_item_active(aItems[j], OReportController::isFormatCommandEnabled(mapToolbarItemToSlotId(aItems[j]), + _xReportControlFormat)); + } + + try + { + vcl::Font aBaseFont( Application::GetDefaultDevice()->GetSettings().GetStyleSettings().GetAppFont() ); + SvxFont aFont( VCLUnoHelper::CreateFont( _xReportControlFormat->getFontDescriptor(), aBaseFont ) ); + aFont.SetFontHeight(o3tl::convert(aFont.GetFontHeight(), o3tl::Length::pt, o3tl::Length::twip)); + aFont.SetEmphasisMark( static_cast< FontEmphasisMark >( _xReportControlFormat->getControlTextEmphasis() ) ); + aFont.SetRelief( static_cast< FontRelief >( _xReportControlFormat->getCharRelief() ) ); + aFont.SetColor( Color(ColorTransparency, _xReportControlFormat->getCharColor()) ); + m_aPreview.SetFont( aFont, aFont, aFont ); + m_aPreview.SetTextLineColor( Color( ColorTransparency, _xReportControlFormat->getCharUnderlineColor() ) ); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("reportdesign"); + } +} + +void Condition::fillFormatCondition(const uno::Reference< report::XFormatCondition >& _xCondition) +{ + const ConditionType eType( impl_getCurrentConditionType() ); + const ComparisonOperation eOperation( impl_getCurrentComparisonOperation() ); + + const OUString sLHS( m_xCondLHS->get_text() ); + const OUString sRHS( m_xCondRHS->get_text() ); + + OUString sUndecoratedFormula( sLHS ); + + if ( eType == eFieldValueComparison ) + { + ReportFormula aFieldContentFormula( m_rAction.getDataField() ); + OUString sUnprefixedFieldContent( aFieldContentFormula.getBracketedFieldOrExpression() ); + + PConditionalExpression pFactory( m_aConditionalExpressions[ eOperation ] ); + sUndecoratedFormula = pFactory->assembleExpression( sUnprefixedFieldContent, sLHS, sRHS ); + } + + ReportFormula aFormula( ReportFormula::Expression, sUndecoratedFormula ); + _xCondition->setFormula( aFormula.getCompleteFormula() ); +} + +void Condition::setConditionIndex( size_t _nCondIndex, size_t _nCondCount ) +{ + m_nCondIndex = _nCondIndex; + OUString sHeader( RptResId( STR_NUMBERED_CONDITION ) ); + sHeader = sHeader.replaceFirst( "$number$", OUString::number( _nCondIndex + 1) ); + m_xHeader->set_label( sHeader ); + + m_xMoveUp->set_sensitive(_nCondIndex > 0); + OSL_PRECOND( _nCondCount > 0, "Condition::setConditionIndex: having no conditions at all is nonsense!" ); + m_xMoveDown->set_sensitive(_nCondIndex < _nCondCount - 1); +} + +bool Condition::isEmpty() const +{ + return m_xCondLHS->get_text().isEmpty(); +} + +} // rptui + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/reportdesign/source/ui/dlg/Condition.hxx b/reportdesign/source/ui/dlg/Condition.hxx new file mode 100644 index 0000000000..a5e02d09bd --- /dev/null +++ b/reportdesign/source/ui/dlg/Condition.hxx @@ -0,0 +1,183 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_REPORTDESIGN_SOURCE_UI_DLG_CONDITION_HXX +#define INCLUDED_REPORTDESIGN_SOURCE_UI_DLG_CONDITION_HXX + +#include <conditionalexpression.hxx> + +#include <com/sun/star/report/XFormatCondition.hpp> + +#include <svx/colorwindow.hxx> +#include <svx/fntctrl.hxx> + +#include <vcl/customweld.hxx> +#include <vcl/weld.hxx> + +#include <memory> + +namespace rptui +{ + class OColorPopup; + class OReportController; + class IConditionalFormatAction; + class Condition; + + class ConditionField + { + Condition* m_pParent; + std::unique_ptr<weld::Entry> m_xSubEdit; + std::unique_ptr<weld::Button> m_xFormula; + + DECL_LINK(OnFormula, weld::Button&, void); + public: + ConditionField(Condition* pParent, std::unique_ptr<weld::Entry> xSubEdit, std::unique_ptr<weld::Button> xFormula); + void grab_focus() { m_xSubEdit->grab_focus(); } + void set_visible(bool bShow) { m_xSubEdit->set_visible(bShow); m_xFormula->set_visible(bShow); } + void set_text(const OUString& rText) { m_xSubEdit->set_text(rText); } + OUString get_text() const { return m_xSubEdit->get_text(); } + }; + + class ConditionColorWrapper + { + public: + ConditionColorWrapper(Condition* pControl, sal_uInt16 nSlotId); + void operator()(const OUString& rCommand, const NamedColor& rColor); + // ColorSelectFunction signature + private: + Condition* mpControl; + sal_uInt16 mnSlotId; + }; + + //= Condition + + class Condition + { + std::shared_ptr<PaletteManager> m_xPaletteManager; + ColorStatus m_aColorStatus; + ConditionColorWrapper m_aBackColorWrapper; + ConditionColorWrapper m_aForeColorWrapper; + + ::rptui::OReportController& m_rController; + IConditionalFormatAction& m_rAction; + + size_t m_nCondIndex; + + ConditionalExpressions m_aConditionalExpressions; + + SvxFontPrevWindow m_aPreview; + weld::Window* m_pDialog; + std::unique_ptr<weld::Builder> m_xBuilder; + std::unique_ptr<weld::Container> m_xContainer; + std::unique_ptr<weld::Label> m_xHeader; + std::unique_ptr<weld::ComboBox> m_xConditionType; + std::unique_ptr<weld::ComboBox> m_xOperationList; + std::unique_ptr<ConditionField> m_xCondLHS; + std::unique_ptr<weld::Label> m_xOperandGlue; + std::unique_ptr<ConditionField> m_xCondRHS; + std::unique_ptr<weld::Toolbar> m_xActions; + std::unique_ptr<weld::CustomWeld> m_xPreview; + std::unique_ptr<weld::Button> m_xMoveUp; + std::unique_ptr<weld::Button> m_xMoveDown; + std::unique_ptr<weld::Button> m_xAddCondition; + std::unique_ptr<weld::Button> m_xRemoveCondition; + std::unique_ptr<ColorWindow> m_xBackColorFloat; + std::unique_ptr<ColorWindow> m_xForeColorFloat; + + void SetBackgroundDropdownClick(); + void SetForegroundDropdownClick(); + + DECL_LINK( OnFormatAction, const OUString&, void ); + DECL_LINK( OnConditionAction, weld::Button&, void ); + + public: + Condition(weld::Container* pParent, weld::Window* pDialog, IConditionalFormatAction& rAction, ::rptui::OReportController& rController); + ~Condition(); + + /** sets the props at the control + @param _xCondition the source + */ + void setCondition(const css::uno::Reference< css::report::XFormatCondition >& _xCondition); + + /** fills from the control + _xCondition the destination + */ + void fillFormatCondition(const css::uno::Reference< css::report::XFormatCondition >& _xCondition); + + /** updates the toolbar + _xCondition the destination + */ + void updateToolbar(const css::uno::Reference< css::report::XReportControlFormat >& _xCondition); + + /// tells the condition its new index within the dialog's condition array + void setConditionIndex( size_t _nCondIndex, size_t _nCondCount ); + + /// returns the condition's index within the dialog's condition array + size_t getConditionIndex() const { return m_nCondIndex; } + + /** determines whether the condition is actually empty + */ + bool isEmpty() const; + + /** forward to the parent class + */ + void ApplyCommand( sal_uInt16 _nCommandId, const NamedColor& rNamedColor ); + + ::rptui::OReportController& getController() const { return m_rController; } + + static sal_uInt16 mapToolbarItemToSlotId(std::u16string_view rItemId); + + css::uno::Reference<css::awt::XWindow> GetXWindow() const { return m_pDialog->GetXWindow(); } + + void grab_focus() { m_xContainer->grab_focus(); } + bool has_focus() const { return m_xContainer->has_focus(); } + Size get_preferred_size() const { return m_xContainer->get_preferred_size(); } + weld::Widget* get_widget() const { return m_xContainer.get(); } + + private: + void impl_layoutOperands(); + + inline ConditionType + impl_getCurrentConditionType() const; + + inline ComparisonOperation + impl_getCurrentComparisonOperation() const; + + void impl_setCondition( const OUString& _rConditionFormula ); + + private: + DECL_LINK( OnTypeSelected, weld::ComboBox&, void ); + DECL_LINK( OnOperationSelected, weld::ComboBox&, void ); + }; + + inline ConditionType Condition::impl_getCurrentConditionType() const + { + return sal::static_int_cast<ConditionType>(m_xConditionType->get_active()); + } + + inline ComparisonOperation Condition::impl_getCurrentComparisonOperation() const + { + return sal::static_int_cast<ComparisonOperation>(m_xOperationList->get_active()); + } + +} // namespace rptui + +#endif // INCLUDED_REPORTDESIGN_SOURCE_UI_DLG_CONDITION_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/reportdesign/source/ui/dlg/DateTime.cxx b/reportdesign/source/ui/dlg/DateTime.cxx new file mode 100644 index 0000000000..90c57927cd --- /dev/null +++ b/reportdesign/source/ui/dlg/DateTime.cxx @@ -0,0 +1,209 @@ +/* -*- 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 <DateTime.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <rptui_slotid.hrc> +#include <connectivity/dbconversion.hxx> +#include <unotools/syslocale.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <strings.hxx> +#include <ReportController.hxx> +#include <com/sun/star/util/NumberFormat.hpp> +#include <com/sun/star/util/XNumberFormatPreviewer.hpp> +#include <algorithm> + +namespace rptui +{ +using namespace ::com::sun::star; +using namespace ::comphelper; + + + +ODateTimeDialog::ODateTimeDialog(weld::Window* _pParent, uno::Reference< report::XSection > _xHoldAlive, + OReportController* _pController) + : GenericDialogController(_pParent, "modules/dbreport/ui/datetimedialog.ui", "DateTimeDialog") + + , m_pController(_pController) + , m_xHoldAlive(std::move(_xHoldAlive)) + , m_xDate(m_xBuilder->weld_check_button("date")) + , m_xFTDateFormat(m_xBuilder->weld_label("datelistbox_label")) + , m_xDateListBox(m_xBuilder->weld_combo_box("datelistbox")) + , m_xTime(m_xBuilder->weld_check_button("time")) + , m_xFTTimeFormat(m_xBuilder->weld_label("timelistbox_label")) + , m_xTimeListBox(m_xBuilder->weld_combo_box("timelistbox")) + , m_xPB_OK(m_xBuilder->weld_button("ok")) +{ + try + { + SvtSysLocale aSysLocale; + m_nLocale = aSysLocale.GetLanguageTag().getLocale(); + // Fill listbox with all well known date types + InsertEntry(util::NumberFormat::DATE); + InsertEntry(util::NumberFormat::TIME); + } + catch (const uno::Exception&) + { + } + + m_xDateListBox->set_active(0); + m_xTimeListBox->set_active(0); + + weld::CheckButton* aCheckBoxes[] = { m_xDate.get(), m_xTime.get() }; + for (weld::CheckButton* pCheckBox : aCheckBoxes) + pCheckBox->connect_toggled(LINK(this,ODateTimeDialog,CBClickHdl)); + CBClickHdl(*m_xTime); +} + +void ODateTimeDialog::InsertEntry(sal_Int16 _nNumberFormatId) +{ + const bool bTime = util::NumberFormat::TIME == _nNumberFormatId; + weld::ComboBox* pListBox = m_xDateListBox.get(); + if (bTime) + pListBox = m_xTimeListBox.get(); + + const uno::Reference< util::XNumberFormatter> xNumberFormatter = m_pController->getReportNumberFormatter(); + const uno::Reference< util::XNumberFormats> xFormats = xNumberFormatter->getNumberFormatsSupplier()->getNumberFormats(); + const uno::Sequence<sal_Int32> aFormatKeys = xFormats->queryKeys(_nNumberFormatId,m_nLocale,true); + for (const sal_Int32 nFormatKey : aFormatKeys) + { + pListBox->append(OUString::number(nFormatKey), getFormatStringByKey(nFormatKey,xFormats,bTime)); + } +} + +short ODateTimeDialog::run() +{ + short nRet = GenericDialogController::run(); + if (nRet == RET_OK && (m_xDate->get_active() || m_xTime->get_active())) + { + try + { + sal_Int32 nLength = 0; + uno::Sequence<beans::PropertyValue> aValues( 6 ); + auto pValues = aValues.getArray(); + pValues[nLength].Name = PROPERTY_SECTION; + pValues[nLength++].Value <<= m_xHoldAlive; + + pValues[nLength].Name = PROPERTY_TIME_STATE; + pValues[nLength++].Value <<= m_xTime->get_active(); + + pValues[nLength].Name = PROPERTY_DATE_STATE; + pValues[nLength++].Value <<= m_xDate->get_active(); + + pValues[nLength].Name = PROPERTY_FORMATKEYDATE; + pValues[nLength++].Value <<= getFormatKey(true); + + pValues[nLength].Name = PROPERTY_FORMATKEYTIME; + pValues[nLength++].Value <<= getFormatKey(false); + + OutputDevice* pDefDev = Application::GetDefaultDevice(); + sal_Int32 nWidth = 0; + if ( m_xDate->get_active() ) + { + OUString sDateFormat = m_xDateListBox->get_active_text(); + nWidth = OutputDevice::LogicToLogic(pDefDev->PixelToLogic(Size(pDefDev->GetCtrlTextWidth(sDateFormat),0)).Width(), + pDefDev->GetMapMode().GetMapUnit(),MapUnit::Map100thMM); + } + if ( m_xTime->get_active() ) + { + OUString sDateFormat = m_xTimeListBox->get_active_text(); + nWidth = ::std::max<sal_Int32>(OutputDevice::LogicToLogic(pDefDev->PixelToLogic(Size(pDefDev->GetCtrlTextWidth(sDateFormat),0)).Width(), + pDefDev->GetMapMode().GetMapUnit(),MapUnit::Map100thMM),nWidth); + } + + if ( nWidth > 4000 ) + { + pValues[nLength].Name = PROPERTY_WIDTH; + pValues[nLength++].Value <<= nWidth; + } + + m_pController->executeChecked(SID_DATETIME,aValues); + } + catch (const uno::Exception&) + { + nRet = RET_NO; + } + } + return nRet; +} + +OUString ODateTimeDialog::getFormatStringByKey(::sal_Int32 _nNumberFormatKey,const uno::Reference< util::XNumberFormats>& _xFormats,bool _bTime) +{ + uno::Reference< beans::XPropertySet> xFormSet = _xFormats->getByKey(_nNumberFormatKey); + OSL_ENSURE(xFormSet.is(),"XPropertySet is null!"); + OUString sFormat; + xFormSet->getPropertyValue("FormatString") >>= sFormat; + + double nValue = 0; + if ( _bTime ) + { + tools::Time aCurrentTime( tools::Time::SYSTEM ); + nValue = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toTime(aCurrentTime.GetTime())); + } + else + { + Date aCurrentDate( Date::SYSTEM ); + static css::util::Date STANDARD_DB_DATE(30,12,1899); + nValue = ::dbtools::DBTypeConversion::toDouble(::dbtools::DBTypeConversion::toDate(aCurrentDate.GetDate()),STANDARD_DB_DATE); + } + + uno::Reference< util::XNumberFormatPreviewer> xPreviewer(m_pController->getReportNumberFormatter(),uno::UNO_QUERY); + OSL_ENSURE(xPreviewer.is(),"XNumberFormatPreviewer is null!"); + return xPreviewer->convertNumberToPreviewString(sFormat,nValue,m_nLocale,true); +} + +IMPL_LINK_NOARG(ODateTimeDialog, CBClickHdl, weld::Toggleable&, void) +{ + const bool bDate = m_xDate->get_active(); + m_xFTDateFormat->set_sensitive(bDate); + m_xDateListBox->set_sensitive(bDate); + + const bool bTime = m_xTime->get_active(); + m_xFTTimeFormat->set_sensitive(bTime); + m_xTimeListBox->set_sensitive(bTime); + + if (!bDate && !bTime) + { + m_xPB_OK->set_sensitive(false); + } + else + { + m_xPB_OK->set_sensitive(true); + } +} + +sal_Int32 ODateTimeDialog::getFormatKey(bool _bDate) const +{ + sal_Int32 nFormatKey; + if ( _bDate ) + { + nFormatKey = m_xDateListBox->get_active_id().toInt32(); + } + else + { + nFormatKey = m_xTimeListBox->get_active_id().toInt32(); + } + return nFormatKey; +} + +} // rptui + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/reportdesign/source/ui/dlg/Formula.cxx b/reportdesign/source/ui/dlg/Formula.cxx new file mode 100644 index 0000000000..9cad568ec6 --- /dev/null +++ b/reportdesign/source/ui/dlg/Formula.cxx @@ -0,0 +1,275 @@ +/* -*- 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 <unotools/viewoptions.hxx> +#include <formula/formdata.hxx> +#include <formula/funcutl.hxx> +#include <formula/tokenarray.hxx> +#include <formula/FormulaCompiler.hxx> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/beans/PropertyValue.hpp> + +#include <memory> + +#include <Formula.hxx> +#include <AddField.hxx> +#include <utility> +#include <helpids.h> + + +namespace rptui +{ + using namespace formula; + using namespace ::com::sun::star; + + +// initialization / shared functions for the dialog + + +FormulaDialog::FormulaDialog(weld::Window* pParent + , const uno::Reference<lang::XMultiServiceFactory>& _xServiceFactory + , const std::shared_ptr< IFunctionManager >& _pFunctionMgr + , const OUString& _sFormula + , css::uno::Reference < css::beans::XPropertySet > _xRowSet + , svl::SharedStringPool& rStrPool ) + : FormulaModalDialog( pParent, _pFunctionMgr.get(),this) + ,m_aFunctionManager(_pFunctionMgr) + ,m_xFormulaData(new FormEditData()) + ,m_xRowSet(std::move(_xRowSet)) + ,m_pEdit(nullptr) + ,m_sFormula("=") + ,m_nStart(0) + ,m_nEnd(1) + ,mrStringPool(rStrPool) +{ + if ( !_sFormula.isEmpty() ) + { + if ( _sFormula[0] != '=' ) + m_sFormula += _sFormula; + else + m_sFormula = _sFormula; + } + m_xParser.set(_xServiceFactory->createInstance("org.libreoffice.report.pentaho.SOFormulaParser"),uno::UNO_QUERY); + if ( m_xParser.is() ) + m_xOpCodeMapper = m_xParser->getFormulaOpCodeMapper(); + fill(); +} + +void FormulaDialog::notifyChange() +{ +} + +void FormulaDialog::fill() +{ + SetMeText(m_sFormula); + Update(m_sFormula); + CheckMatrix(m_sFormula); + Update(); +} + +FormulaDialog::~FormulaDialog() +{ + if ( m_xAddField ) + { + SvtViewOptions aDlgOpt( EViewType::Window, HID_RPT_FIELD_SEL_WIN ); + aDlgOpt.SetWindowState(m_xAddField->getDialog()->get_window_state(vcl::WindowDataMask::Pos | vcl::WindowDataMask::State | vcl::WindowDataMask::Minimized)); + + if (m_xAddField->getDialog()->get_visible()) + m_xAddField->response(RET_CANCEL); + + m_xAddField.reset(); + } + + StoreFormEditData(m_xFormulaData.get()); + m_pEdit = nullptr; +} + +// functions for right side + +bool FormulaDialog::calculateValue( const OUString& rStrExp, OUString& rStrResult, bool /*bMatrixFormula*/ ) +{ + rStrResult = rStrExp; + return false; +} + +std::shared_ptr<formula::FormulaCompiler> FormulaDialog::getCompiler() const +{ + return nullptr; +} + +std::unique_ptr<formula::FormulaCompiler> FormulaDialog::createCompiler( formula::FormulaTokenArray& rArray ) const +{ + return std::unique_ptr<formula::FormulaCompiler>(new FormulaCompiler(rArray)); +} + +void FormulaDialog::doClose(bool _bOk) +{ + response(_bOk ? RET_OK : RET_CANCEL); +} + +void FormulaDialog::insertEntryToLRUList(const IFunctionDescription* /*_pDesc*/) +{ +} +void FormulaDialog::showReference(const OUString& /*_sFormula*/) +{ +} +void FormulaDialog::dispatch(bool /*_bOK*/, bool /*_bMatrixChecked*/) +{ +} +void FormulaDialog::setDispatcherLock( bool /*bLock*/ ) +{ +} +void FormulaDialog::deleteFormData() +{ +} +void FormulaDialog::clear() +{ +} +void FormulaDialog::switchBack() +{ +} +FormEditData* FormulaDialog::getFormEditData() const +{ + return m_xFormulaData.get(); +} +void FormulaDialog::setCurrentFormula(const OUString& _sReplacement) +{ + const sal_Int32 nOldLen = m_nEnd - m_nStart; + const sal_Int32 nNewLen = _sReplacement.getLength(); + if (nOldLen) + m_sFormula = m_sFormula.replaceAt( m_nStart, nOldLen, u"" ); + if (nNewLen) + m_sFormula = m_sFormula.replaceAt( m_nStart, 0, _sReplacement ); + m_nEnd = m_nStart + nNewLen; +} +void FormulaDialog::setSelection(sal_Int32 _nStart, sal_Int32 _nEnd) +{ + if ( _nStart <= _nEnd ) + { + m_nStart = _nStart; + m_nEnd = _nEnd; + } + else + { + m_nEnd = _nStart; + m_nStart = _nEnd; + } +} +void FormulaDialog::getSelection(sal_Int32& _nStart, sal_Int32& _nEnd) const +{ + _nStart = m_nStart; + _nEnd = m_nEnd; +} +OUString FormulaDialog::getCurrentFormula() const +{ + return m_sFormula; +} +IFunctionManager* FormulaDialog::getFunctionManager() +{ + return m_aFunctionManager.get(); +} + +void FormulaDialog::ShowReference(const OUString& /*_sRef*/) +{ +} + +void FormulaDialog::HideReference( bool /*bDoneRefMode*/) +{ +} + +void FormulaDialog::ReleaseFocus( RefEdit* /*pEdit*/) +{ +} + +void FormulaDialog::ToggleCollapsed( RefEdit* _pEdit, RefButton* _pButton) +{ + ::std::pair<RefButton*,RefEdit*> aPair = RefInputStartBefore( _pEdit, _pButton ); + m_pEdit = aPair.second; + if ( m_pEdit ) + m_pEdit->GetWidget()->hide(); + if ( aPair.first ) + aPair.first->GetWidget()->hide(); + + if (!m_xAddField) + { + m_xAddField = std::make_shared<OAddFieldWindow>(m_xDialog.get(), m_xRowSet); + m_xAddField->SetCreateHdl(LINK( this, FormulaDialog, OnClickHdl ) ); + SvtViewOptions aDlgOpt( EViewType::Window, HID_RPT_FIELD_SEL_WIN ); + if ( aDlgOpt.Exists() ) + { + m_xAddField->getDialog()->set_window_state(aDlgOpt.GetWindowState()); + + } + + m_xAddField->Update(); + } + RefInputStartAfter(); + + if (!m_xAddField->getDialog()->get_visible()) + weld::DialogController::runAsync(m_xAddField, [this](sal_Int32 /*nResult*/) { m_xAddField.reset(); }); +} + +IMPL_LINK( FormulaDialog, OnClickHdl, OAddFieldWindow& ,_rAddFieldDlg, void) +{ + const uno::Sequence< beans::PropertyValue > aArgs = _rAddFieldDlg.getSelectedFieldDescriptors(); + // we use this way to create undo actions + if ( m_pEdit && aArgs.getLength() == 1) + { + uno::Sequence< beans::PropertyValue > aValue; + aArgs[0].Value >>= aValue; + svx::ODataAccessDescriptor aDescriptor(aValue); + OUString sName; + aDescriptor[ svx::DataAccessDescriptorProperty::ColumnName ] >>= sName; + if ( !sName.isEmpty() ) + { + sName = "[" + sName + "]"; + m_pEdit->SetText(sName); + } + } + m_pEdit = nullptr; + if (_rAddFieldDlg.getDialog()->get_visible()) + _rAddFieldDlg.response(RET_CANCEL); + RefInputDoneAfter(); +} + +uno::Reference< sheet::XFormulaParser> FormulaDialog::getFormulaParser() const +{ + return m_xParser; +} + +uno::Reference< sheet::XFormulaOpCodeMapper> FormulaDialog::getFormulaOpCodeMapper() const +{ + return m_xOpCodeMapper; +} + +table::CellAddress FormulaDialog::getReferencePosition() const +{ + return table::CellAddress(); +} + +::std::unique_ptr<formula::FormulaTokenArray> FormulaDialog::convertToTokenArray(const uno::Sequence< sheet::FormulaToken >& _aTokenList) +{ + ::std::unique_ptr<formula::FormulaTokenArray> pArray(new FormulaTokenArray()); + pArray->Fill(_aTokenList, mrStringPool, nullptr); + return pArray; +} + +} // rptui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/reportdesign/source/ui/dlg/GroupExchange.cxx b/reportdesign/source/ui/dlg/GroupExchange.cxx new file mode 100644 index 0000000000..7b749c3f4b --- /dev/null +++ b/reportdesign/source/ui/dlg/GroupExchange.cxx @@ -0,0 +1,68 @@ +/* -*- 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 "GroupExchange.hxx" +#include <sot/formats.hxx> +#include <osl/diagnose.h> + +namespace rptui +{ + using namespace ::com::sun::star; + using namespace ::com::sun::star::uno; + + SotClipboardFormatId OGroupExchange::getReportGroupId() + { + static SotClipboardFormatId s_nReportFormat = static_cast<SotClipboardFormatId>(-1); + if ( static_cast<SotClipboardFormatId>(-1) == s_nReportFormat ) + { + s_nReportFormat = SotExchange::RegisterFormatName("application/x-openoffice;windows_formatname=\"reportdesign.GroupFormat\""); + OSL_ENSURE(static_cast<SotClipboardFormatId>(-1) != s_nReportFormat, "Bad exchange id!"); + } + return s_nReportFormat; + } + OGroupExchange::OGroupExchange(const uno::Sequence< uno::Any >& _aGroupRow) + : m_aGroupRow(_aGroupRow) + { + } + + void OGroupExchange::AddSupportedFormats() + { + if ( m_aGroupRow.hasElements() ) + { + AddFormat(OGroupExchange::getReportGroupId()); + } + } + + bool OGroupExchange::GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& /*rDestDoc*/ ) + { + SotClipboardFormatId nFormat = SotExchange::GetFormat(rFlavor); + if(nFormat == OGroupExchange::getReportGroupId() ) + { + return SetAny(uno::Any(m_aGroupRow)); + } + return false; + } + + void OGroupExchange::ObjectReleased() + { + m_aGroupRow.realloc(0); + } + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/reportdesign/source/ui/dlg/GroupExchange.hxx b/reportdesign/source/ui/dlg/GroupExchange.hxx new file mode 100644 index 0000000000..6154e75ae4 --- /dev/null +++ b/reportdesign/source/ui/dlg/GroupExchange.hxx @@ -0,0 +1,43 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_REPORTDESIGN_SOURCE_UI_DLG_GROUPEXCHANGE_HXX +#define INCLUDED_REPORTDESIGN_SOURCE_UI_DLG_GROUPEXCHANGE_HXX + +#include <vcl/transfer.hxx> + +namespace rptui +{ + /** clipboard class for group rows in the groups and sorting dialog + */ + class OGroupExchange : public TransferableHelper + { + css::uno::Sequence< css::uno::Any> m_aGroupRow; + public: + explicit OGroupExchange(const css::uno::Sequence< css::uno::Any>& _aGroupRow); + + static SotClipboardFormatId getReportGroupId(); + protected: + virtual void AddSupportedFormats() override; + virtual bool GetData( const css::datatransfer::DataFlavor& rFlavor, const OUString& rDestDoc ) override; + virtual void ObjectReleased() override; + }; +} +#endif // INCLUDED_REPORTDESIGN_SOURCE_UI_DLG_GROUPEXCHANGE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/reportdesign/source/ui/dlg/GroupsSorting.cxx b/reportdesign/source/ui/dlg/GroupsSorting.cxx new file mode 100644 index 0000000000..38697ca128 --- /dev/null +++ b/reportdesign/source/ui/dlg/GroupsSorting.cxx @@ -0,0 +1,1181 @@ +/* -*- 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 <GroupsSorting.hxx> +#include <svtools/editbrowsebox.hxx> +#include <toolkit/helper/vclunohelper.hxx> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/container/XContainerListener.hpp> +#include <com/sun/star/report/GroupOn.hpp> +#include <com/sun/star/sdbc/DataType.hpp> + +#include <strings.hrc> +#include <rptui_slotid.hrc> +#include <core_resource.hxx> +#include <helpids.h> +#include "GroupExchange.hxx" +#include <UITools.hxx> +#include <UndoActions.hxx> +#include <strings.hxx> +#include <ReportController.hxx> +#include <ColumnInfo.hxx> + +#include <comphelper/propertyvalue.hxx> +#include <cppuhelper/implbase.hxx> +#include <vcl/commandevent.hxx> +#include <vcl/svapp.hxx> +#include <comphelper/diagnose_ex.hxx> + +#include <algorithm> + +#define HANDLE_ID 0 +#define FIELD_EXPRESSION 1 +#define GROUPS_START_LEN 5 +#define NO_GROUP -1 + +namespace rptui +{ +using namespace ::com::sun::star; +using namespace svt; +using namespace ::comphelper; + + static void lcl_addToList_throw( weld::ComboBox& _rListBox, ::std::vector<ColumnInfo>& o_aColumnList,const uno::Reference< container::XNameAccess>& i_xColumns ) + { + const uno::Sequence< OUString > aEntries = i_xColumns->getElementNames(); + for ( const OUString& rEntry : aEntries ) + { + uno::Reference< beans::XPropertySet> xColumn(i_xColumns->getByName(rEntry),uno::UNO_QUERY_THROW); + OUString sLabel; + if ( xColumn->getPropertySetInfo()->hasPropertyByName(PROPERTY_LABEL) ) + xColumn->getPropertyValue(PROPERTY_LABEL) >>= sLabel; + o_aColumnList.emplace_back(rEntry,sLabel ); + if ( !sLabel.isEmpty() ) + _rListBox.append_text( sLabel ); + else + _rListBox.append_text( rEntry ); + } + } + +/** + * Separated out from OFieldExpressionControl to prevent collision of ref-counted base classes + */ +class OFieldExpressionControl; + +namespace { + +class OFieldExpressionControlContainerListener : public ::cppu::WeakImplHelper< container::XContainerListener > +{ + VclPtr<OFieldExpressionControl> mpParent; +public: + explicit OFieldExpressionControlContainerListener(OFieldExpressionControl* pParent) : mpParent(pParent) {} + + // XEventListener + virtual void SAL_CALL disposing(const css::lang::EventObject& Source) override; + // XContainerListener + virtual void SAL_CALL elementInserted(const css::container::ContainerEvent& rEvent) override; + virtual void SAL_CALL elementReplaced(const css::container::ContainerEvent& rEvent) override; + virtual void SAL_CALL elementRemoved(const css::container::ContainerEvent& rEvent) override; +}; + +} + +class OFieldExpressionControl : public ::svt::EditBrowseBox +{ + ::osl::Mutex m_aMutex; + ::std::vector<sal_Int32> m_aGroupPositions; + ::std::vector<ColumnInfo> m_aColumnInfo; + VclPtr< ::svt::ComboBoxControl> m_pComboCell; + sal_Int32 m_nDataPos; + sal_Int32 m_nCurrentPos; + ImplSVEvent * m_nDeleteEvent; + OGroupsSortingDialog* m_pParent; + bool m_bIgnoreEvent; + rtl::Reference<OFieldExpressionControlContainerListener> aContainerListener; + +public: + OFieldExpressionControl(OGroupsSortingDialog* pParentDialog, const css::uno::Reference<css::awt::XWindow> &rParent); + virtual ~OFieldExpressionControl() override; + virtual void dispose() override; + + // XContainerListener + /// @throws css::uno::RuntimeException + void elementInserted(const css::container::ContainerEvent& rEvent); + /// @throws css::uno::RuntimeException + void elementRemoved(const css::container::ContainerEvent& rEvent); + + virtual Size GetOptimalSize() const override; + + void fillColumns(const uno::Reference< container::XNameAccess>& _xColumns); + void lateInit(); + bool IsDeleteAllowed( ) const; + void DeleteRows(); + + sal_Int32 getGroupPosition(sal_Int32 _nRow) const { return _nRow != BROWSER_ENDOFSELECTION ? m_aGroupPositions[_nRow] : sal_Int32(NO_GROUP); } + + /** returns the sequence with the selected groups + */ + uno::Sequence<uno::Any> fillSelectedGroups(); + + /** move groups given by _aGroups + */ + void moveGroups(const uno::Sequence<uno::Any>& _aGroups,sal_Int32 _nRow,bool _bSelect = true); + + virtual bool CursorMoving(sal_Int32 nNewRow, sal_uInt16 nNewCol) override; + using ::svt::EditBrowseBox::GetRowCount; +protected: + virtual bool IsTabAllowed(bool bForward) const override; + + virtual void InitController( ::svt::CellControllerRef& rController, sal_Int32 nRow, sal_uInt16 nCol ) override; + virtual ::svt::CellController* GetController( sal_Int32 nRow, sal_uInt16 nCol ) override; + virtual void PaintCell( OutputDevice& rDev, const tools::Rectangle& rRect, sal_uInt16 nColId ) const override; + virtual bool SeekRow( sal_Int32 nRow ) override; + virtual bool SaveModified() override; + virtual OUString GetCellText( sal_Int32 nRow, sal_uInt16 nColId ) const override; + virtual RowStatus GetRowStatus(sal_Int32 nRow) const override; + + virtual void KeyInput(const KeyEvent& rEvt) override; + virtual void Command( const CommandEvent& rEvt ) override; + + // D&D + virtual void StartDrag( sal_Int8 nAction, const Point& rPosPixel ) override; + virtual sal_Int8 AcceptDrop( const BrowserAcceptDropEvent& rEvt ) override; + virtual sal_Int8 ExecuteDrop( const BrowserExecuteDropEvent& rEvt ) override; + + using BrowseBox::AcceptDrop; + using BrowseBox::ExecuteDrop; + +private: + + DECL_LINK( CBChangeHdl, weld::ComboBox&, void); + +public: + DECL_LINK( DelayedDelete, void*, void ); + +}; + + +void OFieldExpressionControlContainerListener::disposing(const css::lang::EventObject& ) +{} + +void OFieldExpressionControlContainerListener::elementInserted(const css::container::ContainerEvent& rEvent) +{ mpParent->elementInserted(rEvent); } + +void OFieldExpressionControlContainerListener::elementReplaced(const css::container::ContainerEvent& ) +{} + +void OFieldExpressionControlContainerListener::elementRemoved(const css::container::ContainerEvent& rEvent) +{ mpParent->elementRemoved(rEvent); } + +OFieldExpressionControl::OFieldExpressionControl(OGroupsSortingDialog* pParentDialog, const css::uno::Reference<css::awt::XWindow> &rParent) + :EditBrowseBox( VCLUnoHelper::GetWindow(rParent), EditBrowseBoxFlags::NONE, WB_TABSTOP, + BrowserMode::COLUMNSELECTION | BrowserMode::MULTISELECTION | BrowserMode::AUTOSIZE_LASTCOL | + BrowserMode::KEEPHIGHLIGHT | BrowserMode::HLINES | BrowserMode::VLINES) + ,m_aGroupPositions(GROUPS_START_LEN,-1) + ,m_pComboCell(nullptr) + ,m_nDataPos(-1) + ,m_nCurrentPos(-1) + ,m_nDeleteEvent(nullptr) + ,m_pParent(pParentDialog) + ,m_bIgnoreEvent(false) + ,aContainerListener(new OFieldExpressionControlContainerListener(this)) +{ + SetBorderStyle(WindowBorderStyle::MONO); +} + +OFieldExpressionControl::~OFieldExpressionControl() +{ + disposeOnce(); +} + +void OFieldExpressionControl::dispose() +{ + uno::Reference< report::XGroups > xGroups = m_pParent->getGroups(); + xGroups->removeContainerListener(aContainerListener); + + // delete events from queue + if( m_nDeleteEvent ) + Application::RemoveUserEvent( m_nDeleteEvent ); + + m_pComboCell.disposeAndClear(); + m_pParent = nullptr; + ::svt::EditBrowseBox::dispose(); +} + +uno::Sequence<uno::Any> OFieldExpressionControl::fillSelectedGroups() +{ + uno::Sequence<uno::Any> aList; + ::std::vector<uno::Any> vClipboardList; + vClipboardList.reserve(GetSelectRowCount()); + + uno::Reference<report::XGroups> xGroups = m_pParent->getGroups(); + sal_Int32 nCount = xGroups->getCount(); + if ( nCount >= 1 ) + { + for( tools::Long nIndex=FirstSelectedRow(); nIndex != SFX_ENDOFSELECTION; nIndex=NextSelectedRow() ) + { + try + { + if ( m_aGroupPositions[nIndex] != NO_GROUP ) + { + uno::Reference< report::XGroup> xOrgGroup(xGroups->getByIndex(m_aGroupPositions[nIndex]),uno::UNO_QUERY); + /*uno::Reference< report::XGroup> xCopy = xGroups->createGroup(); + ::comphelper::copyProperties(xOrgGroup.get(),xCopy.get());*/ + vClipboardList.push_back( uno::Any(xOrgGroup) ); + } + } + catch(uno::Exception&) + { + OSL_FAIL("Can not access group!"); + } + } + if ( !vClipboardList.empty() ) + aList = uno::Sequence< uno::Any >(vClipboardList.data(), vClipboardList.size()); + } + return aList; +} + +void OFieldExpressionControl::StartDrag( sal_Int8 /*_nAction*/ , const Point& /*_rPosPixel*/ ) +{ + if ( m_pParent && !m_pParent->isReadOnly( ) ) + { + uno::Sequence<uno::Any> aClipboardList = fillSelectedGroups(); + + if( aClipboardList.hasElements() ) + { + rtl::Reference<OGroupExchange> pData = new OGroupExchange(aClipboardList); + pData->StartDrag(this, DND_ACTION_MOVE ); + } + } +} + +sal_Int8 OFieldExpressionControl::AcceptDrop( const BrowserAcceptDropEvent& rEvt ) +{ + sal_Int8 nAction = DND_ACTION_NONE; + if ( IsEditing() ) + { + weld::ComboBox& rComboBox = m_pComboCell->get_widget(); + sal_Int32 nPos = rComboBox.get_active(); + if (nPos != -1 || !rComboBox.get_active_text().isEmpty()) + SaveModified(); + DeactivateCell(); + } + if ( IsDropFormatSupported( OGroupExchange::getReportGroupId() ) && m_pParent->getGroups()->getCount() > 1 && rEvt.GetWindow() == &GetDataWindow() ) + { + nAction = DND_ACTION_MOVE; + } + return nAction; +} + +sal_Int8 OFieldExpressionControl::ExecuteDrop( const BrowserExecuteDropEvent& rEvt ) +{ + sal_Int8 nAction = DND_ACTION_NONE; + if ( IsDropFormatSupported( OGroupExchange::getReportGroupId() ) ) + { + sal_Int32 nRow = GetRowAtYPosPixel(rEvt.maPosPixel.Y(), false); + SetNoSelection(); + + TransferableDataHelper aDropped( rEvt.maDropEvent.Transferable ); + uno::Any aDrop = aDropped.GetAny(OGroupExchange::getReportGroupId(), OUString()); + uno::Sequence< uno::Any > aGroups; + aDrop >>= aGroups; + if ( aGroups.hasElements() ) + { + moveGroups(aGroups,nRow); + nAction = DND_ACTION_MOVE; + } + } + return nAction; +} + +void OFieldExpressionControl::moveGroups(const uno::Sequence<uno::Any>& _aGroups,sal_Int32 _nRow,bool _bSelect) +{ + if ( !_aGroups.hasElements() ) + return; + + m_bIgnoreEvent = true; + { + sal_Int32 nRow = _nRow; + const OUString sUndoAction(RptResId(RID_STR_UNDO_MOVE_GROUP)); + const UndoContext aUndoContext( m_pParent->m_pController->getUndoManager(), sUndoAction ); + + uno::Reference< report::XGroups> xGroups = m_pParent->getGroups(); + for(const uno::Any& rGroup : _aGroups) + { + uno::Reference< report::XGroup> xGroup(rGroup,uno::UNO_QUERY); + if ( xGroup.is() ) + { + uno::Sequence< beans::PropertyValue > aArgs{ comphelper::makePropertyValue( + PROPERTY_GROUP, xGroup) }; + // we use this way to create undo actions + m_pParent->m_pController->executeChecked(SID_GROUP_REMOVE,aArgs); + aArgs.realloc(2); + auto pArgs = aArgs.getArray(); + if ( nRow > xGroups->getCount() ) + nRow = xGroups->getCount(); + if ( _bSelect ) + SelectRow(nRow); + pArgs[1].Name = PROPERTY_POSITIONY; + pArgs[1].Value <<= nRow; + m_pParent->m_pController->executeChecked(SID_GROUP_APPEND,aArgs); + ++nRow; + } + } + } + m_bIgnoreEvent = false; + Invalidate(); +} + +void OFieldExpressionControl::fillColumns(const uno::Reference< container::XNameAccess>& _xColumns) +{ + weld::ComboBox& rComboBox = m_pComboCell->get_widget(); + rComboBox.clear(); + if ( _xColumns.is() ) + lcl_addToList_throw(rComboBox, m_aColumnInfo, _xColumns); +} + +void OFieldExpressionControl::lateInit() +{ + uno::Reference< report::XGroups > xGroups = m_pParent->getGroups(); + sal_Int32 nGroupsCount = xGroups->getCount(); + m_aGroupPositions.resize(::std::max<sal_Int32>(nGroupsCount,sal_Int32(GROUPS_START_LEN)),NO_GROUP); + ::std::vector<sal_Int32>::iterator aIter = m_aGroupPositions.begin(); + for (sal_Int32 i = 0; i < nGroupsCount; ++i,++aIter) + *aIter = i; + + if ( ColCount() == 0 ) + { + vcl::Font aFont( GetDataWindow().GetFont() ); + aFont.SetWeight( WEIGHT_NORMAL ); + GetDataWindow().SetFont( aFont ); + + // Set font of the headline to light + aFont = GetFont(); + aFont.SetWeight( WEIGHT_LIGHT ); + SetFont(aFont); + + InsertHandleColumn(static_cast<sal_uInt16>(GetTextWidth(OUString('0')) * 4)/*, sal_True */); + InsertDataColumn( FIELD_EXPRESSION, RptResId(STR_RPT_EXPRESSION), 100); + + m_pComboCell = VclPtr<ComboBoxControl>::Create( &GetDataWindow() ); + weld::ComboBox& rComboBox = m_pComboCell->get_widget(); + rComboBox.connect_changed(LINK(this,OFieldExpressionControl,CBChangeHdl)); + m_pComboCell->SetHelpId(HID_RPT_FIELDEXPRESSION); + + m_pComboCell->SetFocusInHdl(LINK(m_pParent, OGroupsSortingDialog, OnControlFocusGot)); + + + // set browse mode + BrowserMode nMode(BrowserMode::COLUMNSELECTION | BrowserMode::MULTISELECTION | BrowserMode::KEEPHIGHLIGHT | + BrowserMode::HLINES | BrowserMode::VLINES | BrowserMode::AUTOSIZE_LASTCOL | BrowserMode::AUTO_VSCROLL | BrowserMode::AUTO_HSCROLL); + if( m_pParent->isReadOnly() ) + nMode |= BrowserMode::HIDECURSOR; + SetMode(nMode); + xGroups->addContainerListener(aContainerListener); + } + else + // not the first call + RowRemoved(0, GetRowCount()); + + RowInserted(0, m_aGroupPositions.size()); +} + +IMPL_LINK_NOARG( OFieldExpressionControl, CBChangeHdl, weld::ComboBox&, void ) +{ + SaveModified(); +} + +bool OFieldExpressionControl::IsTabAllowed(bool /*bForward*/) const +{ + return false; +} + +bool OFieldExpressionControl::SaveModified() +{ + sal_Int32 nRow = GetCurRow(); + if ( nRow == BROWSER_ENDOFSELECTION ) + return true; + + try + { + bool bAppend = false; + uno::Reference< report::XGroup> xGroup; + if ( m_aGroupPositions[nRow] == NO_GROUP ) + { + bAppend = true; + OUString sUndoAction(RptResId(RID_STR_UNDO_APPEND_GROUP)); + m_pParent->m_pController->getUndoManager().EnterListAction( sUndoAction, OUString(), 0, ViewShellId(-1) ); + xGroup = m_pParent->getGroups()->createGroup(); + xGroup->setHeaderOn(true); + + // find position where to insert the new group + sal_Int32 nGroupPos = 0; + ::std::vector<sal_Int32>::iterator aIter = m_aGroupPositions.begin(); + ::std::vector<sal_Int32>::const_iterator aEnd = m_aGroupPositions.begin() + nRow; + for(;aIter != aEnd;++aIter) + if ( *aIter != NO_GROUP ) + nGroupPos = *aIter + 1; + uno::Sequence< beans::PropertyValue > aArgs{ + comphelper::makePropertyValue(PROPERTY_GROUP, xGroup), + comphelper::makePropertyValue(PROPERTY_POSITIONY, nGroupPos) + }; + m_bIgnoreEvent = true; + m_pParent->m_pController->executeChecked(SID_GROUP_APPEND,aArgs); + m_bIgnoreEvent = false; + OSL_ENSURE(*aIter == NO_GROUP ,"Illegal iterator!"); + *aIter++ = nGroupPos; + + aEnd = m_aGroupPositions.end(); + for(;aIter != aEnd;++aIter) + if ( *aIter != NO_GROUP ) + ++*aIter; + } + else + xGroup = m_pParent->getGroup(m_aGroupPositions[nRow]); + if ( xGroup.is() ) + { + weld::ComboBox& rComboBox = m_pComboCell->get_widget(); + sal_Int32 nPos = rComboBox.get_active(); + OUString sExpression; + if (nPos == -1) + sExpression = rComboBox.get_active_text(); + else + { + sExpression = m_aColumnInfo[nPos].sColumnName; + } + xGroup->setExpression( sExpression ); + + ::rptui::adjustSectionName(xGroup,nPos); + + if ( bAppend ) + m_pParent->m_pController->getUndoManager().LeaveListAction(); + } + + if (Controller().is()) + Controller()->SaveValue(); + if ( GetRowCount() == m_pParent->getGroups()->getCount() ) + { + RowInserted( GetRowCount()-1); + m_aGroupPositions.push_back(NO_GROUP); + } + + GoToRow(nRow); + m_pParent->DisplayData(nRow); + } + catch(uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "reportdesign", "OFieldExpressionControl::SaveModified"); + } + + return true; +} + +OUString OFieldExpressionControl::GetCellText( sal_Int32 nRow, sal_uInt16 /*nColId*/ ) const +{ + OUString sText; + if ( nRow != BROWSER_ENDOFSELECTION && m_aGroupPositions[nRow] != NO_GROUP ) + { + try + { + uno::Reference< report::XGroup> xGroup = m_pParent->getGroup(m_aGroupPositions[nRow]); + OUString sExpression = xGroup->getExpression(); + + auto aIter = std::find_if(m_aColumnInfo.begin(), m_aColumnInfo.end(), + [&sExpression](const ColumnInfo& rColumnInfo) { return rColumnInfo.sColumnName == sExpression; }); + if (aIter != m_aColumnInfo.end() && !aIter->sLabel.isEmpty()) + sExpression = aIter->sLabel; + sText = sExpression; + } + catch (const uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "reportdesign", "Exception caught while getting expression value from the group"); + } + } + return sText; +} + +void OFieldExpressionControl::InitController( CellControllerRef& /*rController*/, sal_Int32 nRow, sal_uInt16 nColumnId ) +{ + weld::ComboBox& rComboBox = m_pComboCell->get_widget(); + rComboBox.set_entry_text(GetCellText(nRow, nColumnId)); +} + +bool OFieldExpressionControl::CursorMoving(sal_Int32 nNewRow, sal_uInt16 nNewCol) +{ + + if (!EditBrowseBox::CursorMoving(nNewRow, nNewCol)) + return false; + m_nDataPos = nNewRow; + tools::Long nOldDataPos = GetCurRow(); + InvalidateStatusCell( m_nDataPos ); + InvalidateStatusCell( nOldDataPos ); + + m_pParent->SaveData( nOldDataPos ); + m_pParent->DisplayData( m_nDataPos ); + return true; +} + +CellController* OFieldExpressionControl::GetController( sal_Int32 /*nRow*/, sal_uInt16 /*nColumnId*/ ) +{ + ComboBoxCellController* pCellController = new ComboBoxCellController( m_pComboCell ); + pCellController->GetComboBox().set_entry_editable(m_pParent->m_pController->isEditable()); + return pCellController; +} + +bool OFieldExpressionControl::SeekRow( sal_Int32 _nRow ) +{ + // the basis class needs the call, because that's how the class knows which line will be painted + EditBrowseBox::SeekRow(_nRow); + m_nCurrentPos = _nRow; + return true; +} + +void OFieldExpressionControl::PaintCell( OutputDevice& rDev, const tools::Rectangle& rRect, sal_uInt16 nColumnId ) const +{ + OUString aText =GetCellText( m_nCurrentPos, nColumnId ); + + Point aPos( rRect.TopLeft() ); + Size aTextSize( GetDataWindow().GetTextWidth( aText ), GetDataWindow().GetTextHeight() ); + + if( aPos.X() < rRect.Left() || aPos.X() + aTextSize.Width() > rRect.Right() || + aPos.Y() < rRect.Top() || aPos.Y() + aTextSize.Height() > rRect.Bottom() ) + rDev.SetClipRegion(vcl::Region(rRect)); + + rDev.DrawText( aPos, aText ); + + if( rDev.IsClipRegion() ) + rDev.SetClipRegion(); +} + +EditBrowseBox::RowStatus OFieldExpressionControl::GetRowStatus(sal_Int32 nRow) const +{ + if (nRow >= 0 && nRow == m_nDataPos) + return EditBrowseBox::CURRENT; + if ( nRow != BROWSER_ENDOFSELECTION && nRow < static_cast<tools::Long>(m_aGroupPositions.size()) && m_aGroupPositions[nRow] != NO_GROUP ) + { + try + { + uno::Reference< report::XGroup> xGroup = m_pParent->getGroup(m_aGroupPositions[nRow]); + return (xGroup->getHeaderOn() || xGroup->getFooterOn())? EditBrowseBox::HEADERFOOTER : EditBrowseBox::CLEAN; + } + catch(uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "reportdesign", "Exception caught while try to get a group!"); + } + } + return EditBrowseBox::CLEAN; +} + +// XContainerListener + +void OFieldExpressionControl::elementInserted(const container::ContainerEvent& evt) +{ + if ( m_bIgnoreEvent ) + return; + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + sal_Int32 nGroupPos = 0; + if ( !(evt.Accessor >>= nGroupPos) ) + return; + + if ( nGroupPos >= GetRowCount() ) + { + sal_Int32 nAddedRows = nGroupPos - GetRowCount(); + RowInserted(nAddedRows); + for (sal_Int32 i = 0; i < nAddedRows; ++i) + m_aGroupPositions.push_back(NO_GROUP); + m_aGroupPositions[nGroupPos] = nGroupPos; + } + else + { + ::std::vector<sal_Int32>::iterator aFind = m_aGroupPositions.begin()+ nGroupPos; + if ( aFind == m_aGroupPositions.end() ) + aFind = ::std::find(m_aGroupPositions.begin(),m_aGroupPositions.end(),NO_GROUP); + + if ( aFind != m_aGroupPositions.end() ) + { + if ( *aFind != NO_GROUP ) + aFind = m_aGroupPositions.insert(aFind,nGroupPos); + else + *aFind = nGroupPos; + + ::std::vector<sal_Int32>::const_iterator aEnd = m_aGroupPositions.end(); + for(++aFind;aFind != aEnd;++aFind) + if ( *aFind != NO_GROUP ) + ++*aFind; + } + } + Invalidate(); +} + +void OFieldExpressionControl::elementRemoved(const container::ContainerEvent& evt) +{ + SolarMutexGuard aSolarGuard; + ::osl::MutexGuard aGuard( m_aMutex ); + + if ( m_bIgnoreEvent ) + return; + + sal_Int32 nGroupPos = 0; + if ( !(evt.Accessor >>= nGroupPos) ) + return; + + std::vector<sal_Int32>::iterator aEnd = m_aGroupPositions.end(); + std::vector<sal_Int32>::iterator aFind = std::find(m_aGroupPositions.begin(), aEnd, nGroupPos); + if (aFind != aEnd) + { + *aFind = NO_GROUP; + for(++aFind;aFind != aEnd;++aFind) + if ( *aFind != NO_GROUP ) + --*aFind; + Invalidate(); + } +} + +bool OFieldExpressionControl::IsDeleteAllowed( ) const +{ + return !m_pParent->isReadOnly() && GetSelectRowCount() > 0; +} + +void OFieldExpressionControl::KeyInput( const KeyEvent& rEvt ) +{ + if (IsDeleteAllowed()) + { + if (rEvt.GetKeyCode().GetCode() == KEY_DELETE && // Delete rows + !rEvt.GetKeyCode().IsShift() && + !rEvt.GetKeyCode().IsMod1()) + { + DeleteRows(); + return; + } + } + EditBrowseBox::KeyInput(rEvt); +} + +void OFieldExpressionControl::Command(const CommandEvent& rEvt) +{ + switch (rEvt.GetCommand()) + { + case CommandEventId::ContextMenu: + { + if (!rEvt.IsMouseEvent()) + { + EditBrowseBox::Command(rEvt); + return; + } + + sal_uInt16 nColId = GetColumnId(GetColumnAtXPosPixel(rEvt.GetMousePosPixel().X())); + + if ( nColId == HANDLE_ID ) + { + bool bEnable = false; + tools::Long nIndex = FirstSelectedRow(); + while( nIndex != SFX_ENDOFSELECTION && !bEnable ) + { + if ( m_aGroupPositions[nIndex] != NO_GROUP ) + bEnable = true; + nIndex = NextSelectedRow(); + } + + ::tools::Rectangle aRect(rEvt.GetMousePosPixel(), Size(1, 1)); + weld::Window* pPopupParent = weld::GetPopupParent(*this, aRect); + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(pPopupParent, "modules/dbreport/ui/groupsortmenu.ui")); + std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu("menu")); + xContextMenu->set_sensitive("delete", IsDeleteAllowed() && bEnable); + if (!xContextMenu->popup_at_rect(pPopupParent, aRect).isEmpty()) + { + if( m_nDeleteEvent ) + Application::RemoveUserEvent( m_nDeleteEvent ); + m_nDeleteEvent = Application::PostUserEvent( LINK(this, OFieldExpressionControl, DelayedDelete), nullptr, true ); + } + } + [[fallthrough]]; + } + default: + EditBrowseBox::Command(rEvt); + } + +} + +void OFieldExpressionControl::DeleteRows() +{ + + bool bIsEditing = IsEditing(); + if (bIsEditing) + { + DeactivateCell(); + } + tools::Long nIndex = FirstSelectedRow(); + if (nIndex == SFX_ENDOFSELECTION) + { + nIndex = GetCurRow(); + } + bool bFirstTime = true; + + tools::Long nOldDataPos = nIndex; + m_bIgnoreEvent = true; + while( nIndex >= 0 ) + { + if ( m_aGroupPositions[nIndex] != NO_GROUP ) + { + if ( bFirstTime ) + { + bFirstTime = false; + OUString sUndoAction(RptResId(RID_STR_UNDO_REMOVE_SELECTION)); + m_pParent->m_pController->getUndoManager().EnterListAction( sUndoAction, OUString(), 0, ViewShellId(-1) ); + } + + sal_Int32 nGroupPos = m_aGroupPositions[nIndex]; + uno::Reference< report::XGroup> xGroup = m_pParent->getGroup(nGroupPos); + uno::Sequence< beans::PropertyValue > aArgs{ comphelper::makePropertyValue( + PROPERTY_GROUP, xGroup) }; + // we use this way to create undo actions + m_pParent->m_pController->executeChecked(SID_GROUP_REMOVE,aArgs); + + std::vector<sal_Int32>::iterator aEnd = m_aGroupPositions.end(); + std::vector<sal_Int32>::iterator aFind = std::find(m_aGroupPositions.begin(), aEnd, nGroupPos); + if (aFind != aEnd) + { + *aFind = NO_GROUP; + for(++aFind;aFind != aEnd;++aFind) + if ( *aFind != NO_GROUP ) + --*aFind; + } + } + nIndex = NextSelectedRow(); + } + + if ( !bFirstTime ) + m_pParent->m_pController->getUndoManager().LeaveListAction(); + + m_nDataPos = GetCurRow(); + InvalidateStatusCell( nOldDataPos ); + InvalidateStatusCell( m_nDataPos ); + ActivateCell(); + m_pParent->DisplayData( m_nDataPos ); + m_bIgnoreEvent = false; + Invalidate(); +} + +IMPL_LINK_NOARG( OFieldExpressionControl, DelayedDelete, void*, void ) +{ + m_nDeleteEvent = nullptr; + DeleteRows(); +} + +Size OFieldExpressionControl::GetOptimalSize() const +{ + return LogicToPixel(Size(106, 75), MapMode(MapUnit::MapAppFont)); +} + +OGroupsSortingDialog::OGroupsSortingDialog(weld::Window* pParent, bool bReadOnly, + OReportController* pController) + : GenericDialogController(pParent, "modules/dbreport/ui/floatingsort.ui", "FloatingSort") + , OPropertyChangeListener(m_aMutex) + , m_pController(pController) + , m_xGroups(m_pController->getReportDefinition()->getGroups()) + , m_bReadOnly(bReadOnly) + , m_xToolBox(m_xBuilder->weld_toolbar("toolbox")) + , m_xProperties(m_xBuilder->weld_widget("properties")) + , m_xOrderLst(m_xBuilder->weld_combo_box("sorting")) + , m_xHeaderLst(m_xBuilder->weld_combo_box("header")) + , m_xFooterLst(m_xBuilder->weld_combo_box("footer")) + , m_xGroupOnLst(m_xBuilder->weld_combo_box("group")) + , m_xGroupIntervalEd(m_xBuilder->weld_spin_button("interval")) + , m_xKeepTogetherLst(m_xBuilder->weld_combo_box("keep")) + , m_xHelpWindow(m_xBuilder->weld_label("helptext")) + , m_xBox(m_xBuilder->weld_container("box")) + , m_xTableCtrlParent(m_xBox->CreateChildFrame()) + , m_xFieldExpression(VclPtr<OFieldExpressionControl>::Create(this, m_xTableCtrlParent)) +{ + m_xHelpWindow->set_size_request(-1, m_xHelpWindow->get_text_height() * 4); + m_xFieldExpression->set_hexpand(true); + m_xFieldExpression->set_vexpand(true); + + weld::Widget* pControlsLst[] = { m_xHeaderLst.get(), m_xFooterLst.get(), m_xGroupOnLst.get(), + m_xKeepTogetherLst.get(), m_xOrderLst.get(), m_xGroupIntervalEd.get() }; + for (weld::Widget* i : pControlsLst) + { + i->connect_focus_in(LINK(this, OGroupsSortingDialog, OnWidgetFocusGot)); + i->show(); + } + + m_xGroupIntervalEd->connect_focus_out(LINK(this, OGroupsSortingDialog, OnWidgetFocusLost)); + + for (size_t i = 0; i < SAL_N_ELEMENTS(pControlsLst) - 1; ++i) + dynamic_cast<weld::ComboBox&>(*pControlsLst[i]).connect_changed(LINK(this,OGroupsSortingDialog,LBChangeHdl)); + + m_pReportListener = new OPropertyChangeMultiplexer(this, m_pController->getReportDefinition()); + m_pReportListener->addProperty(PROPERTY_COMMAND); + m_pReportListener->addProperty(PROPERTY_COMMANDTYPE); + + m_xFieldExpression->lateInit(); + fillColumns(); + Size aPrefSize = m_xFieldExpression->GetOptimalSize(); + m_xBox->set_size_request(aPrefSize.Width(), aPrefSize.Height()); + m_xFieldExpression->Show(); + + m_xToolBox->connect_clicked(LINK(this, OGroupsSortingDialog, OnFormatAction)); + + checkButtons(0); +} + +OGroupsSortingDialog::~OGroupsSortingDialog() +{ + m_pReportListener->dispose(); + if ( m_pCurrentGroupListener.is() ) + m_pCurrentGroupListener->dispose(); + m_xFieldExpression.disposeAndClear(); + m_xTableCtrlParent->dispose(); + m_xTableCtrlParent.clear(); +} + +void OGroupsSortingDialog::UpdateData( ) +{ + m_xFieldExpression->Invalidate(); + sal_Int32 nCurRow = m_xFieldExpression->GetCurRow(); + m_xFieldExpression->DeactivateCell(); + m_xFieldExpression->ActivateCell(nCurRow, m_xFieldExpression->GetCurColumnId()); + DisplayData(nCurRow); +} + +void OGroupsSortingDialog::DisplayData( sal_Int32 _nRow ) +{ + const sal_Int32 nGroupPos = m_xFieldExpression->getGroupPosition(_nRow); + const bool bEmpty = nGroupPos == NO_GROUP; + m_xProperties->set_sensitive(!bEmpty); + + checkButtons(_nRow); + + if ( m_pCurrentGroupListener.is() ) + m_pCurrentGroupListener->dispose(); + m_pCurrentGroupListener = nullptr; + if (!bEmpty) + { + uno::Reference< report::XGroup> xGroup = getGroup(nGroupPos); + + m_pCurrentGroupListener = new OPropertyChangeMultiplexer(this, xGroup); + m_pCurrentGroupListener->addProperty(PROPERTY_HEADERON); + m_pCurrentGroupListener->addProperty(PROPERTY_FOOTERON); + + displayGroup(xGroup); + } +} + +void OGroupsSortingDialog::SaveData( sal_Int32 _nRow) +{ + sal_Int32 nGroupPos = m_xFieldExpression->getGroupPosition(_nRow); + if ( nGroupPos == NO_GROUP ) + return; + + uno::Reference< report::XGroup> xGroup = getGroup(nGroupPos); + if (m_xHeaderLst->get_value_changed_from_saved()) + xGroup->setHeaderOn( m_xHeaderLst->get_active() == 0 ); + if (m_xFooterLst->get_value_changed_from_saved()) + xGroup->setFooterOn( m_xFooterLst->get_active() == 0 ); + if (m_xKeepTogetherLst->get_value_changed_from_saved()) + xGroup->setKeepTogether( m_xKeepTogetherLst->get_active() ); + if (m_xGroupOnLst->get_value_changed_from_saved()) + { + auto nGroupOn = m_xGroupOnLst->get_active_id().toInt32(); + xGroup->setGroupOn( nGroupOn ); + } + if (m_xGroupIntervalEd->get_value_changed_from_saved()) + { + xGroup->setGroupInterval(m_xGroupIntervalEd->get_value()); + m_xGroupIntervalEd->save_value(); + } + if ( m_xOrderLst->get_value_changed_from_saved() ) + xGroup->setSortAscending( m_xOrderLst->get_active() == 0 ); + + weld::ComboBox* pControls[] = { m_xHeaderLst.get(), m_xFooterLst.get(), m_xGroupOnLst.get(), + m_xKeepTogetherLst.get(), m_xOrderLst.get() }; + for (weld::ComboBox* pControl : pControls) + pControl->save_value(); +} + +sal_Int32 OGroupsSortingDialog::getColumnDataType(const OUString& _sColumnName) +{ + sal_Int32 nDataType = sdbc::DataType::VARCHAR; + try + { + if ( !m_xColumns.is() ) + fillColumns(); + if ( m_xColumns.is() && m_xColumns->hasByName(_sColumnName) ) + { + uno::Reference< beans::XPropertySet> xColumn(m_xColumns->getByName(_sColumnName),uno::UNO_QUERY); + if ( xColumn.is() ) + xColumn->getPropertyValue(PROPERTY_TYPE) >>= nDataType; + } + } + catch(uno::Exception&) + { + TOOLS_WARN_EXCEPTION( "reportdesign", "Exception caught while getting the type of a column"); + } + + return nDataType; +} + +IMPL_LINK_NOARG(OGroupsSortingDialog, OnControlFocusGot, LinkParamNone*, void ) +{ + m_xHelpWindow->set_label(RptResId(STR_RPT_HELP_FIELD)); +} + +IMPL_LINK(OGroupsSortingDialog, OnWidgetFocusGot, weld::Widget&, rControl, void ) +{ + const std::pair<weld::Widget*, TranslateId> pControls[] = { + { m_xHeaderLst.get(), STR_RPT_HELP_HEADER }, + { m_xFooterLst.get(), STR_RPT_HELP_FOOTER }, + { m_xGroupOnLst.get(), STR_RPT_HELP_GROUPON }, + { m_xGroupIntervalEd.get(), STR_RPT_HELP_INTERVAL }, + { m_xKeepTogetherLst.get(), STR_RPT_HELP_KEEP }, + { m_xOrderLst.get(), STR_RPT_HELP_SORT } + }; + for (size_t i = 0; i < SAL_N_ELEMENTS(pControls); ++i) + { + if (&rControl == pControls[i].first) + { + weld::ComboBox* pListBox = dynamic_cast<weld::ComboBox*>( &rControl ); + if ( pListBox ) + pListBox->save_value(); + weld::SpinButton* pNumericField = dynamic_cast<weld::SpinButton*>(&rControl); + if ( pNumericField ) + pNumericField->save_value(); + //shows the text given by the id in the multiline edit + m_xHelpWindow->set_label(RptResId(pControls[i].second)); + break; + } + } +} + +IMPL_LINK_NOARG(OGroupsSortingDialog, OnWidgetFocusLost, weld::Widget&, void) +{ + if (m_xFieldExpression) + { + if (m_xGroupIntervalEd->get_value_changed_from_saved()) + SaveData(m_xFieldExpression->GetCurRow()); + } +} + +IMPL_LINK(OGroupsSortingDialog, OnFormatAction, const OUString&, rCommand, void) +{ + if ( !m_xFieldExpression ) + return; + + tools::Long nIndex = m_xFieldExpression->GetCurrRow(); + sal_Int32 nGroupPos = m_xFieldExpression->getGroupPosition(nIndex); + uno::Sequence<uno::Any> aClipboardList; + if ( nIndex >= 0 && nGroupPos != NO_GROUP ) + { + aClipboardList = { m_xGroups->getByIndex(nGroupPos) }; + } + if (rCommand == "up") + { + --nIndex; + } + if (rCommand == "down") + { + ++nIndex; + } + if (rCommand == "delete") + { + Application::PostUserEvent(LINK(m_xFieldExpression, OFieldExpressionControl, DelayedDelete)); + } + else + { + if ( nIndex >= 0 && aClipboardList.hasElements() ) + { + m_xFieldExpression->SetNoSelection(); + m_xFieldExpression->moveGroups(aClipboardList,nIndex,false); + m_xFieldExpression->DeactivateCell(); + m_xFieldExpression->GoToRow(nIndex); + m_xFieldExpression->ActivateCell(nIndex, m_xFieldExpression->GetCurColumnId()); + DisplayData(nIndex); + } + } +} + +IMPL_LINK( OGroupsSortingDialog, LBChangeHdl, weld::ComboBox&, rListBox, void ) +{ + if ( !rListBox.get_value_changed_from_saved() ) + return; + + sal_Int32 nRow = m_xFieldExpression->GetCurRow(); + sal_Int32 nGroupPos = m_xFieldExpression->getGroupPosition(nRow); + if (&rListBox != m_xHeaderLst.get() && &rListBox != m_xFooterLst.get()) + { + if ( rListBox.get_value_changed_from_saved() ) + SaveData(nRow); + if ( &rListBox == m_xGroupOnLst.get() ) + m_xGroupIntervalEd->set_sensitive(rListBox.get_active() != 0); + } + else if ( nGroupPos != NO_GROUP ) + { + uno::Reference< report::XGroup> xGroup = getGroup(nGroupPos); + const OUString aHeaderFooterOnName(( m_xHeaderLst.get() == &rListBox ) + ? std::u16string_view(PROPERTY_HEADERON) + : std::u16string_view(PROPERTY_FOOTERON)); + uno::Sequence< beans::PropertyValue > aArgs{ + comphelper::makePropertyValue(aHeaderFooterOnName, rListBox.get_active() == 0), + comphelper::makePropertyValue(PROPERTY_GROUP, xGroup) + }; + m_pController->executeChecked(m_xHeaderLst.get() == &rListBox ? SID_GROUPHEADER : SID_GROUPFOOTER, aArgs); + m_xFieldExpression->InvalidateHandleColumn(); + } +} + +void OGroupsSortingDialog::_propertyChanged(const beans::PropertyChangeEvent& _rEvent) +{ + uno::Reference< report::XGroup > xGroup(_rEvent.Source,uno::UNO_QUERY); + if ( xGroup.is() ) + displayGroup(xGroup); + else + fillColumns(); +} + +void OGroupsSortingDialog::fillColumns() +{ + m_xColumns = m_pController->getColumns(); + m_xFieldExpression->fillColumns(m_xColumns); +} + +void OGroupsSortingDialog::displayGroup(const uno::Reference<report::XGroup>& _xGroup) +{ + m_xHeaderLst->set_active(_xGroup->getHeaderOn() ? 0 : 1 ); + m_xFooterLst->set_active(_xGroup->getFooterOn() ? 0 : 1 ); + sal_Int32 nDataType = getColumnDataType(_xGroup->getExpression()); + + // first clear whole group on list + while (m_xGroupOnLst->get_count() > 1 ) + { + m_xGroupOnLst->remove(1); + } + + switch(nDataType) + { + case sdbc::DataType::LONGVARCHAR: + case sdbc::DataType::VARCHAR: + case sdbc::DataType::CHAR: + m_xGroupOnLst->append(OUString::number(report::GroupOn::PREFIX_CHARACTERS), RptResId(STR_RPT_PREFIXCHARS)); + break; + case sdbc::DataType::DATE: + case sdbc::DataType::TIME: + case sdbc::DataType::TIMESTAMP: + { + const TranslateId aIds[] = { STR_RPT_YEAR, STR_RPT_QUARTER,STR_RPT_MONTH,STR_RPT_WEEK,STR_RPT_DAY,STR_RPT_HOUR,STR_RPT_MINUTE }; + for (size_t i = 0; i < SAL_N_ELEMENTS(aIds); ++i) + { + m_xGroupOnLst->append(OUString::number(i+2), RptResId(aIds[i])); + } + } + break; + default: + m_xGroupOnLst->append(OUString::number(report::GroupOn::INTERVAL), RptResId(STR_RPT_INTERVAL)); + break; + } + sal_Int32 nPos = 0; + switch(_xGroup->getGroupOn()) + { + case report::GroupOn::DEFAULT: + nPos = 0; + break; + case report::GroupOn::PREFIX_CHARACTERS: + nPos = 1; + break; + case report::GroupOn::YEAR: + nPos = 1; + break; + case report::GroupOn::QUARTAL: + nPos = 2; + break; + case report::GroupOn::MONTH: + nPos = 3; + break; + case report::GroupOn::WEEK: + nPos = 4; + break; + case report::GroupOn::DAY: + nPos = 5; + break; + case report::GroupOn::HOUR: + nPos = 6; + break; + case report::GroupOn::MINUTE: + nPos = 7; + break; + case report::GroupOn::INTERVAL: + nPos = 1; + break; + default: + nPos = 0; + } + m_xGroupOnLst->set_active(nPos); + m_xGroupIntervalEd->set_value(_xGroup->getGroupInterval()); + m_xGroupIntervalEd->save_value(); + m_xGroupIntervalEd->set_sensitive( nPos != 0 ); + m_xKeepTogetherLst->set_active(_xGroup->getKeepTogether()); + m_xOrderLst->set_active(_xGroup->getSortAscending() ? 0 : 1); + + weld::ComboBox* pControls[] = { m_xHeaderLst.get(), m_xFooterLst.get(), m_xGroupOnLst.get(), + m_xKeepTogetherLst.get(), m_xOrderLst.get() }; + for (weld::ComboBox* pControl : pControls) + pControl->save_value(); + + bool bReadOnly = !m_pController->isEditable(); + for (weld::ComboBox* pControl : pControls) + pControl->set_sensitive(!bReadOnly); + m_xGroupIntervalEd->set_editable(!bReadOnly); +} + +void OGroupsSortingDialog::checkButtons(sal_Int32 _nRow) +{ + sal_Int32 nGroupCount = m_xGroups->getCount(); + sal_Int32 nRowCount = m_xFieldExpression->GetRowCount(); + bool bEnabled = nGroupCount > 1; + + if (bEnabled && _nRow > 0 ) + { + m_xToolBox->set_item_sensitive("up", true); + } + else + { + m_xToolBox->set_item_sensitive("up", false); + } + if (bEnabled && _nRow < (nRowCount - 1) ) + { + m_xToolBox->set_item_sensitive("down", true); + } + else + { + m_xToolBox->set_item_sensitive("down", false); + } + + sal_Int32 nGroupPos = m_xFieldExpression->getGroupPosition(_nRow); + if ( nGroupPos != NO_GROUP ) + { + bool bEnableDelete = nGroupCount > 0; + m_xToolBox->set_item_sensitive("delete", bEnableDelete); + } + else + { + m_xToolBox->set_item_sensitive("delete", false); + } +} + +} // rptui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/reportdesign/source/ui/dlg/Navigator.cxx b/reportdesign/source/ui/dlg/Navigator.cxx new file mode 100644 index 0000000000..690d90ca1b --- /dev/null +++ b/reportdesign/source/ui/dlg/Navigator.cxx @@ -0,0 +1,814 @@ +/* -*- 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 <Navigator.hxx> + +#include <strings.hxx> +#include <bitmaps.hlst> +#include <ReportController.hxx> +#include <UITools.hxx> +#include <reportformula.hxx> +#include <com/sun/star/report/XFixedText.hpp> +#include <com/sun/star/report/XFixedLine.hpp> +#include <com/sun/star/report/XFormattedField.hpp> +#include <com/sun/star/report/XImageControl.hpp> +#include <com/sun/star/report/XShape.hpp> +#include <helpids.h> +#include <strings.hrc> +#include <rptui_slotid.hrc> +#include <comphelper/propmultiplex.hxx> +#include <comphelper/propertyvalue.hxx> +#include <comphelper/containermultiplexer.hxx> +#include <cppuhelper/basemutex.hxx> +#include <comphelper/SelectionMultiplex.hxx> +#include <utility> +#include <vcl/svapp.hxx> +#include <vcl/weld.hxx> +#include <vcl/commandevent.hxx> +#include <ReportVisitor.hxx> +#include <core_resource.hxx> +#include <rtl/ref.hxx> +#include <svx/svxids.hrc> + +#include <memory> +#include <string_view> + +namespace rptui +{ +using namespace ::com::sun::star; +using namespace utl; +using namespace ::comphelper; + +static OUString lcl_getImageId(const uno::Reference< report::XReportComponent>& _xElement) +{ + OUString sId; + uno::Reference< report::XFixedLine> xFixedLine(_xElement,uno::UNO_QUERY); + if ( uno::Reference< report::XFixedText>(_xElement,uno::UNO_QUERY).is() ) + sId = RID_SVXBMP_FM_FIXEDTEXT; + else if ( xFixedLine.is() ) + sId = xFixedLine->getOrientation() ? RID_SVXBMP_INSERT_VFIXEDLINE : RID_SVXBMP_INSERT_HFIXEDLINE; + else if ( uno::Reference< report::XFormattedField>(_xElement,uno::UNO_QUERY).is() ) + sId = RID_SVXBMP_FM_EDIT; + else if ( uno::Reference< report::XImageControl>(_xElement,uno::UNO_QUERY).is() ) + sId = RID_SVXBMP_FM_IMAGECONTROL; + else if ( uno::Reference< report::XShape>(_xElement,uno::UNO_QUERY).is() ) + sId = RID_SVXBMP_DRAWTBX_CS_BASIC; + return sId; +} + +static OUString lcl_getName(const uno::Reference< beans::XPropertySet>& _xElement) +{ + OSL_ENSURE(_xElement.is(),"Found report element which is NULL!"); + OUString sTempName; + _xElement->getPropertyValue(PROPERTY_NAME) >>= sTempName; + OUStringBuffer sName(sTempName); + uno::Reference< report::XFixedText> xFixedText(_xElement,uno::UNO_QUERY); + uno::Reference< report::XReportControlModel> xReportModel(_xElement,uno::UNO_QUERY); + if ( xFixedText.is() ) + { + sName.append(" : " + xFixedText->getLabel()); + } + else if ( xReportModel.is() && _xElement->getPropertySetInfo()->hasPropertyByName(PROPERTY_DATAFIELD) ) + { + ReportFormula aFormula( xReportModel->getDataField() ); + if ( aFormula.isValid() ) + { + sName.append(" : " + aFormula.getUndecoratedContent() ); + } + } + return sName.makeStringAndClear(); +} + +class NavigatorTree : public ::cppu::BaseMutex + , public reportdesign::ITraverseReport + , public comphelper::OSelectionChangeListener + , public ::comphelper::OPropertyChangeListener +{ + class UserData; + friend class UserData; + class UserData : public ::cppu::BaseMutex + ,public ::comphelper::OPropertyChangeListener + ,public ::comphelper::OContainerListener + { + uno::Reference< uno::XInterface > m_xContent; + ::rtl::Reference< comphelper::OPropertyChangeMultiplexer> m_pListener; + ::rtl::Reference< comphelper::OContainerListenerAdapter> m_pContainerListener; + NavigatorTree* m_pTree; + public: + UserData(NavigatorTree* pTree, uno::Reference<uno::XInterface> xContent); + virtual ~UserData() override; + + const uno::Reference< uno::XInterface >& getContent() const { return m_xContent; } + void setContent(const uno::Reference< uno::XInterface >& _xContent) { m_xContent = _xContent; } + + protected: + // OPropertyChangeListener + virtual void _propertyChanged(const beans::PropertyChangeEvent& _rEvent) override; + + // OContainerListener + virtual void _elementInserted( const container::ContainerEvent& _rEvent ) override; + virtual void _elementRemoved( const container::ContainerEvent& Event ) override; + virtual void _elementReplaced( const container::ContainerEvent& _rEvent ) override; + virtual void _disposing(const lang::EventObject& _rSource) override; + }; + + std::unique_ptr<weld::TreeView> m_xTreeView; + OReportController& m_rController; + std::unique_ptr<weld::TreeIter> m_xMasterReport; + ::rtl::Reference< comphelper::OPropertyChangeMultiplexer> m_pReportListener; + ::rtl::Reference< comphelper::OSelectionChangeMultiplexer> m_pSelectionListener; + + void insertEntry(const OUString& rName, const weld::TreeIter* pParent, const OUString& rImageId, int nPosition, const UserData* pData, weld::TreeIter& rRet); + + void traverseSection(const uno::Reference<report::XSection>& xSection, const weld::TreeIter* pParent, const OUString& rImageId, int nPosition = -1); + void traverseFunctions(const uno::Reference< report::XFunctions>& xFunctions, const weld::TreeIter* pParent); + +protected: + // OSelectionChangeListener + virtual void _disposing(const lang::EventObject& _rSource) override; + + // OPropertyChangeListener + virtual void _propertyChanged(const beans::PropertyChangeEvent& _rEvent) override; + + // OContainerListener Helper + void _elementInserted( const container::ContainerEvent& _rEvent ); + void _elementRemoved( const container::ContainerEvent& Event ); + void _elementReplaced( const container::ContainerEvent& _rEvent ); + +public: + NavigatorTree(std::unique_ptr<weld::TreeView>, OReportController& rController); + virtual ~NavigatorTree() override; + + DECL_LINK(OnEntrySelDesel, weld::TreeView&, void); + DECL_LINK(CommandHdl, const CommandEvent&, bool); + + virtual void _selectionChanged( const lang::EventObject& aEvent ) override; + + // ITraverseReport + virtual void traverseReport(const uno::Reference< report::XReportDefinition>& xReport) override; + virtual void traverseReportFunctions(const uno::Reference< report::XFunctions>& xFunctions) override; + virtual void traverseReportHeader(const uno::Reference< report::XSection>& xSection) override; + virtual void traverseReportFooter(const uno::Reference< report::XSection>& xSection) override; + virtual void traversePageHeader(const uno::Reference< report::XSection>& xSection) override; + virtual void traversePageFooter(const uno::Reference< report::XSection>& xSection) override; + + virtual void traverseGroups(const uno::Reference< report::XGroups>& xGroups) override; + virtual void traverseGroup(const uno::Reference< report::XGroup>& xGroup) override; + virtual void traverseGroupFunctions(const uno::Reference< report::XFunctions>& xFunctions) override; + virtual void traverseGroupHeader(const uno::Reference< report::XSection>& xSection) override; + virtual void traverseGroupFooter(const uno::Reference< report::XSection>& xSection) override; + + virtual void traverseDetail(const uno::Reference< report::XSection>& xSection) override; + + bool find(const uno::Reference<uno::XInterface>& xContent, weld::TreeIter& rIter); + void removeEntry(const weld::TreeIter& rEntry, bool bRemove = true); + + void grab_focus() { m_xTreeView->grab_focus(); } + + void set_text(const weld::TreeIter& rIter, const OUString& rStr) + { + m_xTreeView->set_text(rIter, rStr); + } + + void expand_row(const weld::TreeIter& rIter) + { + m_xTreeView->expand_row(rIter); + } + + std::unique_ptr<weld::TreeIter> make_iterator() const + { + return m_xTreeView->make_iterator(); + } + + int iter_n_children(const weld::TreeIter& rIter) const + { + return m_xTreeView->iter_n_children(rIter); + } +}; + +NavigatorTree::NavigatorTree(std::unique_ptr<weld::TreeView> xTreeView, OReportController& rController) + : OPropertyChangeListener(m_aMutex) + , m_xTreeView(std::move(xTreeView)) + , m_rController(rController) +{ + m_xTreeView->set_size_request(m_xTreeView->get_approximate_digit_width() * 25, m_xTreeView->get_height_rows(18)); + + m_pReportListener = new OPropertyChangeMultiplexer(this,m_rController.getReportDefinition()); + m_pReportListener->addProperty(PROPERTY_PAGEHEADERON); + m_pReportListener->addProperty(PROPERTY_PAGEFOOTERON); + m_pReportListener->addProperty(PROPERTY_REPORTHEADERON); + m_pReportListener->addProperty(PROPERTY_REPORTFOOTERON); + + m_pSelectionListener = new OSelectionChangeMultiplexer(this,&m_rController); + + m_xTreeView->set_help_id(HID_REPORT_NAVIGATOR_TREE); + + m_xTreeView->set_selection_mode(SelectionMode::Multiple); + + m_xTreeView->connect_changed(LINK(this, NavigatorTree, OnEntrySelDesel)); + m_xTreeView->connect_popup_menu(LINK(this, NavigatorTree, CommandHdl)); +} + +NavigatorTree::~NavigatorTree() +{ + m_xTreeView->all_foreach([this](weld::TreeIter& rIter) { + UserData* pData = weld::fromId<UserData*>(m_xTreeView->get_id(rIter)); + delete pData; + return false; + }); + m_pSelectionListener->dispose(); + m_pReportListener->dispose(); +} + +namespace +{ + sal_uInt16 mapIdent(std::u16string_view rIdent) + { + if (rIdent == u"sorting") + return SID_SORTINGANDGROUPING; + else if (rIdent == u"page") + return SID_PAGEHEADERFOOTER; + else if (rIdent == u"report") + return SID_REPORTHEADERFOOTER; + else if (rIdent == u"function") + return SID_RPT_NEW_FUNCTION; + else if (rIdent == u"properties") + return SID_SHOW_PROPERTYBROWSER; + else if (rIdent == u"delete") + return SID_DELETE; + return 0; + } +} + +IMPL_LINK(NavigatorTree, CommandHdl, const CommandEvent&, rEvt, bool) +{ + bool bHandled = false; + switch( rEvt.GetCommand()) + { + case CommandEventId::ContextMenu: + { + UserData* pData = weld::fromId<UserData*>(m_xTreeView->get_selected_id()); + if (!pData) + break; + + uno::Reference< report::XFunctionsSupplier> xSupplier(pData->getContent(),uno::UNO_QUERY); + uno::Reference< report::XFunctions> xFunctions(pData->getContent(),uno::UNO_QUERY); + uno::Reference< report::XGroup> xGroup(pData->getContent(),uno::UNO_QUERY); + bool bDeleteAllowed = m_rController.isEditable() && (xGroup.is() || + uno::Reference< report::XFunction>(pData->getContent(),uno::UNO_QUERY).is()); + + std::unique_ptr<weld::Builder> xBuilder(Application::CreateBuilder(m_xTreeView.get(), "modules/dbreport/ui/navigatormenu.ui")); + std::unique_ptr<weld::Menu> xContextMenu(xBuilder->weld_menu("menu")); + + const OUString aIds[] = { "sorting", "page", "report", "function", "properties", "delete" }; + for (size_t i = 0; i < SAL_N_ELEMENTS(aIds); ++i) + { + sal_uInt16 nSId = mapIdent(aIds[i]); + + if (aIds[i] == "page" || aIds[i] == "report" || aIds[i] == "properties") + xContextMenu->set_active(aIds[i], m_rController.isCommandChecked(nSId)); + bool bEnabled = m_rController.isCommandEnabled(nSId); + if (nSId == SID_RPT_NEW_FUNCTION) + xContextMenu->set_sensitive(aIds[i], m_rController.isEditable() && (xSupplier.is() || xFunctions.is())); + // special condition, check for function and group + else if (nSId == SID_DELETE) + xContextMenu->set_sensitive(aIds[i], bDeleteAllowed); + else + xContextMenu->set_sensitive(aIds[i], bEnabled); + } + + // the point that was clicked on + ::Point aWhere(rEvt.GetMousePosPixel()); + OUString sCurItemIdent = xContextMenu->popup_at_rect(m_xTreeView.get(), tools::Rectangle(aWhere, Size(1,1))); + if (!sCurItemIdent.isEmpty()) + { + sal_uInt16 nId = mapIdent(sCurItemIdent); + uno::Sequence< beans::PropertyValue> aArgs; + if ( nId == SID_RPT_NEW_FUNCTION ) + { + aArgs.realloc(1); + aArgs.getArray()[0].Value <<= (xFunctions.is() ? xFunctions : xSupplier->getFunctions()); + } + else if ( nId == SID_DELETE ) + { + if ( xGroup.is() ) + nId = SID_GROUP_REMOVE; + aArgs = { comphelper::makePropertyValue(PROPERTY_GROUP, pData->getContent()) }; + } + m_rController.executeUnChecked(nId,aArgs); + } + + bHandled = true; + } + break; + default: break; + } + + return bHandled; +} + +IMPL_LINK_NOARG(NavigatorTree, OnEntrySelDesel, weld::TreeView&, void) +{ + if ( !m_pSelectionListener->locked() ) + { + m_pSelectionListener->lock(); + std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator(); + bool bEntry = m_xTreeView->get_cursor(xEntry.get()); + uno::Any aSelection; + if (bEntry && m_xTreeView->is_selected(*xEntry)) + aSelection <<= weld::fromId<UserData*>(m_xTreeView->get_id(*xEntry))->getContent(); + m_rController.select(aSelection); + m_pSelectionListener->unlock(); + } +} + +void NavigatorTree::_selectionChanged( const lang::EventObject& aEvent ) +{ + m_pSelectionListener->lock(); + uno::Reference< view::XSelectionSupplier> xSelectionSupplier(aEvent.Source,uno::UNO_QUERY); + uno::Any aSec = xSelectionSupplier->getSelection(); + uno::Sequence< uno::Reference< report::XReportComponent > > aSelection; + aSec >>= aSelection; + std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator(); + if ( !aSelection.hasElements() ) + { + uno::Reference< uno::XInterface> xSelection(aSec,uno::UNO_QUERY); + bool bEntry = find(xSelection, *xEntry); + if (bEntry && !m_xTreeView->is_selected(*xEntry)) + { + m_xTreeView->select(*xEntry); + m_xTreeView->set_cursor(*xEntry); + } + else if (!bEntry) + m_xTreeView->unselect_all(); + } + else + { + for (const uno::Reference<report::XReportComponent>& rElem : std::as_const(aSelection)) + { + bool bEntry = find(rElem, *xEntry); + if (bEntry && !m_xTreeView->is_selected(*xEntry)) + { + m_xTreeView->select(*xEntry); + m_xTreeView->set_cursor(*xEntry); + } + } + } + m_pSelectionListener->unlock(); +} + +void NavigatorTree::insertEntry(const OUString& rName, const weld::TreeIter* pParent, const OUString& rImageId, + int nPosition, const UserData* pData, weld::TreeIter& rRet) +{ + OUString sId = pData ? weld::toId(pData) : OUString(); + m_xTreeView->insert(pParent, nPosition, &rName, &sId, nullptr, nullptr, false, &rRet); + if (!rImageId.isEmpty()) + m_xTreeView->set_image(rRet, rImageId); +} + +void NavigatorTree::traverseSection(const uno::Reference<report::XSection>& xSection, const weld::TreeIter* pParent, const OUString& rImageId, int nPosition) +{ + std::unique_ptr<weld::TreeIter> xSectionIter = m_xTreeView->make_iterator(); + std::unique_ptr<weld::TreeIter> xScratch = m_xTreeView->make_iterator(); + insertEntry(xSection->getName(), pParent, rImageId, nPosition, new UserData(this, xSection), *xSectionIter); + const sal_Int32 nCount = xSection->getCount(); + for (sal_Int32 i = 0; i < nCount; ++i) + { + uno::Reference< report::XReportComponent> xElement(xSection->getByIndex(i), uno::UNO_QUERY_THROW); + insertEntry(lcl_getName(xElement), xSectionIter.get(), lcl_getImageId(xElement), -1, new UserData(this, xElement), *xScratch); + uno::Reference< report::XReportDefinition> xSubReport(xElement,uno::UNO_QUERY); + if ( xSubReport.is() ) + { + bool bMasterReport = find(xSection->getReportDefinition(), *xScratch); + if (!bMasterReport) + m_xMasterReport.reset(); + else + m_xMasterReport = m_xTreeView->make_iterator(xScratch.get()); + reportdesign::OReportVisitor aSubVisitor(this); + aSubVisitor.start(xSubReport); + } + } +} + +void NavigatorTree::traverseFunctions(const uno::Reference< report::XFunctions>& xFunctions, const weld::TreeIter* pParent) +{ + std::unique_ptr<weld::TreeIter> xFunctionIter = m_xTreeView->make_iterator(); + std::unique_ptr<weld::TreeIter> xScratch = m_xTreeView->make_iterator(); + insertEntry(RptResId(RID_STR_FUNCTIONS), pParent, RID_SVXBMP_RPT_NEW_FUNCTION, -1, new UserData(this, xFunctions), *xFunctionIter); + const sal_Int32 nCount = xFunctions->getCount(); + for (sal_Int32 i = 0; i< nCount; ++i) + { + uno::Reference< report::XFunction> xElement(xFunctions->getByIndex(i),uno::UNO_QUERY); + insertEntry(xElement->getName(), xFunctionIter.get(), RID_SVXBMP_RPT_NEW_FUNCTION, -1, new UserData(this,xElement), *xScratch); + } +} + +bool NavigatorTree::find(const uno::Reference<uno::XInterface>& xContent, weld::TreeIter& rRet) +{ + bool bRet = false; + if (xContent.is()) + { + m_xTreeView->all_foreach([this, &xContent, &bRet, &rRet](weld::TreeIter& rIter) { + UserData* pData = weld::fromId<UserData*>(m_xTreeView->get_id(rIter)); + if (pData->getContent() == xContent) + { + m_xTreeView->copy_iterator(rIter, rRet); + bRet = true; + return true; + } + return false; + }); + } + return bRet; +} + +// ITraverseReport + +void NavigatorTree::traverseReport(const uno::Reference< report::XReportDefinition>& xReport) +{ + std::unique_ptr<weld::TreeIter> xScratch = m_xTreeView->make_iterator(); + insertEntry(xReport->getName(), m_xMasterReport.get(), RID_SVXBMP_SELECT_REPORT,-1, new UserData(this, xReport), *xScratch); +} + +void NavigatorTree::traverseReportFunctions(const uno::Reference< report::XFunctions>& xFunctions) +{ + std::unique_ptr<weld::TreeIter> xReport = m_xTreeView->make_iterator(); + bool bReport = find(xFunctions->getParent(), *xReport); + if (!bReport) + xReport.reset(); + traverseFunctions(xFunctions, xReport.get()); +} + +void NavigatorTree::traverseReportHeader(const uno::Reference< report::XSection>& xSection) +{ + std::unique_ptr<weld::TreeIter> xReport = m_xTreeView->make_iterator(); + bool bReport = find(xSection->getReportDefinition(), *xReport); + if (!bReport) + xReport.reset(); + traverseSection(xSection, xReport.get(), RID_SVXBMP_REPORTHEADERFOOTER); +} + +void NavigatorTree::traverseReportFooter(const uno::Reference< report::XSection>& xSection) +{ + std::unique_ptr<weld::TreeIter> xReport = m_xTreeView->make_iterator(); + bool bReport = find(xSection->getReportDefinition(), *xReport); + if (!bReport) + xReport.reset(); + traverseSection(xSection, xReport.get(), RID_SVXBMP_REPORTHEADERFOOTER); +} + +void NavigatorTree::traversePageHeader(const uno::Reference< report::XSection>& xSection) +{ + std::unique_ptr<weld::TreeIter> xReport = m_xTreeView->make_iterator(); + bool bReport = find(xSection->getReportDefinition(), *xReport); + if (!bReport) + xReport.reset(); + traverseSection(xSection, xReport.get(), RID_SVXBMP_PAGEHEADERFOOTER); +} + +void NavigatorTree::traversePageFooter(const uno::Reference< report::XSection>& xSection) +{ + std::unique_ptr<weld::TreeIter> xReport = m_xTreeView->make_iterator(); + bool bReport = find(xSection->getReportDefinition(), *xReport); + if (!bReport) + xReport.reset(); + traverseSection(xSection, xReport.get(), RID_SVXBMP_PAGEHEADERFOOTER); +} + +void NavigatorTree::traverseGroups(const uno::Reference< report::XGroups>& xGroups) +{ + std::unique_ptr<weld::TreeIter> xReport = m_xTreeView->make_iterator(); + bool bReport = find(xGroups->getReportDefinition(), *xReport); + if (!bReport) + xReport.reset(); + std::unique_ptr<weld::TreeIter> xScratch = m_xTreeView->make_iterator(); + insertEntry(RptResId(RID_STR_GROUPS), xReport.get(), RID_SVXBMP_SORTINGANDGROUPING, -1, new UserData(this, xGroups), *xScratch); +} + +void NavigatorTree::traverseGroup(const uno::Reference< report::XGroup>& xGroup) +{ + uno::Reference< report::XGroups> xGroups(xGroup->getParent(),uno::UNO_QUERY); + std::unique_ptr<weld::TreeIter> xGroupsIter = m_xTreeView->make_iterator(); + bool bGroups = find(xGroups, *xGroupsIter); + OSL_ENSURE(bGroups, "No Groups inserted so far. Why!"); + if (!bGroups) + xGroupsIter.reset(); + std::unique_ptr<weld::TreeIter> xScratch = m_xTreeView->make_iterator(); + insertEntry(xGroup->getExpression(), xGroupsIter.get(), RID_SVXBMP_GROUP, rptui::getPositionInIndexAccess(xGroups,xGroup), new UserData(this,xGroup), *xScratch); +} + +void NavigatorTree::traverseGroupFunctions(const uno::Reference< report::XFunctions>& xFunctions) +{ + std::unique_ptr<weld::TreeIter> xReport = m_xTreeView->make_iterator(); + bool bReport = find(xFunctions->getParent(), *xReport); + if (!bReport) + xReport.reset(); + traverseFunctions(xFunctions, xReport.get()); +} + +void NavigatorTree::traverseGroupHeader(const uno::Reference< report::XSection>& xSection) +{ + std::unique_ptr<weld::TreeIter> xReport = m_xTreeView->make_iterator(); + bool bReport = find(xSection->getGroup(), *xReport); + OSL_ENSURE(bReport, "No group found"); + if (!bReport) + xReport.reset(); + traverseSection(xSection, xReport.get(), RID_SVXBMP_GROUPHEADER, 1); +} + +void NavigatorTree::traverseGroupFooter(const uno::Reference< report::XSection>& xSection) +{ + std::unique_ptr<weld::TreeIter> xReport = m_xTreeView->make_iterator(); + bool bReport = find(xSection->getGroup(), *xReport); + OSL_ENSURE(bReport, "No group found"); + if (!bReport) + xReport.reset(); + traverseSection(xSection, xReport.get(), RID_SVXBMP_GROUPFOOTER); +} + +void NavigatorTree::traverseDetail(const uno::Reference< report::XSection>& xSection) +{ + uno::Reference< report::XReportDefinition> xReport = xSection->getReportDefinition(); + std::unique_ptr<weld::TreeIter> xParent = m_xTreeView->make_iterator(); + bool bParent = find(xReport, *xParent); + if (!bParent) + xParent.reset(); + traverseSection(xSection, xParent.get(), RID_SVXBMP_ICON_DETAIL); +} + +void NavigatorTree::_propertyChanged(const beans::PropertyChangeEvent& _rEvent) +{ + uno::Reference< report::XReportDefinition> xReport(_rEvent.Source,uno::UNO_QUERY); + if ( !xReport.is() ) + return; + + bool bEnabled = false; + _rEvent.NewValue >>= bEnabled; + if ( !bEnabled ) + return; + + std::unique_ptr<weld::TreeIter> xParent = m_xTreeView->make_iterator(); + bool bParent = find(xReport, *xParent); + if (!bParent) + xParent.reset(); + if ( _rEvent.PropertyName == PROPERTY_REPORTHEADERON ) + { + int nPos = xReport->getReportHeaderOn() ? 2 : 1; + traverseSection(xReport->getReportHeader(),xParent.get(),RID_SVXBMP_REPORTHEADERFOOTER,nPos); + } + else if ( _rEvent.PropertyName == PROPERTY_PAGEHEADERON ) + { + traverseSection(xReport->getPageHeader(),xParent.get(), RID_SVXBMP_PAGEHEADERFOOTER,1); + } + else if ( _rEvent.PropertyName == PROPERTY_PAGEFOOTERON ) + traverseSection(xReport->getPageFooter(),xParent.get(), RID_SVXBMP_PAGEHEADERFOOTER); + else if ( _rEvent.PropertyName == PROPERTY_REPORTFOOTERON ) + { + int nPos = -1; + if (xReport->getPageFooterOn() && xParent) + nPos = m_xTreeView->iter_n_children(*xParent) - 1; + traverseSection(xReport->getReportFooter(),xParent.get(),RID_SVXBMP_REPORTHEADERFOOTER,nPos); + } +} + +void NavigatorTree::_elementInserted( const container::ContainerEvent& _rEvent ) +{ + std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator(); + bool bEntry = find(_rEvent.Source, *xEntry); + if (!bEntry) + xEntry.reset(); + uno::Reference<beans::XPropertySet> xProp(_rEvent.Element,uno::UNO_QUERY_THROW); + OUString sName; + uno::Reference< beans::XPropertySetInfo> xInfo = xProp->getPropertySetInfo(); + if ( xInfo.is() ) + { + if ( xInfo->hasPropertyByName(PROPERTY_NAME) ) + xProp->getPropertyValue(PROPERTY_NAME) >>= sName; + else if ( xInfo->hasPropertyByName(PROPERTY_EXPRESSION) ) + xProp->getPropertyValue(PROPERTY_EXPRESSION) >>= sName; + } + uno::Reference< report::XGroup> xGroup(xProp,uno::UNO_QUERY); + if ( xGroup.is() ) + { + reportdesign::OReportVisitor aSubVisitor(this); + aSubVisitor.start(xGroup); + } + else + { + uno::Reference< report::XReportComponent> xElement(xProp,uno::UNO_QUERY); + if ( xProp.is() ) + sName = lcl_getName(xProp); + std::unique_ptr<weld::TreeIter> xScratch = m_xTreeView->make_iterator(); + insertEntry(sName, xEntry.get(), (!xElement.is() ? RID_SVXBMP_RPT_NEW_FUNCTION : lcl_getImageId(xElement)), + -1, new UserData(this,xProp), *xScratch); + } + if (bEntry && !m_xTreeView->get_row_expanded(*xEntry)) + m_xTreeView->expand_row(*xEntry); +} + +void NavigatorTree::_elementRemoved( const container::ContainerEvent& _rEvent ) +{ + uno::Reference<beans::XPropertySet> xProp(_rEvent.Element,uno::UNO_QUERY); + std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator(); + bool bEntry = find(xProp, *xEntry); + OSL_ENSURE(bEntry,"NavigatorTree::_elementRemoved: No Entry found!"); + + if (bEntry) + { + removeEntry(*xEntry); + } +} + +void NavigatorTree::_elementReplaced( const container::ContainerEvent& _rEvent ) +{ + uno::Reference<beans::XPropertySet> xProp(_rEvent.ReplacedElement,uno::UNO_QUERY); + std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator(); + bool bEntry = find(xProp, *xEntry); + if (bEntry) + { + UserData* pData = weld::fromId<UserData*>(m_xTreeView->get_id(*xEntry)); + xProp.set(_rEvent.Element,uno::UNO_QUERY); + pData->setContent(xProp); + OUString sName; + xProp->getPropertyValue(PROPERTY_NAME) >>= sName; + m_xTreeView->set_text(*xEntry, sName); + } +} + +void NavigatorTree::_disposing(const lang::EventObject& _rSource) +{ + std::unique_ptr<weld::TreeIter> xEntry = m_xTreeView->make_iterator(); + if (find(_rSource.Source, *xEntry)) + removeEntry(*xEntry); +} + +void NavigatorTree::removeEntry(const weld::TreeIter& rEntry, bool bRemove) +{ + std::unique_ptr<weld::TreeIter> xChild = m_xTreeView->make_iterator(&rEntry); + bool bChild = m_xTreeView->iter_children(*xChild); + while (bChild) + { + removeEntry(*xChild, false); + bChild = m_xTreeView->iter_next_sibling(*xChild); + } + delete weld::fromId<UserData*>(m_xTreeView->get_id(rEntry)); + if (bRemove) + m_xTreeView->remove(rEntry); +} + +NavigatorTree::UserData::UserData(NavigatorTree* pTree,uno::Reference<uno::XInterface> xContent) + : OPropertyChangeListener(m_aMutex) + , OContainerListener(m_aMutex) + , m_xContent(std::move(xContent)) + , m_pTree(pTree) +{ + uno::Reference<beans::XPropertySet> xProp(m_xContent,uno::UNO_QUERY); + if ( xProp.is() ) + { + uno::Reference< beans::XPropertySetInfo> xInfo = xProp->getPropertySetInfo(); + if ( xInfo.is() ) + { + m_pListener = new ::comphelper::OPropertyChangeMultiplexer(this,xProp); + if ( xInfo->hasPropertyByName(PROPERTY_NAME) ) + m_pListener->addProperty(PROPERTY_NAME); + else if ( xInfo->hasPropertyByName(PROPERTY_EXPRESSION) ) + m_pListener->addProperty(PROPERTY_EXPRESSION); + if ( xInfo->hasPropertyByName(PROPERTY_DATAFIELD) ) + m_pListener->addProperty(PROPERTY_DATAFIELD); + if ( xInfo->hasPropertyByName(PROPERTY_LABEL) ) + m_pListener->addProperty(PROPERTY_LABEL); + if ( xInfo->hasPropertyByName(PROPERTY_HEADERON) ) + m_pListener->addProperty(PROPERTY_HEADERON); + if ( xInfo->hasPropertyByName(PROPERTY_FOOTERON) ) + m_pListener->addProperty(PROPERTY_FOOTERON); + } + } + uno::Reference< container::XContainer> xContainer(m_xContent,uno::UNO_QUERY); + if ( xContainer.is() ) + { + m_pContainerListener = new ::comphelper::OContainerListenerAdapter(this,xContainer); + } +} + +NavigatorTree::UserData::~UserData() +{ + if ( m_pContainerListener.is() ) + m_pContainerListener->dispose(); + if ( m_pListener.is() ) + m_pListener->dispose(); +} + +// OPropertyChangeListener +void NavigatorTree::UserData::_propertyChanged(const beans::PropertyChangeEvent& _rEvent) +{ + std::unique_ptr<weld::TreeIter> xEntry = m_pTree->make_iterator(); + bool bEntry = m_pTree->find(_rEvent.Source, *xEntry); + OSL_ENSURE(bEntry,"No entry could be found! Why not!"); + if (!bEntry) + return; + const bool bFooterOn = (PROPERTY_FOOTERON == _rEvent.PropertyName); + try + { + if ( bFooterOn || PROPERTY_HEADERON == _rEvent.PropertyName ) + { + sal_Int32 nPos = 1; + uno::Reference< report::XGroup> xGroup(_rEvent.Source,uno::UNO_QUERY); + ::std::function<bool(OGroupHelper *)> pIsOn = ::std::mem_fn(&OGroupHelper::getHeaderOn); + ::std::function<uno::Reference<report::XSection>(OGroupHelper *)> pMemFunSection = ::std::mem_fn(&OGroupHelper::getHeader); + if ( bFooterOn ) + { + pIsOn = ::std::mem_fn(&OGroupHelper::getFooterOn); + pMemFunSection = ::std::mem_fn(&OGroupHelper::getFooter); + nPos = m_pTree->iter_n_children(*xEntry) - 1; + } + + OGroupHelper aGroupHelper(xGroup); + if ( pIsOn(&aGroupHelper) ) + { + if ( bFooterOn ) + ++nPos; + m_pTree->traverseSection(pMemFunSection(&aGroupHelper),xEntry.get(),bFooterOn ? RID_SVXBMP_GROUPFOOTER : RID_SVXBMP_GROUPHEADER,nPos); + } + } + else if ( PROPERTY_EXPRESSION == _rEvent.PropertyName) + { + OUString sNewName; + _rEvent.NewValue >>= sNewName; + m_pTree->set_text(*xEntry, sNewName); + } + else if ( PROPERTY_DATAFIELD == _rEvent.PropertyName || PROPERTY_LABEL == _rEvent.PropertyName || PROPERTY_NAME == _rEvent.PropertyName ) + { + uno::Reference<beans::XPropertySet> xProp(_rEvent.Source,uno::UNO_QUERY); + m_pTree->set_text(*xEntry, lcl_getName(xProp)); + } + } + catch(const uno::Exception &) + {} +} + +void NavigatorTree::UserData::_elementInserted( const container::ContainerEvent& _rEvent ) +{ + m_pTree->_elementInserted( _rEvent ); +} + +void NavigatorTree::UserData::_elementRemoved( const container::ContainerEvent& _rEvent ) +{ + m_pTree->_elementRemoved( _rEvent ); +} + +void NavigatorTree::UserData::_elementReplaced( const container::ContainerEvent& _rEvent ) +{ + m_pTree->_elementReplaced( _rEvent ); +} + +void NavigatorTree::UserData::_disposing(const lang::EventObject& _rSource) +{ + m_pTree->_disposing( _rSource ); +} + +ONavigator::ONavigator(weld::Window* pParent, OReportController& rController) + : GenericDialogController(pParent, "modules/dbreport/ui/floatingnavigator.ui", "FloatingNavigator") + , m_xReport(rController.getReportDefinition()) + , m_xNavigatorTree(std::make_unique<NavigatorTree>(m_xBuilder->weld_tree_view("treeview"), rController)) +{ + reportdesign::OReportVisitor aVisitor(m_xNavigatorTree.get()); + aVisitor.start(m_xReport); + std::unique_ptr<weld::TreeIter> xScratch = m_xNavigatorTree->make_iterator(); + if (m_xNavigatorTree->find(m_xReport, *xScratch)) + m_xNavigatorTree->expand_row(*xScratch); + lang::EventObject aEvent(rController); + m_xNavigatorTree->_selectionChanged(aEvent); + m_xNavigatorTree->grab_focus(); + + m_xDialog->connect_container_focus_changed(LINK(this, ONavigator, FocusChangeHdl)); +} + +ONavigator::~ONavigator() +{ +} + +IMPL_LINK_NOARG(ONavigator, FocusChangeHdl, weld::Container&, void) +{ + if (m_xDialog->has_toplevel_focus()) + m_xNavigatorTree->grab_focus(); +} + +} // rptui + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/reportdesign/source/ui/dlg/PageNumber.cxx b/reportdesign/source/ui/dlg/PageNumber.cxx new file mode 100644 index 0000000000..1e267fb1fb --- /dev/null +++ b/reportdesign/source/ui/dlg/PageNumber.cxx @@ -0,0 +1,104 @@ +/* -*- 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 <PageNumber.hxx> +#include <rptui_slotid.hrc> +#include <RptDef.hxx> + +#include <strings.hxx> +#include <ReportController.hxx> +#include <comphelper/propertysequence.hxx> +#include <utility> + +namespace rptui +{ +using namespace ::com::sun::star; +using namespace ::comphelper; + + + +OPageNumberDialog::OPageNumberDialog(weld::Window* pParent, + uno::Reference< report::XReportDefinition > _xHoldAlive, + OReportController* _pController) + : GenericDialogController(pParent, "modules/dbreport/ui/pagenumberdialog.ui", "PageNumberDialog") + , m_pController(_pController) + , m_xHoldAlive(std::move(_xHoldAlive)) + , m_xPageNofM(m_xBuilder->weld_radio_button("pagenofm")) + , m_xTopPage(m_xBuilder->weld_radio_button("toppage")) + , m_xAlignmentLst(m_xBuilder->weld_combo_box("alignment")) + , m_xShowNumberOnFirstPage(m_xBuilder->weld_check_button("shownumberonfirstpage")) +{ + m_xShowNumberOnFirstPage->hide(); +} + +OPageNumberDialog::~OPageNumberDialog() +{ +} + +short OPageNumberDialog::run() +{ + short nRet = GenericDialogController::run(); + if (nRet == RET_OK) + { + try + { + sal_Int32 nControlMaxSize = 3000; + sal_Int32 nPosX = 0; + sal_Int32 nPos2X = 0; + awt::Size aRptSize = getStyleProperty<awt::Size>(m_xHoldAlive,PROPERTY_PAPERSIZE); + switch (m_xAlignmentLst->get_active()) + { + case 0: // left + nPosX = getStyleProperty<sal_Int32>(m_xHoldAlive,PROPERTY_LEFTMARGIN); + break; + case 1: // middle + nPosX = getStyleProperty<sal_Int32>(m_xHoldAlive,PROPERTY_LEFTMARGIN) + (aRptSize.Width - getStyleProperty<sal_Int32>(m_xHoldAlive,PROPERTY_LEFTMARGIN) - getStyleProperty<sal_Int32>(m_xHoldAlive,PROPERTY_RIGHTMARGIN) - nControlMaxSize) / 2; + break; + case 2: // right + nPosX = (aRptSize.Width - getStyleProperty<sal_Int32>(m_xHoldAlive,PROPERTY_RIGHTMARGIN) - nControlMaxSize); + break; + case 3: // inner + case 4: // outer + nPosX = getStyleProperty<sal_Int32>(m_xHoldAlive,PROPERTY_LEFTMARGIN); + nPos2X = (aRptSize.Width - getStyleProperty<sal_Int32>(m_xHoldAlive,PROPERTY_RIGHTMARGIN) - nControlMaxSize); + break; + default: + break; + } + if (m_xAlignmentLst->get_active() > 2) + nPosX = nPos2X; + + uno::Sequence<beans::PropertyValue> aValues( comphelper::InitPropertySequence({ + { PROPERTY_POSITION, uno::Any(awt::Point(nPosX,0)) }, + { PROPERTY_PAGEHEADERON, uno::Any(m_xTopPage->get_active()) }, + { PROPERTY_STATE, uno::Any(m_xPageNofM->get_active()) } + })); + + m_pController->executeChecked(SID_INSERT_FLD_PGNUMBER,aValues); + } + catch(uno::Exception&) + { + } + } + return nRet; +} + +} // rptui + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/reportdesign/source/ui/dlg/dlgpage.cxx b/reportdesign/source/ui/dlg/dlgpage.cxx new file mode 100644 index 0000000000..7e99ba19d4 --- /dev/null +++ b/reportdesign/source/ui/dlg/dlgpage.cxx @@ -0,0 +1,78 @@ +/* -*- 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 <svx/dialogs.hrc> +#include <sfx2/sfxdlg.hxx> +#include <dlgpage.hxx> +#include <svl/cjkoptions.hxx> +#include <osl/diagnose.h> + +namespace rptui +{ +/************************************************************************* +|* +|* constructor of the tab dialogs: Add the page to the dialog +|* +\************************************************************************/ + +ORptPageDialog::ORptPageDialog(weld::Window* pParent, const SfxItemSet* pAttr, const OUString &rDialog) + : SfxTabDialogController(pParent, "modules/dbreport/ui/" + + rDialog.toAsciiLowerCase() + ".ui", rDialog, pAttr) +{ + SfxAbstractDialogFactory* pFact = SfxAbstractDialogFactory::Create(); + + if (rDialog == "BackgroundDialog") + { + AddTabPage("background", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BKG ), nullptr ); + } + else if (rDialog == "PageDialog") + { + AddTabPage("page", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_PAGE ), nullptr ); + AddTabPage("background", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BKG ), nullptr ); + } + else if (rDialog == "CharDialog") + { + AddTabPage("font", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_NAME ), nullptr ); + AddTabPage("fonteffects", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_EFFECTS ), nullptr ); + AddTabPage("position", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_POSITION ), nullptr ); + AddTabPage("asianlayout", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_CHAR_TWOLINES ), nullptr ); + AddTabPage("background", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_BKG ), nullptr ); + AddTabPage("alignment", pFact->GetTabPageCreatorFunc( RID_SVXPAGE_ALIGNMENT ), nullptr ); + } + else + OSL_FAIL("Unknown page id"); + + if ( !SvtCJKOptions::IsDoubleLinesEnabled() ) + RemoveTabPage("asianlayout"); +} + +void ORptPageDialog::PageCreated(const OUString& rId, SfxTabPage &rPage) +{ + SfxAllItemSet aSet(*(GetInputSetImpl()->GetPool())); + if (rId == "background") + { + rPage.PageCreated(aSet); + } +} + +} // namespace rptui + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |