diff --git a/src/backend/core/AbstractColumn.cpp b/src/backend/core/AbstractColumn.cpp index bbfbc03d7..4b69cafdc 100644 --- a/src/backend/core/AbstractColumn.cpp +++ b/src/backend/core/AbstractColumn.cpp @@ -1,638 +1,638 @@ /*************************************************************************** File : AbstractColumn.cpp Project : LabPlot Description : Interface definition for data with column logic -------------------------------------------------------------------- Copyright : (C) 2007,2008 Tilman Benkert (thzs@gmx.net) ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "backend/core/AbstractColumn.h" #include "backend/core/AbstractColumnPrivate.h" #include "backend/core/abstractcolumncommands.h" #include "backend/lib/Interval.h" #include "backend/lib/XmlStreamReader.h" #include "backend/lib/SignallingUndoCommand.h" #include #include #include #include #include #include #include #include #include /** * \class AbstractColumn * \brief Interface definition for data with column logic * * This is an abstract base class for column-based data, * i.e. mathematically a vector or technically a 1D array or list. * It only defines the interface but has no data members itself. * * Classes derived from this are typically table columns or outputs * of filters which can be chained between table columns and plots. * From the point of view of the plot functions there will be no difference * between a table column and a filter output since both use this interface. * * Classes derived from this will either store a * vector with entries of one certain data type, e.g. double, QString, * QDateTime, or generate such values on demand. To determine the data * type of a class derived from this, use the columnMode() function. * AbstractColumn defines all access functions for all supported data * types but only those corresponding to the return value of columnMode() * will return a meaningful value. Calling functions not belonging to * the data type of the column is safe, but will do nothing (writing * function) or return some default value (reading functions). * * This class also defines all signals which indicate a data change. * Any class whose output values are subject to change over time must emit * the according signals. These signals notify any object working with the * column before and after a change of the column. * In some cases it will be necessary for a class using * the column to connect aboutToBeDestroyed(), to react * to a column's deletion, e.g. a filter's reaction to a * table deletion. * * All writing functions have a "do nothing" standard implementation to * make deriving a read-only class very easy without bothering about the * writing interface. */ /** * \brief Ctor * * \param name the column name (= aspect name) */ AbstractColumn::AbstractColumn(const QString &name) : AbstractAspect(name), m_abstract_column_private( new AbstractColumnPrivate(this) ) { } AbstractColumn::~AbstractColumn() { aboutToBeDestroyed(this); delete m_abstract_column_private; } /** * \fn bool AbstractColumn::isReadOnly() const * \brief Return whether the object is read-only */ /** * \fn AbstractColumn::ColumnMode AbstractColumn::columnMode() const * \brief Return the column mode * * This function is most used by tables but can also be used * by plots. The column mode specifies how to interpret * the values in the column additional to the data type. */ /** * \brief Set the column mode * * This sets the column mode and, if * necessary, converts it to another datatype. */ void AbstractColumn::setColumnMode(AbstractColumn::ColumnMode) {} /** * \brief Copy another column of the same type * * This function will return false if the data type * of 'other' is not the same as the type of 'this'. * Use a filter to convert a column to another type. */ bool AbstractColumn::copy(const AbstractColumn *other) { Q_UNUSED(other) return false; } /** * \brief Copies part of another column of the same type * * This function will return false if the data type * of 'other' is not the same as the type of 'this'. * \param source pointer to the column to copy * \param source_start first row to copy in the column to copy * \param destination_start first row to copy in * \param num_rows the number of rows to copy */ bool AbstractColumn::copy(const AbstractColumn *source, int source_start, int destination_start, int num_rows) { Q_UNUSED(source) Q_UNUSED(source_start) Q_UNUSED(destination_start) Q_UNUSED(num_rows) return false; } /** * \fn int AbstractColumn::rowCount() const * \brief Return the data vector size */ /** * \brief Insert some empty (or initialized with invalid values) rows */ void AbstractColumn::insertRows(int before, int count) { beginMacro( i18np("%1: insert 1 row", "%1: insert %2 rows", name(), count) ); exec(new SignallingUndoCommand("pre-signal", this, "rowsAboutToBeInserted", "rowsRemoved", Q_ARG(const AbstractColumn*,this), Q_ARG(int,before), Q_ARG(int,count))); handleRowInsertion(before, count); exec(new SignallingUndoCommand("post-signal", this, "rowsInserted", "rowsAboutToBeRemoved", Q_ARG(const AbstractColumn*,this), Q_ARG(int,before), Q_ARG(int,count))); endMacro(); } void AbstractColumn::handleRowInsertion(int before, int count) { exec(new AbstractColumnInsertRowsCmd(this, before, count)); } /** * \brief Remove 'count' rows starting from row 'first' */ void AbstractColumn::removeRows(int first, int count) { beginMacro( i18np("%1: remove 1 row", "%1: remove %2 rows", name(), count) ); exec(new SignallingUndoCommand("change signal", this, "rowsAboutToBeRemoved", "rowsInserted", Q_ARG(const AbstractColumn*,this), Q_ARG(int,first), Q_ARG(int,count))); handleRowRemoval(first, count); exec(new SignallingUndoCommand("change signal", this, "rowsRemoved", "rowsAboutToBeInserted", Q_ARG(const AbstractColumn*,this), Q_ARG(int,first), Q_ARG(int,count))); endMacro(); } void AbstractColumn::handleRowRemoval(int first, int count) { exec(new AbstractColumnRemoveRowsCmd(this, first, count)); } /** * \fn AbstractColumn::PlotDesignation AbstractColumn::plotDesignation() const * \brief Return the column plot designation */ /** * \brief Set the column plot designation */ void AbstractColumn::setPlotDesignation(AbstractColumn::PlotDesignation pd) { Q_UNUSED(pd) } /** * \brief Clear the whole column */ void AbstractColumn::clear() {} /** * \brief Convenience method for mode-independent testing of validity */ bool AbstractColumn::isValid(int row) const { switch (columnMode()) { case AbstractColumn::Numeric: return !std::isnan(valueAt(row)); case AbstractColumn::Text: return !textAt(row).isNull(); case AbstractColumn::DateTime: case AbstractColumn::Month: case AbstractColumn::Day: return dateTimeAt(row).isValid(); } return false; } //////////////////////////////////////////////////////////////////////////////////////////////////// //! \name IntervalAttribute related functions //@{ //////////////////////////////////////////////////////////////////////////////////////////////////// /** * \brief Return whether a certain row is masked */ bool AbstractColumn::isMasked(int row) const { return m_abstract_column_private->m_masking.isSet(row); } /** - * \brief Return whether a certain interval of rows rows is fully masked + * \brief Return whether a certain interval of rows is fully masked */ bool AbstractColumn::isMasked(Interval i) const { return m_abstract_column_private->m_masking.isSet(i); } /** * \brief Return all intervals of masked rows */ QList< Interval > AbstractColumn::maskedIntervals() const { return m_abstract_column_private->m_masking.intervals(); } /** * \brief Clear all masking information */ void AbstractColumn::clearMasks() { exec(new AbstractColumnClearMasksCmd(m_abstract_column_private), "maskingAboutToChange", "maskingChanged", Q_ARG(const AbstractColumn*,this)); } /** * \brief Set an interval masked * * \param i the interval * \param mask true: mask, false: unmask */ void AbstractColumn::setMasked(Interval i, bool mask) { exec(new AbstractColumnSetMaskedCmd(m_abstract_column_private, i, mask), "maskingAboutToChange", "maskingChanged", Q_ARG(const AbstractColumn*,this)); } /** * \brief Overloaded function for convenience */ void AbstractColumn::setMasked(int row, bool mask) { setMasked(Interval(row,row), mask); } //////////////////////////////////////////////////////////////////////////////////////////////////// //@} //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// //! \name Formula related functions //@{ //////////////////////////////////////////////////////////////////////////////////////////////////// /** * \brief Return the formula associated with row 'row' */ QString AbstractColumn::formula(int row) const { Q_UNUSED(row); return QString(); } /** * \brief Return the intervals that have associated formulas * * This can be used to make a list of formulas with their intervals. * Here is some example code: * * \code * QStringList list; * QList< Interval > intervals = my_column.formulaIntervals(); * foreach(Interval interval, intervals) * list << QString(interval.toString() + ": " + my_column.formula(interval.start())); * \endcode */ QList< Interval > AbstractColumn::formulaIntervals() const { return QList< Interval >(); } /** * \brief Set a formula string for an interval of rows */ void AbstractColumn::setFormula(Interval i, QString formula) { Q_UNUSED(i) Q_UNUSED(formula) } /** * \brief Overloaded function for convenience */ void AbstractColumn::setFormula(int row, QString formula) { Q_UNUSED(row) Q_UNUSED(formula) } /** * \brief Clear all formulas */ void AbstractColumn::clearFormulas() {}; //////////////////////////////////////////////////////////////////////////////////////////////////// //@} //////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////// //! \name type specific functions //@{ //////////////////////////////////////////////////////////////////////////////////////////////////// /** * \brief Return the content of row 'row'. * * Use this only when columnMode() is Text */ QString AbstractColumn::textAt(int row) const { Q_UNUSED(row); return ""; } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is Text */ void AbstractColumn::setTextAt(int row, const QString& new_value) { Q_UNUSED(row) Q_UNUSED(new_value) } /** * \brief Replace a range of values * * Use this only when columnMode() is Text */ void AbstractColumn::replaceTexts(int first, const QStringList& new_values) { Q_UNUSED(first) Q_UNUSED(new_values) }; /** * \brief Return the date part of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ QDate AbstractColumn::dateAt(int row) const { Q_UNUSED(row); return QDate(); } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ void AbstractColumn::setDateAt(int row, const QDate& new_value) { Q_UNUSED(row) Q_UNUSED(new_value) }; /** * \brief Return the time part of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ QTime AbstractColumn::timeAt(int row) const { Q_UNUSED(row); return QTime(); } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ void AbstractColumn::setTimeAt(int row, const QTime& new_value) { Q_UNUSED(row) Q_UNUSED(new_value) } /** * \brief Return the QDateTime in row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ QDateTime AbstractColumn::dateTimeAt(int row) const { Q_UNUSED(row); return QDateTime(); } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is DateTime, Month or Day */ void AbstractColumn::setDateTimeAt(int row, const QDateTime& new_value) { Q_UNUSED(row) Q_UNUSED(new_value) }; /** * \brief Replace a range of values * * Use this only when columnMode() is DateTime, Month or Day */ void AbstractColumn::replaceDateTimes(int first, const QList& new_values) { Q_UNUSED(first) Q_UNUSED(new_values) }; /** * \brief Return the double value in row 'row' * * Use this only when columnMode() is Numeric */ double AbstractColumn::valueAt(int row) const { Q_UNUSED(row); return NAN; } /** * \brief Set the content of row 'row' * * Use this only when columnMode() is Numeric */ void AbstractColumn::setValueAt(int row, double new_value) { Q_UNUSED(row) Q_UNUSED(new_value) }; /** * \brief Replace a range of values * * Use this only when columnMode() is Numeric */ void AbstractColumn::replaceValues(int first, const QVector& new_values) { Q_UNUSED(first) Q_UNUSED(new_values) } double AbstractColumn::minimum() const{ double val; double min = INFINITY; for (int row = 0; row < rowCount(); row++) { val = valueAt(row); if (std::isnan(val)) continue; if (val < min) min = val; } return min; } double AbstractColumn::maximum() const{ double val; double max = -INFINITY; for (int row = 0; row < rowCount(); row++) { val = valueAt(row); if (std::isnan(val)) continue; if (val > max) max = val; } return max; } //////////////////////////////////////////////////////////////////////////////////////////////////// //@} //////////////////////////////////////////////////////////////////////////////////////////////////// /** * \fn void AbstractColumn::plotDesignationAboutToChange(const AbstractColumn *source) * \brief Column plot designation will be changed * * 'source' is always the this pointer of the column that * emitted this signal. This way it's easier to use * one handler for lots of columns. */ /** * \fn void AbstractColumn::plotDesignationChanged(const AbstractColumn *source) * \brief Column plot designation changed * * 'source' is always the this pointer of the column that * emitted this signal. This way it's easier to use * one handler for lots of columns. */ /** * \fn void AbstractColumn::modeAboutToChange(const AbstractColumn *source) * \brief Column mode (possibly also the data type) will be changed * * 'source' is always the this pointer of the column that * emitted this signal. This way it's easier to use * one handler for lots of columns. */ /** * \fn void AbstractColumn::modeChanged(const AbstractColumn *source) * \brief Column mode (possibly also the data type) changed * * 'source' is always the this pointer of the column that * emitted this signal. This way it's easier to use * one handler for lots of columns. */ /** * \fn void AbstractColumn::dataAboutToChange(const AbstractColumn *source) * \brief Data of the column will be changed * * 'source' is always the this pointer of the column that * emitted this signal. This way it's easier to use * one handler for lots of columns. */ /** * \fn void AbstractColumn::dataChanged(const AbstractColumn *source) * \brief Data of the column has changed * * Important: When data has changed also the number * of rows in the column may have changed without * any other signal emission. * 'source' is always the this pointer of the column that * emitted this signal. This way it's easier to use * one handler for lots of columns. */ /** * \fn void AbstractColumn::rowsAboutToBeInserted(const AbstractColumn *source, int before, int count) * \brief Rows will be inserted * * \param source the column that emitted the signal * \param before the row to insert before * \param count the number of rows to be inserted */ /** * \fn void AbstractColumn::rowsInserted(const AbstractColumn *source, int before, int count) * \brief Rows have been inserted * * \param source the column that emitted the signal * \param before the row to insert before * \param count the number of rows to be inserted */ /** * \fn void AbstractColumn::rowsAboutToBeRemoved(const AbstractColumn *source, int first, int count) * \brief Rows will be deleted * * \param source the column that emitted the signal * \param first the first row to be deleted * \param count the number of rows to be deleted */ /** * \fn void AbstractColumn::rowsRemoved(const AbstractColumn *source, int first, int count) * \brief Rows have been deleted * * \param source the column that emitted the signal * \param first the first row that was deleted * \param count the number of deleted rows */ /** * \fn void AbstractColumn::maskingAboutToChange(const AbstractColumn *source) * \brief Rows are about to be masked or unmasked */ /** * \fn void AbstractColumn::maskingChanged(const AbstractColumn *source) * \brief Rows have been masked or unmasked */ /** * \fn void AbstractColumn::aboutToBeDestroyed(const AbstractColumn *source) * \brief Emitted shortl before this column is deleted * * \param source the object emitting this signal * * This is needed by AbstractFilter. */ /** * \brief Read XML mask element */ bool AbstractColumn::XmlReadMask(XmlStreamReader *reader) { Q_ASSERT(reader->isStartElement() && reader->name() == "mask"); bool ok1, ok2; int start, end; start = reader->readAttributeInt("start_row", &ok1); end = reader->readAttributeInt("end_row", &ok2); if(!ok1 || !ok2) { reader->raiseError(i18n("invalid or missing start or end row")); return false; } setMasked(Interval(start,end)); if (!reader->skipToEndElement()) return false; return true; } /** * \brief Write XML mask element */ void AbstractColumn::XmlWriteMask(QXmlStreamWriter *writer) const { foreach(const Interval& interval, maskedIntervals()) { writer->writeStartElement("mask"); writer->writeAttribute("start_row", QString::number(interval.start())); writer->writeAttribute("end_row", QString::number(interval.end())); writer->writeEndElement(); } } diff --git a/src/backend/lib/PropertyChangeCommand.h b/src/backend/lib/PropertyChangeCommand.h index 0d5a69c83..359e1d759 100644 --- a/src/backend/lib/PropertyChangeCommand.h +++ b/src/backend/lib/PropertyChangeCommand.h @@ -1,66 +1,66 @@ /*************************************************************************** File : PropertyChangeCommand.h Project : SciDAVis / LabPlot -------------------------------------------------------------------- Copyright : (C) 2010 Knut Franke Email (use @ for *) : Knut.Franke*gmx.net Description : Generic undo command changing a single variable. ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef PROPERTY_CHANGE_COMMAND_H #define PROPERTY_CHANGE_COMMAND_H #include /** * \class PropertyChangeCommand * \brief Generic undo command changing a single variable. * * Given a pointer to a variable (usually a member of the class instantiating the command, or of - * its private implementation class) and a new value, assigns the value to to the variable. A backup + * its private implementation class) and a new value, assigns the value to the variable. A backup * of the old value is made, so that undo/redo can switch back and forth between the two values. * The value type needs to support copy construction and assignment. */ template class PropertyChangeCommand : public QUndoCommand { public: PropertyChangeCommand(const QString &text, T *property, const T &new_value) : m_property(property), m_other_value(new_value) { setText(text); } virtual void redo() { T tmp = *m_property; *m_property = m_other_value; m_other_value = tmp; } virtual void undo() { redo(); } private: T *m_property; T m_other_value; }; #endif // ifndef PROPERTY_CHANGE_COMMAND_H diff --git a/src/kdefrontend/dockwidgets/CartesianPlotLegendDock.h b/src/kdefrontend/dockwidgets/CartesianPlotLegendDock.h index 9a5f3ec65..2655e0580 100644 --- a/src/kdefrontend/dockwidgets/CartesianPlotLegendDock.h +++ b/src/kdefrontend/dockwidgets/CartesianPlotLegendDock.h @@ -1,142 +1,142 @@ /*************************************************************************** File : CartesianPlotLegendDock.h Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2013-2016 Alexander Semke (alexander.semke@web.de) - Description : widget for cartesian legend legend properties + Description : widget for cartesian legend properties ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #ifndef CARTESIANPLOTLEGENDDOCK_H #define CARTESIANPLOTLEGENDDOCK_H #include #include "ui_cartesianplotlegenddock.h" #include "backend/worksheet/plots/PlotArea.h" #include "backend/worksheet/plots/cartesian/CartesianPlotLegend.h" class LabelWidget; class KUrlCompletion; class CartesianPlotLegendDock : public QWidget { Q_OBJECT public: explicit CartesianPlotLegendDock(QWidget*); ~CartesianPlotLegendDock(); void setLegends(QList); void activateTitleTab() const; private: Ui::CartesianPlotLegendDock ui; QList m_legendList; CartesianPlotLegend* m_legend; LabelWidget* labelWidget; bool m_initializing; KUrlCompletion* m_completion; void load(); void loadConfig(KConfig&); private slots: void init(); void retranslateUi(); //SLOTs for changes triggered in CartesianPlotLegendDock //"General"-tab void nameChanged(); void commentChanged(); void visibilityChanged(bool); void labelFontChanged(const QFont&); void labelColorChanged(const QColor&); void labelOrderChanged(int); void lineSymbolWidthChanged(double); void positionXChanged(int); void positionYChanged(int); void customPositionXChanged(double); void customPositionYChanged(double); //"Background"-tab void backgroundTypeChanged(int); void backgroundColorStyleChanged(int); void backgroundImageStyleChanged(int); void backgroundBrushStyleChanged(int); void backgroundFirstColorChanged(const QColor&); void backgroundSecondColorChanged(const QColor&); void selectFile(); void fileNameChanged(); void backgroundOpacityChanged(int); void borderStyleChanged(int); void borderColorChanged(const QColor&); void borderWidthChanged(double); void borderCornerRadiusChanged(double); void borderOpacityChanged(int); //"Layout"-tab void layoutTopMarginChanged(double); void layoutBottomMarginChanged(double); void layoutRightMarginChanged(double); void layoutLeftMarginChanged(double); void layoutHorizontalSpacingChanged(double); void layoutVerticalSpacingChanged(double); void layoutColumnCountChanged(int); //SLOTs for changes triggered in CartesianPlotLegend void legendDescriptionChanged(const AbstractAspect*); void legendLabelFontChanged(QFont&); void legendLabelColorChanged(QColor&); void legendLabelOrderChanged(bool); void legendLineSymbolWidthChanged(float); void legendPositionChanged(const CartesianPlotLegend::PositionWrapper&); void legendVisibilityChanged(bool); void legendBackgroundTypeChanged(PlotArea::BackgroundType); void legendBackgroundColorStyleChanged(PlotArea::BackgroundColorStyle); void legendBackgroundImageStyleChanged(PlotArea::BackgroundImageStyle); void legendBackgroundBrushStyleChanged(Qt::BrushStyle); void legendBackgroundFirstColorChanged(QColor&); void legendBackgroundSecondColorChanged(QColor&); void legendBackgroundFileNameChanged(QString&); void legendBackgroundOpacityChanged(float); void legendBorderPenChanged(QPen&); void legendBorderCornerRadiusChanged(float); void legendBorderOpacityChanged(float); void legendLayoutTopMarginChanged(float); void legendLayoutBottomMarginChanged(float); void legendLayoutLeftMarginChanged(float); void legendLayoutRightMarginChanged(float); void legendLayoutVerticalSpacingChanged(float); void legendLayoutHorizontalSpacingChanged(float); void legendLayoutColumnCountChanged(int); //save/load template void loadConfigFromTemplate(KConfig&); void saveConfigAsTemplate(KConfig&); signals: void info(const QString&); }; #endif diff --git a/src/kdefrontend/widgets/ExpressionTextEdit.cpp b/src/kdefrontend/widgets/ExpressionTextEdit.cpp index e8f57dd03..352b8355a 100644 --- a/src/kdefrontend/widgets/ExpressionTextEdit.cpp +++ b/src/kdefrontend/widgets/ExpressionTextEdit.cpp @@ -1,218 +1,218 @@ /*************************************************************************** File : ExpressionTextEdit.cpp Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2014-2016 Alexander Semke (alexander.semke@web.de) Description : widget for defining mathematical expressions modified version of http://qt-project.org/doc/qt-4.8/tools-customcompleter.html ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ /**************************************************************************** ** ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the examples of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:BSD$ ** You may use this file under the terms of the BSD license as follows: ** ** "Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions are ** met: ** * Redistributions of source code must retain the above copyright ** notice, this list of conditions and the following disclaimer. ** * Redistributions in binary form must reproduce the above copyright ** notice, this list of conditions and the following disclaimer in ** the documentation and/or other materials provided with the ** distribution. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names ** of its contributors may be used to endorse or promote products derived ** from this software without specific prior written permission. ** ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "ExpressionTextEdit.h" #include "backend/gsl/ExpressionParser.h" #include "tools/EquationHighlighter.h" #include #include #include #include /*! \class ExpressionTextEdit - \brief Provides a widget for for defining mathematical expressions + \brief Provides a widget for defining mathematical expressions Supports syntax-highlighting and completion. Modified version of http://qt-project.org/doc/qt-4.8/tools-customcompleter.html \ingroup kdefrontend */ ExpressionTextEdit::ExpressionTextEdit(QWidget *parent) : KTextEdit(parent), m_highlighter(new EquationHighlighter(this)), m_expressionType(XYEquationCurve::Neutral), m_isValid(false) { QStringList list = ExpressionParser::getInstance()->functions(); list.append(ExpressionParser::getInstance()->constants()); setTabChangesFocus(true); m_completer = new QCompleter(list, this); m_completer->setWidget(this); m_completer->setCompletionMode(QCompleter::PopupCompletion); m_completer->setCaseSensitivity(Qt::CaseInsensitive); connect(m_completer, SIGNAL(activated(QString)),this, SLOT(insertCompletion(QString))); connect(this, SIGNAL(textChanged()), this, SLOT(validateExpression()) ); connect(this, SIGNAL(cursorPositionChanged()), m_highlighter, SLOT(rehighlight()) ); } EquationHighlighter* ExpressionTextEdit::highlighter() { return m_highlighter; } bool ExpressionTextEdit::isValid() const { return (!document()->toPlainText().trimmed().isEmpty() && m_isValid); } void ExpressionTextEdit::setExpressionType(XYEquationCurve::EquationType type) { m_expressionType = type; m_variables.clear(); if (type==XYEquationCurve::Cartesian) m_variables<<"x"; else if (type==XYEquationCurve::Polar) m_variables<<"phi"; else if (type==XYEquationCurve::Parametric) m_variables<<"t"; else if (type==XYEquationCurve::Implicit) m_variables<<"x"<<"y"; m_highlighter->setVariables(m_variables); } void ExpressionTextEdit::setVariables(const QStringList& vars) { m_variables = vars; m_highlighter->setVariables(m_variables); validateExpression(true); } void ExpressionTextEdit::insertCompletion(const QString& completion) { QTextCursor tc = textCursor(); int extra = completion.length() - m_completer->completionPrefix().length(); tc.movePosition(QTextCursor::Left); tc.movePosition(QTextCursor::EndOfWord); tc.insertText(completion.right(extra)); setTextCursor(tc); } QString ExpressionTextEdit::textUnderCursor() const { QTextCursor tc = textCursor(); tc.select(QTextCursor::WordUnderCursor); return tc.selectedText(); } void ExpressionTextEdit::focusInEvent(QFocusEvent *e) { m_completer->setWidget(this); QTextEdit::focusInEvent(e); } void ExpressionTextEdit::focusOutEvent(QFocusEvent *e) { //when loosing focus, rehighlight to remove potential highlighting of openning and closing brackets m_highlighter->rehighlight(); QTextEdit::focusOutEvent(e); } void ExpressionTextEdit::keyPressEvent(QKeyEvent *e) { switch (e->key()) { case Qt::Key_Enter: case Qt::Key_Return: e->ignore(); return; default: break; } bool isShortcut = ((e->modifiers() & Qt::ControlModifier) && e->key() == Qt::Key_E); // CTRL+E if (!isShortcut) // do not process the shortcut when we have a completer QTextEdit::keyPressEvent(e); const bool ctrlOrShift = e->modifiers() & (Qt::ControlModifier | Qt::ShiftModifier); if ((ctrlOrShift && e->text().isEmpty())) return; static QString eow("~!@#$%^&*()_+{}|:\"<>?,./;'[]\\-="); // end of word bool hasModifier = (e->modifiers() != Qt::NoModifier) && !ctrlOrShift; QString completionPrefix = textUnderCursor(); if (!isShortcut && (hasModifier || e->text().isEmpty()|| completionPrefix.length() < 1 || eow.contains(e->text().right(1)))) { m_completer->popup()->hide(); return; } if (completionPrefix != m_completer->completionPrefix()) { m_completer->setCompletionPrefix(completionPrefix); m_completer->popup()->setCurrentIndex(m_completer->completionModel()->index(0, 0)); } QRect cr = cursorRect(); cr.setWidth(m_completer->popup()->sizeHintForColumn(0) + m_completer->popup()->verticalScrollBar()->sizeHint().width()); m_completer->complete(cr); // popup it up! } /*! * \brief Validates the current expression if the text was changed and highlights the text field red if the expression is invalid. * \param force forces the validation and highlighting when no text changes were made, used when new parameters/variables were provided */ void ExpressionTextEdit::validateExpression(bool force) { //check whether the expression was changed or only the formating QString text = toPlainText(); if (text != m_currentExpression || force) { m_isValid = ExpressionParser::getInstance()->isValid(text, m_variables); if (!m_isValid) setStyleSheet("QTextEdit{background: red;}"); else setStyleSheet(""); m_currentExpression = text; emit(expressionChanged()); } } diff --git a/src/kdefrontend/widgets/LabelWidget.cpp b/src/kdefrontend/widgets/LabelWidget.cpp index 690dc7caf..9863316e0 100644 --- a/src/kdefrontend/widgets/LabelWidget.cpp +++ b/src/kdefrontend/widgets/LabelWidget.cpp @@ -1,806 +1,806 @@ /*************************************************************************** File : LabelWidget.cc Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2008-2016 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2012-2014 Stefan Gerlach (stefan.gerlach@uni-konstanz.de) Description : label settings widget ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the Free Software * * Foundation, Inc., 51 Franklin Street, Fifth Floor, * * Boston, MA 02110-1301 USA * * * ***************************************************************************/ #include "LabelWidget.h" #include "backend/worksheet/Worksheet.h" #include "backend/worksheet/plots/cartesian/Axis.h" #include "tools/TeXRenderer.h" #include #include #include #include #include #include /*! \class LabelWidget - \brief Widget for editing the properties of a TextLabel object, mostly used in an an appropriate dock widget. + \brief Widget for editing the properties of a TextLabel object, mostly used in an appropriate dock widget. In order the properties of the label to be shown, \c loadConfig() has to be called with the correspondig KConfigGroup (settings for a label in *Plot, Axis etc. or for an independent label on the worksheet). \ingroup kdefrontend */ LabelWidget::LabelWidget(QWidget* parent) : QWidget(parent), m_initializing(false), m_dateTimeMenu(new KMenu(this)), m_teXEnabled(false) { ui.setupUi(this); m_dateTimeMenu->setSeparatorsCollapsible(false); //we don't want the first separator to be removed QGridLayout* layout = static_cast(this->layout()); layout->setContentsMargins(2,2,2,2); layout->setHorizontalSpacing(2); layout->setVerticalSpacing(2); ui.kcbFontColor->setColor(Qt::black); // default color //Icons ui.tbFontBold->setIcon( KIcon("format-text-bold") ); ui.tbFontItalic->setIcon( KIcon("format-text-italic") ); ui.tbFontUnderline->setIcon( KIcon("format-text-underline") ); ui.tbFontStrikeOut->setIcon( KIcon("format-text-strikethrough") ); ui.tbFontSuperScript->setIcon( KIcon("format-text-superscript") ); ui.tbFontSubScript->setIcon( KIcon("format-text-subscript") ); ui.tbSymbols->setIcon( KIcon("labplot-format-text-symbol") ); ui.tbDateTime->setIcon( KIcon("chronometer") ); ui.tbTexUsed->setIcon( KIcon("labplot-TeX-logo") ); //Positioning and alignment ui.cbPositionX->addItem(i18n("left")); ui.cbPositionX->addItem(i18n("center")); ui.cbPositionX->addItem(i18n("right")); ui.cbPositionX->addItem(i18n("custom")); ui.cbPositionY->addItem(i18n("top")); ui.cbPositionY->addItem(i18n("center")); ui.cbPositionY->addItem(i18n("bottom")); ui.cbPositionY->addItem(i18n("custom")); ui.cbHorizontalAlignment->addItem(i18n("left")); ui.cbHorizontalAlignment->addItem(i18n("center")); ui.cbHorizontalAlignment->addItem(i18n("right")); ui.cbVerticalAlignment->addItem(i18n("top")); ui.cbVerticalAlignment->addItem(i18n("center")); ui.cbVerticalAlignment->addItem(i18n("bottom")); //check whether the used latex compiler is available. //Following logic is implemented (s.a. LabelWidget::teXUsedChanged()): //1. in case latex was used to generate the text label in the stored project //and no latex is available on the target system, latex button is toggled and //the user still can switch to the non-latex mode. //2. in case the label was in the non-latex mode and no latex is available, //deactivate the latex button so the user cannot switch to this mode. m_teXEnabled = TeXRenderer::enabled(); //SLOTS // text properties connect(ui.tbTexUsed, SIGNAL(clicked(bool)), this, SLOT(teXUsedChanged(bool)) ); connect(ui.teLabel, SIGNAL(textChanged()), this, SLOT(textChanged())); connect(ui.teLabel, SIGNAL(currentCharFormatChanged(QTextCharFormat)), this, SLOT(charFormatChanged(QTextCharFormat))); connect(ui.kcbFontColor, SIGNAL(changed(QColor)), this, SLOT(fontColorChanged(QColor))); connect(ui.tbFontBold, SIGNAL(clicked(bool)), this, SLOT(fontBoldChanged(bool))); connect(ui.tbFontItalic, SIGNAL(clicked(bool)), this, SLOT(fontItalicChanged(bool))); connect(ui.tbFontUnderline, SIGNAL(clicked(bool)), this, SLOT(fontUnderlineChanged(bool))); connect(ui.tbFontStrikeOut, SIGNAL(clicked(bool)), this, SLOT(fontStrikeOutChanged(bool))); connect(ui.tbFontSuperScript, SIGNAL(clicked(bool)), this, SLOT(fontSuperScriptChanged(bool))); connect(ui.tbFontSubScript, SIGNAL(clicked(bool)), this, SLOT(fontSubScriptChanged(bool))); connect(ui.tbSymbols, SIGNAL(clicked(bool)), this, SLOT(charMenu())); connect(ui.tbDateTime, SIGNAL(clicked(bool)), this, SLOT(dateTimeMenu())); connect(m_dateTimeMenu, SIGNAL(triggered(QAction*)), this, SLOT(insertDateTime(QAction*)) ); connect(ui.kfontRequester, SIGNAL(fontSelected(QFont)), this, SLOT(fontChanged(QFont))); connect(ui.kfontRequesterTeX, SIGNAL(fontSelected(QFont)), this, SLOT(teXFontChanged(QFont))); connect(ui.sbFontSize, SIGNAL(valueChanged(int)), this, SLOT(fontSizeChanged(int)) ); // geometry connect( ui.cbPositionX, SIGNAL(currentIndexChanged(int)), this, SLOT(positionXChanged(int)) ); connect( ui.cbPositionY, SIGNAL(currentIndexChanged(int)), this, SLOT(positionYChanged(int)) ); connect( ui.sbPositionX, SIGNAL(valueChanged(double)), this, SLOT(customPositionXChanged(double)) ); connect( ui.sbPositionY, SIGNAL(valueChanged(double)), this, SLOT(customPositionYChanged(double)) ); connect( ui.cbHorizontalAlignment, SIGNAL(currentIndexChanged(int)), this, SLOT(horizontalAlignmentChanged(int)) ); connect( ui.cbVerticalAlignment, SIGNAL(currentIndexChanged(int)), this, SLOT(verticalAlignmentChanged(int)) ); connect( ui.sbRotation, SIGNAL(valueChanged(int)), this, SLOT(rotationChanged(int)) ); connect( ui.sbOffsetX, SIGNAL(valueChanged(double)), this, SLOT(offsetXChanged(double)) ); connect( ui.sbOffsetY, SIGNAL(valueChanged(double)), this, SLOT(offsetYChanged(double)) ); connect( ui.chbVisible, SIGNAL(clicked(bool)), this, SLOT(visibilityChanged(bool)) ); //TODO: https://bugreports.qt.io/browse/QTBUG-25420 ui.tbFontUnderline->hide(); ui.tbFontStrikeOut->hide(); } void LabelWidget::setLabels(QList labels) { m_labelsList = labels; m_label = labels.first(); ui.lOffsetX->hide(); ui.lOffsetY->hide(); ui.sbOffsetX->hide(); ui.sbOffsetY->hide(); this->load(); initConnections(); } void LabelWidget::setAxes(QList axes) { m_labelsList.clear(); foreach (Axis* axis, axes) { m_labelsList.append(axis->title()); connect(axis, SIGNAL(titleOffsetXChanged(float)), this, SLOT(labelOffsetxChanged(float)) ); connect(axis, SIGNAL(titleOffsetYChanged(float)), this, SLOT(labelOffsetyChanged(float)) ); connect(axis->title(), SIGNAL(rotationAngleChanged(float)), this, SLOT(labelRotationAngleChanged(float)) ); } m_axesList = axes; m_label = m_labelsList.first(); this->load(); initConnections(); } void LabelWidget::initConnections() const { connect( m_label, SIGNAL(textWrapperChanged(TextLabel::TextWrapper)), this, SLOT(labelTextWrapperChanged(TextLabel::TextWrapper)) ); connect( m_label, SIGNAL(teXImageUpdated(bool)), this, SLOT(labelTeXImageUpdated(bool)) ); connect( m_label, SIGNAL(teXFontChanged(QFont)), this, SLOT(labelTeXFontChanged(QFont)) ); connect( m_label, SIGNAL(teXFontColorChanged(QColor)), this, SLOT(labelTeXFontColorChanged(QColor)) ); connect( m_label, SIGNAL(positionChanged(TextLabel::PositionWrapper)), this, SLOT(labelPositionChanged(TextLabel::PositionWrapper)) ); connect( m_label, SIGNAL(horizontalAlignmentChanged(TextLabel::HorizontalAlignment)), this, SLOT(labelHorizontalAlignmentChanged(TextLabel::HorizontalAlignment)) ); connect( m_label, SIGNAL(verticalAlignmentChanged(TextLabel::VerticalAlignment)), this, SLOT(labelVerticalAlignmentChanged(TextLabel::VerticalAlignment)) ); connect( m_label, SIGNAL(rotationAngleChanged(float)), this, SLOT(labelRotationAngleChanged(float)) ); connect( m_label, SIGNAL(visibleChanged(bool)), this, SLOT(labelVisibleChanged(bool)) ); } /*! * enables/disables the "fixed label"-mode, used when displaying * the properties of axis' title label. * In this mode, in the "geometry"-part only the offset (offset to the axis) * and the rotation of the label are available. */ void LabelWidget::setFixedLabelMode(const bool b) { ui.lPositionX->setVisible(!b); ui.cbPositionX->setVisible(!b); ui.sbPositionX->setVisible(!b); ui.lPositionY->setVisible(!b); ui.cbPositionY->setVisible(!b); ui.sbPositionY->setVisible(!b); ui.lHorizontalAlignment->setVisible(!b); ui.cbHorizontalAlignment->setVisible(!b); ui.lVerticalAlignment->setVisible(!b); ui.cbVerticalAlignment->setVisible(!b); ui.lOffsetX->setVisible(b); ui.lOffsetY->setVisible(b); ui.sbOffsetX->setVisible(b); ui.sbOffsetY->setVisible(b); } /*! * enables/disables all geometry relevant widgets. * Used when displaying legend's title label. */ void LabelWidget::setNoGeometryMode(const bool b) { ui.lGeometry->setVisible(!b); ui.lPositionX->setVisible(!b); ui.cbPositionX->setVisible(!b); ui.sbPositionX->setVisible(!b); ui.lPositionY->setVisible(!b); ui.cbPositionY->setVisible(!b); ui.sbPositionY->setVisible(!b); ui.lHorizontalAlignment->setVisible(!b); ui.cbHorizontalAlignment->setVisible(!b); ui.lVerticalAlignment->setVisible(!b); ui.cbVerticalAlignment->setVisible(!b); ui.lOffsetX->setVisible(!b); ui.lOffsetY->setVisible(!b); ui.sbOffsetX->setVisible(!b); ui.sbOffsetY->setVisible(!b); ui.lRotation->setVisible(!b); ui.sbRotation->setVisible(!b); } //********************************************************** //****** SLOTs for changes triggered in LabelWidget ******** //********************************************************** // text formating slots void LabelWidget::textChanged() { if (m_initializing) return; if (ui.tbTexUsed->isChecked()) { QString text=ui.teLabel->toPlainText(); TextLabel::TextWrapper wrapper(text, true); foreach (TextLabel* label, m_labelsList) label->setText(wrapper); } else { //save an empty string instead of a html-string with empty body, if no text available in QTextEdit QString text; if (ui.teLabel->toPlainText() == "") text = ""; else text = ui.teLabel->toHtml(); TextLabel::TextWrapper wrapper(text, false); foreach (TextLabel* label, m_labelsList) label->setText(wrapper); } } void LabelWidget::charFormatChanged(const QTextCharFormat& format) { if (m_initializing) return; // update button state if(format.fontWeight() == QFont::Bold) ui.tbFontBold->setChecked(true); else ui.tbFontBold->setChecked(false); ui.tbFontItalic->setChecked(format.fontItalic()); ui.tbFontUnderline->setChecked(format.fontUnderline()); if(format.verticalAlignment() == QTextCharFormat::AlignSuperScript) ui.tbFontSuperScript->setChecked(true); else ui.tbFontSuperScript->setChecked(false); if(format.verticalAlignment() == QTextCharFormat::AlignSubScript) ui.tbFontSubScript->setChecked(true); else ui.tbFontSubScript->setChecked(false); ui.tbFontStrikeOut->setChecked(format.fontStrikeOut()); if(!ui.tbTexUsed->isChecked()) ui.kcbFontColor->setColor(format.foreground().color()); ui.kfontRequester->setFont(format.font()); } void LabelWidget::teXUsedChanged(bool checked) { //hide text editing elements if TeX-option is used ui.tbFontBold->setVisible(!checked); ui.tbFontItalic->setVisible(!checked); //TODO: https://bugreports.qt.io/browse/QTBUG-25420 // ui.tbFontUnderline->setVisible(!checked); // ui.tbFontStrikeOut->setVisible(!checked); ui.tbFontSubScript->setVisible(!checked); ui.tbFontSuperScript->setVisible(!checked); ui.tbSymbols->setVisible(!checked); ui.lFont->setVisible(!checked); ui.kfontRequester->setVisible(!checked); if (checked) { KConfigGroup group = KGlobal::config()->group(QLatin1String("Settings_Worksheet")); QString engine = group.readEntry("LaTeXEngine", ""); if (engine == "xelatex" || engine == "lualatex") { ui.lFontTeX->setVisible(true); ui.kfontRequesterTeX->setVisible(true); ui.lFontSize->setVisible(false); ui.sbFontSize->setVisible(false); } else { ui.lFontTeX->setVisible(false); ui.kfontRequesterTeX->setVisible(false); ui.lFontSize->setVisible(true); ui.sbFontSize->setVisible(true); } } else { ui.lFontTeX->setVisible(false); ui.kfontRequesterTeX->setVisible(false); ui.lFontSize->setVisible(false); ui.sbFontSize->setVisible(false); } //no latex is available and the user switched to the text mode, //deactivate the button since it shouldn't be possible anymore to switch to the TeX-mode if (!m_teXEnabled && !checked) { ui.tbTexUsed->setEnabled(false); ui.tbTexUsed->setToolTip(i18n("LaTeX typesetting not possible. Please check the settings.")); } else { ui.tbTexUsed->setEnabled(true); ui.tbTexUsed->setToolTip(""); } //when switching to the text mode, set the background color to white just for the case the latex code provided by the user //in the TeX-mode is not valid and the background was set to red (s.a. LabelWidget::labelTeXImageUpdated()) if (!checked) ui.teLabel->setStyleSheet(""); if (m_initializing) return; QString text = checked ? ui.teLabel->toPlainText() : ui.teLabel->toHtml(); TextLabel::TextWrapper wrapper(text, checked); foreach (TextLabel* label, m_labelsList) label->setText(wrapper); } void LabelWidget::fontColorChanged(const QColor& color) { if (m_initializing) return; ui.teLabel->setTextColor(color); foreach (TextLabel* label, m_labelsList) label->setTeXFontColor(color); } void LabelWidget::fontSizeChanged(int value) { if (m_initializing) return; QFont font = m_label->teXFont(); font.setPointSize(value); foreach (TextLabel* label, m_labelsList) label->setTeXFont(font); } void LabelWidget::fontBoldChanged(bool checked) { if (m_initializing) return; if (checked) ui.teLabel->setFontWeight(QFont::Bold); else ui.teLabel->setFontWeight(QFont::Normal); } void LabelWidget::fontItalicChanged(bool checked) { if (m_initializing) return; ui.teLabel->setFontItalic(checked); } void LabelWidget::fontUnderlineChanged(bool checked) { if (m_initializing) return; ui.teLabel->setFontUnderline(checked); } void LabelWidget::fontStrikeOutChanged(bool checked) { if (m_initializing) return; QTextCharFormat format = ui.teLabel->currentCharFormat(); format.setFontStrikeOut(checked); ui.teLabel->setCurrentCharFormat(format); } void LabelWidget::fontSuperScriptChanged(bool checked) { if (m_initializing) return; QTextCharFormat format = ui.teLabel->currentCharFormat(); if (checked) format.setVerticalAlignment(QTextCharFormat::AlignSuperScript); else format.setVerticalAlignment(QTextCharFormat::AlignNormal); ui.teLabel->setCurrentCharFormat(format); } void LabelWidget::fontSubScriptChanged(bool checked) { if (m_initializing) return; QTextCharFormat format = ui.teLabel->currentCharFormat(); if (checked) format.setVerticalAlignment(QTextCharFormat::AlignSubScript); else format.setVerticalAlignment(QTextCharFormat::AlignNormal); ui.teLabel->setCurrentCharFormat(format); } void LabelWidget::fontChanged(const QFont& font) { if (m_initializing) return; // underline and strike-out not included ui.teLabel->setFontFamily(font.family()); ui.teLabel->setFontPointSize(font.pointSize()); ui.teLabel->setFontItalic(font.italic()); ui.teLabel->setFontWeight(font.weight()); } void LabelWidget::teXFontChanged(const QFont& font) { if (m_initializing) return; foreach (TextLabel* label, m_labelsList) label->setTeXFont(font); } void LabelWidget::charMenu() { QMenu menu; KCharSelect selection(this, 0, KCharSelect::SearchLine | KCharSelect::CharacterTable | KCharSelect::BlockCombos | KCharSelect::HistoryButtons); selection.setCurrentFont(ui.teLabel->currentFont()); connect(&selection, SIGNAL(charSelected(QChar)), this, SLOT(insertChar(QChar))); connect(&selection, SIGNAL(charSelected(QChar)), &menu, SLOT(close())); QWidgetAction *widgetAction = new QWidgetAction(this); widgetAction->setDefaultWidget(&selection); menu.addAction(widgetAction); QPoint pos(-menu.sizeHint().width()+ui.tbSymbols->width(),-menu.sizeHint().height()); menu.exec(ui.tbSymbols->mapToGlobal(pos)); } void LabelWidget::insertChar(QChar c) { ui.teLabel->insertPlainText(QString(c)); } void LabelWidget::dateTimeMenu() { m_dateTimeMenu->clear(); QDate date = QDate::currentDate(); m_dateTimeMenu->addSeparator()->setText(i18n("Date")); m_dateTimeMenu->addAction( date.toString(Qt::TextDate) ); m_dateTimeMenu->addAction( date.toString(Qt::ISODate) ); m_dateTimeMenu->addAction( date.toString(Qt::TextDate) ); m_dateTimeMenu->addAction( date.toString(Qt::SystemLocaleShortDate) ); m_dateTimeMenu->addAction( date.toString(Qt::SystemLocaleLongDate) ); QDateTime time = QDateTime::currentDateTime(); m_dateTimeMenu->addSeparator()->setText(i18n("Date and Time")); m_dateTimeMenu->addAction( time.toString(Qt::TextDate) ); m_dateTimeMenu->addAction( time.toString(Qt::ISODate) ); m_dateTimeMenu->addAction( time.toString(Qt::TextDate) ); m_dateTimeMenu->addAction( time.toString(Qt::SystemLocaleShortDate) ); m_dateTimeMenu->addAction( time.toString(Qt::SystemLocaleLongDate) ); m_dateTimeMenu->exec( mapToGlobal(ui.tbDateTime->rect().bottomLeft())); } void LabelWidget::insertDateTime(QAction* action) { ui.teLabel->insertPlainText( action->text().remove('&') ); } // geometry slots /*! called when label's current horizontal position relative to its parent (left, center, right, custom ) is changed. */ void LabelWidget::positionXChanged(int index) { //Enable/disable the spinbox for the x- oordinates if the "custom position"-item is selected/deselected if (index == ui.cbPositionX->count()-1 ) ui.sbPositionX->setEnabled(true); else ui.sbPositionX->setEnabled(false); if (m_initializing) return; TextLabel::PositionWrapper position = m_label->position(); position.horizontalPosition = TextLabel::HorizontalPosition(index); foreach (TextLabel* label, m_labelsList) label->setPosition(position); } /*! called when label's current horizontal position relative to its parent (top, center, bottom, custom ) is changed. */ void LabelWidget::positionYChanged(int index) { //Enable/disable the spinbox for the y-coordinates if the "custom position"-item is selected/deselected if (index == ui.cbPositionY->count()-1 ) ui.sbPositionY->setEnabled(true); else ui.sbPositionY->setEnabled(false); if (m_initializing) return; TextLabel::PositionWrapper position = m_label->position(); position.verticalPosition = TextLabel::VerticalPosition(index); foreach (TextLabel* label, m_labelsList) label->setPosition(position); } void LabelWidget::customPositionXChanged(double value) { if (m_initializing) return; TextLabel::PositionWrapper position = m_label->position(); position.point.setX(Worksheet::convertToSceneUnits(value, Worksheet::Centimeter)); foreach (TextLabel* label, m_labelsList) label->setPosition(position); } void LabelWidget::customPositionYChanged(double value) { if (m_initializing) return; TextLabel::PositionWrapper position = m_label->position(); position.point.setY(Worksheet::convertToSceneUnits(value, Worksheet::Centimeter)); foreach (TextLabel* label, m_labelsList) label->setPosition(position); } void LabelWidget::horizontalAlignmentChanged(int index) { if (m_initializing) return; foreach (TextLabel* label, m_labelsList) label->setHorizontalAlignment(TextLabel::HorizontalAlignment(index)); } void LabelWidget::verticalAlignmentChanged(int index) { if (m_initializing) return; foreach (TextLabel* label, m_labelsList) label->setVerticalAlignment(TextLabel::VerticalAlignment(index)); } void LabelWidget::rotationChanged(int value) { if (m_initializing) return; foreach (TextLabel* label, m_labelsList) label->setRotationAngle(value); } void LabelWidget::offsetXChanged(double value) { if (m_initializing) return; foreach (Axis* axis, m_axesList) axis->setTitleOffsetX( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); } void LabelWidget::offsetYChanged(double value) { if (m_initializing) return; foreach (Axis* axis, m_axesList) axis->setTitleOffsetY( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); } void LabelWidget::visibilityChanged(bool state) { if (m_initializing) return; foreach (TextLabel* label, m_labelsList) label->setVisible(state); } //********************************************************* //****** SLOTs for changes triggered in TextLabel ********* //********************************************************* void LabelWidget::labelTextWrapperChanged(const TextLabel::TextWrapper& text) { m_initializing = true; //save and restore the current cursor position after changing the text QTextCursor cursor = ui.teLabel->textCursor(); int position = cursor.position(); ui.teLabel->setText(text.text); cursor.movePosition(QTextCursor::Start); cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, position); ui.teLabel->setTextCursor(cursor); ui.tbTexUsed->setChecked(text.teXUsed); this->teXUsedChanged(text.teXUsed); m_initializing = false; } /*! * \brief Highlights the text field red if wrong latex syntax was used (null image was produced) * or something else went wrong during rendering (\sa ExpressionTextEdit::validateExpression()) */ void LabelWidget::labelTeXImageUpdated(bool valid) { if (!valid) ui.teLabel->setStyleSheet("QTextEdit{background: red;}"); else ui.teLabel->setStyleSheet(""); } void LabelWidget::labelTeXFontChanged(const QFont& font) { m_initializing = true; ui.kfontRequesterTeX->setFont(font); ui.sbFontSize->setValue(font.pointSize()); m_initializing = false; } void LabelWidget::labelTeXFontColorChanged(const QColor color) { m_initializing = true; ui.kcbFontColor->setColor(color); m_initializing = false; } void LabelWidget::labelPositionChanged(const TextLabel::PositionWrapper& position) { m_initializing = true; ui.sbPositionX->setValue( Worksheet::convertFromSceneUnits(position.point.x(), Worksheet::Centimeter) ); ui.sbPositionY->setValue( Worksheet::convertFromSceneUnits(position.point.y(), Worksheet::Centimeter) ); ui.cbPositionX->setCurrentIndex( position.horizontalPosition ); ui.cbPositionY->setCurrentIndex( position.verticalPosition ); m_initializing = false; } void LabelWidget::labelHorizontalAlignmentChanged(TextLabel::HorizontalAlignment index) { m_initializing = true; ui.cbHorizontalAlignment->setCurrentIndex(index); m_initializing = false; } void LabelWidget::labelVerticalAlignmentChanged(TextLabel::VerticalAlignment index) { m_initializing = true; ui.cbVerticalAlignment->setCurrentIndex(index); m_initializing = false; } void LabelWidget::labelOffsetxChanged(float offset) { m_initializing = true; ui.sbOffsetX->setValue(Worksheet::convertFromSceneUnits(offset, Worksheet::Point)); m_initializing = false; } void LabelWidget::labelOffsetyChanged(float offset) { m_initializing = true; ui.sbOffsetY->setValue(Worksheet::convertFromSceneUnits(offset, Worksheet::Point)); m_initializing = false; } void LabelWidget::labelRotationAngleChanged(float angle) { m_initializing = true; ui.sbRotation->setValue(angle); m_initializing = false; } void LabelWidget::labelVisibleChanged(bool on) { m_initializing = true; ui.chbVisible->setChecked(on); m_initializing = false; } //********************************************************** //******************** SETTINGS **************************** //********************************************************** void LabelWidget::load() { if(m_label == NULL) return; m_initializing = true; ui.chbVisible->setChecked(m_label->isVisible()); //Text ui.teLabel->setHtml(m_label->text().text); ui.teLabel->selectAll(); ui.teLabel->setFocus(); //TeX ui.tbTexUsed->setChecked( (bool) m_label->text().teXUsed ); this->teXUsedChanged(m_label->text().teXUsed); ui.kfontRequesterTeX->setFont(m_label->teXFont()); ui.sbFontSize->setValue( m_label->teXFont().pointSize() ); if(m_label->text().teXUsed) ui.kcbFontColor->setColor(m_label->teXFontColor()); //Set text format ui.tbFontBold->setChecked(ui.teLabel->fontWeight()==QFont::Bold); ui.tbFontItalic->setChecked(ui.teLabel->fontItalic()); ui.tbFontUnderline->setChecked(ui.teLabel->fontUnderline()); QTextCharFormat format = ui.teLabel->currentCharFormat(); ui.tbFontStrikeOut->setChecked(format.fontStrikeOut()); ui.tbFontSuperScript->setChecked(format.verticalAlignment() == QTextCharFormat::AlignSuperScript); ui.tbFontSubScript->setChecked(format.verticalAlignment() == QTextCharFormat::AlignSubScript); ui.kfontRequester->setFont(format.font()); // Geometry ui.cbPositionX->setCurrentIndex( (int) m_label->position().horizontalPosition ); positionXChanged(ui.cbPositionX->currentIndex()); ui.sbPositionX->setValue( Worksheet::convertFromSceneUnits(m_label->position().point.x(),Worksheet::Centimeter) ); ui.cbPositionY->setCurrentIndex( (int) m_label->position().verticalPosition ); positionYChanged(ui.cbPositionY->currentIndex()); ui.sbPositionY->setValue( Worksheet::convertFromSceneUnits(m_label->position().point.y(),Worksheet::Centimeter) ); if (!m_axesList.isEmpty()) { ui.sbOffsetX->setValue( Worksheet::convertFromSceneUnits(m_axesList.first()->titleOffsetX(), Worksheet::Point) ); ui.sbOffsetY->setValue( Worksheet::convertFromSceneUnits(m_axesList.first()->titleOffsetY(), Worksheet::Point) ); } ui.cbHorizontalAlignment->setCurrentIndex( (int) m_label->horizontalAlignment() ); ui.cbVerticalAlignment->setCurrentIndex( (int) m_label->verticalAlignment() ); ui.sbRotation->setValue( m_label->rotationAngle() ); m_initializing = false; } void LabelWidget::loadConfig(KConfigGroup& group) { if(m_label == NULL) return; m_initializing = true; //TeX ui.tbTexUsed->setChecked(group.readEntry("TeXUsed", (bool) m_label->text().teXUsed)); this->teXUsedChanged(m_label->text().teXUsed); ui.sbFontSize->setValue( group.readEntry("TeXFontSize", m_label->teXFont().pointSize()) ); ui.kfontRequester->setFont(group.readEntry("TeXFont", m_label->teXFont())); if(m_label->text().teXUsed) ui.kcbFontColor->setColor(group.readEntry("TeXFontColor", m_label->teXFontColor())); //Set text format ui.tbFontBold->setChecked(ui.teLabel->fontWeight()==QFont::Bold); ui.tbFontItalic->setChecked(ui.teLabel->fontItalic()); ui.tbFontUnderline->setChecked(ui.teLabel->fontUnderline()); QTextCharFormat format = ui.teLabel->currentCharFormat(); ui.tbFontStrikeOut->setChecked(format.fontStrikeOut()); ui.tbFontSuperScript->setChecked(format.verticalAlignment() == QTextCharFormat::AlignSuperScript); ui.tbFontSubScript->setChecked(format.verticalAlignment() == QTextCharFormat::AlignSubScript); ui.kfontRequester->setFont(format.font()); // Geometry ui.cbPositionX->setCurrentIndex( group.readEntry("PositionX", (int) m_label->position().horizontalPosition ) ); ui.sbPositionX->setValue( Worksheet::convertFromSceneUnits(group.readEntry("PositionXValue", m_label->position().point.x()),Worksheet::Centimeter) ); ui.cbPositionY->setCurrentIndex( group.readEntry("PositionY", (int) m_label->position().verticalPosition ) ); ui.sbPositionY->setValue( Worksheet::convertFromSceneUnits(group.readEntry("PositionYValue", m_label->position().point.y()),Worksheet::Centimeter) ); if (!m_axesList.isEmpty()) { ui.sbOffsetX->setValue( Worksheet::convertFromSceneUnits(group.readEntry("OffsetX", m_axesList.first()->titleOffsetX()), Worksheet::Point) ); ui.sbOffsetY->setValue( Worksheet::convertFromSceneUnits(group.readEntry("OffsetY", m_axesList.first()->titleOffsetY()), Worksheet::Point) ); } ui.cbHorizontalAlignment->setCurrentIndex( group.readEntry("HorizontalAlignment", (int) m_label->horizontalAlignment()) ); ui.cbVerticalAlignment->setCurrentIndex( group.readEntry("VerticalAlignment", (int) m_label->verticalAlignment()) ); ui.sbRotation->setValue( group.readEntry("Rotation", m_label->rotationAngle()) ); m_initializing = false; } void LabelWidget::saveConfig(KConfigGroup& group) { //TeX group.writeEntry("TeXUsed", ui.tbTexUsed->isChecked()); group.writeEntry("TeXFontColor", ui.kcbFontColor->color()); group.writeEntry("TeXFont", ui.kfontRequesterTeX->font()); // Geometry group.writeEntry("PositionX", ui.cbPositionX->currentIndex()); group.writeEntry("PositionXValue", Worksheet::convertToSceneUnits(ui.sbPositionX->value(),Worksheet::Centimeter) ); group.writeEntry("PositionY", ui.cbPositionY->currentIndex()); group.writeEntry("PositionYValue", Worksheet::convertToSceneUnits(ui.sbPositionY->value(),Worksheet::Centimeter) ); if (!m_axesList.isEmpty()) { group.writeEntry("OffsetX", Worksheet::convertToSceneUnits(ui.sbOffsetX->value(), Worksheet::Point) ); group.writeEntry("OffsetY", Worksheet::convertToSceneUnits(ui.sbOffsetY->value(), Worksheet::Point) ); } group.writeEntry("HorizontalAlignment", ui.cbHorizontalAlignment->currentIndex()); group.writeEntry("VerticalAlignment", ui.cbVerticalAlignment->currentIndex()); group.writeEntry("Rotation", ui.sbRotation->value()); }