File indexing completed on 2024-05-12 15:26:38

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