diff --git a/ChangeLog b/ChangeLog index 930e0ae50..babd9503a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,223 +1,224 @@ -----2.6----- New features: * Import of ROOT (CERN) TH1 histograms * Import of Ngspice raw files (ASCII and binary) * Import of data in JSON format (JSON arrays and objects) * Convolution/Deconvolution of data sets (sampling interval, linear/circular, normalization, wrap, standard kernel) * Cross-/Autocorrelation of data sets (sampling interval, linear/circular, normalization) * Allow to rotate plot legends * Allow to specify the number format when exporting spreadsheet and matrix * Improved user interface for data fitting (add fit function preview, show parameters directly, make options foldable) * [spreadsheet] when filling a float column with row numbers, automatically convert its type to integer * [spreadsheet] when filling an integer column with function values, automatically convert its type to float * [spreadsheet] data manipulation: add/subtract/multiply/divide for column values + * [worksheet] Allow to specify different border shapes for labels (rectangle, elipse, etc.) Bug fixes: * Fixed several problems in live data support * [spreadsheet] properly calculate function values out of integer x-values * [matrix] fix editing integer values * Don't crash when a scaling factor equal to zero was set for axis -----2.5----- New features: * Support for reading and plotting of live-data * Improved data fitting * Automatically guess parameter of custom models * Better result presentation * Support different weight types * Consider given x- and y-error when fitting (can be switched off) * Show t statistics, P > |t| and confidence interval * Calculate p-value for chi-square and F test in nonlinear fitting * added fit models for most statistical distributions * Improved theming * Apply themes to worksheet and to all its children * Respect theme settings also in plot legends and labels * Allow to disable theming in worksheets and plots after a theme was selected * Show currently active theme in the "Apply theme" button * New application option in the settings for the default theme used for new worksheets. * Support different data types * auto detect integer and datetime data in import * support number locale and datetime formats * improved data type support in spreadsheets * Import from SQL databases (tables or custom queries) * Import Origin OPJ projects * Much better support for Windows and macOS * Syntax highlighting for LaTeX in the text label * Allow to set the background color for LaTeX labels * Support Hermite polynomials from GSL 2.4 * Support error functions and related functions from libcerf * "Used in" sub-menu in column contex menu for faster navigation to the curves consuming the column * Direct application of analysis functions (smoothing, interpolating, etc.) on the ploted data via curve's context menu * Direct application of analysis functions on the data in the spreadsheet and plotting of the results via spreadsheet's context menu * Drag columns in the project explorer and drop them on plots (either in a worksheet view or in the project explorer) to create curves * "Show last N points" and "Show first N points" data ranges in cartesian plot * Added CLI option --presenter to start LabPlot directly in the presenter mode * Added CLI parameter to directly open project files (LabPlot or Origin) * Allow drag&drop of projects files (LabPlot and Origin) on the main window to load the project * Allow drag&drop of data files on the main window to import the data * Show tooltips for the supported mathematical functions and constants in the expression text field * Automatically switch to the scientific representation for numbers bigger than 10^4 on the axis tick labels * Automatically allow the latex typesetting in the application after the latex environment was installed later without making the user to go to the settings dialog * Allow to change the color scheme for the application * Smooth and animated zooming in the worksheet view * Allow to add text labels to plots * Improved building with MSVC, Intel and PGI compiler Performance improvements: * Faster copy&paste in the spreadsheet Bug fixes: * Bug 379877 - masked rows in spreadsheet not restored in project * Calculation of fit results corrected * Axes now support values larger than FLT_MAX (~10^38) and smaller than FLT_MIN (~10^-38) * When a LabPlot project is being droped in the main window, load the project directly instead of showing the import file dialog * Correctly save and restore masked cells * Don't crash if the rc-file was not found during the startup -----2.4----- New features: * Support themes for plots * Import and editing of FITS data files * Data reduction by removing data points using multiple algorithms * Numerical differentiation and integration with several options * Many new pre-defined fit models (Gompertz, Weibull, Log-Normal, Gumbel, etc.) sorted in categories * Fit parameter now support fixed values, lower/upper limits and use Unicode * Fit model and random number distribution formulas are now rendered with LaTeX * Support user specified x range in all analysis functions * Allow to enter complete LaTeX documents in text labels * Configuration parameter to use different LaTex engines (LuaLaTex, XeLatex, pdfLaTex, LaTex) * Disable LaTeX typesetting if no LaTex installation (and other required tools) were found at runtime * Presenter mode for worksheets * Support for Mac OS * Support for Julia's vectors and tuples in CAS worksheets (requires Cantor v. 16.12 or higher) * Allow to jump directly to the data source spreadsheet via XYCurve's context menu * Select and delete multiple objects in project explorer * Improved and extended internal parser for mathematical expressions * Copy of worksheet elements as image to the clipboard via CTRL+C Bug fixes: * BUG: 361326 - Allow to select curves with overlapping bounding boxes * Correctly load worksheet sizes from saved templates * Fixed crash when removing columns in spreadsheet * Fixed crash when fitting using GSL >= 2 * List of available functions corrected * Constants are now available with full accuracy * Windows: Import of files and open recent files fixed -----2.3----- New features: * Integration of Cantor - Support for different open-source computer algebra systems * Statistics on spreadsheets and matrices * Export of spreadsheets and matrices to LaTeX tables * Interpolation of data including different splines, cosine, exponential, cubic Hermite (Catmull-Rom, cardinal, Kochanek-Bartels) and rational functions * Data smoothing using moving average (centered or lagged), percentile filter or Savitzky-Golay algorithm * Fourier filter (low pass, high pass, band pass, band reject) with ideal, Butterworth, Chebychev I+II, Legendre or Bessel-Thomson filter * Fourier transform with many window functions (Welch, Hann, Hamming, etc.) calculating magnitude, amplitude, power, phase, dB, etc. and supporting one/two sided spectrum with or without shift and x scaling to frequency, index or period * Filter and search capabilities in the drop down box for the selection of data sources * Sigmoid function as a new pre-defined fit model * Support for compiling on Microsoft Windows Performance improvements: * Faster generation of random values * Global option to enable/disable the double-buffering for faster painting of curves (enabled on default) Bug fixes: * Save and restore last used setting in RandomValuesDialog * Update axis title shape on title rotations correctly * Save and restore custom column widths in the spreadsheet * Fixed sporadic crashes during project close -----2.2----- New features: * Datapicker - tool for extracting curves and data points from imported images * Custom point on the plot with user-defined position and appearance * Accept drag&drop events * Support GSL 2.x * Import and export dialogs save and restore their settings and sizes Performance improvements: * Faster rendering of the image view of the matrix Bug fixes: * BUG: 354744 - make setting of range auto scaling in CartesianPlot undo/redo-able * Removed couple of hard coded sizes given in pixels for better user-experience on hidpi-displays * Fixes the bug with disabled undo/redo-actions in after the undo-history was cleared * Keep the information about the columns to be shown in the project explorer after project close * Fixed some bugs in painting of the selection band on the worksheet * Allow to open gz- and bz2-compressed LabPlot project files on the command line interface -----2.1----- New features: * New Matrix view for handling matrix data. * Workbook - a new container for grouping several objects of type Spreadsheet and/or Matrix. * Import of binary, image, NetCDF and HDF data into spreadsheet or matrix. * Visual HDF and NetCDF parser to view content of files and import data sets. * Preview of all supported file types in import dialog. * Transparently import compressed data files. * In xy-curve the points may not be connected by the line if there are NANs in-between. This behaviour is controlled by the parameter "skip gaps". * Multiplier of Pi format of the plot axis for periodical functions. * New operations on columns in Spreadsheet - reverse, drop values and mask values. * Formula used to generate the values in a column is stored and can be changed/adjusted in the formula dialog afterwards. * Curve filling: the area below, under, left to or right to the curve can be filled. * Support for multiple variables in "Function Values"-dialog - new data in the spreadsheet can be calculated out of multiple columns. Performance improvements: * Speeded up the creation of new columns during the import Bug fixes: * Fixed wrong behaviour when doing "zoom&select" in a plot and then deselecting the plot - it was not possible anymore to select the plot again on the worksheet. -----2.0.2----- New features: * Plot 2D-curves defined by mathematical equations in cartesian and polar coordinates or via a parametric equation. * Linear and non-linear regression analysis. Several predefined fit-models are provided. User-defined models are also possible. * Besides fixed worksheet sizes (predefined sizes like A4 etc. or user-defined), complete view size can be used. All sizes are automatically adjusted on resize. * Different axis arrow types. * "select region and zoom in", "select x-region and zoom in", "select y-region and zoom in" functions for cartesian plot. * Rounded border for several worksheet objects (plot area, legend etc.) * Hover effect for axis, curve and text label. * Added a MessageBox - ask befor deleting worksheet objects. * Added three new types for drop lines - "zero baseline", "min baseline" and "max baseline" * Fill the selection in Spreadsheet with a constant value provided by the user * Fill columns with uniform and non-uniform random numbers, several distributions are available. * Fill columns with function values * Allow custom resolutions for PNG-export * Export of the spreadsheet to a text file. * Simultaneous zooming and navigation accross multiple plots. * Implemented "Powers of 10/2/e" for the axis tick labels Bug fixes: * Don't crash when trying to create a plot in case no rc-file was installed. * Don't allow to select unwanted objects in TreeViewComboBox in ImportDialog and XYCurveDock. * Corrected painting of background images in plot area and legend. * BUG: 330723 - fixed weird selection in spreadsheet. * BUG: 330774 - fixed wrong positioning of axis on orientation changes. * Update main window title when project name is changed -----2.0.1----- Bug fix release. Solved issues: * Fixed wrong scaling of legend's text labels in pdf-export * Fixed memory corruption in CartesianPlotDock that can lead to crashes -----2.0.0----- First stable release of LabPlot2. LabPlot2 is a complete rewrite of LabPlot1 and lacks in this release a lot of features available in the predecessor. On the other hand, the GUI and the usability is more superior as compared to LabPlot1 and there are several new features that were not available in LabPlot1. Brief summary of features and capabilities of LabPlot2 implemented in the first release: * project-based management of data * created objects are organized in a tree and are visualized and accessible in the project explorer * for a better management of objects, additional folders and sub-folders can be created within a project * spreadsheet with very basic functionality is available for manual data entry * "file data source" can be used to import a file and to let LabPlot2 watch for changes in that file * external data from an ascii file can be also directly imported to a spreadsheet for further editing * worksheet is the main object where plots, labels etc. are placed on * several zooming functions for the worksheet * only cartesian plot is implemented in the first release * arbitrary number of freely positionable axes is possible * xy-curve is implemented. As the data source for the x- and y-values columns either from a spreadsheet or from a file data source can be used * several zooming and "movement" functions are available for the plots which help to navigate through data * legend for xy-plots * a lot of properties of the worksheet elements can be edited in a very easy way in the corresponding dock widgets * plots on the worksheet can be put into a horizontal, vertical or grid layouts * export of worksheet (entire worksheet or current seleciton) to pdf, eps, png and svg diff --git a/src/backend/worksheet/TextLabel.cpp b/src/backend/worksheet/TextLabel.cpp index c141c860d..aa8cb34ae 100644 --- a/src/backend/worksheet/TextLabel.cpp +++ b/src/backend/worksheet/TextLabel.cpp @@ -1,867 +1,1103 @@ /*************************************************************************** File : TextLabel.cpp Project : LabPlot Description : Text label supporting reach text and latex formatting -------------------------------------------------------------------- Copyright : (C) 2009 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2012-2018 Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * 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 "TextLabel.h" #include "Worksheet.h" #include "TextLabelPrivate.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/XmlStreamReader.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /** * \class TextLabel * \brief A label supporting rendering of html- and tex-formatted texts. * * The label is aligned relative to the specified position. * The position can be either specified by providing the x- and y- coordinates * in parent's coordinate system, or by specifying one of the predefined position * flags (\ca HorizontalPosition, \ca VerticalPosition). */ TextLabel::TextLabel(const QString& name, Type type):WorksheetElement(name), d_ptr(new TextLabelPrivate(this)), m_type(type), visibilityAction(nullptr) { init(); } TextLabel::TextLabel(const QString &name, TextLabelPrivate *dd, Type type):WorksheetElement(name), d_ptr(dd), m_type(type), visibilityAction(nullptr) { init(); } TextLabel::Type TextLabel::type() const { return m_type; } void TextLabel::init() { Q_D(TextLabel); KConfig config; KConfigGroup group; if (m_type == AxisTitle) group = config.group("AxisTitle"); else if (m_type == PlotTitle) group = config.group("PlotTitle"); else if (m_type == PlotLegendTitle) group = config.group("PlotLegendTitle"); else group = config.group("TextLabel"); //properties common to all types d->textWrapper.teXUsed = group.readEntry("TeXUsed", false); d->teXFont.setFamily(group.readEntry("TeXFontFamily", "Computer Modern")); d->teXFont.setPointSize(group.readEntry("TeXFontSize", 12)); d->teXFontColor = group.readEntry("TeXFontColor", QColor(Qt::black)); d->teXBackgroundColor = group.readEntry("TeXBackgroundColor", QColor(Qt::white)); d->rotationAngle = group.readEntry("Rotation", 0.0); d->staticText.setTextFormat(Qt::RichText); // explicitly set no wrap mode for text label to avoid unnecessary line breaks QTextOption textOption; textOption.setWrapMode(QTextOption::NoWrap); d->staticText.setTextOption(textOption); //position and alignment relevant properties, dependent on the actual type if (m_type == PlotTitle || m_type == PlotLegendTitle) { d->position.horizontalPosition = (HorizontalPosition) group.readEntry("PositionX", (int)TextLabel::hPositionCenter); d->position.verticalPosition = (VerticalPosition) group.readEntry("PositionY", (int) TextLabel::vPositionTop); d->position.point.setX( group.readEntry("PositionXValue", Worksheet::convertToSceneUnits(1, Worksheet::Centimeter)) ); d->position.point.setY( group.readEntry("PositionYValue", Worksheet::convertToSceneUnits(1, Worksheet::Centimeter)) ); d->horizontalAlignment= (TextLabel::HorizontalAlignment) group.readEntry("HorizontalAlignment", (int)TextLabel::hAlignCenter); d->verticalAlignment= (TextLabel::VerticalAlignment) group.readEntry("VerticalAlignment", (int)TextLabel::vAlignBottom); } else { d->position.horizontalPosition = (HorizontalPosition) group.readEntry("PositionX", (int)TextLabel::hPositionCustom); d->position.verticalPosition = (VerticalPosition) group.readEntry("PositionY", (int) TextLabel::vPositionCustom); d->position.point.setX( group.readEntry("PositionXValue", Worksheet::convertToSceneUnits(1, Worksheet::Centimeter)) ); d->position.point.setY( group.readEntry("PositionYValue", Worksheet::convertToSceneUnits(1, Worksheet::Centimeter)) ); d->horizontalAlignment= (TextLabel::HorizontalAlignment) group.readEntry("HorizontalAlignment", (int)TextLabel::hAlignCenter); d->verticalAlignment= (TextLabel::VerticalAlignment) group.readEntry("VerticalAlignment", (int)TextLabel::vAlignCenter); } + //border + d->borderShape = (TextLabel::BorderShape)group.readEntry("BorderShape", (int)TextLabel::NoBorder); + d->borderPen = QPen(group.readEntry("BorderColor", QColor(Qt::black)), + group.readEntry("BorderWidth", Worksheet::convertToSceneUnits(1.0, Worksheet::Point)), + (Qt::PenStyle) group.readEntry("BorderStyle", (int)Qt::SolidLine)); + d->borderOpacity = group.readEntry("BorderOpacity", 1.0); + //scaling: //we need to scale from the font size specified in points to scene units. //furhermore, we create the tex-image in a higher resolution then usual desktop resolution // -> take this into account d->scaleFactor = Worksheet::convertToSceneUnits(1, Worksheet::Point); d->teXImageResolution = QApplication::desktop()->physicalDpiX(); d->teXImageScaleFactor = Worksheet::convertToSceneUnits(2.54/QApplication::desktop()->physicalDpiX(), Worksheet::Centimeter); connect(&d->teXImageFutureWatcher, &QFutureWatcher::finished, this, &TextLabel::updateTeXImage); } //no need to delete the d-pointer here - it inherits from QGraphicsItem //and is deleted during the cleanup in QGraphicsScene TextLabel::~TextLabel() = default; QGraphicsItem* TextLabel::graphicsItem() const { return d_ptr; } void TextLabel::setParentGraphicsItem(QGraphicsItem* item) { Q_D(TextLabel); d->setParentItem(item); d->updatePosition(); } void TextLabel::retransform() { Q_D(TextLabel); d->retransform(); } void TextLabel::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) { DEBUG("TextLabel::handleResize()"); Q_UNUSED(pageResize); Q_D(TextLabel); double ratio = 0; if (horizontalRatio > 1.0 || verticalRatio > 1.0) ratio = qMax(horizontalRatio, verticalRatio); else ratio = qMin(horizontalRatio, verticalRatio); d->teXFont.setPointSizeF(d->teXFont.pointSizeF() * ratio); d->updateText(); //TODO: doesn't seem to work QTextDocument doc; doc.setHtml(d->textWrapper.text); QTextCursor cursor(&doc); cursor.select(QTextCursor::Document); QTextCharFormat fmt = cursor.charFormat(); QFont font = fmt.font(); font.setPointSizeF(font.pointSizeF() * ratio); fmt.setFont(font); cursor.setCharFormat(fmt); } /*! Returns an icon to be used in the project explorer. */ QIcon TextLabel::icon() const{ return QIcon::fromTheme("draw-text"); } QMenu* TextLabel::createContextMenu() { QMenu *menu = WorksheetElement::createContextMenu(); QAction* firstAction = menu->actions().at(1); //skip the first action because of the "title-action" if (!visibilityAction) { visibilityAction = new QAction(i18n("Visible"), this); visibilityAction->setCheckable(true); connect(visibilityAction, &QAction::triggered, this, &TextLabel::visibilityChanged); } visibilityAction->setChecked(isVisible()); menu->insertAction(firstAction, visibilityAction); menu->insertSeparator(firstAction); return menu; } /* ============================ getter methods ================= */ CLASS_SHARED_D_READER_IMPL(TextLabel, TextLabel::TextWrapper, text, textWrapper) CLASS_SHARED_D_READER_IMPL(TextLabel, QColor, teXFontColor, teXFontColor); CLASS_SHARED_D_READER_IMPL(TextLabel, QColor, teXBackgroundColor, teXBackgroundColor); CLASS_SHARED_D_READER_IMPL(TextLabel, QFont, teXFont, teXFont); CLASS_SHARED_D_READER_IMPL(TextLabel, TextLabel::PositionWrapper, position, position); BASIC_SHARED_D_READER_IMPL(TextLabel, TextLabel::HorizontalAlignment, horizontalAlignment, horizontalAlignment); BASIC_SHARED_D_READER_IMPL(TextLabel, TextLabel::VerticalAlignment, verticalAlignment, verticalAlignment); BASIC_SHARED_D_READER_IMPL(TextLabel, qreal, rotationAngle, rotationAngle); +BASIC_SHARED_D_READER_IMPL(TextLabel, TextLabel::BorderShape, borderShape, borderShape) +CLASS_SHARED_D_READER_IMPL(TextLabel, QPen, borderPen, borderPen) +BASIC_SHARED_D_READER_IMPL(TextLabel, qreal, borderOpacity, borderOpacity) + /* ============================ setter methods and undo commands ================= */ STD_SETTER_CMD_IMPL_F_S(TextLabel, SetText, TextLabel::TextWrapper, textWrapper, updateText); void TextLabel::setText(const TextWrapper &textWrapper) { Q_D(TextLabel); if ( (textWrapper.text != d->textWrapper.text) || (textWrapper.teXUsed != d->textWrapper.teXUsed) ) exec(new TextLabelSetTextCmd(d, textWrapper, ki18n("%1: set label text"))); } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetTeXFont, QFont, teXFont, updateText); void TextLabel::setTeXFont(const QFont& font) { Q_D(TextLabel); if (font != d->teXFont) exec(new TextLabelSetTeXFontCmd(d, font, ki18n("%1: set TeX main font"))); } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetTeXFontColor, QColor, teXFontColor, updateText); void TextLabel::setTeXFontColor(const QColor color) { Q_D(TextLabel); if (color != d->teXFontColor) exec(new TextLabelSetTeXFontColorCmd(d, color, ki18n("%1: set TeX font color"))); } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetTeXBackgroundColor, QColor, teXBackgroundColor, updateText); void TextLabel::setTeXBackgroundColor(const QColor color) { Q_D(TextLabel); if (color != d->teXBackgroundColor) exec(new TextLabelSetTeXBackgroundColorCmd(d, color, ki18n("%1: set TeX background color"))); } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetPosition, TextLabel::PositionWrapper, position, retransform); void TextLabel::setPosition(const PositionWrapper& pos) { Q_D(TextLabel); if (pos.point!=d->position.point || pos.horizontalPosition!=d->position.horizontalPosition || pos.verticalPosition!=d->position.verticalPosition) exec(new TextLabelSetPositionCmd(d, pos, ki18n("%1: set position"))); } /*! sets the position without undo/redo-stuff */ void TextLabel::setPosition(QPointF point) { Q_D(TextLabel); if (point != d->position.point) { d->position.point = point; retransform(); } } /*! * position is set to invalid if the parent item is not drawn on the scene * (e.g. axis is not drawn because it's outside plot ranges -> don't draw axis' title label) */ void TextLabel::setPositionInvalid(bool invalid) { Q_D(TextLabel); if (invalid != d->positionInvalid) { d->positionInvalid = invalid; } } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetRotationAngle, qreal, rotationAngle, recalcShapeAndBoundingRect); void TextLabel::setRotationAngle(qreal angle) { Q_D(TextLabel); if (angle != d->rotationAngle) exec(new TextLabelSetRotationAngleCmd(d, angle, ki18n("%1: set rotation angle"))); } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetHorizontalAlignment, TextLabel::HorizontalAlignment, horizontalAlignment, retransform); void TextLabel::setHorizontalAlignment(const TextLabel::HorizontalAlignment hAlign) { Q_D(TextLabel); if (hAlign != d->horizontalAlignment) exec(new TextLabelSetHorizontalAlignmentCmd(d, hAlign, ki18n("%1: set horizontal alignment"))); } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetVerticalAlignment, TextLabel::VerticalAlignment, verticalAlignment, retransform); void TextLabel::setVerticalAlignment(const TextLabel::VerticalAlignment vAlign) { Q_D(TextLabel); if (vAlign != d->verticalAlignment) exec(new TextLabelSetVerticalAlignmentCmd(d, vAlign, ki18n("%1: set vertical alignment"))); } +//Border +STD_SETTER_CMD_IMPL_F_S(TextLabel, SetBorderShape, TextLabel::BorderShape, borderShape, updateBorder) +void TextLabel::setBorderShape(TextLabel::BorderShape shape) { + Q_D(TextLabel); + if (shape != d->borderShape) + exec(new TextLabelSetBorderShapeCmd(d, shape, ki18n("%1: set border shape"))); +} + +STD_SETTER_CMD_IMPL_F_S(TextLabel, SetBorderPen, QPen, borderPen, update) +void TextLabel::setBorderPen(const QPen &pen) { + Q_D(TextLabel); + if (pen != d->borderPen) + exec(new TextLabelSetBorderPenCmd(d, pen, ki18n("%1: set border"))); +} + +STD_SETTER_CMD_IMPL_F_S(TextLabel, SetBorderOpacity, qreal, borderOpacity, update) +void TextLabel::setBorderOpacity(qreal opacity) { + Q_D(TextLabel); + if (opacity != d->borderOpacity) + exec(new TextLabelSetBorderOpacityCmd(d, opacity, ki18n("%1: set border opacity"))); +} + +//misc STD_SWAP_METHOD_SETTER_CMD_IMPL_F(TextLabel, SetVisible, bool, swapVisible, retransform); void TextLabel::setVisible(bool on) { Q_D(TextLabel); exec(new TextLabelSetVisibleCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible"))); } bool TextLabel::isVisible() const { Q_D(const TextLabel); return d->isVisible(); } void TextLabel::setPrinting(bool on) { Q_D(TextLabel); d->m_printing = on; } void TextLabel::updateTeXImage() { Q_D(TextLabel); d->updateTeXImage(); } //############################################################################## //###### SLOTs for changes triggered via QActions in the context menu ######## //############################################################################## void TextLabel::visibilityChanged() { Q_D(const TextLabel); this->setVisible(!d->isVisible()); } //############################################################################## //####################### Private implementation ############################### //############################################################################## TextLabelPrivate::TextLabelPrivate(TextLabel* owner) : teXRenderSuccessful(false), positionInvalid(false), suppressItemChangeEvent(false), suppressRetransform(false), m_printing(false), m_hovered(false), q(owner) { setFlag(QGraphicsItem::ItemIsSelectable); setFlag(QGraphicsItem::ItemIsMovable); setFlag(QGraphicsItem::ItemSendsGeometryChanges); setAcceptHoverEvents(true); } QString TextLabelPrivate::name() const { return q->name(); } /*! calculates the position and the bounding box of the label. Called on geometry or text changes. */ void TextLabelPrivate::retransform() { if (suppressRetransform) return; if (position.horizontalPosition != TextLabel::hPositionCustom || position.verticalPosition != TextLabel::vPositionCustom) updatePosition(); float x = position.point.x(); float y = position.point.y(); //determine the size of the label in scene units. float w, h; if (textWrapper.teXUsed) { //image size is in pixel, convert to scene units w = teXImage.width()*teXImageScaleFactor; h = teXImage.height()*teXImageScaleFactor; } else { //size is in points, convert to scene units w = staticText.size().width()*scaleFactor; h = staticText.size().height()*scaleFactor; } //depending on the alignment, calculate the new GraphicsItem's position in parent's coordinate system QPointF itemPos; switch (horizontalAlignment) { case TextLabel::hAlignLeft: itemPos.setX(x - w/2); break; case TextLabel::hAlignCenter: itemPos.setX(x); break; case TextLabel::hAlignRight: itemPos.setX(x + w/2); break; } switch (verticalAlignment) { case TextLabel::vAlignTop: itemPos.setY(y - h/2); break; case TextLabel::vAlignCenter: itemPos.setY(y); break; case TextLabel::vAlignBottom: itemPos.setY(y + h/2); break; } suppressItemChangeEvent = true; setPos(itemPos); suppressItemChangeEvent = false; boundingRectangle.setX(-w/2); boundingRectangle.setY(-h/2); boundingRectangle.setWidth(w); boundingRectangle.setHeight(h); - recalcShapeAndBoundingRect(); + updateBorder(); } /*! calculates the position of the label, when the position relative to the parent was specified (left, right, etc.) */ void TextLabelPrivate::updatePosition() { //determine the parent item QRectF parentRect; QGraphicsItem* parent = parentItem(); if (parent) { parentRect = parent->boundingRect(); } else { if (!scene()) return; parentRect = scene()->sceneRect(); } if (position.horizontalPosition != TextLabel::hPositionCustom) { if (position.horizontalPosition == TextLabel::hPositionLeft) position.point.setX( parentRect.x() ); else if (position.horizontalPosition == TextLabel::hPositionCenter) position.point.setX( parentRect.x() + parentRect.width()/2 ); else if (position.horizontalPosition == TextLabel::hPositionRight) position.point.setX( parentRect.x() + parentRect.width() ); } if (position.verticalPosition != TextLabel::vPositionCustom) { if (position.verticalPosition == TextLabel::vPositionTop) position.point.setY( parentRect.y() ); else if (position.verticalPosition == TextLabel::vPositionCenter) position.point.setY( parentRect.y() + parentRect.height()/2 ); else if (position.verticalPosition == TextLabel::vPositionBottom) position.point.setY( parentRect.y() + parentRect.height() ); } emit q->positionChanged(position); } /*! updates the static text. */ void TextLabelPrivate::updateText() { if (suppressRetransform) return; if (textWrapper.teXUsed) { TeXRenderer::Formatting format; format.fontColor = teXFontColor; format.backgroundColor = teXBackgroundColor; format.fontSize = teXFont.pointSize(); format.fontFamily = teXFont.family(); format.dpi = teXImageResolution; QFuture future = QtConcurrent::run(TeXRenderer::renderImageLaTeX, textWrapper.text, &teXRenderSuccessful, format); teXImageFutureWatcher.setFuture(future); //don't need to call retransorm() here since it is done in updateTeXImage //when the asynchronous rendering of the image is finished. } else { staticText.setText(textWrapper.text); //the size of the label was most probably changed. //call retransform() to recalculate the position and the bounding box of the label retransform(); } } void TextLabelPrivate::updateTeXImage() { teXImage = teXImageFutureWatcher.result(); retransform(); DEBUG("teXRenderSuccessful =" << teXRenderSuccessful); emit q->teXImageUpdated(teXRenderSuccessful); } +void TextLabelPrivate::updateBorder() { + borderShapePath = QPainterPath(); + switch (borderShape) { + case (TextLabel::NoBorder): + break; + case (TextLabel::BorderShape::Rect): { + borderShapePath.addRect(boundingRectangle); + break; + } + case (TextLabel::BorderShape::Ellipse): { + const double xs = boundingRectangle.x(); + const double ys = boundingRectangle.y(); + const double w = boundingRectangle.width(); + const double h = boundingRectangle.height(); + borderShapePath.addEllipse(xs - 0.1 * w, ys - 0.1 * h, 1.2 * w, 1.2 * h); + break; + } + case (TextLabel::BorderShape::RoundSideRect): { + const double xs = boundingRectangle.x(); + const double ys = boundingRectangle.y(); + const double w = boundingRectangle.width(); + const double h = boundingRectangle.height(); + borderShapePath.moveTo(xs, ys); + borderShapePath.lineTo(xs + w, ys); + borderShapePath.quadTo(xs + w + h/2, ys + h/2, xs + w, ys + h); + borderShapePath.lineTo(xs, ys + h); + borderShapePath.quadTo(xs - h/2, ys + h/2, xs, ys); + break; + } + case (TextLabel::BorderShape::RoundCornerRect): { + const double xs = boundingRectangle.x(); + const double ys = boundingRectangle.y(); + const double w = boundingRectangle.width(); + const double h = boundingRectangle.height(); + borderShapePath.moveTo(xs + h * 0.2, ys); + borderShapePath.lineTo(xs + w - h * 0.2, ys); + borderShapePath.quadTo(xs + w, ys, xs + w, ys + h * 0.2); + borderShapePath.lineTo(xs + w, ys + h - 0.2 * h); + borderShapePath.quadTo(xs + w, ys + h, xs + w - 0.2 * h, ys + h); + borderShapePath.lineTo(xs + 0.2 * h, ys + h); + borderShapePath.quadTo(xs, ys + h, xs, ys + h - 0.2 * h); + borderShapePath.lineTo(xs, ys + 0.2 * h); + borderShapePath.quadTo(xs, ys, xs + 0.2 * h, ys); + break; + + } + case (TextLabel::BorderShape::InwardsRoundCornerRect): { + const double xs = boundingRectangle.x(); + const double ys = boundingRectangle.y(); + const double w = boundingRectangle.width(); + const double h = boundingRectangle.height(); + borderShapePath.moveTo(xs, ys - 0.3 * h); + borderShapePath.lineTo(xs + w, ys - 0.3 * h); + borderShapePath.quadTo(xs + w, ys, xs + w + 0.3 * h, ys); + borderShapePath.lineTo(xs + w + 0.3 * h, ys + h); + borderShapePath.quadTo(xs + w, ys + h, xs + w, ys + h + 0.3 * h); + borderShapePath.lineTo(xs, ys + h + 0.3 * h); + borderShapePath.quadTo(xs, ys + h, xs - 0.3 * h, ys + h); + borderShapePath.lineTo(xs - 0.3 * h, ys); + borderShapePath.quadTo(xs, ys, xs, ys - 0.3 * h); + break; + } + case (TextLabel::BorderShape::DentedBorderRect): { + const double xs = boundingRectangle.x(); + const double ys = boundingRectangle.y(); + const double w = boundingRectangle.width(); + const double h = boundingRectangle.height(); + borderShapePath.moveTo(xs - 0.2 * h, ys - 0.2 * h); + borderShapePath.quadTo(xs + w / 2, ys, xs + w + 0.2 * h, ys - 0.2 * h); + borderShapePath.quadTo(xs + w, ys + h / 2, xs + w + 0.2 * h, ys + h + 0.2 * h); + borderShapePath.quadTo(xs + w / 2, ys + h, xs - 0.2 * h, ys + h + 0.2 * h); + borderShapePath.quadTo(xs, ys + h / 2, xs - 0.2 * h, ys - 0.2 * h); + break; + } + case (TextLabel::BorderShape::Cuboid): { + const double xs = boundingRectangle.x(); + const double ys = boundingRectangle.y(); + const double w = boundingRectangle.width(); + const double h = boundingRectangle.height(); + borderShapePath.moveTo(xs, ys); + borderShapePath.lineTo(xs + w, ys); + borderShapePath.lineTo(xs + w, ys + h); + borderShapePath.lineTo(xs, ys + h); + borderShapePath.lineTo(xs, ys); + borderShapePath.lineTo(xs + 0.3 * h, ys - 0.2 * h); + borderShapePath.lineTo(xs + w + 0.3 * h, ys - 0.2 * h); + borderShapePath.lineTo(xs + w, ys); + borderShapePath.moveTo(xs + w, ys + h); + borderShapePath.lineTo(xs + w + 0.3 * h, ys + h - 0.2 * h); + borderShapePath.lineTo(xs + w + 0.3 * h, ys - 0.2 * h); + break; + } + case (TextLabel::BorderShape::UpPointingRectangle): { + const double xs = boundingRectangle.x(); + const double ys = boundingRectangle.y(); + const double w = boundingRectangle.width(); + const double h = boundingRectangle.height(); + borderShapePath.moveTo(xs + h * 0.2, ys); + borderShapePath.lineTo(xs + w / 2 - 0.2 * h, ys); + borderShapePath.lineTo(xs + w / 2, ys - 0.2 * h); + borderShapePath.lineTo(xs + w / 2 + 0.2 * h, ys); + borderShapePath.lineTo(xs + w - h * 0.2, ys); + borderShapePath.quadTo(xs + w, ys, xs + w, ys + h * 0.2); + borderShapePath.lineTo(xs + w, ys + h - 0.2 * h); + borderShapePath.quadTo(xs + w, ys + h, xs + w - 0.2 * h, ys + h); + borderShapePath.lineTo(xs + 0.2 * h, ys + h); + borderShapePath.quadTo(xs, ys + h, xs, ys + h - 0.2 * h); + borderShapePath.lineTo(xs, ys + 0.2 * h); + borderShapePath.quadTo(xs, ys, xs + 0.2 * h, ys); + break; + } + case (TextLabel::BorderShape::DownPointingRectangle): { + const double xs = boundingRectangle.x(); + const double ys = boundingRectangle.y(); + const double w = boundingRectangle.width(); + const double h = boundingRectangle.height(); + borderShapePath.moveTo(xs +h * 0.2, ys); + borderShapePath.lineTo(xs + w - h * 0.2, ys); + borderShapePath.quadTo(xs + w, ys, xs + w, ys + h * 0.2); + borderShapePath.lineTo(xs + w, ys + h - 0.2 * h); + borderShapePath.quadTo(xs + w, ys + h, xs + w - 0.2 * h, ys + h); + borderShapePath.lineTo(xs + w / 2 + 0.2 * h, ys + h); + borderShapePath.lineTo(xs + w / 2, ys + h + 0.2 * h); + borderShapePath.lineTo(xs + w / 2 - 0.2 * h, ys + h); + borderShapePath.lineTo(xs + 0.2 * h, ys + h); + borderShapePath.quadTo(xs, ys + h, xs, ys + h - 0.2 * h); + borderShapePath.lineTo(xs, ys + 0.2 * h); + borderShapePath.quadTo(xs, ys, xs + 0.2 * h, ys); + break; + } + case (TextLabel::BorderShape::LeftPointingRectangle): { + const double xs = boundingRectangle.x(); + const double ys = boundingRectangle.y(); + const double w = boundingRectangle.width(); + const double h = boundingRectangle.height(); + borderShapePath.moveTo(xs + h*0.2, ys); + borderShapePath.lineTo(xs + w - h * 0.2, ys); + borderShapePath.quadTo(xs + w, ys, xs + w, ys + h * 0.2); + borderShapePath.lineTo(xs + w, ys + h - 0.2 * h); + borderShapePath.quadTo(xs + w, ys + h, xs + w - 0.2 * h, ys + h); + borderShapePath.lineTo(xs + 0.2 * h, ys + h); + borderShapePath.quadTo(xs, ys + h, xs, ys + h - 0.2 * h); + borderShapePath.lineTo(xs, ys + h / 2 + 0.2 * h); + borderShapePath.lineTo(xs - 0.2 * h, ys + h / 2); + borderShapePath.lineTo(xs, ys + h / 2 - 0.2 * h); + borderShapePath.lineTo(xs, ys + 0.2 * h); + borderShapePath.quadTo(xs, ys, xs + 0.2 * h, ys); + break; + } + case (TextLabel::BorderShape::RightPointingRectangle): { + const double xs = boundingRectangle.x(); + const double ys = boundingRectangle.y(); + const double w = boundingRectangle.width(); + const double h = boundingRectangle.height(); + borderShapePath.moveTo(xs + h * 0.2, ys); + borderShapePath.lineTo(xs + w - h * 0.2, ys); + borderShapePath.quadTo(xs + w, ys, xs + w, ys + h * 0.2); + borderShapePath.lineTo(xs + w, ys + h / 2 - 0.2 * h); + borderShapePath.lineTo(xs + w + 0.2 * h, ys + h / 2); + borderShapePath.lineTo(xs + w, ys + h / 2 + 0.2 * h); + borderShapePath.lineTo(xs + w, ys + h - 0.2 * h); + borderShapePath.quadTo(xs + w, ys + h, xs + w - 0.2 * h, ys + h); + borderShapePath.lineTo(xs + 0.2 * h, ys + h); + borderShapePath.quadTo(xs, ys + h, xs, ys + h - 0.2 * h); + borderShapePath.lineTo(xs, ys + 0.2 * h); + borderShapePath.quadTo(xs, ys, xs + 0.2 * h, ys); + break; + } + } + + recalcShapeAndBoundingRect(); +} + bool TextLabelPrivate::swapVisible(bool on) { bool oldValue = isVisible(); setVisible(on); emit q->changed(); emit q->visibleChanged(on); return oldValue; } /*! Returns the outer bounds of the item as a rectangle. */ QRectF TextLabelPrivate::boundingRect() const { return transformedBoundingRectangle; } /*! Returns the shape of this item as a QPainterPath in local coordinates. */ QPainterPath TextLabelPrivate::shape() const { return labelShape; } /*! recalculates the outer bounds and the shape of the label. */ void TextLabelPrivate::recalcShapeAndBoundingRect() { prepareGeometryChange(); QMatrix matrix; matrix.rotate(-rotationAngle); - transformedBoundingRectangle = matrix.mapRect(boundingRectangle); - labelShape = QPainterPath(); + if (borderShape != TextLabel::NoBorder) { + labelShape.addPath(WorksheetElement::shapeFromPath(borderShapePath, borderPen)); + transformedBoundingRectangle = matrix.mapRect(labelShape.boundingRect()); + } else + transformedBoundingRectangle = matrix.mapRect(boundingRectangle); + labelShape.addRect(boundingRectangle); labelShape = matrix.map(labelShape); emit q->changed(); } void TextLabelPrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { Q_UNUSED(option) Q_UNUSED(widget) if (positionInvalid) return; if (textWrapper.text.isEmpty()) return; painter->save(); // painter->resetMatrix(); painter->rotate(-rotationAngle); if (textWrapper.teXUsed) { if (boundingRect().width() != 0.0 && boundingRect().height() != 0.0) painter->drawImage(boundingRect(), teXImage); } else { painter->scale(scaleFactor, scaleFactor); float w = staticText.size().width(); float h = staticText.size().height(); painter->drawStaticText(QPoint(-w/2,-h/2), staticText); } painter->restore(); + painter->setPen(borderPen); + painter->setOpacity(borderOpacity); + painter->drawPath(borderShapePath); + if (m_hovered && !isSelected() && !m_printing){ painter->setPen(QPen(QApplication::palette().color(QPalette::Shadow), 2, Qt::SolidLine)); - painter->drawPath(labelShape); + painter->drawPath(borderShapePath); } if (isSelected() && !m_printing){ painter->setPen(QPen(QApplication::palette().color(QPalette::Highlight), 2, Qt::SolidLine)); - painter->drawPath(labelShape); + painter->drawPath(borderShapePath); } } QVariant TextLabelPrivate::itemChange(GraphicsItemChange change, const QVariant &value) { if (suppressItemChangeEvent) return value; if (change == QGraphicsItem::ItemPositionChange) { //convert item's center point in parent's coordinates TextLabel::PositionWrapper tempPosition; tempPosition.point = positionFromItemPosition(value.toPointF()); tempPosition.horizontalPosition = TextLabel::hPositionCustom; tempPosition.verticalPosition = TextLabel::vPositionCustom; //emit the signals in order to notify the UI. //we don't set the position related member variables during the mouse movements. //this is done on mouse release events only. emit q->positionChanged(tempPosition); } return QGraphicsItem::itemChange(change, value); } void TextLabelPrivate::mouseReleaseEvent(QGraphicsSceneMouseEvent* event) { //convert position of the item in parent coordinates to label's position QPointF point = positionFromItemPosition(pos()); if (qAbs(point.x()-position.point.x())>20 && qAbs(point.y()-position.point.y())>20 ) { //position was changed -> set the position related member variables suppressRetransform = true; TextLabel::PositionWrapper tempPosition; tempPosition.point = point; tempPosition.horizontalPosition = TextLabel::hPositionCustom; tempPosition.verticalPosition = TextLabel::vPositionCustom; q->setPosition(tempPosition); suppressRetransform = false; } QGraphicsItem::mouseReleaseEvent(event); } /*! * converts label's position to GraphicsItem's position. */ QPointF TextLabelPrivate::positionFromItemPosition(QPointF itemPos) { float x = itemPos.x(); float y = itemPos.y(); float w, h; QPointF tmpPosition; if (textWrapper.teXUsed) { w = teXImage.width()*scaleFactor; h = teXImage.height()*scaleFactor; } else { w = staticText.size().width()*scaleFactor; h = staticText.size().height()*scaleFactor; } //depending on the alignment, calculate the new position switch (horizontalAlignment) { case TextLabel::hAlignLeft: tmpPosition.setX(x + w/2); break; case TextLabel::hAlignCenter: tmpPosition.setX(x); break; case TextLabel::hAlignRight: tmpPosition.setX(x - w/2); break; } switch (verticalAlignment) { case TextLabel::vAlignTop: tmpPosition.setY(y + h/2); break; case TextLabel::vAlignCenter: tmpPosition.setY(y); break; case TextLabel::vAlignBottom: tmpPosition.setY(y - h/2); break; } return tmpPosition; } void TextLabelPrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { q->createContextMenu()->exec(event->screenPos()); } void TextLabelPrivate::hoverEnterEvent(QGraphicsSceneHoverEvent*) { if (!isSelected()) { m_hovered = true; emit q->hovered(); update(); } } void TextLabelPrivate::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { if (m_hovered) { m_hovered = false; emit q->unhovered(); update(); } } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void TextLabel::save(QXmlStreamWriter* writer) const { Q_D(const TextLabel); writer->writeStartElement( "textLabel" ); writeBasicAttributes(writer); writeCommentElement(writer); //geometry writer->writeStartElement( "geometry" ); writer->writeAttribute( "x", QString::number(d->position.point.x()) ); writer->writeAttribute( "y", QString::number(d->position.point.y()) ); writer->writeAttribute( "horizontalPosition", QString::number(d->position.horizontalPosition) ); writer->writeAttribute( "verticalPosition", QString::number(d->position.verticalPosition) ); writer->writeAttribute( "horizontalAlignment", QString::number(d->horizontalAlignment) ); writer->writeAttribute( "verticalAlignment", QString::number(d->verticalAlignment) ); writer->writeAttribute( "rotationAngle", QString::number(d->rotationAngle) ); writer->writeAttribute( "visible", QString::number(d->isVisible()) ); writer->writeEndElement(); writer->writeStartElement( "text" ); writer->writeCharacters( d->textWrapper.text ); writer->writeEndElement(); writer->writeStartElement( "format" ); writer->writeAttribute( "teXUsed", QString::number(d->textWrapper.teXUsed) ); WRITE_QFONT(d->teXFont); writer->writeAttribute( "teXFontColor_r", QString::number(d->teXFontColor.red()) ); writer->writeAttribute( "teXFontColor_g", QString::number(d->teXFontColor.green()) ); writer->writeAttribute( "teXFontColor_b", QString::number(d->teXFontColor.blue()) ); writer->writeEndElement(); + //border + writer->writeStartElement("border"); + writer->writeAttribute("borderShape", QString::number(d->borderShape)); + WRITE_QPEN(d->borderPen); + writer->writeAttribute("borderOpacity", QString::number(d->borderOpacity)); + writer->writeEndElement(); + if (d->textWrapper.teXUsed) { writer->writeStartElement("teXImage"); QByteArray ba; QBuffer buffer(&ba); buffer.open(QIODevice::WriteOnly); d->teXImage.save(&buffer, "PNG"); writer->writeCharacters(ba.toBase64()); writer->writeEndElement(); } writer->writeEndElement(); // close "textLabel" section } //! Load from XML bool TextLabel::load(XmlStreamReader* reader, bool preview) { if (!readBasicAttributes(reader)) return false; Q_D(TextLabel); KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; bool teXImageFound = false; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "textLabel") break; if (!reader->isStartElement()) continue; if (!preview && reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (!preview && reader->name() == "geometry") { attribs = reader->attributes(); str = attribs.value("x").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.subs("x").toString()); else d->position.point.setX(str.toDouble()); str = attribs.value("y").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.subs("y").toString()); else d->position.point.setY(str.toDouble()); str = attribs.value("horizontalPosition").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.subs("horizontalPosition").toString()); else d->position.horizontalPosition = (TextLabel::HorizontalPosition)str.toInt(); str = attribs.value("verticalPosition").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.subs("verticalPosition").toString()); else d->position.verticalPosition = (TextLabel::VerticalPosition)str.toInt(); str = attribs.value("horizontalAlignment").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.subs("horizontalAlignment").toString()); else d->horizontalAlignment = (TextLabel::HorizontalAlignment)str.toInt(); str = attribs.value("verticalAlignment").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.subs("verticalAlignment").toString()); else d->verticalAlignment = (TextLabel::VerticalAlignment)str.toInt(); str = attribs.value("rotationAngle").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.subs("rotationAngle").toString()); else d->rotationAngle = str.toInt(); str = attribs.value("visible").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.subs("visible").toString()); else d->setVisible(str.toInt()); } else if (!preview && reader->name() == "text") { d->textWrapper.text = reader->readElementText(); } else if (!preview && reader->name() == "format") { attribs = reader->attributes(); str = attribs.value("teXUsed").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.subs("teXUsed").toString()); else d->textWrapper.teXUsed = str.toInt(); READ_QFONT(d->teXFont); str = attribs.value("teXFontColor_r").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.subs("teXFontColor_r").toString()); else d->teXFontColor.setRed( str.toInt() ); str = attribs.value("teXFontColor_g").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.subs("teXFontColor_g").toString()); else d->teXFontColor.setGreen( str.toInt() ); str = attribs.value("teXFontColor_b").toString(); if(str.isEmpty()) reader->raiseWarning(attributeWarning.subs("teXFontColor_b").toString()); else d->teXFontColor.setBlue( str.toInt() ); + } else if (!preview && reader->name() == "border") { + attribs = reader->attributes(); + READ_INT_VALUE("borderShape", borderShape, BorderShape); + READ_QPEN(d->borderPen); + READ_DOUBLE_VALUE("borderOpacity", borderOpacity); } else if (!preview && reader->name() == "teXImage") { reader->readNext(); QString content = reader->text().toString().trimmed(); QByteArray ba = QByteArray::fromBase64(content.toLatin1()); teXImageFound = d->teXImage.loadFromData(ba); } else { // unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } if (preview) return true; //in case we use latex and the image was stored (older versions of LabPlot didn't save the image)and loaded, //we just need to retransform. //otherwise, we set the static text and retransform in updateText() if ( !(d->textWrapper.teXUsed && teXImageFound) ) d->updateText(); else retransform(); return true; } //############################################################################## //######################### Theme management ################################## //############################################################################## void TextLabel::loadThemeConfig(const KConfig& config) { Q_D(TextLabel); KConfigGroup group = config.group("Label"); const QColor fontColor = group.readEntry("FontColor", QColor(Qt::white)); const QColor backgroundColor = group.readEntry("BackgroundColor", QColor(Qt::black)); d->suppressRetransform = true; if (!d->textWrapper.teXUsed && !d->textWrapper.text.isEmpty()) { //replace colors in the html-formatted string QTextDocument doc; doc.setHtml(d->textWrapper.text); QTextCharFormat fmt; fmt.setForeground(QBrush(fontColor)); fmt.setBackground(QBrush(backgroundColor)); QTextCursor cursor(&doc); cursor.select(QTextCursor::Document); cursor.setCharFormat(fmt); TextLabel::TextWrapper wrapper(doc.toHtml(), d->textWrapper.teXUsed); this->setText(wrapper); } else { //replace colors in the TeX-string this->setTeXFontColor(fontColor); this->setTeXBackgroundColor(backgroundColor); } + + group = config.group("CartesianPlot"); + QPen pen = this->borderPen(); + pen.setColor(group.readEntry("BorderColor", pen.color())); + pen.setStyle((Qt::PenStyle)(group.readEntry("BorderStyle", (int) pen.style()))); + pen.setWidthF(group.readEntry("BorderWidth", pen.widthF())); + this->setBorderPen(pen); + this->setBorderOpacity(group.readEntry("BorderOpacity", this->borderOpacity())); + d->suppressRetransform = false; d->updateText(); } void TextLabel::saveThemeConfig(const KConfig& config) { KConfigGroup group = config.group("Label"); //TODO // group.writeEntry("TeXFontColor", (QColor) this->teXFontColor()); } diff --git a/src/backend/worksheet/TextLabel.h b/src/backend/worksheet/TextLabel.h index 33c9649d7..e47d0f722 100644 --- a/src/backend/worksheet/TextLabel.h +++ b/src/backend/worksheet/TextLabel.h @@ -1,137 +1,147 @@ /*************************************************************************** File : TextLabel.h Project : LabPlot Description : Text label supporting reach text and latex formatting -------------------------------------------------------------------- Copyright : (C) 2009 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2012-2014 Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * 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 TEXTLABEL_H #define TEXTLABEL_H #include "backend/lib/macros.h" #include "tools/TeXRenderer.h" #include "backend/worksheet/WorksheetElement.h" #include #include #include class TextLabelPrivate; class TextLabel : public WorksheetElement { Q_OBJECT public: enum Type {General, PlotTitle, AxisTitle, PlotLegendTitle}; enum HorizontalPosition {hPositionLeft, hPositionCenter, hPositionRight, hPositionCustom}; enum VerticalPosition {vPositionTop, vPositionCenter, vPositionBottom, vPositionCustom}; enum HorizontalAlignment {hAlignLeft, hAlignCenter, hAlignRight}; enum VerticalAlignment {vAlignTop, vAlignCenter, vAlignBottom}; + enum BorderShape {NoBorder, Rect, Ellipse, RoundSideRect, RoundCornerRect, InwardsRoundCornerRect, DentedBorderRect, + Cuboid, UpPointingRectangle, DownPointingRectangle, LeftPointingRectangle, RightPointingRectangle}; + struct TextWrapper { TextWrapper() : teXUsed(false) {} TextWrapper(const QString& t, bool b) : text(t), teXUsed(b) {} TextWrapper(const QString& t) : text(t), teXUsed(false) {} QString text; bool teXUsed; }; struct PositionWrapper { QPointF point; HorizontalPosition horizontalPosition; VerticalPosition verticalPosition; }; explicit TextLabel(const QString& name, Type type = General); ~TextLabel() override; Type type() const; QIcon icon() const override; QMenu* createContextMenu() override; QGraphicsItem* graphicsItem() const override; void setParentGraphicsItem(QGraphicsItem*); void save(QXmlStreamWriter*) const override; bool load(XmlStreamReader*, bool preview) override; void loadThemeConfig(const KConfig& config) override; void saveThemeConfig(const KConfig& config) override; CLASS_D_ACCESSOR_DECL(TextWrapper, text, Text) BASIC_D_ACCESSOR_DECL(QColor, teXFontColor, TeXFontColor) BASIC_D_ACCESSOR_DECL(QColor, teXBackgroundColor, TeXBackgroundColor) CLASS_D_ACCESSOR_DECL(QFont, teXFont, TeXFont) CLASS_D_ACCESSOR_DECL(PositionWrapper, position, Position) void setPosition(QPointF); void setPositionInvalid(bool); BASIC_D_ACCESSOR_DECL(HorizontalAlignment, horizontalAlignment, HorizontalAlignment) BASIC_D_ACCESSOR_DECL(VerticalAlignment, verticalAlignment, VerticalAlignment) BASIC_D_ACCESSOR_DECL(qreal, rotationAngle, RotationAngle) + BASIC_D_ACCESSOR_DECL(BorderShape, borderShape, BorderShape); + CLASS_D_ACCESSOR_DECL(QPen, borderPen, BorderPen) + BASIC_D_ACCESSOR_DECL(qreal, borderOpacity, BorderOpacity) + void setVisible(bool on) override; bool isVisible() const override; void setPrinting(bool) override; void retransform() override; void handleResize(double horizontalRatio, double verticalRatio, bool pageResize) override; typedef TextLabelPrivate Private; private slots: void updateTeXImage(); //SLOTs for changes triggered via QActions in the context menu void visibilityChanged(); protected: TextLabelPrivate* const d_ptr; TextLabel(const QString& name, TextLabelPrivate* dd, Type type = General); private: Q_DECLARE_PRIVATE(TextLabel) void init(); Type m_type; QAction* visibilityAction; signals: void textWrapperChanged(const TextLabel::TextWrapper&); void teXFontSizeChanged(const int); void teXFontChanged(const QFont); void teXFontColorChanged(const QColor); void teXBackgroundColorChanged(const QColor); void positionChanged(const TextLabel::PositionWrapper&); void horizontalAlignmentChanged(TextLabel::HorizontalAlignment); void verticalAlignmentChanged(TextLabel::VerticalAlignment); void rotationAngleChanged(qreal); void visibleChanged(bool); + void borderShapeChanged(TextLabel::BorderShape); + void borderPenChanged(QPen&); + void borderOpacityChanged(float); void teXImageUpdated(bool); void changed(); }; #endif diff --git a/src/backend/worksheet/TextLabelPrivate.h b/src/backend/worksheet/TextLabelPrivate.h index 0657f7300..ee55206cd 100644 --- a/src/backend/worksheet/TextLabelPrivate.h +++ b/src/backend/worksheet/TextLabelPrivate.h @@ -1,94 +1,100 @@ /*************************************************************************** File : TextLabelPrivate.h Project : LabPlot Description : Private members of TextLabel -------------------------------------------------------------------- Copyright : (C) 2012-2014 by Alexander Semke (alexander.semke@web.de) ***************************************************************************/ /*************************************************************************** * * * 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 TEXTLABELPRIVATE_H #define TEXTLABELPRIVATE_H #include #include #include class QGraphicsSceneHoverEvent; class TextLabelPrivate: public QGraphicsItem { public: explicit TextLabelPrivate(TextLabel*); qreal rotationAngle; float scaleFactor; int teXImageResolution; float teXImageScaleFactor; TextLabel::TextWrapper textWrapper; QFont teXFont; QColor teXFontColor; QColor teXBackgroundColor; QImage teXImage; QFutureWatcher teXImageFutureWatcher; bool teXRenderSuccessful; TextLabel::PositionWrapper position; //position in parent's coordinate system, the label gets aligned around this point. bool positionInvalid; TextLabel::HorizontalAlignment horizontalAlignment; TextLabel::VerticalAlignment verticalAlignment; + TextLabel::BorderShape borderShape; + QPen borderPen; + qreal borderOpacity; + QString name() const; void retransform(); bool swapVisible(bool on); virtual void recalcShapeAndBoundingRect(); void updatePosition(); QPointF positionFromItemPosition(QPointF); void updateText(); void updateTeXImage(); + void updateBorder(); QStaticText staticText; bool suppressItemChangeEvent; bool suppressRetransform; bool m_printing; bool m_hovered; QRectF boundingRectangle; //bounding rectangle of the text QRectF transformedBoundingRectangle; //bounding rectangle of transformed (rotated etc.) text + QPainterPath borderShapePath; QPainterPath labelShape; //reimplemented from QGraphicsItem QRectF boundingRect() const override; QPainterPath shape() const override; void paint(QPainter*, const QStyleOptionGraphicsItem*, QWidget* widget = nullptr) override; QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; TextLabel* const q; private: void contextMenuEvent(QGraphicsSceneContextMenuEvent*) override; void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override; void hoverEnterEvent(QGraphicsSceneHoverEvent*) override; void hoverLeaveEvent(QGraphicsSceneHoverEvent*) override; }; #endif diff --git a/src/kdefrontend/ui/labelwidget.ui b/src/kdefrontend/ui/labelwidget.ui index 3a9bb97d3..2b476043a 100644 --- a/src/kdefrontend/ui/labelwidget.ui +++ b/src/kdefrontend/ui/labelwidget.ui @@ -1,546 +1,677 @@ LabelWidget 0 0 - 656 - 948 + 633 + 1051 - + + + + + 0 + 0 + + + + Vertical position relative to label's parent + + + cm + + + -99.989999999999995 + + + 0.500000000000000 + + + + + + + + + + Rotation: + + + + 75 true QFrame::NoFrame Text: Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - - Qt::Horizontal + + + + Font: - - QSizePolicy::Fixed + + + + + + Distance to the axis tick labels - - - 13 - 23 - + + pt - + + -99.989999999999995 + + + 0.500000000000000 + + + + + + + + + + Vert. align.: + + + + + + + pt + + + 0.500000000000000 + + - + + + + Color: + + + + + + + + 0 + 0 + + + + The opacity ranges from 0 to 100, where 0 is fully transparent and 100 is fully opaque. + + + % + + + + + + 0 + + + 100 + + + 10 + + + 100 + + + + + + + + 75 + true + + + + QFrame::NoFrame + + + Border + + + + + + + + + + + 0 + 0 + + + + Vertical position relative to label's parent + + + y: + + + + + + + 0 0 QFrame::NoFrame QFrame::Raised 0 0 0 0 0 true true true true true true Qt::Horizontal QSizePolicy::Expanding 20 18 0 0 TeX mode true 0 0 16777215 16777215 - - - - Font: + + + + Qt::Horizontal - + + QSizePolicy::Fixed + + + + 13 + 23 + + + - - + + - - - - Main font: + + + + Qt::Vertical - - - - + + + 20 + 37 + + + - + Size: - - + + - - + + - Foreground color: + Background color: - - + + - + 0 0 - - - 20 - 20 - + + Horizontal position relative to label's parent - - - - - - Background color: + + cm - - - - - - - 0 - 0 - + + -99.989999999999995 - - - 20 - 20 - + + 0.500000000000000 - + Qt::Vertical QSizePolicy::Fixed 58 20 - + + + + Vertical position relative to label's parent + + + + 75 true QFrame::NoFrame Geometry - - - - Horizontal position relative to label's parent + + + + Width: + + + + - x: + Shape: - + + + + Style: + + + + + + + Foreground color: + + + + 0 0 Horizontal position relative to label's parent - - - - - 0 - 0 - - + + Horizontal position relative to label's parent - - cm - - - -99.989999999999995 - - - 0.500000000000000 + + x: - - + + - + 0 0 - - Vertical position relative to label's parent + + + + + + + 0 + 0 + - - y: + + + 20 + 20 + - - - - Vertical position relative to label's parent + + + + Opacity: - - + + - + 0 0 - - Vertical position relative to label's parent - - - cm - - - -99.989999999999995 - - - 0.500000000000000 + + + 20 + 20 + - - + + Distance to the axis tick labels - Offset X: + Offset Y: - - - - Distance to the axis tick labels + + + + Main font: + + + + - pt + ° - -99.989999999999995 + -360 + + + 360 - 0.500000000000000 + 5 - - - - Distance to the axis tick labels + + + + Qt::Vertical - - Offset Y: + + QSizePolicy::Fixed - + + + 58 + 20 + + + - + Distance to the axis tick labels pt -99.989999999999995 0.500000000000000 - + Hor. align.: - - - - - - - Vert. align.: - - - - - - - - - - Rotation: + + + + + 0 + 0 + - - - - ° - - - -360 - - - 360 + + + + Distance to the axis tick labels - - 5 + + Offset X: - + Visible - - - - Qt::Vertical - - - - 20 - 37 - - - - - - KFontRequester - QWidget -
kfontrequester.h
-
KComboBox QComboBox
kcombobox.h
+ + KFontRequester + QWidget +
kfontrequester.h
+
KColorButton QPushButton
kcolorbutton.h
ResizableTextEdit QTextEdit
src/kdefrontend/widgets/ResizableTextEdit.h
diff --git a/src/kdefrontend/widgets/LabelWidget.cpp b/src/kdefrontend/widgets/LabelWidget.cpp index a50ba239b..77b9c7c19 100644 --- a/src/kdefrontend/widgets/LabelWidget.cpp +++ b/src/kdefrontend/widgets/LabelWidget.cpp @@ -1,823 +1,974 @@ /*************************************************************************** File : LabelWidget.cc Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2008-2017 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2012-2017 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 "kdefrontend/GuiTools.h" #include "tools/TeXRenderer.h" #include #include #include #include #include #include #include #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING #include #include #include #endif +#include + /*! \class LabelWidget \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 corresponding 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_label(nullptr), m_initializing(false), m_dateTimeMenu(new QMenu(this)), m_teXEnabled(false) { ui.setupUi(this); m_dateTimeMenu->setSeparatorsCollapsible(false); //we don't want the first separator to be removed ui.kcbFontColor->setColor(Qt::black); // default color //Icons ui.tbFontBold->setIcon( QIcon::fromTheme(QLatin1String("format-text-bold")) ); ui.tbFontItalic->setIcon( QIcon::fromTheme(QLatin1String("format-text-italic")) ); ui.tbFontUnderline->setIcon( QIcon::fromTheme(QLatin1String("format-text-underline")) ); ui.tbFontStrikeOut->setIcon( QIcon::fromTheme(QLatin1String("format-text-strikethrough")) ); ui.tbFontSuperScript->setIcon( QIcon::fromTheme(QLatin1String("format-text-superscript")) ); ui.tbFontSubScript->setIcon( QIcon::fromTheme(QLatin1String("format-text-subscript")) ); ui.tbSymbols->setIcon( QIcon::fromTheme(QLatin1String("labplot-format-text-symbol")) ); ui.tbDateTime->setIcon( QIcon::fromTheme(QLatin1String("chronometer")) ); ui.tbTexUsed->setIcon( QIcon::fromTheme(QLatin1String("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")); + ui.cbBorderShape->addItem(i18n("No Border")); + ui.cbBorderShape->addItem(i18n("Rectangle")); + ui.cbBorderShape->addItem(i18n("Ellipse")); + ui.cbBorderShape->addItem(i18n("Round sided rectangle")); + ui.cbBorderShape->addItem(i18n("Round corner rectangle")); + ui.cbBorderShape->addItem(i18n("Inwards round corner rectangle")); + ui.cbBorderShape->addItem(i18n("Dented border rectangle")); + ui.cbBorderShape->addItem(i18n("Cuboid")); + ui.cbBorderShape->addItem(i18n("Up Pointing rectangle")); + ui.cbBorderShape->addItem(i18n("Down Pointing rectangle")); + ui.cbBorderShape->addItem(i18n("Left Pointing rectangle")); + ui.cbBorderShape->addItem(i18n("Right Pointing rectangle")); + + ui.cbBorderStyle->addItem(i18n("No line")); + ui.cbBorderStyle->addItem(i18n("Solid line")); + ui.cbBorderStyle->addItem(i18n("Dash line")); + ui.cbBorderStyle->addItem(i18n("Dot line")); + ui.cbBorderStyle->addItem(i18n("Dash dot line")); + ui.cbBorderStyle->addItem(i18n("Dash dot dot line")); + //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(); #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING m_highlighter = new KSyntaxHighlighting::SyntaxHighlighter(ui.teLabel->document()); m_highlighter->setDefinition(m_repository.definitionForName(QLatin1String("LaTeX"))); m_highlighter->setTheme( (palette().color(QPalette::Base).lightness() < 128) ? m_repository.defaultTheme(KSyntaxHighlighting::Repository::DarkTheme) : m_repository.defaultTheme(KSyntaxHighlighting::Repository::LightTheme) ); #endif //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.kcbBackgroundColor, SIGNAL(changed(QColor)), this, SLOT(backgroundColorChanged(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)) ); + //Border + connect(ui.cbBorderShape, static_cast(&QComboBox::currentIndexChanged), this, &LabelWidget::borderShapeChanged); + connect(ui.cbBorderStyle, static_cast(&QComboBox::currentIndexChanged), this, &LabelWidget::borderStyleChanged); + connect(ui.kcbBorderColor, &KColorButton::changed, this, &LabelWidget::borderColorChanged); + connect(ui.sbBorderWidth, static_cast(&QDoubleSpinBox::valueChanged), this, &LabelWidget::borderWidthChanged); + connect(ui.sbBorderOpacity, static_cast(&QSpinBox::valueChanged), this, &LabelWidget::borderOpacityChanged); + //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(); + borderShapeChanged(ui.cbBorderShape->currentIndex()); + initConnections(); } void LabelWidget::setAxes(QList axes) { m_labelsList.clear(); for (auto* axis : axes) { m_labelsList.append(axis->title()); connect(axis, SIGNAL(titleOffsetXChanged(qreal)), this, SLOT(labelOffsetxChanged(qreal)) ); connect(axis, SIGNAL(titleOffsetYChanged(qreal)), this, SLOT(labelOffsetyChanged(qreal)) ); connect(axis->title(), SIGNAL(rotationAngleChanged(qreal)), this, SLOT(labelRotationAngleChanged(qreal)) ); } 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(qreal)), this, SLOT(labelRotationAngleChanged(qreal)) ); + connect(m_label, &TextLabel::borderShapeChanged, this, &LabelWidget::labelBorderShapeChanged); + connect(m_label, &TextLabel::borderPenChanged, this, &LabelWidget::labelBorderPenChanged); + connect(m_label, &TextLabel::borderOpacityChanged, this, &LabelWidget::labelBorderOpacityChanged); 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 formatting slots void LabelWidget::textChanged() { if (m_initializing) return; if (ui.tbTexUsed->isChecked()) { QString text=ui.teLabel->toPlainText(); TextLabel::TextWrapper wrapper(text, true); for (auto* 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); for (auto* label : m_labelsList) label->setText(wrapper); } } void LabelWidget::charFormatChanged(const QTextCharFormat& format) { if(ui.tbTexUsed->isChecked()) return; // update button state ui.tbFontBold->setChecked(ui.teLabel->fontWeight()==QFont::Bold); ui.tbFontItalic->setChecked(ui.teLabel->fontItalic()); ui.tbFontUnderline->setChecked(ui.teLabel->fontUnderline()); ui.tbFontStrikeOut->setChecked(format.fontStrikeOut()); ui.tbFontSuperScript->setChecked(format.verticalAlignment() == QTextCharFormat::AlignSuperScript); ui.tbFontSubScript->setChecked(format.verticalAlignment() == QTextCharFormat::AlignSubScript); //font and colors ui.kcbFontColor->setColor(format.foreground().color()); ui.kcbBackgroundColor->setColor(format.background().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) { //reset all applied formattings when switching from html to tex mode QTextCursor cursor = ui.teLabel->textCursor(); int position = cursor.position(); ui.teLabel->selectAll(); QTextCharFormat format; ui.teLabel->setCurrentCharFormat(format); cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, position); ui.teLabel->setTextCursor(cursor); #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING m_highlighter->setDocument(ui.teLabel->document()); #endif KConfigGroup conf(KSharedConfig::openConfig(), QLatin1String("Settings_Worksheet")); QString engine = conf.readEntry(QLatin1String("LaTeXEngine"), ""); if (engine == QLatin1String("xelatex") || engine == QLatin1String("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); } //update TeX colors ui.kcbFontColor->setColor(m_label->teXFontColor()); ui.kcbBackgroundColor->setColor(m_label->teXBackgroundColor()); } else { #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING m_highlighter->setDocument(nullptr); #endif ui.lFontTeX->setVisible(false); ui.kfontRequesterTeX->setVisible(false); ui.lFontSize->setVisible(false); ui.sbFontSize->setVisible(false); //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()) ui.teLabel->setStyleSheet(QLatin1String("")); } //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(QLatin1String("")); } if (m_initializing) return; QString text = checked ? ui.teLabel->toPlainText() : ui.teLabel->toHtml(); TextLabel::TextWrapper wrapper(text, checked); for (auto* label : m_labelsList) label->setText(wrapper); } void LabelWidget::fontColorChanged(const QColor& color) { if (m_initializing) return; ui.teLabel->setTextColor(color); for (auto* label : m_labelsList) label->setTeXFontColor(color); } void LabelWidget::backgroundColorChanged(const QColor& color) { if (m_initializing) return; ui.teLabel->setTextBackgroundColor(color); for (auto* label : m_labelsList) label->setTeXBackgroundColor(color); } void LabelWidget::fontSizeChanged(int value) { if (m_initializing) return; QFont font = m_label->teXFont(); font.setPointSize(value); for (auto* 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; for (auto* label : m_labelsList) label->setTeXFont(font); } void LabelWidget::charMenu() { QMenu menu; KCharSelect selection(this, nullptr, 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())); auto* 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); for (auto* 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); for (auto* 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)); for (auto* 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)); for (auto* label : m_labelsList) label->setPosition(position); } void LabelWidget::horizontalAlignmentChanged(int index) { if (m_initializing) return; for (auto* label : m_labelsList) label->setHorizontalAlignment(TextLabel::HorizontalAlignment(index)); } void LabelWidget::verticalAlignmentChanged(int index) { if (m_initializing) return; for (auto* label : m_labelsList) label->setVerticalAlignment(TextLabel::VerticalAlignment(index)); } void LabelWidget::rotationChanged(int value) { if (m_initializing) return; for (auto* label : m_labelsList) label->setRotationAngle(value); } void LabelWidget::offsetXChanged(double value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setTitleOffsetX( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); } void LabelWidget::offsetYChanged(double value) { if (m_initializing) return; for (auto* axis : m_axesList) axis->setTitleOffsetY( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); } void LabelWidget::visibilityChanged(bool state) { if (m_initializing) return; for (auto* label : m_labelsList) label->setVisible(state); } +//border +void LabelWidget::borderShapeChanged(int index) { + TextLabel::BorderShape shape = (TextLabel::BorderShape)index; + bool b = (shape != TextLabel::NoBorder); + ui.lBorderStyle->setVisible(b); + ui.cbBorderStyle->setVisible(b); + ui.lBorderWidth->setVisible(b); + ui.sbBorderWidth->setVisible(b); + ui.lBorderColor->setVisible(b); + ui.kcbBorderColor->setVisible(b); + ui.lBorderOpacity->setVisible(b); + ui.sbBorderOpacity->setVisible(b); + + if (m_initializing) + return; + + for (auto* label : m_labelsList) + label->setBorderShape(shape); +} + +void LabelWidget::borderStyleChanged(int index) { + if (m_initializing) + return; + + auto penStyle = Qt::PenStyle(index); + QPen pen; + for (auto* label : m_labelsList) { + pen = label->borderPen(); + pen.setStyle(penStyle); + label->setBorderPen(pen); + } +} + +void LabelWidget::borderColorChanged(const QColor& color) { + if (m_initializing) + return; + + QPen pen; + for (auto* label : m_labelsList) { + pen = label->borderPen(); + pen.setColor(color); + label->setBorderPen(pen); + } + + m_initializing = true; + GuiTools::updatePenStyles(ui.cbBorderStyle, color); + m_initializing = false; +} + +void LabelWidget::borderWidthChanged(double value) { + if (m_initializing) + return; + + QPen pen; + for (auto* label : m_labelsList) { + pen = label->borderPen(); + pen.setWidthF( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); + label->setBorderPen(pen); + } +} + +void LabelWidget::borderOpacityChanged(int value) { + if (m_initializing) + return; + + qreal opacity = (float)value/100.; + for (auto* label : m_labelsList) + label->setBorderOpacity(opacity); +} + //********************************************************* //****** 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(); if (text.teXUsed) ui.teLabel->setText(text.text); else ui.teLabel->setHtml(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(QLatin1String("QTextEdit{background: red;}")); else ui.teLabel->setStyleSheet(QLatin1String("")); } 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(qreal offset) { m_initializing = true; ui.sbOffsetX->setValue(Worksheet::convertFromSceneUnits(offset, Worksheet::Point)); m_initializing = false; } void LabelWidget::labelOffsetyChanged(qreal offset) { m_initializing = true; ui.sbOffsetY->setValue(Worksheet::convertFromSceneUnits(offset, Worksheet::Point)); m_initializing = false; } void LabelWidget::labelRotationAngleChanged(qreal 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; } +//border +void LabelWidget::labelBorderShapeChanged(TextLabel::BorderShape shape) { + m_initializing = true; + ui.cbBorderShape->setCurrentIndex(shape); + m_initializing = false; +} + +void LabelWidget::labelBorderPenChanged(QPen& pen) { + m_initializing = true; + if (ui.cbBorderStyle->currentIndex() != pen.style()) + ui.cbBorderStyle->setCurrentIndex(pen.style()); + if (ui.kcbBorderColor->color() != pen.color()) + ui.kcbBorderColor->setColor(pen.color()); + if (ui.sbBorderWidth->value() != pen.widthF()) + ui.sbBorderWidth->setValue(Worksheet::convertFromSceneUnits(pen.widthF(),Worksheet::Point)); + m_initializing = false; +} + +void LabelWidget::labelBorderOpacityChanged(float value) { + m_initializing = true; + float v = (float)value*100.; + ui.sbBorderOpacity->setValue(v); + m_initializing = false; +} + //********************************************************** //******************** SETTINGS **************************** //********************************************************** void LabelWidget::load() { if(!m_label) return; m_initializing = true; ui.chbVisible->setChecked(m_label->isVisible()); //Text/TeX ui.tbTexUsed->setChecked( (bool) m_label->text().teXUsed ); if (m_label->text().teXUsed) ui.teLabel->setText(m_label->text().text); else ui.teLabel->setHtml(m_label->text().text); this->teXUsedChanged(m_label->text().teXUsed); ui.kfontRequesterTeX->setFont(m_label->teXFont()); ui.sbFontSize->setValue( m_label->teXFont().pointSize() ); //move the cursor to the end and set the focus to the text editor QTextCursor cursor = ui.teLabel->textCursor(); cursor.movePosition(QTextCursor::End); ui.teLabel->setTextCursor(cursor); ui.teLabel->setFocus(); // 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() ); + //Border + ui.cbBorderShape->setCurrentIndex( m_label->borderShape() ); + ui.kcbBorderColor->setColor( m_label->borderPen().color() ); + ui.cbBorderStyle->setCurrentIndex( (int) m_label->borderPen().style() ); + ui.sbBorderWidth->setValue( Worksheet::convertFromSceneUnits(m_label->borderPen().widthF(), Worksheet::Point) ); + ui.sbBorderOpacity->setValue( round(m_label->borderOpacity()*100) ); + GuiTools::updatePenStyles(ui.cbBorderStyle, ui.kcbBorderColor->color()); + m_initializing = false; } void LabelWidget::loadConfig(KConfigGroup& group) { if(!m_label) 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.kfontRequesterTeX->setFont(group.readEntry("TeXFont", m_label->teXFont())); // 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()) ); + //Border + ui.cbBorderShape->setCurrentIndex(group.readEntry("BorderShape").toInt()); + ui.kcbBorderColor->setColor( group.readEntry("BorderColor", m_label->borderPen().color()) ); + ui.cbBorderStyle->setCurrentIndex( group.readEntry("BorderStyle", (int)m_label->borderPen().style()) ); + ui.sbBorderWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("BorderWidth", m_label->borderPen().widthF()), Worksheet::Point) ); + ui.sbBorderOpacity->setValue( group.readEntry("BorderOpacity", m_label->borderOpacity())*100 ); m_initializing = false; } void LabelWidget::saveConfig(KConfigGroup& group) { //TeX group.writeEntry("TeXUsed", ui.tbTexUsed->isChecked()); group.writeEntry("TeXFontColor", ui.kcbFontColor->color()); group.writeEntry("TeXBackgroundColor", ui.kcbBackgroundColor->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()); + + //Border + group.writeEntry("BorderShape", ui.cbBorderShape->currentIndex()); + group.writeEntry("BorderStyle", ui.cbBorderStyle->currentIndex()); + group.writeEntry("BorderColor", ui.kcbBorderColor->color()); + group.writeEntry("BorderWidth", Worksheet::convertToSceneUnits(ui.sbBorderWidth->value(), Worksheet::Point)); + group.writeEntry("BorderOpacity", ui.sbBorderOpacity->value()/100.0); } diff --git a/src/kdefrontend/widgets/LabelWidget.h b/src/kdefrontend/widgets/LabelWidget.h index 8407a908b..30993d582 100644 --- a/src/kdefrontend/widgets/LabelWidget.h +++ b/src/kdefrontend/widgets/LabelWidget.h @@ -1,128 +1,139 @@ /*************************************************************************** File : LabelWidget.h 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 * * * ***************************************************************************/ #ifndef LABELWIDGET_H #define LABELWIDGET_H #include "ui_labelwidget.h" #include "backend/worksheet/TextLabel.h" #include #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING #include namespace KSyntaxHighlighting { class SyntaxHighlighter; } #endif class Label; class Axis; class QMenu; class LabelWidget : public QWidget { Q_OBJECT public: explicit LabelWidget(QWidget*); void setLabels(QList); void setAxes(QList); void load(); void loadConfig(KConfigGroup&); void saveConfig(KConfigGroup&); void setNoGeometryMode(const bool); void setFixedLabelMode(const bool); private: Ui::LabelWidget ui; TextLabel* m_label; QList m_labelsList; QList m_axesList; bool m_initializing; QMenu* m_dateTimeMenu; bool m_teXEnabled; #ifdef HAVE_KF5_SYNTAX_HIGHLIGHTING KSyntaxHighlighting::SyntaxHighlighter* m_highlighter; KSyntaxHighlighting::Repository m_repository; #endif void initConnections() const; signals: void dataChanged(bool); private slots: //SLOTs for changes triggered in LabelWidget void textChanged(); void charFormatChanged(const QTextCharFormat&); void teXUsedChanged(bool); void fontColorChanged(const QColor&); void backgroundColorChanged(const QColor&); void fontBoldChanged(bool); void fontItalicChanged(bool); void fontUnderlineChanged(bool); void fontStrikeOutChanged(bool); void fontSuperScriptChanged(bool); void fontSubScriptChanged(bool); void charMenu(); void insertChar(QChar); void fontChanged(const QFont&); void teXFontChanged(const QFont&); void fontSizeChanged(int); void dateTimeMenu(); void insertDateTime(QAction*); void positionXChanged(int); void positionYChanged(int); void customPositionXChanged(double); void customPositionYChanged(double); void horizontalAlignmentChanged(int); void verticalAlignmentChanged(int); void rotationChanged(int); void offsetXChanged(double); void offsetYChanged(double); + void borderShapeChanged(int); + void borderStyleChanged(int); + void borderColorChanged(const QColor&); + void borderWidthChanged(double); + void borderOpacityChanged(int); + void visibilityChanged(bool); //SLOTs for changes triggered in TextLabel void labelTextWrapperChanged(const TextLabel::TextWrapper&); void labelTeXImageUpdated(bool); void labelTeXFontChanged(const QFont&); void labelTeXFontColorChanged(const QColor); void labelPositionChanged(const TextLabel::PositionWrapper&); void labelHorizontalAlignmentChanged(TextLabel::HorizontalAlignment); void labelVerticalAlignmentChanged(TextLabel::VerticalAlignment); void labelOffsetxChanged(qreal); void labelOffsetyChanged(qreal); void labelRotationAngleChanged(qreal); + + void labelBorderShapeChanged(TextLabel::BorderShape); + void labelBorderPenChanged(QPen&); + void labelBorderOpacityChanged(float); + void labelVisibleChanged(bool); }; #endif //LABELWIDGET_H