File indexing completed on 2024-05-12 03:47:26

0001 /*
0002     File                 : AbstractFilter.cpp
0003     Project              : LabPlot
0004     Description          : Base class for all analysis operations.
0005     --------------------------------------------------------------------
0006     SPDX-FileCopyrightText: 2007 Knut Franke Tilman Benkert <knut.franke*gmx.de, thzs*gmx.net (use @ for *)>
0007     SPDX-License-Identifier: GPL-2.0-or-later
0008 */
0009 
0010 #include "AbstractFilter.h"
0011 #include <KLocalizedString>
0012 
0013 #include "backend/core/AbstractColumn.h"
0014 #include "backend/lib/macros.h"
0015 
0016 /**
0017  * \class AbstractFilter
0018  * \brief Base class for all analysis operations.
0019  *
0020  * AbstractFilter provides an abstraction for analysis operations. It is modelled on an
0021  * electronic filtering circuit: From the outside, a filter appears as a black box with
0022  * a number of input and output ports (obviously, those numbers do not necessarily agree).
0023  *
0024  * \section using Using AbstractFilter
0025  * You can connect one AbstractColumn to each input port using
0026  * input(int port, AbstractColumn* source). Every output(int port) is realized
0027  * again by an AbstractColumn, which you can connect to as many other filters, tables
0028  * or plots as you like.
0029  * Ownership of the data sources always stays with the class which is providing the data,
0030  * that is, neither input() nor output() transfer ownership.
0031  *
0032  * Furthermore, you can use inputCount() and outputCount() to query the number of
0033  * input and output ports, respectively and you can obtain label strings for inputs (via
0034  * inputLabel()) and outputs (via AbstractColumn::label()). This allows generic filter
0035  * handling routines to be written, which is important for using filters provided by plugins.
0036  *
0037  * Its simplicity of use notwithstanding, AbstractFilter provides a powerful and versatile
0038  * basis also for analysis operations that would not commonly be referred to as "filter".
0039  * An example of such a more advanced filter implementation is StatisticsFilter.
0040  *
0041  * \section subclassing Subclassing AbstractFilter
0042  * The main design goal was to make implementing new filters as easy as possible.
0043  * Filters with only one output port can subclass AbstractSimpleFilter, which is even easier
0044  * to use. Filters with more than one output port have to subclass
0045  * AbstractFilter directly, which is slightly more involved, because in
0046  * addition to data transfer between these classes the signals defined by AbstractColumn
0047  * have to be handled on both inputs and outputs. Signals from data sources connected to the input
0048  * ports are automatically connected to a matching set of virtual methods, which can be
0049  * reimplemented by subclasses to handle these events.
0050  *
0051  * While AbstractFilter handles the tedious part of connecting a data source to an input port,
0052  * its subclasses are given a chance to reject such connections (e.g., based on the data type
0053  * of the source) by reimplementing inputAcceptable().
0054  *
0055  * \sa AbstractSimpleFilter
0056  */
0057 
0058 /**
0059  * \fn AbstractFilter::inputCount() const
0060  * \brief Return the number of input ports supported by the filter or -1 if any number of inputs is acceptable.
0061  */
0062 
0063 /**
0064  * \fn AbstractFilter::outputCount() const
0065  * \brief Return the number of output ports provided by the filter.
0066  *
0067  * %Note that this number need not be static, but can be dynamically determined, for example
0068  * based on the inputs provided to the filter.
0069  */
0070 
0071 /**
0072  * \brief Return the index of the highest input port that is connected.
0073  *
0074  * Note that this is different from both the number of ports that <em>could</em> be connected,
0075  * inputCount(), and the number of ports that actually <em>have been</em> connected, which are
0076  * not necessarily sequential. In conjunction with input(int), this method can be used to
0077  * traverse the connected inputs.
0078  */
0079 int AbstractFilter::highestConnectedInput() const {
0080     return m_inputs.count() - 1;
0081 }
0082 
0083 /**
0084  * \brief Connect the provided data source to the specified input port.
0085  * \param port the port number to which to connect
0086  * \param source the data source to connect to the input port
0087  * \returns true if the connection was accepted, false otherwise.
0088  *
0089  * The port number is checked for validity against inputCount() and both port number and data
0090  * source are passed to inputAcceptable() for review. If both checks succeed, the
0091  * source is recorded in #m_inputs.
0092  * If applicable, the previously connected data source is disconnected before replacing it.
0093  *
0094  * You can also use this method to disconnect an input without replacing it with a new one by
0095  * calling it with source=0.
0096  *
0097  * \sa inputAcceptable(), #m_inputs
0098  */
0099 bool AbstractFilter::input(int port, const AbstractColumn* source) {
0100     //  DEBUG("AbstractFilter::input()");
0101 
0102     if (port < 0 || (inputCount() >= 0 && port >= inputCount()))
0103         return false;
0104     if (source && !inputAcceptable(port, source))
0105         return false;
0106 
0107     if (port >= m_inputs.size())
0108         m_inputs.resize(port + 1);
0109     const AbstractColumn* old_input = m_inputs.value(port);
0110     if (source == old_input)
0111         return true;
0112 
0113     if (old_input) {
0114         disconnect(old_input, nullptr, this, nullptr);
0115         // replace input, notifying the filter implementation of the changes
0116         inputDescriptionAboutToChange(old_input);
0117         inputPlotDesignationAboutToChange(old_input);
0118         inputMaskingAboutToChange(old_input);
0119         inputDataAboutToChange(old_input);
0120         if (source && source->columnMode() != old_input->columnMode())
0121             inputModeAboutToChange(old_input);
0122     }
0123     if (!source)
0124         inputAboutToBeDisconnected(old_input);
0125     m_inputs[port] = source;
0126     if (source) { // we have a new source
0127         //      DEBUG(" new source");
0128         if (old_input && source->columnMode() != old_input->columnMode())
0129             inputModeAboutToChange(source);
0130         inputDataChanged(source);
0131         inputMaskingChanged(source);
0132         inputPlotDesignationChanged(source);
0133         inputDescriptionChanged(source);
0134         // connect the source's signals
0135         connect(source,
0136                 &AbstractColumn::aspectDescriptionAboutToChange,
0137                 this,
0138                 static_cast<void (AbstractFilter::*)(const AbstractAspect*)>(&AbstractFilter::inputDescriptionAboutToChange));
0139         connect(source,
0140                 &AbstractColumn::aspectDescriptionChanged,
0141                 this,
0142                 static_cast<void (AbstractFilter::*)(const AbstractAspect*)>(&AbstractFilter::inputDescriptionChanged));
0143         connect(source, &AbstractColumn::plotDesignationAboutToChange, this, &AbstractFilter::inputPlotDesignationAboutToChange);
0144         connect(source, &AbstractColumn::plotDesignationChanged, this, &AbstractFilter::inputPlotDesignationChanged);
0145         connect(source, &AbstractColumn::modeAboutToChange, this, &AbstractFilter::inputModeAboutToChange);
0146         connect(source, &AbstractColumn::modeChanged, this, &AbstractFilter::inputModeChanged);
0147         connect(source, &AbstractColumn::dataAboutToChange, this, &AbstractFilter::inputDataAboutToChange);
0148         connect(source, &AbstractColumn::dataChanged, this, &AbstractFilter::inputDataChanged);
0149         connect(source, &AbstractColumn::rowsAboutToBeInserted, this, &AbstractFilter::inputRowsAboutToBeInserted);
0150         connect(source, &AbstractColumn::rowsInserted, this, &AbstractFilter::inputRowsInserted);
0151         connect(source, &AbstractColumn::rowsAboutToBeRemoved, this, &AbstractFilter::inputRowsAboutToBeRemoved);
0152         connect(source, &AbstractColumn::rowsRemoved, this, &AbstractFilter::inputRowsRemoved);
0153         connect(source, &AbstractColumn::maskingAboutToChange, this, &AbstractFilter::inputMaskingAboutToChange);
0154         connect(source, &AbstractColumn::maskingChanged, this, &AbstractFilter::inputMaskingChanged);
0155         connect(source, &AbstractColumn::aboutToBeDestroyed, this, &AbstractFilter::inputAboutToBeDestroyed);
0156     } else { // source == 0, that is, the input port has been disconnected
0157         //      DEBUG(" no source");
0158         // try to shrink m_inputs
0159         int num_connected_inputs = m_inputs.size();
0160         while (m_inputs.at(num_connected_inputs - 1) == nullptr) {
0161             num_connected_inputs--;
0162             if (!num_connected_inputs)
0163                 break;
0164         }
0165         m_inputs.resize(num_connected_inputs);
0166     }
0167 
0168     return true;
0169 }
0170 
0171 /**
0172  * \brief Connect all outputs of the provided filter to the corresponding inputs of this filter.
0173  * \returns true if all connections were accepted, false otherwise
0174  *
0175  * Overloaded method provided for convenience.
0176  */
0177 bool AbstractFilter::input(const AbstractFilter* sources) {
0178     if (!sources)
0179         return false;
0180     bool result = true;
0181     for (int i = 0; i < sources->outputCount(); ++i)
0182         if (!input(i, sources->output(i)))
0183             result = false;
0184     return result;
0185 }
0186 
0187 /**
0188  * \brief Return the input currently connected to the specified port, or 0.
0189  */
0190 const AbstractColumn* AbstractFilter::input(int port) const {
0191     return m_inputs.value(port);
0192 }
0193 
0194 /**
0195  * \brief Return the label associated to the given input port.
0196  *
0197  * Default labels are In1, In2, ... (or translated equivalents), but implementations can
0198  * reimplement this method to produce more meaningful labels.
0199  *
0200  * Output ports are implicitly labeled through AbstractAspect::name().
0201  */
0202 QString AbstractFilter::inputLabel(int port) const {
0203     return i18nc("default labels of filter input ports", "In%1", port + 1);
0204 }
0205 
0206 /**
0207  * \fn AbstractColumn *AbstractFilter::output(int port=0)
0208  * \brief Get the data source associated with the specified output port.
0209  *
0210  * The returned pointer may be 0 even for valid port numbers, for example if not all required
0211  * input ports have been connected.
0212  */
0213 
0214 /**
0215  * \fn const AbstractColumn *AbstractFilter::output(int port=0) const
0216  * \brief Overloaded method for const access.
0217  */
0218 
0219 /**
0220  * \brief Return the input port to which the column is connected or -1 if it's not connected
0221  */
0222 int AbstractFilter::portIndexOf(const AbstractColumn* column) {
0223     for (int i = 0; i < m_inputs.size(); ++i)
0224         if (m_inputs.at(i) == column)
0225             return i;
0226     return -1;
0227 }
0228 
0229 /**
0230  * \brief Give implementations a chance to reject connections to their input ports.
0231  *
0232  * If not reimplemented, all connections to ports within [0, inputCount()-1] will be accepted.
0233  */
0234 bool AbstractFilter::inputAcceptable(int /*port*/, const AbstractColumn* /*source*/) {
0235     return true;
0236 }
0237 
0238 /**
0239  * \brief Called whenever an input is disconnected or deleted.
0240  *
0241  * This is only to notify implementations of the event, the default implementation is a
0242  * no-op.
0243  */
0244 void AbstractFilter::inputAboutToBeDisconnected(const AbstractColumn* /*source*/) {
0245 }
0246 
0247 ////////////////////////////////////////////////////////////////////////////////////////////////////
0248 //!\name signal handlers
0249 //@{
0250 ////////////////////////////////////////////////////////////////////////////////////////////////////
0251 
0252 /**
0253  * \brief Name and/or comment of an input will be changed.
0254  *
0255  * \param source is always the this pointer of the column that emitted the signal.
0256  */
0257 void AbstractFilter::inputDescriptionAboutToChange(const AbstractColumn* /*source*/) {
0258 }
0259 
0260 void AbstractFilter::inputDescriptionAboutToChange(const AbstractAspect* aspect) {
0261     const auto* col = qobject_cast<const AbstractColumn*>(aspect);
0262     if (col)
0263         inputDescriptionAboutToChange(col);
0264 }
0265 
0266 /**
0267  * \brief Name and/or comment of an input changed.
0268  *
0269  * \param source is always the this pointer of the column that emitted the signal.
0270  */
0271 void AbstractFilter::inputDescriptionChanged(const AbstractColumn* /*source*/) {
0272 }
0273 
0274 void AbstractFilter::inputDescriptionChanged(const AbstractAspect* aspect) {
0275     const auto* col = qobject_cast<const AbstractColumn*>(aspect);
0276     if (col && m_inputs.contains(col))
0277         inputDescriptionChanged(col);
0278 }
0279 
0280 /**
0281  * \brief The plot designation of an input is about to change.
0282  *
0283  * \param source is always the this pointer of the column that emitted the signal.
0284  */
0285 void AbstractFilter::inputPlotDesignationAboutToChange(const AbstractColumn* /*source*/) {
0286 }
0287 
0288 /**
0289  * \brief The plot designation of an input changed.
0290  *
0291  * \param source is always the this pointer of the column that emitted the signal.
0292  */
0293 void AbstractFilter::inputPlotDesignationChanged(const AbstractColumn* /*source*/) {
0294 }
0295 
0296 /**
0297  * \brief The display mode and possibly the data type of an input is about to change.
0298  *
0299  * \param source is always the this pointer of the column that emitted the signal.
0300  */
0301 void AbstractFilter::inputModeAboutToChange(const AbstractColumn* /*source*/) {
0302 }
0303 
0304 /**
0305  * \brief The display mode and possibly the data type has changed.
0306  *
0307  * \param source is always the this pointer of the column that emitted the signal.
0308  */
0309 void AbstractFilter::inputModeChanged(const AbstractColumn* /*source*/) {
0310 }
0311 
0312 /**
0313  * \brief The data of an input is about to change.
0314  *
0315  * \param source is always the this pointer of the column that emitted the signal.
0316  */
0317 void AbstractFilter::inputDataAboutToChange(const AbstractColumn* /*source*/) {
0318 }
0319 
0320 /**
0321  * \brief The data of an input has changed.
0322  *
0323  * \param source is always the this pointer of the column that emitted the signal.
0324  */
0325 void AbstractFilter::inputDataChanged(const AbstractColumn* /*source*/) {
0326 }
0327 
0328 ////////////////////////////////////////////////////////////////////////////////////////////////////
0329 //@}
0330 ////////////////////////////////////////////////////////////////////////////////////////////////////
0331 
0332 /**
0333  * \var AbstractFilter::m_inputs
0334  * \brief The data sources connected to my input ports.
0335  */