diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 56c0388b7..24d13c6b4 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,495 +1,498 @@ find_package(SharedMimeInfo REQUIRED) set(KDE_FRONTEND true) set(KDEFRONTEND_DIR kdefrontend) set(BACKEND_DIR backend) set(COMMONFRONTEND_DIR commonfrontend) set(CANTOR_DIR cantor) set(TOOLS_DIR tools) set(CMAKE_AUTOMOC ON) set(SRC_BUILD_DIR ${CMAKE_CURRENT_BINARY_DIR} PARENT_SCOPE) # do not process this file set_property(SOURCE gsl_parser.h PROPERTY SKIP_AUTOMOC ON) set(GUI_SOURCES ${KDEFRONTEND_DIR}/GuiObserver.cpp ${KDEFRONTEND_DIR}/GuiTools.cpp ${KDEFRONTEND_DIR}/HistoryDialog.cpp ${KDEFRONTEND_DIR}/MainWin.cpp ${KDEFRONTEND_DIR}/SettingsDialog.cpp ${KDEFRONTEND_DIR}/SettingsGeneralPage.cpp ${KDEFRONTEND_DIR}/SettingsWorksheetPage.cpp ${KDEFRONTEND_DIR}/SettingsPage.h ${KDEFRONTEND_DIR}/TemplateHandler.cpp ${KDEFRONTEND_DIR}/ThemeHandler.cpp ${KDEFRONTEND_DIR}/datasources/AsciiOptionsWidget.cpp ${KDEFRONTEND_DIR}/datasources/BinaryOptionsWidget.cpp ${KDEFRONTEND_DIR}/datasources/DatabaseManagerDialog.cpp ${KDEFRONTEND_DIR}/datasources/DatabaseManagerWidget.cpp ${KDEFRONTEND_DIR}/datasources/HDF5OptionsWidget.cpp ${KDEFRONTEND_DIR}/datasources/FileInfoDialog.cpp ${KDEFRONTEND_DIR}/datasources/ImageOptionsWidget.cpp ${KDEFRONTEND_DIR}/datasources/ImportDialog.cpp ${KDEFRONTEND_DIR}/datasources/ImportFileWidget.cpp ${KDEFRONTEND_DIR}/datasources/ImportFileDialog.cpp ${KDEFRONTEND_DIR}/datasources/ImportProjectDialog.cpp ${KDEFRONTEND_DIR}/datasources/ImportSQLDatabaseDialog.cpp ${KDEFRONTEND_DIR}/datasources/ImportSQLDatabaseWidget.cpp ${KDEFRONTEND_DIR}/datasources/NetCDFOptionsWidget.cpp ${KDEFRONTEND_DIR}/datasources/ROOTOptionsWidget.cpp ${KDEFRONTEND_DIR}/datasources/FITSOptionsWidget.cpp ${KDEFRONTEND_DIR}/datasources/MQTTErrorWidget.cpp ${KDEFRONTEND_DIR}/datasources/MQTTSubscriptionWidget.cpp ${KDEFRONTEND_DIR}/datasources/JsonOptionsWidget.cpp ${KDEFRONTEND_DIR}/datasources/MQTTConnectionManagerWidget.cpp ${KDEFRONTEND_DIR}/datasources/MQTTConnectionManagerDialog.cpp ${KDEFRONTEND_DIR}/dockwidgets/BaseDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/AxisDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/CursorDock.cpp + ${KDEFRONTEND_DIR}/dockwidgets/ImageDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/NoteDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/CartesianPlotDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/CartesianPlotLegendDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/HistogramDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/CustomPointDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/ColumnDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/LiveDataDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/MatrixDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/ProjectDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/SpreadsheetDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYEquationCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYDataReductionCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYDifferentiationCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYIntegrationCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYInterpolationCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYSmoothCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYFitCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYFourierFilterCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYFourierTransformCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYConvolutionCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/XYCorrelationCurveDock.cpp ${KDEFRONTEND_DIR}/dockwidgets/WorksheetDock.cpp ${KDEFRONTEND_DIR}/matrix/MatrixFunctionDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/PlotDataDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/EquidistantValuesDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/ExportSpreadsheetDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/AddSubtractValueDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/DropValuesDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/FunctionValuesDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/RandomValuesDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/SortDialog.cpp ${KDEFRONTEND_DIR}/spreadsheet/StatisticsDialog.cpp ${KDEFRONTEND_DIR}/worksheet/ExportWorksheetDialog.cpp ${KDEFRONTEND_DIR}/worksheet/GridDialog.cpp ${KDEFRONTEND_DIR}/worksheet/DynamicPresenterWidget.cpp ${KDEFRONTEND_DIR}/worksheet/PresenterWidget.cpp ${KDEFRONTEND_DIR}/worksheet/SlidingPanel.cpp ${KDEFRONTEND_DIR}/widgets/ConstantsWidget.cpp ${KDEFRONTEND_DIR}/widgets/ThemesComboBox.cpp ${KDEFRONTEND_DIR}/widgets/ThemesWidget.cpp ${KDEFRONTEND_DIR}/widgets/ExpressionTextEdit.cpp ${KDEFRONTEND_DIR}/widgets/FitOptionsWidget.cpp ${KDEFRONTEND_DIR}/widgets/FitParametersWidget.cpp ${KDEFRONTEND_DIR}/widgets/FunctionsWidget.cpp ${KDEFRONTEND_DIR}/widgets/LabelWidget.cpp ${KDEFRONTEND_DIR}/widgets/DatapickerImageWidget.cpp ${KDEFRONTEND_DIR}/widgets/DatapickerCurveWidget.cpp ${KDEFRONTEND_DIR}/widgets/FITSHeaderEditWidget.cpp ${KDEFRONTEND_DIR}/widgets/FITSHeaderEditNewKeywordDialog.cpp ${KDEFRONTEND_DIR}/widgets/FITSHeaderEditAddUnitDialog.cpp ${KDEFRONTEND_DIR}/widgets/FITSHeaderEditDialog.cpp ${KDEFRONTEND_DIR}/widgets/ResizableTextEdit.cpp ${KDEFRONTEND_DIR}/widgets/MQTTWillSettingsWidget.cpp ) if(APPLE) list(APPEND GUI_SOURCES ${KDEFRONTEND_DIR}/worksheet/PresenterWidget_mac.mm) list(APPEND GUI_SOURCES ${KDEFRONTEND_DIR}/worksheet/DynamicPresenterWidget_mac.mm) endif() set(UI_SOURCES ${KDEFRONTEND_DIR}/ui/constantswidget.ui ${KDEFRONTEND_DIR}/ui/functionswidget.ui ${KDEFRONTEND_DIR}/ui/fitoptionswidget.ui ${KDEFRONTEND_DIR}/ui/fitparameterswidget.ui ${KDEFRONTEND_DIR}/ui/labelwidget.ui ${KDEFRONTEND_DIR}/ui/settingsgeneralpage.ui ${KDEFRONTEND_DIR}/ui/settingsworksheetpage.ui ${KDEFRONTEND_DIR}/ui/settingsprintingpage.ui ${KDEFRONTEND_DIR}/ui/mqttwillsettingswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/asciioptionswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/binaryoptionswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/databasemanagerwidget.ui ${KDEFRONTEND_DIR}/ui/datasources/hdf5optionswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/imageoptionswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/importfilewidget.ui ${KDEFRONTEND_DIR}/ui/datasources/importprojectwidget.ui ${KDEFRONTEND_DIR}/ui/datasources/importsqldatabasewidget.ui ${KDEFRONTEND_DIR}/ui/datasources/netcdfoptionswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/rootoptionswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/fitsoptionswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/mqtterrorwidget.ui ${KDEFRONTEND_DIR}/ui/datasources/mqttsubscriptionwidget.ui ${KDEFRONTEND_DIR}/ui/datasources/jsonoptionswidget.ui ${KDEFRONTEND_DIR}/ui/datasources/mqttconnectionmanagerwidget.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/axisdock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/cursordock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/cartesianplotdock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/cartesianplotlegenddock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/histogramdock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/columndock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/custompointdock.ui + ${KDEFRONTEND_DIR}/ui/dockwidgets/imagedock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/livedatadock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/notedock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/matrixdock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/projectdock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/spreadsheetdock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xycurvedock.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xycurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xydatareductioncurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xydifferentiationcurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xyintegrationcurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xyinterpolationcurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xysmoothcurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xyfitcurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xyfourierfiltercurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xyfouriertransformcurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xyconvolutioncurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xycorrelationcurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/xyequationcurvedockgeneraltab.ui ${KDEFRONTEND_DIR}/ui/dockwidgets/worksheetdock.ui ${KDEFRONTEND_DIR}/ui/matrix/matrixfunctionwidget.ui ${KDEFRONTEND_DIR}/ui/spreadsheet/plotdatawidget.ui ${KDEFRONTEND_DIR}/ui/spreadsheet/equidistantvalueswidget.ui ${KDEFRONTEND_DIR}/ui/spreadsheet/exportspreadsheetwidget.ui ${KDEFRONTEND_DIR}/ui/spreadsheet/addsubtractvaluewidget.ui ${KDEFRONTEND_DIR}/ui/spreadsheet/dropvalueswidget.ui ${KDEFRONTEND_DIR}/ui/spreadsheet/functionvalueswidget.ui ${KDEFRONTEND_DIR}/ui/spreadsheet/randomvalueswidget.ui ${KDEFRONTEND_DIR}/ui/spreadsheet/sortdialogwidget.ui ${KDEFRONTEND_DIR}/ui/worksheet/exportworksheetwidget.ui ${KDEFRONTEND_DIR}/ui/datapickerimagewidget.ui ${KDEFRONTEND_DIR}/ui/datapickercurvewidget.ui ${KDEFRONTEND_DIR}/ui/fitsheadereditwidget.ui ${KDEFRONTEND_DIR}/ui/fitsheadereditnewkeywordwidget.ui ${KDEFRONTEND_DIR}/ui/fitsheadereditaddunitwidget.ui ) set(BACKEND_SOURCES ${BACKEND_DIR}/core/Folder.cpp ${BACKEND_DIR}/core/AbstractAspect.cpp ${BACKEND_DIR}/core/AbstractColumn.cpp ${BACKEND_DIR}/core/AbstractColumnPrivate.cpp ${BACKEND_DIR}/core/abstractcolumncommands.cpp ${BACKEND_DIR}/core/AbstractFilter.cpp ${BACKEND_DIR}/core/AbstractSimpleFilter.cpp ${BACKEND_DIR}/core/column/Column.cpp ${BACKEND_DIR}/core/column/ColumnPrivate.cpp ${BACKEND_DIR}/core/column/ColumnStringIO.cpp ${BACKEND_DIR}/core/column/columncommands.cpp ${BACKEND_DIR}/core/Project.cpp ${BACKEND_DIR}/core/AbstractPart.cpp ${BACKEND_DIR}/core/Workbook.cpp ${BACKEND_DIR}/core/AspectTreeModel.cpp ${BACKEND_DIR}/core/datatypes/SimpleCopyThroughFilter.h ${BACKEND_DIR}/core/datatypes/Double2DateTimeFilter.h ${BACKEND_DIR}/core/datatypes/Double2DayOfWeekFilter.h ${BACKEND_DIR}/core/datatypes/Double2IntegerFilter.h ${BACKEND_DIR}/core/datatypes/Double2MonthFilter.h ${BACKEND_DIR}/core/datatypes/Double2StringFilter.cpp ${BACKEND_DIR}/core/datatypes/Integer2DateTimeFilter.h ${BACKEND_DIR}/core/datatypes/Integer2DayOfWeekFilter.h ${BACKEND_DIR}/core/datatypes/Integer2DoubleFilter.h ${BACKEND_DIR}/core/datatypes/Integer2MonthFilter.h ${BACKEND_DIR}/core/datatypes/Integer2StringFilter.h ${BACKEND_DIR}/core/datatypes/String2DayOfWeekFilter.h ${BACKEND_DIR}/core/datatypes/String2DoubleFilter.h ${BACKEND_DIR}/core/datatypes/String2IntegerFilter.h ${BACKEND_DIR}/core/datatypes/String2MonthFilter.h ${BACKEND_DIR}/core/datatypes/String2DateTimeFilter.cpp ${BACKEND_DIR}/core/datatypes/DateTime2DoubleFilter.h ${BACKEND_DIR}/core/datatypes/DateTime2IntegerFilter.h ${BACKEND_DIR}/core/datatypes/DateTime2StringFilter.cpp ${BACKEND_DIR}/core/datatypes/Month2DoubleFilter.h ${BACKEND_DIR}/core/datatypes/Month2IntegerFilter.h ${BACKEND_DIR}/core/datatypes/DayOfWeek2DoubleFilter.h ${BACKEND_DIR}/core/datatypes/DayOfWeek2IntegerFilter.h ${BACKEND_DIR}/core/plugin/PluginLoader.cpp ${BACKEND_DIR}/core/plugin/PluginManager.cpp ${BACKEND_DIR}/datasources/AbstractDataSource.cpp ${BACKEND_DIR}/datasources/LiveDataSource.cpp ${BACKEND_DIR}/datasources/MQTTClient.cpp ${BACKEND_DIR}/datasources/MQTTSubscription.cpp ${BACKEND_DIR}/datasources/MQTTTopic.cpp ${BACKEND_DIR}/datasources/filters/AbstractFileFilter.cpp ${BACKEND_DIR}/datasources/filters/AsciiFilter.cpp ${BACKEND_DIR}/datasources/filters/BinaryFilter.cpp ${BACKEND_DIR}/datasources/filters/HDF5Filter.cpp ${BACKEND_DIR}/datasources/filters/ImageFilter.cpp ${BACKEND_DIR}/datasources/filters/JsonFilter.cpp ${BACKEND_DIR}/datasources/filters/NetCDFFilter.cpp ${BACKEND_DIR}/datasources/filters/NgspiceRawAsciiFilter.cpp ${BACKEND_DIR}/datasources/filters/NgspiceRawBinaryFilter.cpp ${BACKEND_DIR}/datasources/filters/FITSFilter.cpp ${BACKEND_DIR}/datasources/filters/QJsonModel.cpp ${BACKEND_DIR}/datasources/filters/ROOTFilter.cpp ${BACKEND_DIR}/datasources/projects/ProjectParser.cpp ${BACKEND_DIR}/datasources/projects/LabPlotProjectParser.cpp ${BACKEND_DIR}/gsl/ExpressionParser.cpp ${BACKEND_DIR}/matrix/Matrix.cpp ${BACKEND_DIR}/matrix/matrixcommands.cpp ${BACKEND_DIR}/matrix/MatrixModel.cpp ${BACKEND_DIR}/spreadsheet/Spreadsheet.cpp ${BACKEND_DIR}/spreadsheet/SpreadsheetModel.cpp ${BACKEND_DIR}/lib/XmlStreamReader.cpp ${BACKEND_DIR}/note/Note.cpp + ${BACKEND_DIR}/worksheet/Image.cpp ${BACKEND_DIR}/worksheet/WorksheetElement.cpp ${BACKEND_DIR}/worksheet/TextLabel.cpp ${BACKEND_DIR}/worksheet/Worksheet.cpp ${BACKEND_DIR}/worksheet/WorksheetElementContainer.cpp ${BACKEND_DIR}/worksheet/WorksheetElementGroup.cpp ${BACKEND_DIR}/worksheet/TreeModel.cpp ${BACKEND_DIR}/worksheet/plots/AbstractPlot.cpp ${BACKEND_DIR}/worksheet/plots/AbstractCoordinateSystem.cpp ${BACKEND_DIR}/worksheet/plots/PlotArea.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/Axis.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/CartesianCoordinateSystem.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/CartesianPlot.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/CartesianPlotLegend.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/Histogram.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/CustomPoint.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/Symbol.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYAnalysisCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYEquationCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYDataReductionCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYDifferentiationCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYIntegrationCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYInterpolationCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYSmoothCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYFitCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYFourierFilterCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYFourierTransformCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYConvolutionCurve.cpp ${BACKEND_DIR}/worksheet/plots/cartesian/XYCorrelationCurve.cpp ${BACKEND_DIR}/lib/SignallingUndoCommand.cpp ${BACKEND_DIR}/datapicker/DatapickerPoint.cpp ${BACKEND_DIR}/datapicker/DatapickerImage.cpp ${BACKEND_DIR}/datapicker/Datapicker.cpp ${BACKEND_DIR}/datapicker/Transform.cpp ${BACKEND_DIR}/datapicker/ImageEditor.cpp ${BACKEND_DIR}/datapicker/Segment.cpp ${BACKEND_DIR}/datapicker/Segments.cpp ${BACKEND_DIR}/datapicker/DatapickerCurve.cpp ) IF (ENABLE_LIBORIGIN) list(APPEND BACKEND_SOURCES ${BACKEND_DIR}/datasources/projects/OriginProjectParser.cpp) ENDIF () set(NSL_SOURCES ${BACKEND_DIR}/nsl/nsl_conv.c ${BACKEND_DIR}/nsl/nsl_corr.c ${BACKEND_DIR}/nsl/nsl_dft.c ${BACKEND_DIR}/nsl/nsl_diff.c ${BACKEND_DIR}/nsl/nsl_filter.c ${BACKEND_DIR}/nsl/nsl_fit.c ${BACKEND_DIR}/nsl/nsl_geom.c ${BACKEND_DIR}/nsl/nsl_geom_linesim.c ${BACKEND_DIR}/nsl/nsl_int.c ${BACKEND_DIR}/nsl/nsl_interp.c ${BACKEND_DIR}/nsl/nsl_math.c ${BACKEND_DIR}/nsl/nsl_sf_basic.c ${BACKEND_DIR}/nsl/nsl_sf_kernel.c ${BACKEND_DIR}/nsl/nsl_sf_poly.c ${BACKEND_DIR}/nsl/nsl_sf_stats.c ${BACKEND_DIR}/nsl/nsl_sf_window.c ${BACKEND_DIR}/nsl/nsl_smooth.c ${BACKEND_DIR}/nsl/nsl_sort.c ${BACKEND_DIR}/nsl/nsl_stats.c ) IF (NOT MSVC_FOUND) IF (NOT LIBCERF_FOUND) list(APPEND NSL_SOURCES ${BACKEND_DIR}/nsl/Faddeeva.c ) ENDIF () ENDIF () set(COMMONFRONTEND_SOURCES ${COMMONFRONTEND_DIR}/matrix/MatrixView.cpp ${COMMONFRONTEND_DIR}/note/NoteView.cpp ${COMMONFRONTEND_DIR}/spreadsheet/SpreadsheetCommentsHeaderModel.cpp ${COMMONFRONTEND_DIR}/spreadsheet/SpreadsheetHeaderView.cpp ${COMMONFRONTEND_DIR}/spreadsheet/SpreadsheetItemDelegate.cpp ${COMMONFRONTEND_DIR}/spreadsheet/SpreadsheetView.cpp ${COMMONFRONTEND_DIR}/workbook/WorkbookView.cpp ${COMMONFRONTEND_DIR}/worksheet/WorksheetView.cpp ${COMMONFRONTEND_DIR}/ProjectExplorer.cpp ${COMMONFRONTEND_DIR}/core/PartMdiView.cpp ${COMMONFRONTEND_DIR}/widgets/TreeViewComboBox.cpp ${COMMONFRONTEND_DIR}/widgets/qxtspanslider.cpp ${COMMONFRONTEND_DIR}/widgets/MemoryWidget.cpp ${COMMONFRONTEND_DIR}/widgets/DateTimeSpinBox.cpp ${COMMONFRONTEND_DIR}/datapicker/DatapickerView.cpp ${COMMONFRONTEND_DIR}/datapicker/DatapickerImageView.cpp ) IF (${Cantor_FOUND}) set(CANTOR_SOURCES ${KDEFRONTEND_DIR}/dockwidgets/CantorWorksheetDock.cpp ${BACKEND_DIR}/cantorWorksheet/VariableParser.cpp ${BACKEND_DIR}/cantorWorksheet/CantorWorksheet.cpp ${COMMONFRONTEND_DIR}/cantorWorksheet/CantorWorksheetView.cpp ) set(CANTOR_UI_SOURCES ${KDEFRONTEND_DIR}/ui/dockwidgets/cantorworksheetdock.ui) set(UI_SOURCES ${UI_SOURCES} ${CANTOR_UI_SOURCES}) ELSE () set(CANTOR_SOURCES "") ENDIF () set(TOOLS_SOURCES ${TOOLS_DIR}/EquationHighlighter.cpp ${TOOLS_DIR}/ImageTools.cpp ${TOOLS_DIR}/TeXRenderer.cpp ) bison_target(GslParser ${BACKEND_DIR}/gsl/parser.y ${CMAKE_CURRENT_BINARY_DIR}/gsl_parser.c ) set(GENERATED_SOURCES ${BISON_GslParser_OUTPUTS} ) set_property(SOURCE gsl_parser.h PROPERTY SKIP_AUTOMOC ON) ############################################################################## INCLUDE_DIRECTORIES( . ${BACKEND_DIR}/gsl ${GSL_INCLUDE_DIR} ) set( LABPLOT_SRCS ${GUI_SOURCES} ) ki18n_wrap_ui( LABPLOT_SRCS ${UI_SOURCES} ) # see also QT_MINIMUM_VERSION add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x50500) # static library add_library( labplot2lib STATIC ${LABPLOT_SRCS} ${BACKEND_SOURCES} ${NSL_SOURCES} ${CANTOR_SOURCES} ${DATASOURCES_SOURCES} ${COMMONFRONTEND_SOURCES} ${TOOLS_SOURCES} ${GENERATED_SOURCES} ${QTMOC_HDRS} ) # set_property(TARGET ${objlib} PROPERTY POSITION_INDEPENDENT_CODE 1) target_link_libraries( labplot2lib KF5::Archive KF5::Completion KF5::ConfigCore KF5::Crash KF5::I18n KF5::IconThemes KF5::KIOCore KF5::KIOFileWidgets KF5::KIOWidgets KF5::TextWidgets KF5::XmlGui Qt5::Svg Qt5::Core Qt5::Network Qt5::PrintSupport Qt5::Sql ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES} ) IF (Qt5SerialPort_FOUND) target_link_libraries( labplot2lib Qt5::SerialPort ) ENDIF () IF (Qt5Mqtt_FOUND) target_link_libraries( labplot2lib Qt5::Mqtt ) ENDIF () IF (KF5SyntaxHighlighting_FOUND) target_link_libraries( labplot2lib KF5::SyntaxHighlighting ) ENDIF () #TODO: KF5::NewStuff IF (Cantor_FOUND) target_link_libraries( labplot2lib Cantor::cantorlibs KF5::Service KF5::Parts) IF (${Cantor_VERSION} VERSION_GREATER "19.11") target_link_libraries( labplot2lib Poppler::Qt5) ENDIF() ENDIF () IF (HDF5_FOUND) target_link_libraries( labplot2lib ${HDF5_LIBRARIES} ) ENDIF () IF (FFTW3_FOUND) target_link_libraries( labplot2lib ${FFTW3_LIBRARIES} ) ENDIF () IF (netCDF_FOUND) target_link_libraries( labplot2lib netcdf ) ENDIF () IF (CFITSIO_FOUND) target_link_libraries( labplot2lib ${CFITSIO_LIBRARIES} ) ENDIF () IF (LIBCERF_FOUND) target_link_libraries( labplot2lib ${LIBCERF_LIBRARY} ) ENDIF () IF (ZLIB_FOUND AND LZ4_FOUND) target_link_libraries( labplot2lib ${ZLIB_LIBRARY} ${LZ4_LIBRARY} ) ENDIF () IF (ENABLE_LIBORIGIN) target_link_libraries( labplot2lib liborigin-static ) ENDIF () IF (WIN32) target_link_libraries( labplot2lib ${PSAPI} ) ENDIF () # icons for the executable and project files on Windows and Mac OS X set(LABPLOT_ICONS ${CMAKE_CURRENT_SOURCE_DIR}/../icons/16-apps-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/32-apps-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/48-apps-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/64-apps-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/128-apps-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/256-apps-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/512-apps-labplot2.png ) set(LML_ICONS ${CMAKE_CURRENT_SOURCE_DIR}/../icons/16-application-x-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/32-application-x-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/48-application-x-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/64-application-x-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/128-application-x-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/256-application-x-labplot2.png ${CMAKE_CURRENT_SOURCE_DIR}/../icons/512-application-x-labplot2.png ) add_subdirectory(3rdparty) # main executable set(LABPLOT_SOURCE ${KDEFRONTEND_DIR}/LabPlot.cpp) # create icon files on WIN/MAC and add icons to the executable IF (${ECM_VERSION} VERSION_GREATER "5.48.0") # creates LABPLOT_ICONS.ico/LABPLOT_ICONS.icns ecm_add_app_icon(LABPLOT_SOURCE ICONS ${LABPLOT_ICONS} OUTFILE_BASENAME LABPLOT_ICONS) ELSE () # creates LABPLOT_SOURCE.ico/LABPLOT_SOURCE.icns ecm_add_app_icon(LABPLOT_SOURCE ICONS ${LABPLOT_ICONS}) ENDIF () # create LML_ICONS.icns on MACOSX IF (APPLE AND ${ECM_VERSION} VERSION_GREATER "5.48.0") ecm_add_app_icon(LABPLOT_SOURCE ICONS ${LML_ICONS} OUTFILE_BASENAME LML_ICONS) ENDIF () add_executable(labplot2 ${LABPLOT_SOURCE}) target_link_libraries(labplot2 labplot2lib) IF (APPLE) target_link_libraries(labplot2 KDMacTouchBar) ENDIF() ############## installation ################################ install( TARGETS labplot2 DESTINATION ${INSTALL_TARGETS_DEFAULT_ARGS} ) install( FILES ${KDEFRONTEND_DIR}/labplot2ui.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/${PROJECT_NAME} ) install( FILES ${KDEFRONTEND_DIR}/splash.png ${KDEFRONTEND_DIR}/labplot2.ico ${CMAKE_CURRENT_SOURCE_DIR}/../icons/application-x-labplot2.ico DESTINATION ${DATA_INSTALL_DIR}/${PROJECT_NAME} ) install( PROGRAMS org.kde.labplot2.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) install( FILES labplot2.xml DESTINATION ${XDG_MIME_INSTALL_DIR} ) install( FILES labplot2_themes.knsrc DESTINATION ${CONFIG_INSTALL_DIR} ) update_xdg_mimetypes( ${XDG_MIME_INSTALL_DIR} ) diff --git a/src/backend/core/AbstractAspect.h b/src/backend/core/AbstractAspect.h index a45023114..09910adf1 100644 --- a/src/backend/core/AbstractAspect.h +++ b/src/backend/core/AbstractAspect.h @@ -1,298 +1,299 @@ /*************************************************************************** File : AbstractAspect.h Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2007-2009 by Tilman Benkert (thzs@gmx.net) Copyright : (C) 2007-2010 by Knut Franke (knut.franke@gmx.de) Copyright : (C) 2011-2015 by Alexander Semke (alexander.semke@web.de) Description : Base class for all objects in a Project. ***************************************************************************/ /*************************************************************************** * * * 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 ABSTRACT_ASPECT_H #define ABSTRACT_ASPECT_H #include #include class AbstractAspectPrivate; class Folder; class Project; class XmlStreamReader; class QDateTime; class QDropEvent; class QIcon; class QMenu; class QUndoCommand; class QUndoStack; class QXmlStreamWriter; /// Information about class inheritance /// enum values are chosen such that @verbatim inherits(base)@endverbatim /// returns true iff the class inherits from @verbatim base@endverbatim. /// /// AspectType is used in GuiObserver to select the correct dock widget. enum class AspectType : quint64 { AbstractAspect = 0, // classes without inheriters AbstractFilter = 0x0100001, DatapickerCurve = 0x0100002, DatapickerPoint = 0x0100004, WorksheetElement = 0x0200000, Axis = 0x0210001, CartesianPlotLegend = 0x0210002, CustomPoint = 0x0210004, Histogram = 0x0210008, PlotArea = 0x0210010, TextLabel = 0x0210020, + Image = 0x0210030, WorksheetElementContainer = 0x0220000, AbstractPlot = 0x0221000, CartesianPlot = 0x0221001, WorksheetElementGroup = 0x0222000, XYCurve = 0x0240000, XYEquationCurve = 0x0240001, XYAnalysisCurve = 0x0280000, XYConvolution = 0x0280001, XYCorrelationCurve = 0x0280002, XYDataReductionCurve = 0x0280004, XYDifferentiationCurve = 0x0280008, XYFitCurve = 0x0280010, XYFourierFilterCurve = 0x0280020, XYFourierTransformCurve = 0x0280040, XYInterpolationCurve = 0x0280080, XYIntegrationCurve = 0x0280100, XYSmoothCurve = 0x0280200, AbstractPart = 0x0400000, AbstractDataSource = 0x0410000, Matrix = 0x0411000, Spreadsheet = 0x0412000, LiveDataSource = 0x0412001, MQTTTopic = 0x0412002, CantorWorksheet = 0x0420001, Datapicker = 0x0420002, DatapickerImage = 0x0420004, Note = 0x0420008, Workbook = 0x0420010, Worksheet = 0x0420020, AbstractColumn = 0x1000000, Column = 0x1000001, SimpleFilterColumn = 0x1000002, ColumnStringIO = 0x1000004, Folder = 0x2000000, Project = 0x2000001, MQTTClient = 0x2000002, MQTTSubscription = 0x2000004, }; class AbstractAspect : public QObject { Q_OBJECT public: enum ChildIndexFlag { IncludeHidden = 0x01, Recursive = 0x02, Compress = 0x04 }; Q_DECLARE_FLAGS(ChildIndexFlags, ChildIndexFlag) friend class AspectChildAddCmd; friend class AspectChildRemoveCmd; friend class AbstractAspectPrivate; AbstractAspect(const QString& name, AspectType type); ~AbstractAspect() override; QString name() const; QString comment() const; void setCreationTime(const QDateTime&); QDateTime creationTime() const; virtual Project* project(); virtual QString path() const; void setHidden(bool); bool hidden() const; void setSelected(bool); void setIsLoading(bool); bool isLoading() const; virtual QIcon icon() const; virtual QMenu* createContextMenu(); AspectType type() const; bool inherits(AspectType type) const; //functions related to the handling of the tree-like project structure AbstractAspect* parentAspect() const; AbstractAspect* parent(AspectType type) const; void setParentAspect(AbstractAspect*); Folder* folder(); bool isDescendantOf(AbstractAspect* other); void addChild(AbstractAspect*); void addChildFast(AbstractAspect*); virtual void finalizeAdd() {}; QVector children(AspectType type, ChildIndexFlags flags=nullptr) const; void insertChildBefore(AbstractAspect* child, AbstractAspect* before); void insertChildBeforeFast(AbstractAspect* child, AbstractAspect* before); void reparent(AbstractAspect* newParent, int newIndex = -1); void removeChild(AbstractAspect*); void removeAllChildren(); virtual QVector dependsOn() const; virtual bool isDraggable() const; virtual QVector dropableOn() const; virtual void processDropEvent(QDropEvent*) {}; template T* ancestor() const { AbstractAspect* parent = parentAspect(); while (parent) { T* ancestorAspect = dynamic_cast(parent); if (ancestorAspect) return ancestorAspect; parent = parent->parentAspect(); } return nullptr; } template QVector children(ChildIndexFlags flags = nullptr) const { QVector result; for (auto* child: children()) { if (flags & IncludeHidden || !child->hidden()) { T* i = dynamic_cast(child); if (i) result << i; if (child && flags & Recursive) result << child->template children(flags); } } return result; } template T* child(int index, ChildIndexFlags flags=nullptr) const { int i = 0; for (auto* child: children()) { T* c = dynamic_cast(child); if (c && (flags & IncludeHidden || !child->hidden()) && index == i++) return c; } return nullptr; } template T* child(const QString& name) const { for (auto* child: children()) { T* c = dynamic_cast(child); if (c && child->name() == name) return c; } return nullptr; } template int childCount(ChildIndexFlags flags = nullptr) const { int result = 0; for (auto* child: children()) { T* i = dynamic_cast(child); if (i && (flags & IncludeHidden || !child->hidden())) result++; } return result; } template int indexOfChild(const AbstractAspect* child, ChildIndexFlags flags = nullptr) const { int index = 0; for (auto* c: children()) { if (child == c) return index; T* i = dynamic_cast(c); if (i && (flags & IncludeHidden || !c->hidden())) index++; } return -1; } //undo/redo related functions void setUndoAware(bool); virtual QUndoStack* undoStack() const; void exec(QUndoCommand*); void exec(QUndoCommand* command, const char* preChangeSignal, const char* postChangeSignal, QGenericArgument val0 = QGenericArgument(), QGenericArgument val1 = QGenericArgument(), QGenericArgument val2 = QGenericArgument(), QGenericArgument val3 = QGenericArgument()); void beginMacro(const QString& text); void endMacro(); //save/load virtual void save(QXmlStreamWriter*) const = 0; virtual bool load(XmlStreamReader*, bool preview) = 0; protected: void info(const QString& text) { emit statusInfo(text); } //serialization/deserialization bool readBasicAttributes(XmlStreamReader*); void writeBasicAttributes(QXmlStreamWriter*) const; void writeCommentElement(QXmlStreamWriter*) const; bool readCommentElement(XmlStreamReader*); const AspectType m_type; private: AbstractAspectPrivate* d; QString uniqueNameFor(const QString&) const; const QVector children() const; void connectChild(AbstractAspect*); public slots: bool setName(const QString&, bool autoUnique = true); void setComment(const QString&); void remove(); protected slots: virtual void childSelected(const AbstractAspect*); virtual void childDeselected(const AbstractAspect*); signals: void aspectDescriptionAboutToChange(const AbstractAspect*); void aspectDescriptionChanged(const AbstractAspect*); void aspectAboutToBeAdded(const AbstractAspect* parent, const AbstractAspect* before, const AbstractAspect* child); void aspectAdded(const AbstractAspect*); void aspectAboutToBeRemoved(const AbstractAspect*); void aspectRemoved(const AbstractAspect* parent, const AbstractAspect* before, const AbstractAspect* child); void aspectHiddenAboutToChange(const AbstractAspect*); void aspectHiddenChanged(const AbstractAspect*); void statusInfo(const QString&); void renameRequested(); //selection/deselection in model (project explorer) void selected(const AbstractAspect*); void deselected(const AbstractAspect*); //selection/deselection in view void childAspectSelectedInView(const AbstractAspect*); void childAspectDeselectedInView(const AbstractAspect*); }; Q_DECLARE_OPERATORS_FOR_FLAGS(AbstractAspect::ChildIndexFlags) #endif // ifndef ABSTRACT_ASPECT_H diff --git a/src/backend/worksheet/Image.cpp b/src/backend/worksheet/Image.cpp new file mode 100644 index 000000000..4423750dc --- /dev/null +++ b/src/backend/worksheet/Image.cpp @@ -0,0 +1,666 @@ +/*************************************************************************** + File : Image.cpp + Project : LabPlot + Description : Worksheet element to draw images + -------------------------------------------------------------------- + Copyright : (C) 2019 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 "Image.h" +#include "Worksheet.h" +#include "ImagePrivate.h" +#include "backend/lib/commandtemplates.h" +#include "backend/lib/XmlStreamReader.h" + +// #include +#include +#include +#include +#include + +#include +#include +#include +#include + +/** + * \class Image + * \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). + */ + + +Image::Image(const QString& name) + : WorksheetElement(name, AspectType::Image), d_ptr(new ImagePrivate(this)) { + + init(); +} + +Image::Image(const QString &name, ImagePrivate *dd) + : WorksheetElement(name, AspectType::Image), d_ptr(dd) { + + init(); +} + +void Image::init() { + Q_D(Image); + + KConfig config; + KConfigGroup group = config.group("Image"); + + //geometry + d->position.point.setX( group.readEntry("PositionXValue", d->position.point.x()) ); + d->position.point.setY( group.readEntry("PositionYValue", d->position.point.y()) ); + d->position.horizontalPosition = (WorksheetElement::HorizontalPosition) group.readEntry("PositionX", (int)d->position.horizontalPosition); + d->position.verticalPosition = (WorksheetElement::VerticalPosition) group.readEntry("PositionY", (int)d->position.verticalPosition); + d->horizontalAlignment = (WorksheetElement::HorizontalAlignment) group.readEntry("HorizontalAlignment", (int)d->horizontalAlignment); + d->verticalAlignment = (WorksheetElement::VerticalAlignment) group.readEntry("VerticalAlignment", (int)d->verticalAlignment); + d->rotationAngle = group.readEntry("Rotation", d->rotationAngle); + + //border + d->borderPen = QPen(group.readEntry("BorderColor", d->borderPen.color()), + group.readEntry("BorderWidth", d->borderPen.width()), + (Qt::PenStyle) group.readEntry("BorderStyle", (int)(d->borderPen.style()))); + d->borderOpacity = group.readEntry("BorderOpacity", d->borderOpacity); +} + +//no need to delete the d-pointer here - it inherits from QGraphicsItem +//and is deleted during the cleanup in QGraphicsScene +Image::~Image() = default; + +QGraphicsItem* Image::graphicsItem() const { + return d_ptr; +} + +void Image::setParentGraphicsItem(QGraphicsItem* item) { + Q_D(Image); + d->setParentItem(item); + d->updatePosition(); +} + +void Image::retransform() { + Q_D(Image); + d->retransform(); +} + +void Image::handleResize(double horizontalRatio, double verticalRatio, bool pageResize) { + DEBUG("Image::handleResize()"); + Q_UNUSED(pageResize); + Q_D(Image); + +// double ratio = 0; +// if (horizontalRatio > 1.0 || verticalRatio > 1.0) +// ratio = qMax(horizontalRatio, verticalRatio); +// else +// ratio = qMin(horizontalRatio, verticalRatio); +} + +/*! + Returns an icon to be used in the project explorer. +*/ +QIcon Image::icon() const{ + return QIcon::fromTheme("draw-image"); +} + +QMenu* Image::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, &Image::visibilityChanged); + } + + visibilityAction->setChecked(isVisible()); + menu->insertAction(firstAction, visibilityAction); + menu->insertSeparator(firstAction); + + return menu; +} + +/* ============================ getter methods ================= */ +CLASS_SHARED_D_READER_IMPL(Image, QString, fileName, fileName) +CLASS_SHARED_D_READER_IMPL(Image, WorksheetElement::PositionWrapper, position, position) +BASIC_SHARED_D_READER_IMPL(Image, WorksheetElement::HorizontalAlignment, horizontalAlignment, horizontalAlignment) +BASIC_SHARED_D_READER_IMPL(Image, WorksheetElement::VerticalAlignment, verticalAlignment, verticalAlignment) +BASIC_SHARED_D_READER_IMPL(Image, qreal, rotationAngle, rotationAngle) + +CLASS_SHARED_D_READER_IMPL(Image, QPen, borderPen, borderPen) +BASIC_SHARED_D_READER_IMPL(Image, qreal, borderOpacity, borderOpacity) + +/* ============================ setter methods and undo commands ================= */ +STD_SETTER_CMD_IMPL_F_S(Image, SetFileName, QString, fileName, updateImage) +void Image::setFileName(const QString& fileName) { + Q_D(Image); + if (fileName != d->fileName) + exec(new ImageSetFileNameCmd(d, fileName, ki18n("%1: set image"))); +} + +STD_SETTER_CMD_IMPL_F_S(Image, SetOpacity, qreal, opacity, update) +void Image::setOpacity(qreal opacity) { + Q_D(Image); + if (opacity != d->opacity) + exec(new ImageSetOpacityCmd(d, opacity, ki18n("%1: set border opacity"))); +} + +STD_SETTER_CMD_IMPL_F_S(Image, SetPosition, WorksheetElement::PositionWrapper, position, retransform); +void Image::setPosition(const WorksheetElement::PositionWrapper& pos) { + Q_D(Image); + if (pos.point != d->position.point || pos.horizontalPosition != d->position.horizontalPosition || pos.verticalPosition != d->position.verticalPosition) + exec(new ImageSetPositionCmd(d, pos, ki18n("%1: set position"))); +} + +/*! + sets the position without undo/redo-stuff +*/ +void Image::setPosition(QPointF point) { + Q_D(Image); + if (point != d->position.point) { + d->position.point = point; + retransform(); + } +} + +STD_SETTER_CMD_IMPL_F_S(Image, SetRotationAngle, qreal, rotationAngle, recalcShapeAndBoundingRect); +void Image::setRotationAngle(qreal angle) { + Q_D(Image); + if (angle != d->rotationAngle) + exec(new ImageSetRotationAngleCmd(d, angle, ki18n("%1: set rotation angle"))); +} + +STD_SETTER_CMD_IMPL_F_S(Image, SetHorizontalAlignment, WorksheetElement::HorizontalAlignment, horizontalAlignment, retransform); +void Image::setHorizontalAlignment(const WorksheetElement::HorizontalAlignment hAlign) { + Q_D(Image); + if (hAlign != d->horizontalAlignment) + exec(new ImageSetHorizontalAlignmentCmd(d, hAlign, ki18n("%1: set horizontal alignment"))); +} + +STD_SETTER_CMD_IMPL_F_S(Image, SetVerticalAlignment, WorksheetElement::VerticalAlignment, verticalAlignment, retransform); +void Image::setVerticalAlignment(const WorksheetElement::VerticalAlignment vAlign) { + Q_D(Image); + if (vAlign != d->verticalAlignment) + exec(new ImageSetVerticalAlignmentCmd(d, vAlign, ki18n("%1: set vertical alignment"))); +} + +//Border +STD_SETTER_CMD_IMPL_F_S(Image, SetBorderPen, QPen, borderPen, update) +void Image::setBorderPen(const QPen &pen) { + Q_D(Image); + if (pen != d->borderPen) + exec(new ImageSetBorderPenCmd(d, pen, ki18n("%1: set border"))); +} + +STD_SETTER_CMD_IMPL_F_S(Image, SetBorderOpacity, qreal, borderOpacity, update) +void Image::setBorderOpacity(qreal opacity) { + Q_D(Image); + if (opacity != d->borderOpacity) + exec(new ImageSetBorderOpacityCmd(d, opacity, ki18n("%1: set border opacity"))); +} + +//misc +STD_SWAP_METHOD_SETTER_CMD_IMPL_F(Image, SetVisible, bool, swapVisible, retransform); +void Image::setVisible(bool on) { + Q_D(Image); + exec(new ImageSetVisibleCmd(d, on, on ? ki18n("%1: set visible") : ki18n("%1: set invisible"))); +} + +bool Image::isVisible() const { + Q_D(const Image); + return d->isVisible(); +} + +void Image::setPrinting(bool on) { + Q_D(Image); + d->m_printing = on; +} + +//############################################################################## +//###### SLOTs for changes triggered via QActions in the context menu ######## +//############################################################################## +void Image::visibilityChanged() { + Q_D(const Image); + this->setVisible(!d->isVisible()); +} + +//############################################################################## +//####################### Private implementation ############################### +//############################################################################## +ImagePrivate::ImagePrivate(Image* owner) : q(owner) { + setFlag(QGraphicsItem::ItemIsSelectable); + setFlag(QGraphicsItem::ItemIsMovable); + setFlag(QGraphicsItem::ItemSendsGeometryChanges); + setAcceptHoverEvents(true); +} + +QString ImagePrivate::name() const { + return q->name(); +} + +/*! + calculates the position and the bounding box of the label. Called on geometry or text changes. + */ +void ImagePrivate::retransform() { + if (suppressRetransform) + return; + + if (position.horizontalPosition != WorksheetElement::hPositionCustom + || position.verticalPosition != WorksheetElement::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 (!image.isNull()) { + w = image.width(); + h = image.height(); + } else { + w = Worksheet::convertToSceneUnits(2, Worksheet::Centimeter); + h = Worksheet::convertToSceneUnits(3, Worksheet::Centimeter); + } + + //depending on the alignment, calculate the new GraphicsItem's position in parent's coordinate system + QPointF itemPos; + switch (horizontalAlignment) { + case WorksheetElement::hAlignLeft: + itemPos.setX(x - w/2); + break; + case WorksheetElement::hAlignCenter: + itemPos.setX(x); + break; + case WorksheetElement::hAlignRight: + itemPos.setX(x + w/2); + break; + } + + switch (verticalAlignment) { + case WorksheetElement::vAlignTop: + itemPos.setY(y - h/2); + break; + case WorksheetElement::vAlignCenter: + itemPos.setY(y); + break; + case WorksheetElement::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); + + updateBorder(); +} + +void ImagePrivate::updateImage() { + if (!fileName.isEmpty()) + image = QImage(fileName); + else + image = QImage(); + + retransform(); +} + +/*! + calculates the position of the label, when the position relative to the parent was specified (left, right, etc.) +*/ +void ImagePrivate::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 != WorksheetElement::hPositionCustom) { + if (position.horizontalPosition == WorksheetElement::hPositionLeft) + position.point.setX( parentRect.x() ); + else if (position.horizontalPosition == WorksheetElement::hPositionCenter) + position.point.setX( parentRect.x() + parentRect.width()/2 ); + else if (position.horizontalPosition == WorksheetElement::hPositionRight) + position.point.setX( parentRect.x() + parentRect.width() ); + } + + if (position.verticalPosition != WorksheetElement::vPositionCustom) { + if (position.verticalPosition == WorksheetElement::vPositionTop) + position.point.setY( parentRect.y() ); + else if (position.verticalPosition == WorksheetElement::vPositionCenter) + position.point.setY( parentRect.y() + parentRect.height()/2 ); + else if (position.verticalPosition == WorksheetElement::vPositionBottom) + position.point.setY( parentRect.y() + parentRect.height() ); + } + + emit q->positionChanged(position); +} + +void ImagePrivate::updateBorder() { + borderShapePath = QPainterPath(); + borderShapePath.addRect(boundingRectangle); + recalcShapeAndBoundingRect(); +} + +bool ImagePrivate::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 ImagePrivate::boundingRect() const { + return transformedBoundingRectangle; +} + +/*! + Returns the shape of this item as a QPainterPath in local coordinates. +*/ +QPainterPath ImagePrivate::shape() const { + return imageShape; +} + +/*! + recalculates the outer bounds and the shape of the label. +*/ +void ImagePrivate::recalcShapeAndBoundingRect() { + prepareGeometryChange(); + + QMatrix matrix; + matrix.rotate(-rotationAngle); + imageShape = QPainterPath(); + if (borderPen.style() != Qt::NoPen) { + imageShape.addPath(WorksheetElement::shapeFromPath(borderShapePath, borderPen)); + transformedBoundingRectangle = matrix.mapRect(imageShape.boundingRect()); + } else { + imageShape.addRect(boundingRectangle); + transformedBoundingRectangle = matrix.mapRect(boundingRectangle); + } + + imageShape = matrix.map(imageShape); + + emit q->changed(); +} + +void ImagePrivate::paint(QPainter* painter, const QStyleOptionGraphicsItem* option, QWidget* widget) { + Q_UNUSED(option) + Q_UNUSED(widget) + + painter->save(); + + //draw the text + painter->rotate(-rotationAngle); + painter->setOpacity(opacity); + if (!image.isNull()) + painter->drawImage(boundingRectangle.topLeft(), image, image.rect()); + else { + painter->setPen(Qt::black); + painter->drawPath(borderShapePath); + painter->fillRect(image.rect(), QBrush(Qt::Dense6Pattern)); + } + painter->restore(); + + //draw the border + if (borderPen.style() != Qt::NoPen) { + painter->save(); + painter->rotate(-rotationAngle); + painter->setPen(borderPen); + painter->setOpacity(borderOpacity); + painter->drawPath(borderShapePath); + painter->restore(); + } + + if (m_hovered && !isSelected() && !m_printing) { + painter->setPen(QPen(QApplication::palette().color(QPalette::Shadow), 2, Qt::SolidLine)); + painter->drawPath(imageShape); + } + + if (isSelected() && !m_printing) { + painter->setPen(QPen(QApplication::palette().color(QPalette::Highlight), 2, Qt::SolidLine)); + painter->drawPath(imageShape); + } +} + +QVariant ImagePrivate::itemChange(GraphicsItemChange change, const QVariant &value) { + if (suppressItemChangeEvent) + return value; + + if (change == QGraphicsItem::ItemPositionChange) { + //convert item's center point in parent's coordinates + WorksheetElement::PositionWrapper tempPosition; + tempPosition.point = positionFromItemPosition(value.toPointF()); + tempPosition.horizontalPosition = WorksheetElement::hPositionCustom; + tempPosition.verticalPosition = WorksheetElement::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 ImagePrivate::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; + WorksheetElement::PositionWrapper tempPosition; + tempPosition.point = point; + tempPosition.horizontalPosition = WorksheetElement::hPositionCustom; + tempPosition.verticalPosition = WorksheetElement::vPositionCustom; + q->setPosition(tempPosition); + suppressRetransform = false; + } + + QGraphicsItem::mouseReleaseEvent(event); +} + +/*! + * converts label's position to GraphicsItem's position. + */ +QPointF ImagePrivate::positionFromItemPosition(QPointF itemPos) { + float x = itemPos.x(); + float y = itemPos.y(); + float w = image.width(); + float h = image.height(); + QPointF tmpPosition; + + //depending on the alignment, calculate the new position + switch (horizontalAlignment) { + case WorksheetElement::hAlignLeft: + tmpPosition.setX(x + w/2); + break; + case WorksheetElement::hAlignCenter: + tmpPosition.setX(x); + break; + case WorksheetElement::hAlignRight: + tmpPosition.setX(x - w/2); + break; + } + + switch (verticalAlignment) { + case WorksheetElement::vAlignTop: + tmpPosition.setY(y + h/2); + break; + case WorksheetElement::vAlignCenter: + tmpPosition.setY(y); + break; + case WorksheetElement::vAlignBottom: + tmpPosition.setY(y - h/2); + break; + } + + return tmpPosition; +} + +void ImagePrivate::contextMenuEvent(QGraphicsSceneContextMenuEvent* event) { + q->createContextMenu()->exec(event->screenPos()); +} + +void ImagePrivate::hoverEnterEvent(QGraphicsSceneHoverEvent*) { + if (!isSelected()) { + m_hovered = true; + emit q->hovered(); + update(); + } +} + +void ImagePrivate::hoverLeaveEvent(QGraphicsSceneHoverEvent*) { + if (m_hovered) { + m_hovered = false; + emit q->unhovered(); + update(); + } +} + +//############################################################################## +//################## Serialization/Deserialization ########################### +//############################################################################## +//! Save as XML +void Image::save(QXmlStreamWriter* writer) const { + Q_D(const Image); + + writer->writeStartElement("image"); + writeBasicAttributes(writer); + writeCommentElement(writer); + + //general + writer->writeStartElement("image"); + writer->writeAttribute("fileName", d->fileName); + writer->writeAttribute("opacity", QString::number(d->borderOpacity)); + writer->writeEndElement(); + + //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(); + + //border + writer->writeStartElement("border"); + WRITE_QPEN(d->borderPen); + writer->writeAttribute("borderOpacity", QString::number(d->borderOpacity)); + writer->writeEndElement(); + + writer->writeEndElement(); // close "image" section +} + +//! Load from XML +bool Image::load(XmlStreamReader* reader, bool preview) { + if (!readBasicAttributes(reader)) + return false; + + Q_D(Image); + KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); + QXmlStreamAttributes attribs; + QString str; + + while (!reader->atEnd()) { + reader->readNext(); + if (reader->isEndElement() && reader->name() == "image") + 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()); + + READ_INT_VALUE("horizontalPosition", position.horizontalPosition, WorksheetElement::HorizontalPosition); + READ_INT_VALUE("verticalPosition", position.verticalPosition, WorksheetElement::VerticalPosition); + READ_INT_VALUE("horizontalAlignment", horizontalAlignment, WorksheetElement::HorizontalAlignment); + READ_INT_VALUE("verticalAlignment", verticalAlignment, WorksheetElement::VerticalAlignment); + READ_DOUBLE_VALUE("rotationAngle", rotationAngle); + + str = attribs.value("visible").toString(); + if (str.isEmpty()) + reader->raiseWarning(attributeWarning.subs("visible").toString()); + else + d->setVisible(str.toInt()); + } else if (!preview && reader->name() == "border") { + attribs = reader->attributes(); + READ_QPEN(d->borderPen); + READ_DOUBLE_VALUE("borderOpacity", borderOpacity); + } else { // unknown element + reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); + if (!reader->skipToEndElement()) return false; + } + } + + if (preview) + return true; + + retransform(); + + return true; +} + +//############################################################################## +//######################### Theme management ################################## +//############################################################################## +void Image::loadThemeConfig(const KConfig& config) { + Q_UNUSED(config) +} + +void Image::saveThemeConfig(const KConfig& config) { + Q_UNUSED(config) +} diff --git a/src/backend/worksheet/TextLabel.h b/src/backend/worksheet/Image.h similarity index 52% copy from src/backend/worksheet/TextLabel.h copy to src/backend/worksheet/Image.h index 8c9e477bb..e5a8a7777 100644 --- a/src/backend/worksheet/TextLabel.h +++ b/src/backend/worksheet/Image.h @@ -1,147 +1,105 @@ /*************************************************************************** - File : TextLabel.h + File : Image.h Project : LabPlot - Description : Text label supporting reach text and latex formatting + Description : Worksheet element to draw images -------------------------------------------------------------------- - Copyright : (C) 2009 Tilman Benkert (thzs@gmx.net) - Copyright : (C) 2012-2014 Alexander Semke (alexander.semke@web.de) + Copyright : (C) 2019 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 +#ifndef IMAGE_H +#define IMAGE_H #include "backend/lib/macros.h" -#include "tools/TeXRenderer.h" #include "backend/worksheet/WorksheetElement.h" #include class QBrush; class QFont; -class TextLabelPrivate; +class ImagePrivate; -class TextLabel : public WorksheetElement { +class Image : public WorksheetElement { Q_OBJECT public: - enum Type {General, PlotTitle, AxisTitle, PlotLegendTitle}; + explicit Image(const QString& name); + ~Image() override; - 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() {} - TextWrapper(const QString& t, bool b) : text(t), teXUsed(b) {} - TextWrapper(const QString& t) : text(t) {} - - QString text; - bool teXUsed{false}; - }; - - 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&) override; void saveThemeConfig(const KConfig&) override; - CLASS_D_ACCESSOR_DECL(TextWrapper, text, Text) - BASIC_D_ACCESSOR_DECL(QColor, fontColor, FontColor) - BASIC_D_ACCESSOR_DECL(QColor, backgroundColor, BackgroundColor) - CLASS_D_ACCESSOR_DECL(QFont, teXFont, TeXFont) - CLASS_D_ACCESSOR_DECL(PositionWrapper, position, Position) + CLASS_D_ACCESSOR_DECL(QString, fileName, FileName) + BASIC_D_ACCESSOR_DECL(qreal, opacity, Opacity) + CLASS_D_ACCESSOR_DECL(WorksheetElement::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(WorksheetElement::HorizontalAlignment, horizontalAlignment, HorizontalAlignment) + BASIC_D_ACCESSOR_DECL(WorksheetElement::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; + typedef ImagePrivate 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); + ImagePrivate* const d_ptr; + Image(const QString&, ImagePrivate*); private: - Q_DECLARE_PRIVATE(TextLabel) + Q_DECLARE_PRIVATE(Image) void init(); - Type m_type; QAction* visibilityAction{nullptr}; signals: - void textWrapperChanged(const TextLabel::TextWrapper&); - void teXFontSizeChanged(const int); - void teXFontChanged(const QFont); - void fontColorChanged(const QColor); - void backgroundColorChanged(const QColor); - void positionChanged(const TextLabel::PositionWrapper&); - void horizontalAlignmentChanged(TextLabel::HorizontalAlignment); - void verticalAlignmentChanged(TextLabel::VerticalAlignment); + void fileNameChanged(const QString&); + void opacityChanged(float); + void positionChanged(const WorksheetElement::PositionWrapper&); + void horizontalAlignmentChanged(WorksheetElement::HorizontalAlignment); + void verticalAlignmentChanged(WorksheetElement::VerticalAlignment); void rotationAngleChanged(qreal); - void visibleChanged(bool); - void borderShapeChanged(TextLabel::BorderShape); void borderPenChanged(QPen&); void borderOpacityChanged(float); - - void teXImageUpdated(bool); + void visibleChanged(bool); void changed(); }; #endif diff --git a/src/backend/worksheet/ImagePrivate.h b/src/backend/worksheet/ImagePrivate.h new file mode 100644 index 000000000..500bdad96 --- /dev/null +++ b/src/backend/worksheet/ImagePrivate.h @@ -0,0 +1,96 @@ +/*************************************************************************** + File : ImagePrivate.h + Project : LabPlot + Description : Worksheet element to draw images + -------------------------------------------------------------------- + Copyright : (C) 2019 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 IMAGEPRIVATE_H +#define IMAGEPRIVATE_H + +#include + +#include "backend/worksheet/TextLabel.h" + +class QGraphicsSceneHoverEvent; + +class ImagePrivate: public QGraphicsItem { +public: + explicit ImagePrivate(Image*); + + QImage image; + QString fileName; + qreal opacity{0.0}; + qreal rotationAngle{0.0}; + + // position in parent's coordinate system, the image will be aligned around this point + TextLabel::PositionWrapper position{ + QPoint(Worksheet::convertToSceneUnits(1, Worksheet::Centimeter), + Worksheet::convertToSceneUnits(1, Worksheet::Centimeter)), + TextLabel::hPositionCenter, + TextLabel::vPositionCenter}; + + //alignement + TextLabel::HorizontalAlignment horizontalAlignment{TextLabel::hAlignCenter}; + TextLabel::VerticalAlignment verticalAlignment{TextLabel::vAlignCenter}; + + //border + QPen borderPen{Qt::black, Worksheet::convertToSceneUnits(1.0, Worksheet::Point), Qt::SolidLine}; + qreal borderOpacity{1.0}; + + QString name() const; + void retransform(); + bool swapVisible(bool on); + virtual void recalcShapeAndBoundingRect(); + void updateImage(); + void updatePosition(); + QPointF positionFromItemPosition(QPointF); + void updateBorder(); + + bool suppressItemChangeEvent{false}; + bool suppressRetransform{false}; + bool m_printing{false}; + bool m_hovered{false}; + + QRectF boundingRectangle; //bounding rectangle of the text + QRectF transformedBoundingRectangle; //bounding rectangle of transformed (rotated etc.) text + QPainterPath borderShapePath; + QPainterPath imageShape; + + //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; + + Image* const q; + +private: + void contextMenuEvent(QGraphicsSceneContextMenuEvent*) override; + void mouseReleaseEvent(QGraphicsSceneMouseEvent*) override; + void hoverEnterEvent(QGraphicsSceneHoverEvent*) override; + void hoverLeaveEvent(QGraphicsSceneHoverEvent*) override; +}; + +#endif diff --git a/src/backend/worksheet/TextLabel.cpp b/src/backend/worksheet/TextLabel.cpp index 9ae694d52..efefc0cf5 100644 --- a/src/backend/worksheet/TextLabel.cpp +++ b/src/backend/worksheet/TextLabel.cpp @@ -1,1063 +1,1063 @@ /*************************************************************************** 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) Copyright : (C) 2019 by Stefan Gerlach (stefan.gerlach@uni.kn) ***************************************************************************/ /*************************************************************************** * * * 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, AspectType::TextLabel), d_ptr(new TextLabelPrivate(this)), m_type(type) { init(); } TextLabel::TextLabel(const QString &name, TextLabelPrivate *dd, Type type) : WorksheetElement(name, AspectType::TextLabel), d_ptr(dd), m_type(type) { init(); } TextLabel::Type TextLabel::type() const { return m_type; } void TextLabel::init() { Q_D(TextLabel); QString groupName; switch (m_type) { case General: groupName = "TextLabel"; break; case PlotTitle: groupName = "PlotTitle"; break; case AxisTitle: groupName = "AxisTitle"; break; case PlotLegendTitle: groupName = "PlotLegendTitle"; break; } const KConfig config; DEBUG(" config has group \"" << groupName.toStdString() << "\": " << config.hasGroup(groupName)); // group is always valid if you call config.group(..; KConfigGroup group; if (config.hasGroup(groupName)) group = config.group(groupName); // non-default settings 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); if (m_type != PlotTitle && m_type != PlotLegendTitle) { - d->position.horizontalPosition = TextLabel::hPositionCustom; - d->position.verticalPosition = TextLabel::vPositionCustom; - d->verticalAlignment = TextLabel::vAlignCenter; + d->position.horizontalPosition = WorksheetElement::hPositionCustom; + d->position.verticalPosition = WorksheetElement::vPositionCustom; + d->verticalAlignment = WorksheetElement::vAlignCenter; } // read settings from config if group exists if (group.isValid()) { //properties common to all types d->textWrapper.teXUsed = group.readEntry("TeXUsed", d->textWrapper.teXUsed); d->teXFont.setFamily(group.readEntry("TeXFontFamily", d->teXFont.family())); d->teXFont.setPointSize(group.readEntry("TeXFontSize", d->teXFont.pointSize())); d->fontColor = group.readEntry("TeXFontColor", d->fontColor); d->backgroundColor = group.readEntry("TeXBackgroundColor", d->backgroundColor); d->rotationAngle = group.readEntry("Rotation", d->rotationAngle); //border d->borderShape = (TextLabel::BorderShape)group.readEntry("BorderShape", (int)d->borderShape); d->borderPen = QPen(group.readEntry("BorderColor", d->borderPen.color()), group.readEntry("BorderWidth", d->borderPen.width()), (Qt::PenStyle) group.readEntry("BorderStyle", (int)(d->borderPen.style()))); d->borderOpacity = group.readEntry("BorderOpacity", d->borderOpacity); //position and alignment relevant properties d->position.point.setX( group.readEntry("PositionXValue", d->position.point.x()) ); d->position.point.setY( group.readEntry("PositionYValue", d->position.point.y()) ); d->position.horizontalPosition = (HorizontalPosition) group.readEntry("PositionX", (int)d->position.horizontalPosition); d->position.verticalPosition = (VerticalPosition) group.readEntry("PositionY", (int)d->position.verticalPosition); - d->horizontalAlignment = (TextLabel::HorizontalAlignment) group.readEntry("HorizontalAlignment", (int)d->horizontalAlignment); - d->verticalAlignment = (TextLabel::VerticalAlignment) group.readEntry("VerticalAlignment", (int)d->verticalAlignment); + d->horizontalAlignment = (WorksheetElement::HorizontalAlignment) group.readEntry("HorizontalAlignment", (int)d->horizontalAlignment); + d->verticalAlignment = (WorksheetElement::VerticalAlignment) group.readEntry("VerticalAlignment", (int)d->verticalAlignment); } DEBUG("CHECK: default/run time image resolution: " << d->teXImageResolution << '/' << QApplication::desktop()->physicalDpiX()); 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, fontColor, fontColor); CLASS_SHARED_D_READER_IMPL(TextLabel, QColor, backgroundColor, backgroundColor); 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, WorksheetElement::HorizontalAlignment, horizontalAlignment, horizontalAlignment); +BASIC_SHARED_D_READER_IMPL(TextLabel, WorksheetElement::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, fontColor, updateText); void TextLabel::setFontColor(const QColor color) { Q_D(TextLabel); if (color != d->fontColor) exec(new TextLabelSetTeXFontColorCmd(d, color, ki18n("%1: set font color"))); } STD_SETTER_CMD_IMPL_F_S(TextLabel, SetTeXBackgroundColor, QColor, backgroundColor, updateText); void TextLabel::setBackgroundColor(const QColor color) { Q_D(TextLabel); if (color != d->backgroundColor) exec(new TextLabelSetTeXBackgroundColorCmd(d, color, ki18n("%1: set 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) { +void TextLabel::setHorizontalAlignment(const WorksheetElement::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); +STD_SETTER_CMD_IMPL_F_S(TextLabel, SetVerticalAlignment, WorksheetElement::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) : 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) + if (position.horizontalPosition != WorksheetElement::hPositionCustom + || position.verticalPosition != WorksheetElement::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: + case WorksheetElement::hAlignLeft: itemPos.setX(x - w/2); break; - case TextLabel::hAlignCenter: + case WorksheetElement::hAlignCenter: itemPos.setX(x); break; - case TextLabel::hAlignRight: + case WorksheetElement::hAlignRight: itemPos.setX(x + w/2); break; } switch (verticalAlignment) { - case TextLabel::vAlignTop: + case WorksheetElement::vAlignTop: itemPos.setY(y - h/2); break; - case TextLabel::vAlignCenter: + case WorksheetElement::vAlignCenter: itemPos.setY(y); break; - case TextLabel::vAlignBottom: + case WorksheetElement::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); 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) + if (position.horizontalPosition != WorksheetElement::hPositionCustom) { + if (position.horizontalPosition == WorksheetElement::hPositionLeft) position.point.setX( parentRect.x() ); - else if (position.horizontalPosition == TextLabel::hPositionCenter) + else if (position.horizontalPosition == WorksheetElement::hPositionCenter) position.point.setX( parentRect.x() + parentRect.width()/2 ); - else if (position.horizontalPosition == TextLabel::hPositionRight) + else if (position.horizontalPosition == WorksheetElement::hPositionRight) position.point.setX( parentRect.x() + parentRect.width() ); } - if (position.verticalPosition != TextLabel::vPositionCustom) { - if (position.verticalPosition == TextLabel::vPositionTop) + if (position.verticalPosition != WorksheetElement::vPositionCustom) { + if (position.verticalPosition == WorksheetElement::vPositionTop) position.point.setY( parentRect.y() ); - else if (position.verticalPosition == TextLabel::vPositionCenter) + else if (position.verticalPosition == WorksheetElement::vPositionCenter) position.point.setY( parentRect.y() + parentRect.height()/2 ); - else if (position.verticalPosition == TextLabel::vPositionBottom) + else if (position.verticalPosition == WorksheetElement::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 = fontColor; format.backgroundColor = backgroundColor; 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); labelShape = QPainterPath(); if (borderShape != TextLabel::NoBorder) { labelShape.addPath(WorksheetElement::shapeFromPath(borderShapePath, borderPen)); transformedBoundingRectangle = matrix.mapRect(labelShape.boundingRect()); } else { labelShape.addRect(boundingRectangle); transformedBoundingRectangle = matrix.mapRect(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(); //draw the text painter->rotate(-rotationAngle); if (textWrapper.teXUsed) { if (boundingRect().width() != 0.0 && boundingRect().height() != 0.0) painter->drawImage(boundingRect(), teXImage); } else { painter->setPen(fontColor); painter->scale(scaleFactor, scaleFactor); float w = staticText.size().width(); float h = staticText.size().height(); painter->drawStaticText(QPoint(-w/2,-h/2), staticText); } painter->restore(); //draw the border if (borderShape != TextLabel::NoBorder) { painter->save(); painter->rotate(-rotationAngle); painter->setPen(borderPen); painter->setOpacity(borderOpacity); painter->drawPath(borderShapePath); painter->restore(); } if (m_hovered && !isSelected() && !m_printing) { painter->setPen(QPen(QApplication::palette().color(QPalette::Shadow), 2, Qt::SolidLine)); painter->drawPath(labelShape); } if (isSelected() && !m_printing) { painter->setPen(QPen(QApplication::palette().color(QPalette::Highlight), 2, Qt::SolidLine)); painter->drawPath(labelShape); } } 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; + tempPosition.horizontalPosition = WorksheetElement::hPositionCustom; + tempPosition.verticalPosition = WorksheetElement::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: + case WorksheetElement::hAlignLeft: tmpPosition.setX(x + w/2); break; - case TextLabel::hAlignCenter: + case WorksheetElement::hAlignCenter: tmpPosition.setX(x); break; - case TextLabel::hAlignRight: + case WorksheetElement::hAlignRight: tmpPosition.setX(x - w/2); break; } switch (verticalAlignment) { - case TextLabel::vAlignTop: + case WorksheetElement::vAlignTop: tmpPosition.setY(y + h/2); break; - case TextLabel::vAlignCenter: + case WorksheetElement::vAlignCenter: tmpPosition.setY(y); break; - case TextLabel::vAlignBottom: + case WorksheetElement::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( "fontColor_r", QString::number(d->fontColor.red()) ); writer->writeAttribute( "fontColor_g", QString::number(d->fontColor.green()) ); writer->writeAttribute( "fontColor_b", QString::number(d->fontColor.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()); - READ_INT_VALUE("horizontalPosition", position.horizontalPosition, TextLabel::HorizontalPosition); - READ_INT_VALUE("verticalPosition", position.verticalPosition, TextLabel::VerticalPosition); - READ_INT_VALUE("horizontalAlignment", horizontalAlignment, TextLabel::HorizontalAlignment); - READ_INT_VALUE("verticalAlignment", verticalAlignment, TextLabel::VerticalAlignment); + READ_INT_VALUE("horizontalPosition", position.horizontalPosition, WorksheetElement::HorizontalPosition); + READ_INT_VALUE("verticalPosition", position.verticalPosition, WorksheetElement::VerticalPosition); + READ_INT_VALUE("horizontalAlignment", horizontalAlignment, WorksheetElement::HorizontalAlignment); + READ_INT_VALUE("verticalAlignment", verticalAlignment, WorksheetElement::VerticalAlignment); READ_DOUBLE_VALUE("rotationAngle", rotationAngle); 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(); READ_INT_VALUE("teXUsed", textWrapper.teXUsed, bool); READ_QFONT(d->teXFont); str = attribs.value("fontColor_r").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.subs("fontColor_r").toString()); else d->fontColor.setRed( str.toInt() ); str = attribs.value("fontColor_g").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.subs("fontColor_g").toString()); else d->fontColor.setGreen( str.toInt() ); str = attribs.value("fontColor_b").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.subs("fontColor_b").toString()); else d->fontColor.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"); d->fontColor = group.readEntry("FontColor", QColor(Qt::white)); d->backgroundColor = group.readEntry("BackgroundColor", QColor(Qt::black)); 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->updateText(); } void TextLabel::saveThemeConfig(const KConfig& config) { KConfigGroup group = config.group("Label"); //TODO // group.writeEntry("TeXFontColor", (QColor) this->fontColor()); } diff --git a/src/backend/worksheet/TextLabel.h b/src/backend/worksheet/TextLabel.h index 8c9e477bb..15261653a 100644 --- a/src/backend/worksheet/TextLabel.h +++ b/src/backend/worksheet/TextLabel.h @@ -1,147 +1,135 @@ /*************************************************************************** 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 class QBrush; class QFont; 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() {} TextWrapper(const QString& t, bool b) : text(t), teXUsed(b) {} TextWrapper(const QString& t) : text(t) {} QString text; bool teXUsed{false}; }; - 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&) override; void saveThemeConfig(const KConfig&) override; CLASS_D_ACCESSOR_DECL(TextWrapper, text, Text) BASIC_D_ACCESSOR_DECL(QColor, fontColor, FontColor) BASIC_D_ACCESSOR_DECL(QColor, backgroundColor, BackgroundColor) CLASS_D_ACCESSOR_DECL(QFont, teXFont, TeXFont) - CLASS_D_ACCESSOR_DECL(PositionWrapper, position, Position) + CLASS_D_ACCESSOR_DECL(WorksheetElement::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(WorksheetElement::HorizontalAlignment, horizontalAlignment, HorizontalAlignment) + BASIC_D_ACCESSOR_DECL(WorksheetElement::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{nullptr}; signals: void textWrapperChanged(const TextLabel::TextWrapper&); void teXFontSizeChanged(const int); void teXFontChanged(const QFont); void fontColorChanged(const QColor); void backgroundColorChanged(const QColor); - void positionChanged(const TextLabel::PositionWrapper&); - void horizontalAlignmentChanged(TextLabel::HorizontalAlignment); - void verticalAlignmentChanged(TextLabel::VerticalAlignment); + void positionChanged(const WorksheetElement::PositionWrapper&); + void horizontalAlignmentChanged(WorksheetElement::HorizontalAlignment); + void verticalAlignmentChanged(WorksheetElement::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/Worksheet.cpp b/src/backend/worksheet/Worksheet.cpp index 9b1bdab78..44d0650ae 100644 --- a/src/backend/worksheet/Worksheet.cpp +++ b/src/backend/worksheet/Worksheet.cpp @@ -1,1566 +1,1574 @@ /*************************************************************************** File : Worksheet.cpp Project : LabPlot Description : Worksheet -------------------------------------------------------------------- Copyright : (C) 2009 Tilman Benkert (thzs@gmx.net) Copyright : (C) 2011-2019 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 * * * ***************************************************************************/ #include "Worksheet.h" #include "WorksheetPrivate.h" #include "WorksheetElement.h" #include "commonfrontend/worksheet/WorksheetView.h" #include "backend/core/Project.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" +#include "backend/worksheet/Image.h" #include "backend/worksheet/TreeModel.h" #include "backend/worksheet/TextLabel.h" #include "backend/lib/commandtemplates.h" #include "backend/lib/XmlStreamReader.h" #include "kdefrontend/worksheet/ExportWorksheetDialog.h" #include "kdefrontend/ThemeHandler.h" #include #include #include #include #include #include #include #include #include #include #include /** * \class Worksheet * \brief Top-level container for worksheet elements like plot, labels, etc. * * The worksheet is, besides the data containers \c Spreadsheet and \c Matrix, another central part of the application * and provides an area for showing and grouping together different kinds of worksheet objects - plots, labels &etc; * * * \ingroup worksheet */ Worksheet::Worksheet(const QString& name, bool loading) : AbstractPart(name, AspectType::Worksheet), d(new WorksheetPrivate(this)) { connect(this, &Worksheet::aspectAdded, this, &Worksheet::handleAspectAdded); connect(this, &Worksheet::aspectAboutToBeRemoved, this, &Worksheet::handleAspectAboutToBeRemoved); connect(this, &Worksheet::aspectRemoved, this, &Worksheet::handleAspectRemoved); if (!loading) init(); } Worksheet::~Worksheet() { delete d; } void Worksheet::init() { KConfig config; KConfigGroup group = config.group("Worksheet"); //size d->scaleContent = group.readEntry("ScaleContent", false); d->useViewSize = group.readEntry("UseViewSize", false); d->pageRect.setX(0); d->pageRect.setY(0); d->pageRect.setWidth(group.readEntry("Width", 1000)); d->pageRect.setHeight(group.readEntry("Height", 1000)); d->m_scene->setSceneRect(d->pageRect); //background d->backgroundType = (PlotArea::BackgroundType) group.readEntry("BackgroundType", (int) PlotArea::Color); d->backgroundColorStyle = (PlotArea::BackgroundColorStyle) group.readEntry("BackgroundColorStyle", (int) PlotArea::SingleColor); d->backgroundImageStyle = (PlotArea::BackgroundImageStyle) group.readEntry("BackgroundImageStyle", (int) PlotArea::Scaled); d->backgroundBrushStyle = (Qt::BrushStyle) group.readEntry("BackgroundBrushStyle", (int) Qt::SolidPattern); d->backgroundFileName = group.readEntry("BackgroundFileName", QString()); d->backgroundFirstColor = group.readEntry("BackgroundFirstColor", QColor(Qt::white)); d->backgroundSecondColor = group.readEntry("BackgroundSecondColor", QColor(Qt::black)); d->backgroundOpacity = group.readEntry("BackgroundOpacity", 1.0); //layout d->layout = (Worksheet::Layout) group.readEntry("Layout", (int) Worksheet::VerticalLayout); d->layoutTopMargin = group.readEntry("LayoutTopMargin", convertToSceneUnits(1, Centimeter)); d->layoutBottomMargin = group.readEntry("LayoutBottomMargin", convertToSceneUnits(1, Centimeter)); d->layoutLeftMargin = group.readEntry("LayoutLeftMargin", convertToSceneUnits(1, Centimeter)); d->layoutRightMargin = group.readEntry("LayoutRightMargin", convertToSceneUnits(1, Centimeter)); d->layoutVerticalSpacing = group.readEntry("LayoutVerticalSpacing", convertToSceneUnits(1, Centimeter)); d->layoutHorizontalSpacing = group.readEntry("LayoutHorizontalSpacing", convertToSceneUnits(1, Centimeter)); d->layoutRowCount = group.readEntry("LayoutRowCount", 2); d->layoutColumnCount = group.readEntry("LayoutColumnCount", 2); //default theme KConfigGroup settings = KSharedConfig::openConfig()->group(QLatin1String("Settings_Worksheet")); d->theme = settings.readEntry(QStringLiteral("Theme"), QString()); if (!d->theme.isEmpty()) loadTheme(d->theme); } /*! converts from \c unit to the scene units. At the moment, 1 scene unit corresponds to 1/10 mm. */ float Worksheet::convertToSceneUnits(const float value, const Worksheet::Unit unit) { switch (unit) { case Worksheet::Millimeter: return value*10.0; case Worksheet::Centimeter: return value*100.0; case Worksheet::Inch: return value*25.4*10.; case Worksheet::Point: return value*25.4/72.*10.; } return 0; } /*! converts from the scene units to \c unit . At the moment, 1 scene unit corresponds to 1/10 mm. */ float Worksheet::convertFromSceneUnits(const float value, const Worksheet::Unit unit) { switch (unit) { case Worksheet::Millimeter: return value/10.0; case Worksheet::Centimeter: return value/100.0; case Worksheet::Inch: return value/25.4/10.; case Worksheet::Point: return value/25.4/10.*72.; } return 0; } QIcon Worksheet::icon() const { return QIcon::fromTheme("labplot-worksheet"); } /** * Return a new context menu. The caller takes ownership of the menu. */ QMenu* Worksheet::createContextMenu() { QMenu* menu = AbstractPart::createContextMenu(); Q_ASSERT(menu); emit requestProjectContextMenu(menu); return menu; } //! Construct a primary view on me. /** * This method may be called multiple times during the life time of an Aspect, or it might not get * called at all. Aspects must not depend on the existence of a view for their operation. */ QWidget* Worksheet::view() const { if (!m_partView) { m_view = new WorksheetView(const_cast(this)); m_partView = m_view; connect(m_view, &WorksheetView::statusInfo, this, &Worksheet::statusInfo); connect(this, &Worksheet::cartesianPlotMouseModeChanged, m_view, &WorksheetView::cartesianPlotMouseModeChangedSlot); } return m_partView; } /*! * returns the list of all parent aspects (folders and sub-folders) * together with all the data containers required to plot the data in the worksheet */ QVector Worksheet::dependsOn() const { //add all parent aspects (folders and sub-folders) QVector aspects = AbstractAspect::dependsOn(); //traverse all plots and add all data containers they depend on for (const auto* plot : children()) aspects << plot->dependsOn(); return aspects; } bool Worksheet::exportView() const { auto* dlg = new ExportWorksheetDialog(m_view); dlg->setFileName(name()); bool ret; if ( (ret = (dlg->exec() == QDialog::Accepted)) ) { QString path = dlg->path(); const WorksheetView::ExportFormat format = dlg->exportFormat(); const WorksheetView::ExportArea area = dlg->exportArea(); const bool background = dlg->exportBackground(); const int resolution = dlg->exportResolution(); WAIT_CURSOR; m_view->exportToFile(path, format, area, background, resolution); RESET_CURSOR; } delete dlg; return ret; } bool Worksheet::printView() { QPrinter printer; auto* dlg = new QPrintDialog(&printer, m_view); dlg->setWindowTitle(i18nc("@title:window", "Print Worksheet")); bool ret; if ( (ret = (dlg->exec() == QDialog::Accepted)) ) m_view->print(&printer); delete dlg; return ret; } bool Worksheet::printPreview() const { auto* dlg = new QPrintPreviewDialog(m_view); connect(dlg, &QPrintPreviewDialog::paintRequested, m_view, &WorksheetView::print); return dlg->exec(); } void Worksheet::handleAspectAdded(const AbstractAspect* aspect) { const auto* addedElement = qobject_cast(aspect); if (!addedElement) return; if (aspect->parentAspect() != this) return; //add the GraphicsItem of the added child to the scene QGraphicsItem* item = addedElement->graphicsItem(); d->m_scene->addItem(item); const CartesianPlot* plot = dynamic_cast(aspect); if (plot) { connect(plot, &CartesianPlot::mouseMoveCursorModeSignal, this, &Worksheet::cartesianPlotMouseMoveCursorMode); connect(plot, &CartesianPlot::mouseMoveZoomSelectionModeSignal, this, &Worksheet::cartesianPlotMouseMoveZoomSelectionMode); connect(plot, &CartesianPlot::mousePressCursorModeSignal, this, &Worksheet::cartesianPlotMousePressCursorMode); connect(plot, &CartesianPlot::mousePressZoomSelectionModeSignal, this, &Worksheet::cartesianPlotMousePressZoomSelectionMode); connect(plot, &CartesianPlot::mouseReleaseZoomSelectionModeSignal, this, &Worksheet::cartesianPlotMouseReleaseZoomSelectionMode); connect(plot, &CartesianPlot::mouseHoverZoomSelectionModeSignal, this, &Worksheet::cartesianPlotMouseHoverZoomSelectionMode); connect(plot, &CartesianPlot::mouseHoverOutsideDataRectSignal, this, &Worksheet::cartesianPlotMouseHoverOutsideDataRect); connect(plot, &CartesianPlot::aspectDescriptionChanged, this, &Worksheet::updateCompleteCursorTreeModel); connect(plot, &CartesianPlot::curveNameChanged, this, &Worksheet::updateCompleteCursorTreeModel); connect(plot, &CartesianPlot::curveRemoved, this, &Worksheet::curveRemoved); connect(plot, &CartesianPlot::curveAdded, this, &Worksheet::curveAdded); connect(plot, &CartesianPlot::visibleChanged, this, &Worksheet::updateCompleteCursorTreeModel); connect(plot, &CartesianPlot::curveVisibilityChangedSignal, this, &Worksheet::updateCompleteCursorTreeModel); connect(plot, &CartesianPlot::curveDataChanged, this, &Worksheet::curveDataChanged); connect(plot, static_cast(&CartesianPlot::curveLinePenChanged), this, &Worksheet::updateCurveBackground); connect(plot, &CartesianPlot::mouseModeChanged, this, &Worksheet::cartesianPlotMouseModeChangedSlot); auto* p = const_cast(plot); p->setLocked(d->plotsLocked); cursorModelPlotAdded(p->name()); } qreal zVal = 0; for (auto* child : children(IncludeHidden)) child->graphicsItem()->setZValue(zVal++); //if a theme was selected in the worksheet, apply this theme for newly added children if (!d->theme.isEmpty() && !isLoading()) { KConfig config(ThemeHandler::themeFilePath(d->theme), KConfig::SimpleConfig); const_cast(addedElement)->loadThemeConfig(config); } //recalculated the layout if (!isLoading()) { if (d->layout != Worksheet::NoLayout) d->updateLayout(false); } } void Worksheet::handleAspectAboutToBeRemoved(const AbstractAspect* aspect) { const auto* removedElement = qobject_cast(aspect); if (removedElement) { QGraphicsItem* item = removedElement->graphicsItem(); d->m_scene->removeItem(item); } } void Worksheet::handleAspectRemoved(const AbstractAspect* parent, const AbstractAspect* before, const AbstractAspect* child) { Q_UNUSED(parent); Q_UNUSED(before); if (d->layout != Worksheet::NoLayout) d->updateLayout(false); auto* plot = dynamic_cast(child); if (plot) cursorModelPlotRemoved(plot->name()); } QGraphicsScene* Worksheet::scene() const { return d->m_scene; } QRectF Worksheet::pageRect() const { return d->m_scene->sceneRect(); } /*! this slot is called when a worksheet element is selected in the project explorer. emits \c itemSelected() which forwards this event to the \c WorksheetView in order to select the corresponding \c QGraphicsItem. */ void Worksheet::childSelected(const AbstractAspect* aspect) { auto* element = qobject_cast(const_cast(aspect)); if (element) emit itemSelected(element->graphicsItem()); } /*! this slot is called when a worksheet element is deselected in the project explorer. emits \c itemDeselected() which forwards this event to \c WorksheetView in order to deselect the corresponding \c QGraphicsItem. */ void Worksheet::childDeselected(const AbstractAspect* aspect) { auto* element = qobject_cast(const_cast(aspect)); if (element) emit itemDeselected(element->graphicsItem()); } /*! * Emits the signal to select or to deselect the aspect corresponding to \c QGraphicsItem \c item in the project explorer, * if \c selected=true or \c selected=false, respectively. * The signal is handled in \c AspectTreeModel and forwarded to the tree view in \c ProjectExplorer. * This function is called in \c WorksheetView upon selection changes. */ void Worksheet::setItemSelectedInView(const QGraphicsItem* item, const bool b) { //determine the corresponding aspect const AbstractAspect* aspect(nullptr); for (const auto* child : children(IncludeHidden) ) { aspect = this->aspectFromGraphicsItem(child, item); if (aspect) break; } if (!aspect) return; //forward selection/deselection to AbstractTreeModel if (b) emit childAspectSelectedInView(aspect); else emit childAspectDeselectedInView(aspect); } /*! * helper function: checks whether \c aspect or one of its children has the \c GraphicsItem \c item * Returns a pointer to \c WorksheetElement having this item. */ WorksheetElement* Worksheet::aspectFromGraphicsItem(const WorksheetElement* aspect, const QGraphicsItem* item) const { if ( aspect->graphicsItem() == item ) return const_cast(aspect); else { for (const auto* child : aspect->children(AbstractAspect::IncludeHidden) ) { WorksheetElement* a = this->aspectFromGraphicsItem(child, item); if (a) return a; } return nullptr; } } /*! Selects or deselects the worksheet in the project explorer. This function is called in \c WorksheetView. The worksheet gets deselected if there are selected items in the view, and selected if there are no selected items in the view. */ void Worksheet::setSelectedInView(const bool b) { if (b) emit childAspectSelectedInView(this); else emit childAspectDeselectedInView(this); } void Worksheet::deleteAspectFromGraphicsItem(const QGraphicsItem* item) { Q_ASSERT(item); //determine the corresponding aspect AbstractAspect* aspect(nullptr); for (const auto* child : children(IncludeHidden) ) { aspect = this->aspectFromGraphicsItem(child, item); if (aspect) break; } if (!aspect) return; if (aspect->parentAspect()) aspect->parentAspect()->removeChild(aspect); else this->removeChild(aspect); } void Worksheet::setIsClosing() { if (m_view) m_view->setIsClosing(); } /*! * \brief Worksheet::getPlotCount * \return number of CartesianPlot's in the Worksheet */ int Worksheet::getPlotCount() { return children().length(); } /*! * \brief Worksheet::getPlot * \param index Number of plot which should be returned * \return Pointer to the CartesianPlot which was searched with index */ WorksheetElement *Worksheet::getPlot(int index) { QVector cartesianPlots = children(); if (cartesianPlots.length()-1 >= index) return cartesianPlots[index]; return nullptr; } TreeModel* Worksheet::cursorModel() { return d->cursorData; } void Worksheet::update() { emit requestUpdate(); } void Worksheet::setSuppressLayoutUpdate(bool value) { d->suppressLayoutUpdate = value; } void Worksheet::updateLayout() { d->updateLayout(); } Worksheet::CartesianPlotActionMode Worksheet::cartesianPlotActionMode() { return d->cartesianPlotActionMode; } Worksheet::CartesianPlotActionMode Worksheet::cartesianPlotCursorMode() { return d->cartesianPlotCursorMode; } bool Worksheet::plotsLocked() { return d->plotsLocked; } void Worksheet::setCartesianPlotActionMode(Worksheet::CartesianPlotActionMode mode) { if (d->cartesianPlotActionMode == mode) return; d->cartesianPlotActionMode = mode; project()->setChanged(true); } void Worksheet::setCartesianPlotCursorMode(Worksheet::CartesianPlotActionMode mode) { if (d->cartesianPlotCursorMode == mode) return; d->cartesianPlotCursorMode = mode; if (mode == Worksheet::CartesianPlotActionMode::ApplyActionToAll) { d->suppressCursorPosChanged = true; QVector plots = children(); QPointF logicPos; if (!plots.isEmpty()) { for (int i = 0; i < 2; i++) { logicPos = QPointF(plots[0]->cursorPos(i), 0); // y value does not matter cartesianPlotMousePressCursorMode(i, logicPos); } } d->suppressCursorPosChanged = false; } updateCompleteCursorTreeModel(); project()->setChanged(true); } void Worksheet::setPlotsLocked(bool lock) { if (d->plotsLocked == lock) return; d->plotsLocked = lock; for (auto* plot: children()) plot->setLocked(lock); project()->setChanged(true); } void Worksheet::registerShortcuts() { m_view->registerShortcuts(); } void Worksheet::unregisterShortcuts() { m_view->unregisterShortcuts(); } /* =============================== getter methods for general options ==================================== */ BASIC_D_READER_IMPL(Worksheet, bool, scaleContent, scaleContent) BASIC_D_READER_IMPL(Worksheet, bool, useViewSize, useViewSize) /* =============================== getter methods for background options ================================= */ BASIC_D_READER_IMPL(Worksheet, PlotArea::BackgroundType, backgroundType, backgroundType) BASIC_D_READER_IMPL(Worksheet, PlotArea::BackgroundColorStyle, backgroundColorStyle, backgroundColorStyle) BASIC_D_READER_IMPL(Worksheet, PlotArea::BackgroundImageStyle, backgroundImageStyle, backgroundImageStyle) BASIC_D_READER_IMPL(Worksheet, Qt::BrushStyle, backgroundBrushStyle, backgroundBrushStyle) CLASS_D_READER_IMPL(Worksheet, QColor, backgroundFirstColor, backgroundFirstColor) CLASS_D_READER_IMPL(Worksheet, QColor, backgroundSecondColor, backgroundSecondColor) CLASS_D_READER_IMPL(Worksheet, QString, backgroundFileName, backgroundFileName) BASIC_D_READER_IMPL(Worksheet, float, backgroundOpacity, backgroundOpacity) /* =============================== getter methods for layout options ====================================== */ BASIC_D_READER_IMPL(Worksheet, Worksheet::Layout, layout, layout) BASIC_D_READER_IMPL(Worksheet, float, layoutTopMargin, layoutTopMargin) BASIC_D_READER_IMPL(Worksheet, float, layoutBottomMargin, layoutBottomMargin) BASIC_D_READER_IMPL(Worksheet, float, layoutLeftMargin, layoutLeftMargin) BASIC_D_READER_IMPL(Worksheet, float, layoutRightMargin, layoutRightMargin) BASIC_D_READER_IMPL(Worksheet, float, layoutHorizontalSpacing, layoutHorizontalSpacing) BASIC_D_READER_IMPL(Worksheet, float, layoutVerticalSpacing, layoutVerticalSpacing) BASIC_D_READER_IMPL(Worksheet, int, layoutRowCount, layoutRowCount) BASIC_D_READER_IMPL(Worksheet, int, layoutColumnCount, layoutColumnCount) CLASS_D_READER_IMPL(Worksheet, QString, theme, theme) /* ============================ setter methods and undo commands for general options ===================== */ void Worksheet::setUseViewSize(bool useViewSize) { if (useViewSize != d->useViewSize) { d->useViewSize = useViewSize; emit useViewSizeRequested(); } } STD_SETTER_CMD_IMPL_S(Worksheet, SetScaleContent, bool, scaleContent) void Worksheet::setScaleContent(bool scaleContent) { if (scaleContent != d->scaleContent) exec(new WorksheetSetScaleContentCmd(d, scaleContent, ki18n("%1: change \"rescale the content\" property"))); } /* ============================ setter methods and undo commands for background options ================= */ STD_SETTER_CMD_IMPL_F_S(Worksheet, SetBackgroundType, PlotArea::BackgroundType, backgroundType, update) void Worksheet::setBackgroundType(PlotArea::BackgroundType type) { if (type != d->backgroundType) exec(new WorksheetSetBackgroundTypeCmd(d, type, ki18n("%1: background type changed"))); } STD_SETTER_CMD_IMPL_F_S(Worksheet, SetBackgroundColorStyle, PlotArea::BackgroundColorStyle, backgroundColorStyle, update) void Worksheet::setBackgroundColorStyle(PlotArea::BackgroundColorStyle style) { if (style != d->backgroundColorStyle) exec(new WorksheetSetBackgroundColorStyleCmd(d, style, ki18n("%1: background color style changed"))); } STD_SETTER_CMD_IMPL_F_S(Worksheet, SetBackgroundImageStyle, PlotArea::BackgroundImageStyle, backgroundImageStyle, update) void Worksheet::setBackgroundImageStyle(PlotArea::BackgroundImageStyle style) { if (style != d->backgroundImageStyle) exec(new WorksheetSetBackgroundImageStyleCmd(d, style, ki18n("%1: background image style changed"))); } STD_SETTER_CMD_IMPL_F_S(Worksheet, SetBackgroundBrushStyle, Qt::BrushStyle, backgroundBrushStyle, update) void Worksheet::setBackgroundBrushStyle(Qt::BrushStyle style) { if (style != d->backgroundBrushStyle) exec(new WorksheetSetBackgroundBrushStyleCmd(d, style, ki18n("%1: background brush style changed"))); } STD_SETTER_CMD_IMPL_F_S(Worksheet, SetBackgroundFirstColor, QColor, backgroundFirstColor, update) void Worksheet::setBackgroundFirstColor(const QColor &color) { if (color!= d->backgroundFirstColor) exec(new WorksheetSetBackgroundFirstColorCmd(d, color, ki18n("%1: set background first color"))); } STD_SETTER_CMD_IMPL_F_S(Worksheet, SetBackgroundSecondColor, QColor, backgroundSecondColor, update) void Worksheet::setBackgroundSecondColor(const QColor &color) { if (color!= d->backgroundSecondColor) exec(new WorksheetSetBackgroundSecondColorCmd(d, color, ki18n("%1: set background second color"))); } STD_SETTER_CMD_IMPL_F_S(Worksheet, SetBackgroundFileName, QString, backgroundFileName, update) void Worksheet::setBackgroundFileName(const QString& fileName) { if (fileName!= d->backgroundFileName) exec(new WorksheetSetBackgroundFileNameCmd(d, fileName, ki18n("%1: set background image"))); } STD_SETTER_CMD_IMPL_F_S(Worksheet, SetBackgroundOpacity, float, backgroundOpacity, update) void Worksheet::setBackgroundOpacity(float opacity) { if (opacity != d->backgroundOpacity) exec(new WorksheetSetBackgroundOpacityCmd(d, opacity, ki18n("%1: set opacity"))); } /* ============================ setter methods and undo commands for layout options ================= */ STD_SETTER_CMD_IMPL_F_S(Worksheet, SetLayout, Worksheet::Layout, layout, updateLayout) void Worksheet::setLayout(Worksheet::Layout layout) { if (layout != d->layout) { beginMacro(i18n("%1: set layout", name())); exec(new WorksheetSetLayoutCmd(d, layout, ki18n("%1: set layout"))); endMacro(); } } STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutTopMargin, float, layoutTopMargin, updateLayout) void Worksheet::setLayoutTopMargin(float margin) { if (margin != d->layoutTopMargin) { beginMacro(i18n("%1: set layout top margin", name())); exec(new WorksheetSetLayoutTopMarginCmd(d, margin, ki18n("%1: set layout top margin"))); endMacro(); } } STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutBottomMargin, float, layoutBottomMargin, updateLayout) void Worksheet::setLayoutBottomMargin(float margin) { if (margin != d->layoutBottomMargin) { beginMacro(i18n("%1: set layout bottom margin", name())); exec(new WorksheetSetLayoutBottomMarginCmd(d, margin, ki18n("%1: set layout bottom margin"))); endMacro(); } } STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutLeftMargin, float, layoutLeftMargin, updateLayout) void Worksheet::setLayoutLeftMargin(float margin) { if (margin != d->layoutLeftMargin) { beginMacro(i18n("%1: set layout left margin", name())); exec(new WorksheetSetLayoutLeftMarginCmd(d, margin, ki18n("%1: set layout left margin"))); endMacro(); } } STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutRightMargin, float, layoutRightMargin, updateLayout) void Worksheet::setLayoutRightMargin(float margin) { if (margin != d->layoutRightMargin) { beginMacro(i18n("%1: set layout right margin", name())); exec(new WorksheetSetLayoutRightMarginCmd(d, margin, ki18n("%1: set layout right margin"))); endMacro(); } } STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutVerticalSpacing, float, layoutVerticalSpacing, updateLayout) void Worksheet::setLayoutVerticalSpacing(float spacing) { if (spacing != d->layoutVerticalSpacing) { beginMacro(i18n("%1: set layout vertical spacing", name())); exec(new WorksheetSetLayoutVerticalSpacingCmd(d, spacing, ki18n("%1: set layout vertical spacing"))); endMacro(); } } STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutHorizontalSpacing, float, layoutHorizontalSpacing, updateLayout) void Worksheet::setLayoutHorizontalSpacing(float spacing) { if (spacing != d->layoutHorizontalSpacing) { beginMacro(i18n("%1: set layout horizontal spacing", name())); exec(new WorksheetSetLayoutHorizontalSpacingCmd(d, spacing, ki18n("%1: set layout horizontal spacing"))); endMacro(); } } STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutRowCount, int, layoutRowCount, updateLayout) void Worksheet::setLayoutRowCount(int count) { if (count != d->layoutRowCount) { beginMacro(i18n("%1: set layout row count", name())); exec(new WorksheetSetLayoutRowCountCmd(d, count, ki18n("%1: set layout row count"))); endMacro(); } } STD_SETTER_CMD_IMPL_M_F_S(Worksheet, SetLayoutColumnCount, int, layoutColumnCount, updateLayout) void Worksheet::setLayoutColumnCount(int count) { if (count != d->layoutColumnCount) { beginMacro(i18n("%1: set layout column count", name())); exec(new WorksheetSetLayoutColumnCountCmd(d, count, ki18n("%1: set layout column count"))); endMacro(); } } class WorksheetSetPageRectCmd : public StandardMacroSetterCmd { public: WorksheetSetPageRectCmd(Worksheet::Private* target, QRectF newValue, const KLocalizedString& description) : StandardMacroSetterCmd(target, &Worksheet::Private::pageRect, newValue, description) {} void finalize() override { m_target->updatePageRect(); emit m_target->q->pageRectChanged(m_target->*m_field); } void finalizeUndo() override { m_target->m_scene->setSceneRect(m_target->*m_field); emit m_target->q->pageRectChanged(m_target->*m_field); } }; void Worksheet::setPageRect(const QRectF& rect) { //don't allow any rectangulars of width/height equal to zero if (qFuzzyCompare(rect.width(), 0.) || qFuzzyCompare(rect.height(), 0.)) { emit pageRectChanged(d->pageRect); return; } if (rect != d->pageRect) { if (!d->useViewSize) { beginMacro(i18n("%1: set page size", name())); exec(new WorksheetSetPageRectCmd(d, rect, ki18n("%1: set page size"))); endMacro(); } else { d->pageRect = rect; d->updatePageRect(); emit pageRectChanged(d->pageRect); } } } void Worksheet::setPrinting(bool on) const { QVector childElements = children(AbstractAspect::Recursive | AbstractAspect::IncludeHidden); for (auto* child : childElements) child->setPrinting(on); } STD_SETTER_CMD_IMPL_S(Worksheet, SetTheme, QString, theme) void Worksheet::setTheme(const QString& theme) { if (theme != d->theme) { if (!theme.isEmpty()) { beginMacro( i18n("%1: load theme %2", name(), theme) ); exec(new WorksheetSetThemeCmd(d, theme, ki18n("%1: set theme"))); loadTheme(theme); endMacro(); } else { exec(new WorksheetSetThemeCmd(d, theme, ki18n("%1: disable theming"))); } } } void Worksheet::cartesianPlotMousePressZoomSelectionMode(QPointF logicPos) { if (cartesianPlotActionMode() == Worksheet::ApplyActionToAll) { auto plots = children(AbstractAspect::Recursive | AbstractAspect::IncludeHidden); for (auto* plot : plots) plot->mousePressZoomSelectionMode(logicPos); } else { CartesianPlot* plot = static_cast(QObject::sender()); plot->mousePressZoomSelectionMode(logicPos); } } void Worksheet::cartesianPlotMouseReleaseZoomSelectionMode() { if (cartesianPlotActionMode() == Worksheet::ApplyActionToAll) { auto plots = children(AbstractAspect::Recursive | AbstractAspect::IncludeHidden); for (auto* plot : plots) plot->mouseReleaseZoomSelectionMode(); } else { CartesianPlot* plot = static_cast(QObject::sender()); plot->mouseReleaseZoomSelectionMode(); } } void Worksheet::cartesianPlotMousePressCursorMode(int cursorNumber, QPointF logicPos) { if (cartesianPlotCursorMode() == Worksheet::ApplyActionToAll) { auto plots = children(AbstractAspect::Recursive | AbstractAspect::IncludeHidden); for (auto* plot : plots) plot->mousePressCursorMode(cursorNumber, logicPos); } else { CartesianPlot* plot = static_cast(QObject::sender()); plot->mousePressCursorMode(cursorNumber, logicPos); } cursorPosChanged(cursorNumber, logicPos.x()); } void Worksheet::cartesianPlotMouseMoveZoomSelectionMode(QPointF logicPos) { if (cartesianPlotActionMode() == Worksheet::ApplyActionToAll) { QVector plots = children(AbstractAspect::Recursive | AbstractAspect::IncludeHidden); for (auto* plot : plots) plot->mouseMoveZoomSelectionMode(logicPos); } else { CartesianPlot* plot = static_cast(QObject::sender()); plot->mouseMoveZoomSelectionMode(logicPos); } } void Worksheet::cartesianPlotMouseHoverZoomSelectionMode(QPointF logicPos) { if (cartesianPlotActionMode() == Worksheet::ApplyActionToAll) { auto plots = children(AbstractAspect::Recursive | AbstractAspect::IncludeHidden); for (auto* plot : plots) plot->mouseHoverZoomSelectionMode(logicPos); } else { CartesianPlot* plot = static_cast(QObject::sender()); plot->mouseHoverZoomSelectionMode(logicPos); } } void Worksheet::cartesianPlotMouseHoverOutsideDataRect() { if (cartesianPlotActionMode() == Worksheet::ApplyActionToAll) { auto plots = children(AbstractAspect::Recursive | AbstractAspect::IncludeHidden); for (auto* plot : plots) plot->mouseHoverOutsideDataRect(); } else { CartesianPlot* plot = static_cast(QObject::sender()); plot->mouseHoverOutsideDataRect(); } } void Worksheet::cartesianPlotMouseMoveCursorMode(int cursorNumber, QPointF logicPos) { if (cartesianPlotCursorMode() == Worksheet::ApplyActionToAll) { auto plots = children(AbstractAspect::Recursive | AbstractAspect::IncludeHidden); for (auto* plot : plots) plot->mouseMoveCursorMode(cursorNumber, logicPos); } else { CartesianPlot* plot = static_cast(QObject::sender()); plot->mouseMoveCursorMode(cursorNumber, logicPos); } cursorPosChanged(cursorNumber, logicPos.x()); } /*! * \brief Worksheet::cursorPosChanged * Updates the cursor treemodel with the new data * \param xPos: new position of the cursor * It is assumed, that the plots/curves are in the same order than receiving from * the children() function. It's not checked if the names are the same */ void Worksheet::cursorPosChanged(int cursorNumber, double xPos) { if (d->suppressCursorPosChanged) return; TreeModel* treeModel = cursorModel(); auto* sender = dynamic_cast(QObject::sender()); // if ApplyActionToSelection, each plot has it's own x value if (cartesianPlotCursorMode() == Worksheet::ApplyActionToAll) { // x values int rowPlot = 1; QModelIndex xName = treeModel->index(0, WorksheetPrivate::TreeModelColumn::SIGNALNAME); treeModel->setData(xName, QVariant("X")); double valueCursor[2]; for (int i = 0; i < 2; i++) { // need both cursors to calculate diff valueCursor[i] = sender->cursorPos(i); treeModel->setTreeData(QVariant(valueCursor[i]), 0, WorksheetPrivate::TreeModelColumn::CURSOR0+i); } treeModel->setTreeData(QVariant(valueCursor[1] - valueCursor[0]), 0, WorksheetPrivate::TreeModelColumn::CURSORDIFF); // y values for (int i = 0; i < getPlotCount(); i++) { // i=0 is the x Axis auto* plot = dynamic_cast(getPlot(i)); if (!plot || !plot->isVisible()) continue; QModelIndex plotIndex = treeModel->index(rowPlot, WorksheetPrivate::TreeModelColumn::PLOTNAME); // curves int rowCurve = 0; for (int j = 0; j < plot->curveCount(); j++) { // assumption: index of signals in model is the same than the index of the signal in the plot bool valueFound; const XYCurve* curve = plot->getCurve(j); if (!curve->isVisible()) continue; double value = curve->y(xPos, valueFound); if (cursorNumber == 0) { treeModel->setTreeData(QVariant(value), rowCurve, WorksheetPrivate::TreeModelColumn::CURSOR0, plotIndex); double valueCursor1 = treeModel->treeData(rowCurve, WorksheetPrivate::TreeModelColumn::CURSOR1, plotIndex).toDouble(); treeModel->setTreeData(QVariant(valueCursor1 - value), rowCurve, WorksheetPrivate::TreeModelColumn::CURSORDIFF, plotIndex); } else { treeModel->setTreeData(QVariant(value), rowCurve, WorksheetPrivate::TreeModelColumn::CURSOR1, plotIndex); double valueCursor0 = treeModel->treeData(rowCurve, WorksheetPrivate::TreeModelColumn::CURSOR0, plotIndex).toDouble(); treeModel->setTreeData(QVariant(value - valueCursor0), rowCurve, WorksheetPrivate::TreeModelColumn::CURSORDIFF, plotIndex); } rowCurve++; } rowPlot++; } } else { // apply to selection // assumption: plot is visible int rowCount = treeModel->rowCount(); for (int i = 0; i < rowCount; i++) { QModelIndex plotIndex = treeModel->index(i, WorksheetPrivate::TreeModelColumn::PLOTNAME); if (plotIndex.data().toString().compare(sender->name()) != 0) continue; // x values (first row always exist) treeModel->setTreeData(QVariant("X"), 0, WorksheetPrivate::TreeModelColumn::SIGNALNAME, plotIndex); double valueCursor[2]; for (int i = 0; i < 2; i++) { // need both cursors to calculate diff valueCursor[i] = sender->cursorPos(i); treeModel->setTreeData(QVariant(valueCursor[i]), 0, WorksheetPrivate::TreeModelColumn::CURSOR0+i, plotIndex); } treeModel->setTreeData(QVariant(valueCursor[1]-valueCursor[0]), 0, WorksheetPrivate::TreeModelColumn::CURSORDIFF, plotIndex); // y values int rowCurve = 1; // first is x value for (int j = 0; j< sender->curveCount(); j++) { // j=0 are the x values const XYCurve* curve = sender->getCurve(j); // -1 because we start with 1 for the x axis if (!curve->isVisible()) continue; // assumption: index of signals in model is the same than the index of the signal in the plot bool valueFound; double value = curve->y(xPos, valueFound); if (cursorNumber == 0) { treeModel->setTreeData(QVariant(value), rowCurve, WorksheetPrivate::TreeModelColumn::CURSOR0, plotIndex); double valueCursor1 = treeModel->treeData(rowCurve, WorksheetPrivate::TreeModelColumn::CURSOR1, plotIndex).toDouble(); treeModel->setTreeData(QVariant(valueCursor1 - value), rowCurve, WorksheetPrivate::TreeModelColumn::CURSORDIFF, plotIndex); } else { treeModel->setTreeData(QVariant(value), rowCurve, WorksheetPrivate::TreeModelColumn::CURSOR1, plotIndex); double valueCursor0 = treeModel->treeData(rowCurve, WorksheetPrivate::TreeModelColumn::CURSOR0, plotIndex).toDouble(); treeModel->setTreeData(QVariant(value - valueCursor0), rowCurve, WorksheetPrivate::TreeModelColumn::CURSORDIFF, plotIndex); } rowCurve++; } } } } void Worksheet::cursorModelPlotAdded(QString name) { Q_UNUSED(name); // TreeModel* treeModel = cursorModel(); // int rowCount = treeModel->rowCount(); // // add plot at the end // treeModel->insertRows(rowCount, 1); // add empty rows. Then they become filled // treeModel->setTreeData(QVariant(name), rowCount, WorksheetPrivate::TreeModelColumn::PLOTNAME); // rowCount instead of rowCount -1 because first row is the x value updateCompleteCursorTreeModel(); } void Worksheet::cursorModelPlotRemoved(QString name) { TreeModel* treeModel = cursorModel(); int rowCount = treeModel->rowCount(); // first is x Axis for (int i = 1; i < rowCount; i++) { QModelIndex plotIndex = treeModel->index(i, WorksheetPrivate::TreeModelColumn::PLOTNAME); if (plotIndex.data().toString().compare(name) != 0) continue; treeModel->removeRows(plotIndex.row(), 1); return; } } void Worksheet::cartesianPlotMouseModeChangedSlot(CartesianPlot::MouseMode mode) { if (d->updateCompleteCursorModel) { updateCompleteCursorTreeModel(); d->updateCompleteCursorModel = false; } emit cartesianPlotMouseModeChanged(mode); } void Worksheet::curveDataChanged(const XYCurve* curve) { auto* plot = dynamic_cast(QObject::sender()); if (!plot) return; TreeModel* treeModel = cursorModel(); int rowCount = treeModel->rowCount(); for (int i = 0; i < rowCount; i++) { QModelIndex plotIndex = treeModel->index(i, WorksheetPrivate::TreeModelColumn::PLOTNAME); if (plotIndex.data().toString().compare(plot->name()) != 0) continue; for (int j = 0; j < plot->curveCount(); j++) { if (plot->getCurve(j)->name().compare(curve->name()) != 0) continue; treeModel->setTreeData(QVariant(curve->name()), j, WorksheetPrivate::TreeModelColumn::SIGNALNAME, plotIndex); bool valueFound; double valueCursor0 = curve->y(plot->cursorPos(0), valueFound); treeModel->setTreeData(QVariant(valueCursor0), j, WorksheetPrivate::TreeModelColumn::CURSOR0, plotIndex); double valueCursor1 = curve->y(plot->cursorPos(1), valueFound); treeModel->setTreeData(QVariant(valueCursor1), j, WorksheetPrivate::TreeModelColumn::CURSOR1, plotIndex); treeModel->setTreeData(QVariant(valueCursor1-valueCursor0), j, WorksheetPrivate::TreeModelColumn::CURSORDIFF, plotIndex); break; } break; } } void Worksheet::curveAdded(const XYCurve* curve) { auto* plot = dynamic_cast(QObject::sender()); if (!plot) return; TreeModel* treeModel = cursorModel(); int rowCount = treeModel->rowCount(); int i = 0; // first row is the x axis when applied to all plots. Starting at the second row if (cartesianPlotCursorMode() == Worksheet::ApplyActionToAll) i = 1; for (; i < rowCount; i++) { QModelIndex plotIndex = treeModel->index(i, WorksheetPrivate::TreeModelColumn::PLOTNAME); if (plotIndex.data().toString().compare(plot->name()) != 0) continue; int row = 0; for (int j = 0; j < plot->curveCount(); j++) { if (plot->getCurve(j)->name().compare(curve->name()) != 0) { if (plot->getCurve(j)->isVisible()) row ++; continue; } treeModel->insertRow(row, plotIndex); treeModel->setTreeData(QVariant(curve->name()), row, WorksheetPrivate::TreeModelColumn::SIGNALNAME, plotIndex); QColor curveColor = curve->linePen().color(); curveColor.setAlpha(50); treeModel->setTreeData(QVariant(curveColor),row, WorksheetPrivate::TreeModelColumn::SIGNALNAME, plotIndex, Qt::BackgroundRole); bool valueFound; double valueCursor0 = curve->y(plot->cursorPos(0), valueFound); treeModel->setTreeData(QVariant(valueCursor0), row, WorksheetPrivate::TreeModelColumn::CURSOR0, plotIndex); double valueCursor1 = curve->y(plot->cursorPos(1), valueFound); treeModel->setTreeData(QVariant(valueCursor1), row, WorksheetPrivate::TreeModelColumn::CURSOR1, plotIndex); treeModel->setTreeData(QVariant(valueCursor1-valueCursor0), row, WorksheetPrivate::TreeModelColumn::CURSORDIFF, plotIndex); break; } break; } } void Worksheet::curveRemoved(const XYCurve* curve) { auto* plot = dynamic_cast(QObject::sender()); if (!plot) return; TreeModel* treeModel = cursorModel(); int rowCount = treeModel->rowCount(); for (int i = 0; i < rowCount; i++) { QModelIndex plotIndex = treeModel->index(i, WorksheetPrivate::TreeModelColumn::PLOTNAME); if (plotIndex.data().toString().compare(plot->name()) != 0) continue; int curveCount = treeModel->rowCount(plotIndex); for (int j = 0; j < curveCount; j++) { QModelIndex curveIndex = treeModel->index(j, WorksheetPrivate::TreeModelColumn::SIGNALNAME, plotIndex); if (curveIndex.data().toString().compare(curve->name()) != 0) continue; treeModel->removeRow(j, plotIndex); break; } break; } } /*! * Updates the background of the cuves entry in the treeview * @param pen Pen of the curve * @param curveName Curve name to find in treemodel */ void Worksheet::updateCurveBackground(QPen pen, QString curveName) { const CartesianPlot* plot = static_cast(QObject::sender()); TreeModel* treeModel = cursorModel(); int rowCount = treeModel->rowCount(); for (int i = 0; i < rowCount; i++) { QModelIndex plotIndex = treeModel->index(i, WorksheetPrivate::TreeModelColumn::PLOTNAME); if (plotIndex.data().toString().compare(plot->name()) != 0) continue; int curveCount = treeModel->rowCount(plotIndex); for (int j = 0; j < curveCount; j++) { QModelIndex curveIndex = treeModel->index(j, WorksheetPrivate::TreeModelColumn::SIGNALNAME, plotIndex); if (curveIndex.data().toString().compare(curveName) != 0) continue; QColor curveColor = pen.color(); curveColor.setAlpha(50); treeModel->setTreeData(QVariant(curveColor), j, WorksheetPrivate::TreeModelColumn::SIGNALNAME, plotIndex, Qt::BackgroundRole); return; } return; } } /** * @brief Worksheet::updateCompleteCursorTreeModel * If the plot or the curve are not available, the plot/curve is not in the treemodel! */ void Worksheet::updateCompleteCursorTreeModel() { if (isLoading()) return; TreeModel* treeModel = cursorModel(); if (treeModel->rowCount() > 0) treeModel->removeRows(0, treeModel->rowCount()); // remove all data int plotCount = getPlotCount(); if (plotCount < 1) return; if (cartesianPlotCursorMode() == Worksheet::CartesianPlotActionMode::ApplyActionToAll) { // 1 because of the X data treeModel->insertRows(0, 1); //, treeModel->index(0,0)); // add empty rows. Then they become filled // set X data QModelIndex xName = treeModel->index(0, WorksheetPrivate::TreeModelColumn::SIGNALNAME); treeModel->setData(xName, QVariant("X")); CartesianPlot* plot0 = dynamic_cast(getPlot(0)); double valueCursor[2]; for (int i = 0; i < 2; i++) { valueCursor[i] = plot0->cursorPos(i); QModelIndex cursor = treeModel->index(0,WorksheetPrivate::TreeModelColumn::CURSOR0+i); treeModel->setData(cursor, QVariant(valueCursor[i])); } QModelIndex diff = treeModel->index(0,WorksheetPrivate::TreeModelColumn::CURSORDIFF); treeModel->setData(diff, QVariant(valueCursor[1]-valueCursor[0])); } else { //treeModel->insertRows(0, plotCount, treeModel->index(0,0)); // add empty rows. Then they become filled } // set plot name, y value, background for (int i = 0; i < plotCount; i++) { CartesianPlot* plot = dynamic_cast(getPlot(i)); QModelIndex plotName; int addOne = 0; if (!plot || !plot->isVisible()) continue; // add new entry for the plot treeModel->insertRows(treeModel->rowCount(), 1); //, treeModel->index(0, 0)); // add plot name and X row if needed if (cartesianPlotCursorMode() == Worksheet::CartesianPlotActionMode::ApplyActionToAll) { plotName = treeModel->index(i + 1, WorksheetPrivate::TreeModelColumn::PLOTNAME); // plus one because first row are the x values treeModel->setData(plotName, QVariant(plot->name())); } else { addOne = 1; plotName = treeModel->index(i, WorksheetPrivate::TreeModelColumn::PLOTNAME); treeModel->setData(plotName, QVariant(plot->name())); treeModel->insertRows(0, 1, plotName); // one, because the first row are the x values QModelIndex xName = treeModel->index(0, WorksheetPrivate::TreeModelColumn::SIGNALNAME, plotName); treeModel->setData(xName, QVariant("X")); double valueCursor[2]; for (int i = 0; i < 2; i++) { valueCursor[i] = plot->cursorPos(i); QModelIndex cursor = treeModel->index(0, WorksheetPrivate::TreeModelColumn::CURSOR0+i, plotName); treeModel->setData(cursor, QVariant(valueCursor[i])); } QModelIndex diff = treeModel->index(0, WorksheetPrivate::TreeModelColumn::CURSORDIFF, plotName); treeModel->setData(diff, QVariant(valueCursor[1]-valueCursor[0])); } int rowCurve = addOne; for (int j = 0; j < plot->curveCount(); j++) { double cursorValue[2] = {NAN, NAN}; const XYCurve* curve = plot->getCurve(j); if (!curve->isVisible()) continue; for (int k = 0; k < 2; k++) { double xPos = plot->cursorPos(k); bool valueFound; cursorValue[k] = curve->y(xPos,valueFound); } treeModel->insertRows(rowCurve, 1, plotName); QColor curveColor = curve->linePen().color(); curveColor.setAlpha(50); treeModel->setTreeData(QVariant(curveColor), rowCurve, 0, plotName, Qt::BackgroundRole); treeModel->setTreeData(QVariant(curve->name()), rowCurve, WorksheetPrivate::TreeModelColumn::SIGNALNAME, plotName); treeModel->setTreeData(QVariant(cursorValue[0]), rowCurve, WorksheetPrivate::TreeModelColumn::CURSOR0, plotName); treeModel->setTreeData(QVariant(cursorValue[1]), rowCurve, WorksheetPrivate::TreeModelColumn::CURSOR1, plotName); treeModel->setTreeData(QVariant(cursorValue[1]-cursorValue[0]), rowCurve, WorksheetPrivate::TreeModelColumn::CURSORDIFF, plotName); rowCurve++; } } } //############################################################################## //###################### Private implementation ############################### //############################################################################## WorksheetPrivate::WorksheetPrivate(Worksheet* owner) : q(owner), m_scene(new QGraphicsScene()) { QStringList headers = {i18n("Curves"), "V1", "V2", "V2-V1"}; cursorData = new TreeModel(headers, nullptr); } QString WorksheetPrivate::name() const { return q->name(); } /*! * called if the worksheet page (the actual size of worksheet's rectangular) was changed. * if a layout is active, it is is updated - this adjusts the sizes of the elements in the layout to the new page size. * if no layout is active and the option "scale content" is active, \c handleResize() is called to adjust zhe properties. */ void WorksheetPrivate::updatePageRect() { if (q->isLoading()) return; QRectF oldRect = m_scene->sceneRect(); m_scene->setSceneRect(pageRect); if (layout != Worksheet::NoLayout) updateLayout(); else { if (scaleContent) { qreal horizontalRatio = pageRect.width() / oldRect.width(); qreal verticalRatio = pageRect.height() / oldRect.height(); QVector childElements = q->children(AbstractAspect::IncludeHidden); if (useViewSize) { //don't make the change of the geometry undoable/redoable if the view size is used. for (auto* elem : childElements) { elem->setUndoAware(false); elem->handleResize(horizontalRatio, verticalRatio, true); elem->setUndoAware(true); } } else { // for (auto* child : childElements) // child->handleResize(horizontalRatio, verticalRatio, true); } } } } void WorksheetPrivate::update() { q->update(); } WorksheetPrivate::~WorksheetPrivate() { delete m_scene; } void WorksheetPrivate::updateLayout(bool undoable) { if (suppressLayoutUpdate) return; QVector list = q->children(); if (layout == Worksheet::NoLayout) { for (auto* elem : list) elem->graphicsItem()->setFlag(QGraphicsItem::ItemIsMovable, true); return; } float x = layoutLeftMargin; float y = layoutTopMargin; float w, h; int count = list.count(); if (layout == Worksheet::VerticalLayout) { w = m_scene->sceneRect().width() - layoutLeftMargin - layoutRightMargin; h = (m_scene->sceneRect().height()-layoutTopMargin-layoutBottomMargin- (count-1)*layoutVerticalSpacing)/count; for (auto* elem : list) { setContainerRect(elem, x, y, h, w, undoable); y += h + layoutVerticalSpacing; } } else if (layout == Worksheet::HorizontalLayout) { w = (m_scene->sceneRect().width()-layoutLeftMargin-layoutRightMargin- (count-1)*layoutHorizontalSpacing)/count; h = m_scene->sceneRect().height() - layoutTopMargin-layoutBottomMargin; for (auto* elem : list) { setContainerRect(elem, x, y, h, w, undoable); x += w + layoutHorizontalSpacing; } } else { //GridLayout //add new rows, if not sufficient if (count > layoutRowCount*layoutColumnCount) { layoutRowCount = floor( (float)count/layoutColumnCount + 0.5); emit q->layoutRowCountChanged(layoutRowCount); } w = (m_scene->sceneRect().width()-layoutLeftMargin-layoutRightMargin- (layoutColumnCount-1)*layoutHorizontalSpacing)/layoutColumnCount; h = (m_scene->sceneRect().height()-layoutTopMargin-layoutBottomMargin- (layoutRowCount-1)*layoutVerticalSpacing)/layoutRowCount; int columnIndex = 0; //counts the columns in a row for (auto* elem : list) { setContainerRect(elem, x, y, h, w, undoable); x += w + layoutHorizontalSpacing; columnIndex++; if (columnIndex == layoutColumnCount) { columnIndex = 0; x = layoutLeftMargin; y += h + layoutVerticalSpacing; } } } } void WorksheetPrivate::setContainerRect(WorksheetElementContainer* elem, float x, float y, float h, float w, bool undoable) { if (useViewSize) { //when using the view size, no need to put rect changes onto the undo-stack elem->setUndoAware(false); elem->setRect(QRectF(x,y,w,h)); elem->setUndoAware(true); } else { //don't put rect changed onto the undo-stack if undoable-flag is set to true, //e.g. when new child is added or removed (the layout and the childrend rects will be updated anyway) if (!undoable) { elem->setUndoAware(false); elem->setRect(QRectF(x,y,w,h)); elem->setUndoAware(true); } else elem->setRect(QRectF(x,y,w,h)); } elem->graphicsItem()->setFlag(QGraphicsItem::ItemIsMovable, false); } //############################################################################## //################## Serialization/Deserialization ########################### //############################################################################## //! Save as XML void Worksheet::save(QXmlStreamWriter* writer) const { writer->writeStartElement( "worksheet" ); writeBasicAttributes(writer); writeCommentElement(writer); //applied theme if (!d->theme.isEmpty()) { writer->writeStartElement( "theme" ); writer->writeAttribute("name", d->theme); writer->writeEndElement(); } //geometry writer->writeStartElement( "geometry" ); QRectF rect = d->m_scene->sceneRect(); writer->writeAttribute( "x", QString::number(rect.x()) ); writer->writeAttribute( "y", QString::number(rect.y()) ); writer->writeAttribute( "width", QString::number(rect.width()) ); writer->writeAttribute( "height", QString::number(rect.height()) ); writer->writeAttribute( "useViewSize", QString::number(d->useViewSize) ); writer->writeEndElement(); //layout writer->writeStartElement( "layout" ); writer->writeAttribute( "layout", QString::number(d->layout) ); writer->writeAttribute( "topMargin", QString::number(d->layoutTopMargin) ); writer->writeAttribute( "bottomMargin", QString::number(d->layoutBottomMargin) ); writer->writeAttribute( "leftMargin", QString::number(d->layoutLeftMargin) ); writer->writeAttribute( "rightMargin", QString::number(d->layoutRightMargin) ); writer->writeAttribute( "verticalSpacing", QString::number(d->layoutVerticalSpacing) ); writer->writeAttribute( "horizontalSpacing", QString::number(d->layoutHorizontalSpacing) ); writer->writeAttribute( "columnCount", QString::number(d->layoutColumnCount) ); writer->writeAttribute( "rowCount", QString::number(d->layoutRowCount) ); writer->writeEndElement(); //background properties writer->writeStartElement( "background" ); writer->writeAttribute( "type", QString::number(d->backgroundType) ); writer->writeAttribute( "colorStyle", QString::number(d->backgroundColorStyle) ); writer->writeAttribute( "imageStyle", QString::number(d->backgroundImageStyle) ); writer->writeAttribute( "brushStyle", QString::number(d->backgroundBrushStyle) ); writer->writeAttribute( "firstColor_r", QString::number(d->backgroundFirstColor.red()) ); writer->writeAttribute( "firstColor_g", QString::number(d->backgroundFirstColor.green()) ); writer->writeAttribute( "firstColor_b", QString::number(d->backgroundFirstColor.blue()) ); writer->writeAttribute( "secondColor_r", QString::number(d->backgroundSecondColor.red()) ); writer->writeAttribute( "secondColor_g", QString::number(d->backgroundSecondColor.green()) ); writer->writeAttribute( "secondColor_b", QString::number(d->backgroundSecondColor.blue()) ); writer->writeAttribute( "fileName", d->backgroundFileName ); writer->writeAttribute( "opacity", QString::number(d->backgroundOpacity) ); writer->writeEndElement(); // cartesian properties writer->writeStartElement( "plotProperties" ); writer->writeAttribute( "plotsLocked", QString::number(d->plotsLocked) ); writer->writeAttribute( "cartesianPlotActionMode", QString::number(d->cartesianPlotActionMode)); writer->writeAttribute( "cartesianPlotCursorMode", QString::number(d->cartesianPlotCursorMode)); writer->writeEndElement(); //serialize all children for (auto* child : children(IncludeHidden)) child->save(writer); writer->writeEndElement(); // close "worksheet" section } //! Load from XML bool Worksheet::load(XmlStreamReader* reader, bool preview) { if (!readBasicAttributes(reader)) return false; //clear the theme that was potentially set in init() in order to correctly load here the worksheets without any theme used d->theme.clear(); KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used"); QXmlStreamAttributes attribs; QString str; QRectF rect; while (!reader->atEnd()) { reader->readNext(); if (reader->isEndElement() && reader->name() == "worksheet") break; if (!reader->isStartElement()) continue; if (reader->name() == "comment") { if (!readCommentElement(reader)) return false; } else if (!preview && reader->name() == "theme") { attribs = reader->attributes(); d->theme = attribs.value("name").toString(); } else if (!preview && reader->name() == "geometry") { attribs = reader->attributes(); str = attribs.value("x").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.subs("x").toString()); else rect.setX(str.toDouble()); str = attribs.value("y").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.subs("y").toString()); else rect.setY(str.toDouble()); str = attribs.value("width").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.subs("width").toString()); else rect.setWidth(str.toDouble()); str = attribs.value("height").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.subs("height").toString()); else rect.setHeight(str.toDouble()); READ_INT_VALUE("useViewSize", useViewSize, int); } else if (!preview && reader->name() == "layout") { attribs = reader->attributes(); READ_INT_VALUE("layout", layout, Worksheet::Layout); READ_DOUBLE_VALUE("topMargin", layoutTopMargin); READ_DOUBLE_VALUE("bottomMargin", layoutBottomMargin); READ_DOUBLE_VALUE("leftMargin", layoutLeftMargin); READ_DOUBLE_VALUE("rightMargin", layoutRightMargin); READ_DOUBLE_VALUE("verticalSpacing", layoutVerticalSpacing); READ_DOUBLE_VALUE("horizontalSpacing", layoutHorizontalSpacing); READ_INT_VALUE("columnCount", layoutColumnCount, int); READ_INT_VALUE("rowCount", layoutRowCount, int); } else if (!preview && reader->name() == "background") { attribs = reader->attributes(); READ_INT_VALUE("type", backgroundType, PlotArea::BackgroundType); READ_INT_VALUE("colorStyle", backgroundColorStyle, PlotArea::BackgroundColorStyle); READ_INT_VALUE("imageStyle", backgroundImageStyle, PlotArea::BackgroundImageStyle); READ_INT_VALUE("brushStyle", backgroundBrushStyle, Qt::BrushStyle); str = attribs.value("firstColor_r").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.subs("firstColor_r").toString()); else d->backgroundFirstColor.setRed(str.toInt()); str = attribs.value("firstColor_g").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.subs("firstColor_g").toString()); else d->backgroundFirstColor.setGreen(str.toInt()); str = attribs.value("firstColor_b").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.subs("firstColor_b").toString()); else d->backgroundFirstColor.setBlue(str.toInt()); str = attribs.value("secondColor_r").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.subs("secondColor_r").toString()); else d->backgroundSecondColor.setRed(str.toInt()); str = attribs.value("secondColor_g").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.subs("secondColor_g").toString()); else d->backgroundSecondColor.setGreen(str.toInt()); str = attribs.value("secondColor_b").toString(); if (str.isEmpty()) reader->raiseWarning(attributeWarning.subs("secondColor_b").toString()); else d->backgroundSecondColor.setBlue(str.toInt()); str = attribs.value("fileName").toString(); d->backgroundFileName = str; READ_DOUBLE_VALUE("opacity", backgroundOpacity); } else if(!preview && reader->name() == "plotProperties") { attribs = reader->attributes(); READ_INT_VALUE("plotsLocked", plotsLocked, bool); READ_INT_VALUE("cartesianPlotActionMode", cartesianPlotActionMode, Worksheet::CartesianPlotActionMode); READ_INT_VALUE("cartesianPlotCursorMode", cartesianPlotCursorMode, Worksheet::CartesianPlotActionMode); } else if (reader->name() == "cartesianPlot") { CartesianPlot* plot = new CartesianPlot(QString()); plot->setIsLoading(true); if (!plot->load(reader, preview)) { delete plot; return false; } else addChildFast(plot); } else if (reader->name() == "textLabel") { TextLabel* label = new TextLabel(QString()); if (!label->load(reader, preview)) { delete label; return false; } else addChildFast(label); + } else if (reader->name() == "image") { + Image* image = new Image(QString()); + if (!image->load(reader, preview)) { + delete image; + return false; + } else + addChildFast(image); } else { // unknown element reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString())); if (!reader->skipToEndElement()) return false; } } if (!preview) { d->m_scene->setSceneRect(rect); d->updateLayout(); updateCompleteCursorTreeModel(); } return true; } //############################################################################## //######################### Theme management ################################## //############################################################################## void Worksheet::loadTheme(const QString& theme) { KConfig config(ThemeHandler::themeFilePath(theme), KConfig::SimpleConfig); //apply the same background color for Worksheet as for the CartesianPlot const KConfigGroup group = config.group("CartesianPlot"); this->setBackgroundBrushStyle((Qt::BrushStyle)group.readEntry("BackgroundBrushStyle",(int) this->backgroundBrushStyle())); this->setBackgroundColorStyle((PlotArea::BackgroundColorStyle)(group.readEntry("BackgroundColorStyle",(int) this->backgroundColorStyle()))); this->setBackgroundFirstColor(group.readEntry("BackgroundFirstColor",(QColor) this->backgroundFirstColor())); this->setBackgroundImageStyle((PlotArea::BackgroundImageStyle)group.readEntry("BackgroundImageStyle",(int) this->backgroundImageStyle())); this->setBackgroundOpacity(group.readEntry("BackgroundOpacity", this->backgroundOpacity())); this->setBackgroundSecondColor(group.readEntry("BackgroundSecondColor",(QColor) this->backgroundSecondColor())); this->setBackgroundType((PlotArea::BackgroundType)(group.readEntry("BackgroundType",(int) this->backgroundType()))); //load the theme for all the children const QVector& childElements = children(AbstractAspect::IncludeHidden); for (auto* child : childElements) child->loadThemeConfig(config); } diff --git a/src/compile_commands.json b/src/compile_commands.json new file mode 120000 index 000000000..f40e179b9 --- /dev/null +++ b/src/compile_commands.json @@ -0,0 +1 @@ +../build-clang/compile_commands.json \ No newline at end of file diff --git a/src/kdefrontend/GuiObserver.cpp b/src/kdefrontend/GuiObserver.cpp index 9144b629b..6483c0f79 100644 --- a/src/kdefrontend/GuiObserver.cpp +++ b/src/kdefrontend/GuiObserver.cpp @@ -1,452 +1,459 @@ /*************************************************************************** File : GuiObserver.cpp Project : LabPlot Description : GUI observer -------------------------------------------------------------------- Copyright : (C) 2010-2015 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2015-2018 Stefan Gerlach (stefan.gerlach@uni.kn) Copyright : (C) 2016 Garvit Khatri (garvitdelhi@gmail.com) ***************************************************************************/ /*************************************************************************** * * * 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 "kdefrontend/GuiObserver.h" #include "backend/core/AspectTreeModel.h" #include "backend/core/AbstractAspect.h" #include "backend/datasources/LiveDataSource.h" #include "backend/matrix/Matrix.h" #include "backend/spreadsheet/Spreadsheet.h" #include "backend/worksheet/Worksheet.h" #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #include "backend/worksheet/plots/cartesian/CartesianPlotLegend.h" #include "backend/worksheet/plots/cartesian/XYCurve.h" #include "backend/worksheet/plots/cartesian/Axis.h" #include "backend/worksheet/plots/cartesian/CustomPoint.h" #include "backend/worksheet/plots/cartesian/Histogram.h" +#include "backend/worksheet/Image.h" #include "backend/worksheet/TextLabel.h" #ifdef HAVE_CANTOR_LIBS #include "backend/cantorWorksheet/CantorWorksheet.h" #endif #ifdef HAVE_MQTT #include "backend/datasources/MQTTClient.h" #include "backend/datasources/MQTTSubscription.h" #include "backend/datasources/MQTTTopic.h" #endif #include "backend/core/Project.h" #include "backend/datapicker/Datapicker.h" #include "backend/datapicker/DatapickerImage.h" #include "backend/datapicker/DatapickerCurve.h" #include "commonfrontend/ProjectExplorer.h" #include "kdefrontend/MainWin.h" #include "kdefrontend/dockwidgets/AxisDock.h" #include "kdefrontend/dockwidgets/NoteDock.h" #include "kdefrontend/dockwidgets/CursorDock.h" #include "kdefrontend/dockwidgets/CartesianPlotDock.h" #include "kdefrontend/dockwidgets/CartesianPlotLegendDock.h" #include "kdefrontend/dockwidgets/ColumnDock.h" +#include "kdefrontend/dockwidgets/ImageDock.h" #include "kdefrontend/dockwidgets/LiveDataDock.h" #include "kdefrontend/dockwidgets/MatrixDock.h" #include "kdefrontend/dockwidgets/ProjectDock.h" #include "kdefrontend/dockwidgets/SpreadsheetDock.h" #include "kdefrontend/dockwidgets/XYCurveDock.h" #include "kdefrontend/dockwidgets/HistogramDock.h" #include "kdefrontend/dockwidgets/XYEquationCurveDock.h" #include "kdefrontend/dockwidgets/XYDataReductionCurveDock.h" #include "kdefrontend/dockwidgets/XYDifferentiationCurveDock.h" #include "kdefrontend/dockwidgets/XYIntegrationCurveDock.h" #include "kdefrontend/dockwidgets/XYInterpolationCurveDock.h" #include "kdefrontend/dockwidgets/XYSmoothCurveDock.h" #include "kdefrontend/dockwidgets/XYFitCurveDock.h" #include "kdefrontend/dockwidgets/XYFourierFilterCurveDock.h" #include "kdefrontend/dockwidgets/XYFourierTransformCurveDock.h" #include "kdefrontend/dockwidgets/XYConvolutionCurveDock.h" #include "kdefrontend/dockwidgets/XYCorrelationCurveDock.h" #include "kdefrontend/dockwidgets/CustomPointDock.h" #include "kdefrontend/dockwidgets/WorksheetDock.h" #ifdef HAVE_CANTOR_LIBS #include "kdefrontend/dockwidgets/CantorWorksheetDock.h" #endif #include "kdefrontend/widgets/LabelWidget.h" #include "kdefrontend/widgets/DatapickerImageWidget.h" #include "kdefrontend/widgets/DatapickerCurveWidget.h" #include #include #include #include #include /*! \class GuiObserver \brief The GUI observer looks for the selection changes in the main window and shows/hides the correspondings dock widgets, toolbars etc. This class is intended to simplify (or not to overload) the code in MainWin. \ingroup kdefrontend */ namespace GuiObserverHelper { template bool raiseDock(T*& dock, QStackedWidget* parent) { const bool generated = !dock; if (generated) { dock = new T(parent); parent->addWidget(dock); } parent->setCurrentWidget(dock); return generated; } template void raiseDockConnect(T*& dock, QStatusBar* statusBar, QStackedWidget* parent) { if (raiseDock(dock, parent)) QObject::connect(dock, &T::info, [=](const QString& text){ statusBar->showMessage(text); }); } template void raiseDockSetupConnect(T*& dock, QStatusBar* statusBar, QStackedWidget* parent) { if (raiseDock(dock, parent)) { dock->setupGeneral(); QObject::connect(dock, &T::info, [=](const QString& text){ statusBar->showMessage(text); }); } } template QList castList(QList& selectedAspects) { QList list; for (auto* aspect : selectedAspects) list << static_cast(aspect); return list; } } using namespace GuiObserverHelper; GuiObserver::GuiObserver(MainWin* mainWin) { connect(mainWin->m_projectExplorer, &ProjectExplorer::selectedAspectsChanged, this, &GuiObserver::selectedAspectsChanged); connect(mainWin->m_projectExplorer, &ProjectExplorer::hiddenAspectSelected, this, &GuiObserver::hiddenAspectSelected); m_mainWindow = mainWin; } /*! called on selection changes in the project explorer. Determines the type of the currently selected objects (aspects) and activates the corresponding dockwidgets, toolbars etc. */ void GuiObserver::selectedAspectsChanged(QList& selectedAspects) const { auto clearDock = [&](){ if (m_mainWindow->stackedWidget->currentWidget()) m_mainWindow->stackedWidget->currentWidget()->hide(); m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Properties")); }; if (selectedAspects.isEmpty()) { clearDock(); return; } // update cursor dock AbstractAspect* parent = selectedAspects[0]->parent(AspectType::Worksheet); if (selectedAspects[0]->inherits(AspectType::Worksheet)) { if (m_mainWindow->cursorWidget) { Worksheet* worksheet = static_cast(selectedAspects[0]); m_mainWindow->cursorWidget->setWorksheet(worksheet); } } else if (parent) { if (m_mainWindow->cursorWidget) { Worksheet* worksheet = static_cast(parent); m_mainWindow->cursorWidget->setWorksheet(worksheet); } } const AspectType type = selectedAspects.front()->type(); // Check, whether objects of different types were selected. // Don't show any dock widgets in this case. for (auto* aspect : selectedAspects) { if (aspect->type() != type) { clearDock(); return; } } switch (type) { case AspectType::Spreadsheet: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Spreadsheet")); raiseDockConnect(m_mainWindow->spreadsheetDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->spreadsheetDock->setSpreadsheets(castList(selectedAspects)); break; case AspectType::Column: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Column")); raiseDockConnect(m_mainWindow->columnDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->columnDock->setColumns(castList(selectedAspects)); break; case AspectType::Matrix: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Matrix")); raiseDockConnect(m_mainWindow->matrixDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->matrixDock->setMatrices(castList(selectedAspects)); break; case AspectType::Worksheet: { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Worksheet")); if (!m_mainWindow->worksheetDock) { m_mainWindow->worksheetDock = new WorksheetDock(m_mainWindow->stackedWidget); connect(m_mainWindow->worksheetDock, SIGNAL(info(QString)), m_mainWindow->statusBar(), SLOT(showMessage(QString))); m_mainWindow->stackedWidget->addWidget(m_mainWindow->worksheetDock); } raiseDockConnect(m_mainWindow->worksheetDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->worksheetDock->setWorksheets(castList(selectedAspects)); break; } case AspectType::CartesianPlot: { m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Cartesian Plot")); raiseDockConnect(m_mainWindow->cartesianPlotDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->cartesianPlotDock->setPlots(castList(selectedAspects)); break; } case AspectType::CartesianPlotLegend: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Legend")); raiseDockConnect(m_mainWindow->cartesianPlotLegendDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->cartesianPlotLegendDock->setLegends(castList(selectedAspects)); break; case AspectType::Axis: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Axis")); raiseDockConnect(m_mainWindow->axisDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->axisDock->setAxes(castList(selectedAspects)); break; case AspectType::XYCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "xy-Curve")); raiseDockSetupConnect(m_mainWindow->xyCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYEquationCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "xy-Equation")); raiseDockSetupConnect(m_mainWindow->xyEquationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyEquationCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYDataReductionCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Data Reduction")); if (!m_mainWindow->xyDataReductionCurveDock) { m_mainWindow->xyDataReductionCurveDock = new XYDataReductionCurveDock(m_mainWindow->stackedWidget, m_mainWindow->statusBar()); m_mainWindow->xyDataReductionCurveDock->setupGeneral(); connect(m_mainWindow->xyDataReductionCurveDock, &XYDataReductionCurveDock::info, [&](const QString& text){ m_mainWindow->statusBar()->showMessage(text); }); m_mainWindow->stackedWidget->addWidget(m_mainWindow->xyDataReductionCurveDock); } m_mainWindow->xyDataReductionCurveDock->setCurves(castList(selectedAspects)); m_mainWindow->stackedWidget->setCurrentWidget(m_mainWindow->xyDataReductionCurveDock); break; case AspectType::XYDifferentiationCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Differentiation")); raiseDockSetupConnect(m_mainWindow->xyDifferentiationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyDifferentiationCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYIntegrationCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Integration")); raiseDockSetupConnect(m_mainWindow->xyIntegrationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyIntegrationCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYInterpolationCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Interpolation")); raiseDockSetupConnect(m_mainWindow->xyInterpolationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyInterpolationCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYSmoothCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Smoothing")); raiseDockSetupConnect(m_mainWindow->xySmoothCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xySmoothCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYFitCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Fit")); raiseDockSetupConnect(m_mainWindow->xyFitCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyFitCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYFourierTransformCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Fourier Transform")); raiseDockSetupConnect(m_mainWindow->xyFourierTransformCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyFourierTransformCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYFourierFilterCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Fourier Filter")); raiseDockSetupConnect(m_mainWindow->xyFourierFilterCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyFourierFilterCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYConvolution: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Convolution/Deconvolution")); raiseDockSetupConnect(m_mainWindow->xyConvolutionCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyConvolutionCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::XYCorrelationCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Auto-/Cross-Correlation")); raiseDockSetupConnect(m_mainWindow->xyCorrelationCurveDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->xyCorrelationCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::Histogram: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Histogram Properties")); raiseDockConnect(m_mainWindow->histogramDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); m_mainWindow->histogramDock->setCurves(castList(selectedAspects)); break; case AspectType::TextLabel: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Text Label")); raiseDock(m_mainWindow->textLabelDock, m_mainWindow->stackedWidget); m_mainWindow->textLabelDock->setLabels(castList(selectedAspects)); break; + case AspectType::Image: + m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Image")); + raiseDock(m_mainWindow->imageDock, m_mainWindow->stackedWidget); + m_mainWindow->imageDock->setImages(castList(selectedAspects)); + break; case AspectType::CustomPoint: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Custom Point")); raiseDock(m_mainWindow->customPointDock, m_mainWindow->stackedWidget); m_mainWindow->customPointDock->setPoints(castList(selectedAspects)); break; case AspectType::DatapickerCurve: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Datapicker Curve")); raiseDock(m_mainWindow->datapickerCurveDock, m_mainWindow->stackedWidget); m_mainWindow->datapickerCurveDock->setCurves(castList(selectedAspects)); break; case AspectType::Datapicker: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Datapicker")); raiseDock(m_mainWindow->datapickerImageDock, m_mainWindow->stackedWidget); { QList list; for (auto* aspect : selectedAspects) list << qobject_cast(aspect)->image(); m_mainWindow->datapickerImageDock->setImages(list); } break; case AspectType::Project: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Project")); raiseDock(m_mainWindow->projectDock, m_mainWindow->stackedWidget); m_mainWindow->projectDock->setProject(m_mainWindow->m_project); break; case AspectType::CantorWorksheet: #ifdef HAVE_CANTOR_LIBS raiseDockConnect(m_mainWindow->cantorWorksheetDock, m_mainWindow->statusBar(), m_mainWindow->stackedWidget); { QList list = castList(selectedAspects); if (list.size() == 1) m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window %1 is a Cantor backend", "%1 Properties", list.first()->backendName())); else m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "CAS Properties")); m_mainWindow->cantorWorksheetDock->setCantorWorksheets(list); } #endif break; case AspectType::Note: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Notes")); raiseDock(m_mainWindow->notesDock, m_mainWindow->stackedWidget); m_mainWindow->notesDock->setNotesList(castList(selectedAspects)); break; case AspectType::MQTTClient: #ifdef HAVE_MQTT m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "MQTT Data Source")); raiseDock(m_mainWindow->m_liveDataDock, m_mainWindow->stackedWidget); m_mainWindow->m_liveDataDock->setMQTTClient(static_cast(selectedAspects.first())); #endif break; case AspectType::MQTTSubscription: #ifdef HAVE_MQTT m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "MQTT Data Source")); raiseDock(m_mainWindow->m_liveDataDock, m_mainWindow->stackedWidget); m_mainWindow->m_liveDataDock->setMQTTClient(static_cast(selectedAspects.first())->mqttClient()); #endif break; case AspectType::MQTTTopic: #ifdef HAVE_MQTT m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "MQTT Data Source")); raiseDock(m_mainWindow->m_liveDataDock, m_mainWindow->stackedWidget); m_mainWindow->m_liveDataDock->setMQTTClient(static_cast(selectedAspects.first())->mqttClient()); #endif break; case AspectType::LiveDataSource: m_mainWindow->m_propertiesDock->setWindowTitle(i18nc("@title:window", "Live Data Source")); raiseDock(m_mainWindow->m_liveDataDock, m_mainWindow->stackedWidget); m_mainWindow->m_liveDataDock->setLiveDataSource(static_cast(selectedAspects.first())); break; case AspectType::AbstractAspect: case AspectType::AbstractColumn: case AspectType::AbstractDataSource: case AspectType::AbstractFilter: case AspectType::AbstractPart: case AspectType::AbstractPlot: case AspectType::ColumnStringIO: case AspectType::DatapickerImage: case AspectType::DatapickerPoint: case AspectType::Folder: case AspectType::PlotArea: case AspectType::SimpleFilterColumn: case AspectType::Workbook: case AspectType::WorksheetElement: case AspectType::WorksheetElementContainer: case AspectType::WorksheetElementGroup: case AspectType::XYAnalysisCurve: clearDock(); return; } m_mainWindow->stackedWidget->currentWidget()->show(); } /*! handles the selection of a hidden aspect \c aspect in the view (relevant for WorksheetView only at the moment). Currently, a hidden aspect can only be a plot title lable or an axis label. -> Activate the corresponding DockWidget and make the title tab current. */ void GuiObserver::hiddenAspectSelected(const AbstractAspect* aspect) const { const AbstractAspect* parent = aspect->parentAspect(); if (!parent) return; switch (static_cast(parent->type())) { // cast the enum to turn off warnings about unhandled cases case static_cast(AspectType::Axis): if (!m_mainWindow->axisDock) { m_mainWindow->axisDock = new AxisDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->axisDock); } m_mainWindow->axisDock->activateTitleTab(); break; case static_cast(AspectType::CartesianPlot): if (!m_mainWindow->cartesianPlotDock) { m_mainWindow->cartesianPlotDock = new CartesianPlotDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->cartesianPlotDock); } m_mainWindow->cartesianPlotDock->activateTitleTab(); break; case static_cast(AspectType::CartesianPlotLegend): if (!m_mainWindow->cartesianPlotLegendDock) { m_mainWindow->cartesianPlotLegendDock = new CartesianPlotLegendDock(m_mainWindow->stackedWidget); m_mainWindow->stackedWidget->addWidget(m_mainWindow->cartesianPlotLegendDock); } m_mainWindow->cartesianPlotLegendDock->activateTitleTab(); break; default: break; } } diff --git a/src/kdefrontend/MainWin.h b/src/kdefrontend/MainWin.h index 32461ca40..89d5d2297 100644 --- a/src/kdefrontend/MainWin.h +++ b/src/kdefrontend/MainWin.h @@ -1,324 +1,325 @@ /*************************************************************************** File : MainWin.h Project : LabPlot -------------------------------------------------------------------- Copyright : (C) 2011-2018 Alexander Semke (alexander.semke@web.de) Copyright : (C) 2008-2018 by Stefan Gerlach (stefan.gerlach@uni.kn) Description : Main window of the application ***************************************************************************/ /*************************************************************************** * * * 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 MAINWIN_H #define MAINWIN_H #include "backend/worksheet/plots/cartesian/CartesianPlot.h" #include #include class AbstractAspect; class AspectTreeModel; class Folder; class ProjectExplorer; class Project; class Worksheet; class Note; class Workbook; class Datapicker; -class Image; class Spreadsheet; class Matrix; class GuiObserver; class AxisDock; class CursorDock; class NoteDock; class CartesianPlotDock; class HistogramDock; class BarChartPlotDock; class CartesianPlotLegendDock; class CustomPointDock; class ColumnDock; class LiveDataDock; class MatrixDock; class ProjectDock; class SpreadsheetDock; class XYCurveDock; class XYEquationCurveDock; class XYDataReductionCurveDock; class XYDifferentiationCurveDock; class XYIntegrationCurveDock; class XYInterpolationCurveDock; class XYSmoothCurveDock; class XYFitCurveDock; class XYFourierFilterCurveDock; class XYFourierTransformCurveDock; class XYConvolutionCurveDock; class XYCorrelationCurveDock; class WorksheetDock; +class ImageDock; class LabelWidget; class DatapickerImageWidget; class DatapickerCurveWidget; class MemoryWidget; class CartesianPlot; #ifdef HAVE_CANTOR_LIBS class CantorWorksheet; class CantorWorksheetDock; #endif class QDockWidget; class QStackedWidget; class QDragEnterEvent; class QDropEvent; class QMdiArea; class QMdiSubWindow; class QToolButton; class KRecentFilesAction; class TreeModel; #ifdef Q_OS_MAC class KDMacTouchBar; #endif class MainWin : public KXmlGuiWindow { Q_OBJECT public: explicit MainWin(QWidget* parent = nullptr, const QString& filename = nullptr); ~MainWin() override; void showPresenter(); AspectTreeModel* model() const; Project* project() const; void addAspectToProject(AbstractAspect*); private: QMdiArea* m_mdiArea; QMdiSubWindow* m_currentSubWindow{nullptr}; Project* m_project{nullptr}; AspectTreeModel* m_aspectTreeModel{nullptr}; ProjectExplorer* m_projectExplorer{nullptr}; QDockWidget* m_projectExplorerDock{nullptr}; QDockWidget* m_propertiesDock{nullptr}; AbstractAspect* m_currentAspect{nullptr}; Folder* m_currentFolder{nullptr}; QString m_currentFileName; QString m_undoViewEmptyLabel; bool m_suppressCurrentSubWindowChangedEvent{false}; bool m_closing{false}; bool m_projectClosing{false}; bool m_autoSaveActive{false}; QTimer m_autoSaveTimer; bool m_showMemoryInfo{true}; MemoryWidget* m_memoryInfoWidget{nullptr}; Qt::WindowStates m_lastWindowState; //< last window state before switching to full screen mode #ifdef Q_OS_MAC KDMacTouchBar* m_touchBar; #endif KRecentFilesAction* m_recentProjectsAction; QAction* m_saveAction; QAction* m_saveAsAction; QAction* m_printAction; QAction* m_printPreviewAction; QAction* m_importFileAction; QAction* m_importSqlAction; QAction* m_importLabPlotAction; QAction* m_importOpjAction; QAction* m_exportAction; QAction* m_closeAction; QAction* m_newFolderAction; QAction* m_newWorkbookAction; QAction* m_newSpreadsheetAction; QAction* m_newMatrixAction; QAction* m_newWorksheetAction; QAction* m_newNotesAction; QAction* m_newLiveDataSourceAction; QAction* m_newSqlDataSourceAction; QAction* m_newScriptAction; QAction* m_newProjectAction; QAction* m_openProjectAction; QAction* m_historyAction; QAction* m_undoAction; QAction* m_redoAction; QAction* m_tileWindows; QAction* m_cascadeWindows; QAction* m_newDatapickerAction; QAction* m_editFitsFileAction; //toggling doch widgets QAction* m_toggleProjectExplorerDocQAction; QAction* m_togglePropertiesDocQAction; //worksheet actions QAction* worksheetZoomInAction; QAction* worksheetZoomOutAction; QAction* worksheetZoomOriginAction; QAction* worksheetZoomFitPageHeightAction; QAction* worksheetZoomFitPageWidthAction; QAction* worksheetZoomFitSelectionAction; QAction* worksheetNavigationModeAction; QAction* worksheetZoomModeAction; QAction* worksheetSelectionModeAction; QAction* worksheetVerticalLayoutAction; QAction* worksheetHorizontalLayoutAction; QAction* worksheetGridLayoutAction; QAction* worksheetBreakLayoutAction; QAction* m_visibilityFolderAction; QAction* m_visibilitySubfolderAction; QAction* m_visibilityAllAction; QAction* m_toggleProjectExplorerDockAction; QAction* m_togglePropertiesDockAction; //Menus QMenu* m_visibilityMenu{nullptr}; QMenu* m_newMenu{nullptr}; QMenu* m_importMenu; QMenu* m_editMenu{nullptr}; //Docks QStackedWidget* stackedWidget; AxisDock* axisDock{nullptr}; QDockWidget* cursorDock{nullptr}; CursorDock* cursorWidget{nullptr}; NoteDock* notesDock{nullptr}; CartesianPlotDock* cartesianPlotDock{nullptr}; CartesianPlotLegendDock* cartesianPlotLegendDock{nullptr}; ColumnDock* columnDock{nullptr}; LiveDataDock* m_liveDataDock{nullptr}; MatrixDock* matrixDock{nullptr}; SpreadsheetDock* spreadsheetDock{nullptr}; ProjectDock* projectDock{nullptr}; XYCurveDock* xyCurveDock{nullptr}; XYEquationCurveDock* xyEquationCurveDock{nullptr}; XYDataReductionCurveDock* xyDataReductionCurveDock{nullptr}; XYDifferentiationCurveDock* xyDifferentiationCurveDock{nullptr}; XYIntegrationCurveDock* xyIntegrationCurveDock{nullptr}; XYInterpolationCurveDock* xyInterpolationCurveDock{nullptr}; XYSmoothCurveDock* xySmoothCurveDock{nullptr}; XYFitCurveDock* xyFitCurveDock{nullptr}; XYFourierFilterCurveDock* xyFourierFilterCurveDock{nullptr}; XYFourierTransformCurveDock* xyFourierTransformCurveDock{nullptr}; XYConvolutionCurveDock* xyConvolutionCurveDock{nullptr}; XYCorrelationCurveDock* xyCorrelationCurveDock{nullptr}; HistogramDock* histogramDock{nullptr}; WorksheetDock* worksheetDock{nullptr}; LabelWidget* textLabelDock{nullptr}; + ImageDock* imageDock{nullptr}; CustomPointDock* customPointDock{nullptr}; DatapickerImageWidget* datapickerImageDock{nullptr}; DatapickerCurveWidget* datapickerCurveDock{nullptr}; void initActions(); void initMenus(); bool warnModified(); void activateSubWindowForAspect(const AbstractAspect*) const; bool save(const QString&); // void toggleShowWidget(QWidget* widget, bool showToRight); // void toggleHideWidget(QWidget* widget, bool hideToLeft); Spreadsheet* activeSpreadsheet() const; //Cantor #ifdef HAVE_CANTOR_LIBS QMenu* m_newCantorWorksheetMenu; CantorWorksheetDock* cantorWorksheetDock{nullptr}; #endif friend class GuiObserver; GuiObserver* m_guiObserver{nullptr}; protected: void closeEvent(QCloseEvent*) override; void dragEnterEvent(QDragEnterEvent*) override; void dropEvent(QDropEvent*) override; private slots: void initGUI(const QString&); void updateGUI(); void updateGUIOnProjectChanges(); void undo(); void redo(); bool newProject(); void openProject(); void openProject(const QString&); void openRecentProject(const QUrl&); bool closeProject(); bool saveProject(); bool saveProjectAs(); void autoSaveProject(); void print(); void printPreview(); void historyDialog(); void importFileDialog(const QString& fileName = QString()); void importSqlDialog(); void importProjectDialog(); void exportDialog(); void editFitsFileDialog(); void settingsDialog(); void projectChanged(); void colorSchemeChanged(QAction*); //Cantor #ifdef HAVE_CANTOR_LIBS void newCantorWorksheet(QAction* action); void cantorSettingsDialog(); #endif void newFolder(); void newWorkbook(); void newSpreadsheet(); void newMatrix(); void newWorksheet(); void newNotes(); void newDatapicker(); //TODO: void newScript(); void newLiveDataSourceActionTriggered(); void createContextMenu(QMenu*) const; void createFolderContextMenu(const Folder*, QMenu*) const; void handleAspectAdded(const AbstractAspect*); void handleAspectAboutToBeRemoved(const AbstractAspect*); void handleAspectRemoved(const AbstractAspect*,const AbstractAspect*,const AbstractAspect*); void handleCurrentAspectChanged(AbstractAspect* ); void handleCurrentSubWindowChanged(QMdiSubWindow*); void handleShowSubWindowRequested(); void handleSettingsChanges(); void setMdiWindowVisibility(QAction*); void updateMdiWindowVisibility() const; void toggleDockWidget(QAction*); void toggleFullScreen(); void projectExplorerDockVisibilityChanged(bool); void propertiesDockVisibilityChanged(bool); void cursorDockVisibilityChanged(bool); void cartesianPlotMouseModeChanged(CartesianPlot::MouseMode); }; #endif diff --git a/src/kdefrontend/dockwidgets/ImageDock.cpp b/src/kdefrontend/dockwidgets/ImageDock.cpp new file mode 100644 index 000000000..deea49b35 --- /dev/null +++ b/src/kdefrontend/dockwidgets/ImageDock.cpp @@ -0,0 +1,518 @@ +/*************************************************************************** + File : ImageDock.cpp + Project : LabPlot + Description : widget for image properties + -------------------------------------------------------------------- + Copyright : (C) 2019 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 * + * * + ***************************************************************************/ + +#include "ImageDock.h" +#include "backend/worksheet/Image.h" +#include "backend/worksheet/Worksheet.h" +#include "kdefrontend/GuiTools.h" +#include "kdefrontend/ThemeHandler.h" +#include "kdefrontend/TemplateHandler.h" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +/*! + \class ImageDock + \brief Provides a widget for editing the properties of the worksheets image element. + + \ingroup kdefrontend +*/ + +ImageDock::ImageDock(QWidget *parent): BaseDock(parent) { + ui.setupUi(this); + m_leName = ui.leName; + m_leComment = ui.leComment; + + ui.bOpen->setIcon( QIcon::fromTheme("document-open") ); + ui.leFileName->setCompleter(new QCompleter(new QDirModel, this)); + + ui.cbSize->addItem(i18n("Original")); + ui.cbSize->addItem(i18n("Custom")); + + //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.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")); + + //SLOTs + //General + connect(ui.leName, &QLineEdit::textChanged, this, &ImageDock::nameChanged); + connect(ui.leComment, &QLineEdit::textChanged, this, &ImageDock::commentChanged); + connect(ui.bOpen, &QPushButton::clicked, this, &ImageDock::selectFile); + connect(ui.leFileName, &QLineEdit::returnPressed, this, &ImageDock::fileNameChanged); + connect(ui.leFileName, &QLineEdit::textChanged, this, &ImageDock::fileNameChanged); + + //Geometry + connect(ui.cbSize, static_cast(&QComboBox::currentIndexChanged), this, &ImageDock::sizeChanged); + connect(ui.cbPositionX, static_cast(&QComboBox::currentIndexChanged), this, &ImageDock::positionXChanged); + connect(ui.cbPositionY, static_cast(&QComboBox::currentIndexChanged), this, &ImageDock::positionYChanged); + connect(ui.sbPositionX, static_cast(&QDoubleSpinBox::valueChanged), this, &ImageDock::customPositionXChanged); + connect(ui.sbPositionY, static_cast(&QDoubleSpinBox::valueChanged), this, &ImageDock::customPositionYChanged); + connect(ui.cbHorizontalAlignment, static_cast(&QComboBox::currentIndexChanged), this, &ImageDock::horizontalAlignmentChanged); + connect(ui.cbVerticalAlignment, static_cast(&QComboBox::currentIndexChanged), this, &ImageDock::verticalAlignmentChanged); + connect(ui.sbRotation, static_cast(&QSpinBox::valueChanged), this, &ImageDock::rotationChanged); + + connect(ui.chbVisible, &QCheckBox::clicked, this, &ImageDock::visibilityChanged); + + //Border + connect(ui.cbBorderStyle, static_cast(&QComboBox::currentIndexChanged), this, &ImageDock::borderStyleChanged); + connect(ui.kcbBorderColor, &KColorButton::changed, this, &ImageDock::borderColorChanged); + connect(ui.sbBorderWidth, static_cast(&QDoubleSpinBox::valueChanged), this, &ImageDock::borderWidthChanged); + connect(ui.sbBorderOpacity, static_cast(&QSpinBox::valueChanged), this, &ImageDock::borderOpacityChanged); +} + +void ImageDock::setImages(QList list) { + m_initializing = true; + m_imageList = list; + m_image = list.first(); + m_aspect = list.first(); + + //if there are more then one image in the list, disable the name and comment field in the tab "general" + if (list.size() == 1) { + ui.lName->setEnabled(true); + ui.leName->setEnabled(true); + ui.lComment->setEnabled(true); + ui.leComment->setEnabled(true); + + ui.leName->setText(m_image->name()); + ui.leComment->setText(m_image->comment()); + } else { + ui.lName->setEnabled(false); + ui.leName->setEnabled(false); + ui.lComment->setEnabled(false); + ui.leComment->setEnabled(false); + + ui.leName->setText(QString()); + ui.leComment->setText(QString()); + } + ui.leName->setStyleSheet(""); + ui.leName->setToolTip(""); + + //show the properties of the first image + this->load(); + + //init connections + //General + connect(m_image, &Image::fileNameChanged, this, &ImageDock::imageFileNameChanged); + connect(m_image, &Image::opacityChanged, this, &ImageDock::imageOpacityChanged); + connect(m_image, &Image::visibleChanged, this, &ImageDock::imageVisibleChanged); + + //Geometry + connect(m_image, &Image::positionChanged, this, &ImageDock::imagePositionChanged); + connect(m_image, &Image::horizontalAlignmentChanged, this, &ImageDock::imageHorizontalAlignmentChanged); + connect(m_image, &Image::verticalAlignmentChanged, this, &ImageDock::imageVerticalAlignmentChanged); + connect(m_image, &Image::rotationAngleChanged, this, &ImageDock::imageRotationAngleChanged); + + //Border + connect(m_image, &Image::borderPenChanged, this, &ImageDock::imageBorderPenChanged); + connect(m_image, &Image::borderOpacityChanged, this, &ImageDock::imageBorderOpacityChanged); + + m_initializing = false; +} + + +//************************************************************* +//******** SLOTs for changes triggered in ImageDock *********** +//************************************************************* + +/*! + opens a file dialog and lets the user select the image file. +*/ +void ImageDock::selectFile() { + KConfigGroup conf(KSharedConfig::openConfig(), "ImageDock"); + QString dir = conf.readEntry("LastImageDir", ""); + + QString formats; + for (const QByteArray& format : QImageReader::supportedImageFormats()) { + QString f = "*." + QString(format.constData()); + if (f == QLatin1String("*.svg")) + continue; + formats.isEmpty() ? formats += f : formats += ' ' + f; + } + + QString path = QFileDialog::getOpenFileName(this, i18n("Select the image file"), dir, i18n("Images (%1)", formats)); + if (path.isEmpty()) + return; //cancel was clicked in the file-dialog + + int pos = path.lastIndexOf(QDir::separator()); + if (pos != -1) { + QString newDir = path.left(pos); + if (newDir != dir) + conf.writeEntry("LastImageDir", newDir); + } + + ui.leFileName->setText(path); +} + +void ImageDock::fileNameChanged() { + if (m_initializing) + return; + + QString fileName = ui.leFileName->text(); + if (!fileName.isEmpty() && !QFile::exists(fileName)) + ui.leFileName->setStyleSheet("QLineEdit{background:red;}"); + else + ui.leFileName->setStyleSheet(QString()); + + for (auto* image : m_imageList) + image->setFileName(fileName); +} + +void ImageDock::opacityChanged(int value) { + if (m_initializing) + return; + + float opacity = (float)value/100; + for (auto* image : m_imageList) + image->setOpacity(opacity); +} + +// geometry slots +void ImageDock::sizeChanged(int index) { + Q_UNUSED(index); +} +/*! + called when label's current horizontal position relative to its parent (left, center, right, custom ) is changed. +*/ +void ImageDock::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; + + WorksheetElement::PositionWrapper position = m_image->position(); + position.horizontalPosition = WorksheetElement::HorizontalPosition(index); + for (auto* image : m_imageList) + image->setPosition(position); +} + +/*! + called when label's current horizontal position relative to its parent (top, center, bottom, custom ) is changed. +*/ +void ImageDock::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; + + WorksheetElement::PositionWrapper position = m_image->position(); + position.verticalPosition = WorksheetElement::VerticalPosition(index); + for (auto* image : m_imageList) + image->setPosition(position); +} + +void ImageDock::customPositionXChanged(double value) { + if (m_initializing) + return; + + WorksheetElement::PositionWrapper position = m_image->position(); + position.point.setX(Worksheet::convertToSceneUnits(value, Worksheet::Centimeter)); + for (auto* image : m_imageList) + image->setPosition(position); +} + +void ImageDock::customPositionYChanged(double value) { + if (m_initializing) + return; + + WorksheetElement::PositionWrapper position = m_image->position(); + position.point.setY(Worksheet::convertToSceneUnits(value, Worksheet::Centimeter)); + for (auto* image : m_imageList) + image->setPosition(position); +} + +void ImageDock::horizontalAlignmentChanged(int index) { + if (m_initializing) + return; + + for (auto* image : m_imageList) + image->setHorizontalAlignment(WorksheetElement::HorizontalAlignment(index)); +} + +void ImageDock::verticalAlignmentChanged(int index) { + if (m_initializing) + return; + + for (auto* image : m_imageList) + image->setVerticalAlignment(WorksheetElement::VerticalAlignment(index)); +} + +void ImageDock::rotationChanged(int value) { + if (m_initializing) + return; + + for (auto* image : m_imageList) + image->setRotationAngle(value); +} + +void ImageDock::visibilityChanged(bool state) { + if (m_initializing) + return; + + for (auto* image : m_imageList) + image->setVisible(state); +} + +//border +void ImageDock::borderStyleChanged(int index) { + if (m_initializing) + return; + + auto penStyle = Qt::PenStyle(index); + QPen pen; + for (auto* image : m_imageList) { + pen = image->borderPen(); + pen.setStyle(penStyle); + image->setBorderPen(pen); + } +} + +void ImageDock::borderColorChanged(const QColor& color) { + if (m_initializing) + return; + + QPen pen; + for (auto* image : m_imageList) { + pen = image->borderPen(); + pen.setColor(color); + image->setBorderPen(pen); + } + + m_initializing = true; + GuiTools::updatePenStyles(ui.cbBorderStyle, color); + m_initializing = false; +} + +void ImageDock::borderWidthChanged(double value) { + if (m_initializing) + return; + + QPen pen; + for (auto* image : m_imageList) { + pen = image->borderPen(); + pen.setWidthF( Worksheet::convertToSceneUnits(value, Worksheet::Point) ); + image->setBorderPen(pen); + } +} + +void ImageDock::borderOpacityChanged(int value) { + if (m_initializing) + return; + + qreal opacity = (float)value/100.; + for (auto* image : m_imageList) + image->setBorderOpacity(opacity); +} + +//************************************************************* +//********** SLOTs for changes triggered in Image ************* +//************************************************************* +void ImageDock::imageDescriptionChanged(const AbstractAspect* aspect) { + if (m_image != aspect) + return; + + m_initializing = true; + if (aspect->name() != ui.leName->text()) + ui.leName->setText(aspect->name()); + else if (aspect->comment() != ui.leComment->text()) + ui.leComment->setText(aspect->comment()); + m_initializing = false; +} + +void ImageDock::imageFileNameChanged(const QString& name) { + m_initializing = true; + ui.leFileName->setText(name); + m_initializing = false; +} + +void ImageDock::imageOpacityChanged(float opacity) { + m_initializing = true; + ui.sbOpacity->setValue( qRound(opacity*100.0) ); + m_initializing = false; +} + +void ImageDock::imagePositionChanged(const WorksheetElement::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 ImageDock::imageHorizontalAlignmentChanged(WorksheetElement::HorizontalAlignment index) { + m_initializing = true; + ui.cbHorizontalAlignment->setCurrentIndex(index); + m_initializing = false; +} + +void ImageDock::imageVerticalAlignmentChanged(WorksheetElement::VerticalAlignment index) { + m_initializing = true; + ui.cbVerticalAlignment->setCurrentIndex(index); + m_initializing = false; +} + +void ImageDock::imageRotationAngleChanged(qreal angle) { + m_initializing = true; + ui.sbRotation->setValue(angle); + m_initializing = false; +} + +void ImageDock::imageVisibleChanged(bool on) { + m_initializing = true; + ui.chbVisible->setChecked(on); + m_initializing = false; +} + +//border + +void ImageDock::imageBorderPenChanged(const 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 ImageDock::imageBorderOpacityChanged(float value) { + m_initializing = true; + float v = (float)value*100.; + ui.sbBorderOpacity->setValue(v); + m_initializing = false; +} + +//************************************************************* +//******************** SETTINGS ******************************* +//************************************************************* +void ImageDock::load() { + if (!m_image) + return; + + m_initializing = true; + + ui.leFileName->setText(m_image->fileName()); + ui.chbVisible->setChecked(m_image->isVisible()); + + // Geometry + ui.cbPositionX->setCurrentIndex( (int) m_image->position().horizontalPosition ); + positionXChanged(ui.cbPositionX->currentIndex()); + ui.sbPositionX->setValue( Worksheet::convertFromSceneUnits(m_image->position().point.x(),Worksheet::Centimeter) ); + ui.cbPositionY->setCurrentIndex( (int) m_image->position().verticalPosition ); + positionYChanged(ui.cbPositionY->currentIndex()); + ui.sbPositionY->setValue( Worksheet::convertFromSceneUnits(m_image->position().point.y(),Worksheet::Centimeter) ); + + ui.cbHorizontalAlignment->setCurrentIndex( (int) m_image->horizontalAlignment() ); + ui.cbVerticalAlignment->setCurrentIndex( (int) m_image->verticalAlignment() ); + ui.sbRotation->setValue( m_image->rotationAngle() ); + + //Border + ui.kcbBorderColor->setColor( m_image->borderPen().color() ); + ui.cbBorderStyle->setCurrentIndex( (int) m_image->borderPen().style() ); + ui.sbBorderWidth->setValue( Worksheet::convertFromSceneUnits(m_image->borderPen().widthF(), Worksheet::Point) ); + ui.sbBorderOpacity->setValue( round(m_image->borderOpacity()*100) ); + GuiTools::updatePenStyles(ui.cbBorderStyle, ui.kcbBorderColor->color()); + + m_initializing = false; +} + +void ImageDock::loadConfigFromTemplate(KConfig& config) { + +} + +void ImageDock::loadConfig(KConfig& config) { + if (!m_image) + return; + + m_initializing = true; + + // Geometry +// ui.cbPositionX->setCurrentIndex( group.readEntry("PositionX", (int) m_image->position().horizontalPosition ) ); +// ui.sbPositionX->setValue( Worksheet::convertFromSceneUnits(group.readEntry("PositionXValue", m_image->position().point.x()),Worksheet::Centimeter) ); +// ui.cbPositionY->setCurrentIndex( group.readEntry("PositionY", (int) m_image->position().verticalPosition ) ); +// ui.sbPositionY->setValue( Worksheet::convertFromSceneUnits(group.readEntry("PositionYValue", m_image->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_image->horizontalAlignment()) ); +// ui.cbVerticalAlignment->setCurrentIndex( group.readEntry("VerticalAlignment", (int) m_image->verticalAlignment()) ); +// ui.sbRotation->setValue( group.readEntry("Rotation", m_image->rotationAngle()) ); +// +// //Border +// ui.cbBorderShape->setCurrentIndex(group.readEntry("BorderShape").toInt()); +// ui.kcbBorderColor->setColor( group.readEntry("BorderColor", m_image->borderPen().color()) ); +// ui.cbBorderStyle->setCurrentIndex( group.readEntry("BorderStyle", (int)m_image->borderPen().style()) ); +// ui.sbBorderWidth->setValue( Worksheet::convertFromSceneUnits(group.readEntry("BorderWidth", m_image->borderPen().widthF()), Worksheet::Point) ); +// ui.sbBorderOpacity->setValue( group.readEntry("BorderOpacity", m_image->borderOpacity())*100 ); + m_initializing = false; +} + +void ImageDock::saveConfigAsTemplate(KConfig& config) { + +} diff --git a/src/kdefrontend/dockwidgets/ImageDock.h b/src/kdefrontend/dockwidgets/ImageDock.h new file mode 100644 index 000000000..729245ab5 --- /dev/null +++ b/src/kdefrontend/dockwidgets/ImageDock.h @@ -0,0 +1,102 @@ +/*************************************************************************** + File : ImageDock.h + Project : LabPlot + Description : widget for image properties + -------------------------------------------------------------------- + Copyright : (C) 2019 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 IMAGEDOCK_H +#define IMAGEDOCK_H + +#include "kdefrontend/dockwidgets/BaseDock.h" +#include "backend/worksheet/TextLabel.h" +#include "ui_imagedock.h" + +class AbstractAspect; +class Image; +class KConfig; + +class ImageDock : public BaseDock { + Q_OBJECT + +public: + explicit ImageDock(QWidget*); + void setImages(QList); + +private: + Ui::ImageDock ui; + QList m_imageList; + Image* m_image{nullptr}; + + void load(); + void loadConfig(KConfig&); + +private slots: + //SLOTs for changes triggered in ImageDock + void selectFile(); + void fileNameChanged(); + void opacityChanged(int); + + //geometry + void sizeChanged(int); + void positionXChanged(int); + void positionYChanged(int); + void customPositionXChanged(double); + void customPositionYChanged(double); + void horizontalAlignmentChanged(int); + void verticalAlignmentChanged(int); + void rotationChanged(int); + + //border + void borderStyleChanged(int); + void borderColorChanged(const QColor&); + void borderWidthChanged(double); + void borderOpacityChanged(int); + + void visibilityChanged(bool); + + //SLOTs for changes triggered in Image + void imageDescriptionChanged(const AbstractAspect*); + void imageFileNameChanged(const QString&); + void imageOpacityChanged(float); + + void imagePositionChanged(const WorksheetElement::PositionWrapper&); + void imageHorizontalAlignmentChanged(WorksheetElement::HorizontalAlignment); + void imageVerticalAlignmentChanged(WorksheetElement::VerticalAlignment); + void imageRotationAngleChanged(qreal); + + void imageBorderPenChanged(const QPen&); + void imageBorderOpacityChanged(float); + + void imageVisibleChanged(bool); + + //save/load templates + void loadConfigFromTemplate(KConfig&); + void saveConfigAsTemplate(KConfig&); + +signals: + void info(const QString&); +}; + +#endif // WORKSHEETDOCK_H diff --git a/src/kdefrontend/ui/dockwidgets/imagedock.ui b/src/kdefrontend/ui/dockwidgets/imagedock.ui new file mode 100644 index 000000000..1d7c5be4b --- /dev/null +++ b/src/kdefrontend/ui/dockwidgets/imagedock.ui @@ -0,0 +1,493 @@ + + + ImageDock + + + + 0 + 0 + 581 + 1030 + + + + Form + + + + + + + 0 + 0 + + + + Name: + + + + + + + Qt::Horizontal + + + QSizePolicy::Fixed + + + + 13 + 23 + + + + + + + + + + + + 0 + 0 + + + + Comment: + + + + + + + + 0 + 0 + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 58 + 13 + + + + + + + + File name: + + + + + + + Specify the name of the image file. + + + true + + + + + + + + 0 + 0 + + + + Select the file to import + + + + + + + + + + Opacity: + + + + + + + + 0 + 0 + + + + The opacity ranges from 0 to 100, where 0 is fully transparent and 100 is fully opaque. + + + % + + + 0 + + + 100 + + + 10 + + + 100 + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 58 + 13 + + + + + + + + + 75 + true + + + + Geometry + + + + + + + Size: + + + + + + + + + + Horizontal position relative to label's parent + + + x: + + + + + + + + + + 0 + 0 + + + + Horizontal position relative to label's parent + + + + + + + + 0 + 0 + + + + Horizontal position relative to label's parent + + + cm + + + -99.989999999999995 + + + 0.500000000000000 + + + + + + + + + Vertical position relative to label's parent + + + y: + + + + + + + + + + 0 + 0 + + + + Vertical position relative to label's parent + + + + + + + + 0 + 0 + + + + Vertical position relative to label's parent + + + cm + + + -99.989999999999995 + + + 0.500000000000000 + + + + + + + + + Hor. align.: + + + + + + + + + + Vert. align.: + + + + + + + + + + Rotation: + + + + + + + ° + + + -360 + + + 360 + + + 5 + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 58 + 13 + + + + + + + + + 75 + true + + + + QFrame::NoFrame + + + Border + + + + + + + Style: + + + + + + + + 0 + 0 + + + + + + + + Color: + + + + + + + + 0 + 0 + + + + + + + + Width: + + + + + + + pt + + + 0.500000000000000 + + + + + + + Opacity: + + + + + + + + 0 + 0 + + + + The opacity ranges from 0 to 100, where 0 is fully transparent and 100 is fully opaque. + + + % + + + + + + 0 + + + 100 + + + 10 + + + 100 + + + + + + + Qt::Vertical + + + + 20 + 128 + + + + + + + + Visible + + + + + + + + QComboBox + QComboBox +
kcombobox.h
+
+ + KColorButton + QPushButton +
kcolorbutton.h
+
+
+ + +
diff --git a/src/kdefrontend/widgets/LabelWidget.cpp b/src/kdefrontend/widgets/LabelWidget.cpp index cf5bd07e9..c26d29228 100644 --- a/src/kdefrontend/widgets/LabelWidget.cpp +++ b/src/kdefrontend/widgets/LabelWidget.cpp @@ -1,1122 +1,1121 @@ /*************************************************************************** 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 "kdefrontend/dockwidgets/BaseDock.h" #include "tools/TeXRenderer.h" #include #include #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_dateTimeMenu(new QMenu(this)) { 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")) ); ui.tbFontBold->setToolTip(i18n("Bold")); ui.tbFontItalic->setToolTip(i18n("Italic")); ui.tbFontUnderline->setToolTip(i18n("Underline")); ui.tbFontStrikeOut->setToolTip(i18n("Strike Out")); ui.tbFontSuperScript->setToolTip(i18n("Super Script")); ui.tbFontSubScript->setToolTip(i18n("Sub-Script")); ui.tbSymbols->setToolTip(i18n("Insert Symbol")); ui.tbDateTime->setToolTip(i18n("Insert Date/Time")); ui.tbTexUsed->setToolTip(i18n("Switch to TeX mode")); //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")); ui.kcbBackgroundColor->setAlphaChannelEnabled(true); ui.kcbBackgroundColor->setColor(QColor(0,0,0, 0)); // transparent ui.kcbFontColor->setAlphaChannelEnabled(true); ui.kcbFontColor->setColor(QColor(255,255,255, 255)); // black ui.kcbBorderColor->setAlphaChannelEnabled(true); ui.kcbBorderColor->setColor(QColor(255,255,255, 255)); // black //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, &QToolButton::clicked, this, &LabelWidget::teXUsedChanged ); connect(ui.teLabel, &ResizableTextEdit::textChanged, this, &LabelWidget::textChanged); connect(ui.teLabel, &ResizableTextEdit::currentCharFormatChanged, this, &LabelWidget::charFormatChanged); connect(ui.kcbFontColor, &KColorButton::changed, this, &LabelWidget::fontColorChanged); connect(ui.kcbBackgroundColor, &KColorButton::changed, this, &LabelWidget::backgroundColorChanged); connect(ui.tbFontBold, &QToolButton::clicked, this, &LabelWidget::fontBoldChanged); connect(ui.tbFontItalic, &QToolButton::clicked, this, &LabelWidget::fontItalicChanged); connect(ui.tbFontUnderline, &QToolButton::clicked, this, &LabelWidget::fontUnderlineChanged); connect(ui.tbFontStrikeOut, &QToolButton::clicked, this, &LabelWidget::fontStrikeOutChanged); connect(ui.tbFontSuperScript, &QToolButton::clicked, this, &LabelWidget::fontSuperScriptChanged); connect(ui.tbFontSubScript, &QToolButton::clicked, this, &LabelWidget::fontSubScriptChanged); connect(ui.tbSymbols, &QToolButton::clicked, this, &LabelWidget::charMenu); connect(ui.tbDateTime, &QToolButton::clicked, this, &LabelWidget::dateTimeMenu); connect(m_dateTimeMenu, &QMenu::triggered, this, &LabelWidget::insertDateTime ); connect(ui.kfontRequester, &KFontRequester::fontSelected, this, &LabelWidget::fontChanged); connect(ui.kfontRequesterTeX, &KFontRequester::fontSelected, this, &LabelWidget::teXFontChanged); connect(ui.sbFontSize, static_cast(&QSpinBox::valueChanged), this, &LabelWidget::fontSizeChanged); // geometry connect( ui.cbPositionX, static_cast(&KComboBox::currentIndexChanged), this, &LabelWidget::positionXChanged); connect( ui.cbPositionY, static_cast(&KComboBox::currentIndexChanged), this, &LabelWidget::positionYChanged); connect( ui.sbPositionX, static_cast(&QDoubleSpinBox::valueChanged), this, &LabelWidget::customPositionXChanged); connect( ui.sbPositionY, static_cast(&QDoubleSpinBox::valueChanged), this, &LabelWidget::customPositionYChanged); connect( ui.cbHorizontalAlignment, static_cast(&KComboBox::currentIndexChanged), this, &LabelWidget::horizontalAlignmentChanged); connect( ui.cbVerticalAlignment, static_cast(&KComboBox::currentIndexChanged), this, &LabelWidget::verticalAlignmentChanged); connect( ui.sbRotation, static_cast(&QSpinBox::valueChanged), this, &LabelWidget::rotationChanged); connect( ui.sbOffsetX, static_cast(&QDoubleSpinBox::valueChanged), this, &LabelWidget::offsetXChanged); connect( ui.sbOffsetY, static_cast(&QDoubleSpinBox::valueChanged), this, &LabelWidget::offsetYChanged); connect( ui.chbVisible, &QCheckBox::clicked, this, &LabelWidget::visibilityChanged); //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, &Axis::titleOffsetXChanged, this, &LabelWidget::labelOffsetxChanged); connect(axis, &Axis::titleOffsetYChanged, this, &LabelWidget::labelOffsetyChanged ); connect(axis->title(), &TextLabel::rotationAngleChanged, this, &LabelWidget::labelRotationAngleChanged ); } m_axesList = axes; m_label = m_labelsList.first(); this->load(); initConnections(); } void LabelWidget::initConnections() const { - connect( m_label, &TextLabel::textWrapperChanged, this, &LabelWidget::labelTextWrapperChanged); - connect( m_label, &TextLabel::teXImageUpdated, this, &LabelWidget::labelTeXImageUpdated); - connect( m_label, &TextLabel::teXFontChanged, this, &LabelWidget::labelTeXFontChanged); - connect( m_label, &TextLabel::fontColorChanged, this, &LabelWidget::labelFontColorChanged); - connect (m_label, &TextLabel::backgroundColorChanged, this, &LabelWidget::labelBackgroundColorChanged); - connect( m_label, &TextLabel::positionChanged, this, &LabelWidget::labelPositionChanged); - connect( m_label, &TextLabel::horizontalAlignmentChanged, this, &LabelWidget::labelHorizontalAlignmentChanged); - connect( m_label, &TextLabel::verticalAlignmentChanged, this, &LabelWidget::labelVerticalAlignmentChanged); - connect( m_label, &TextLabel::rotationAngleChanged, this, &LabelWidget::labelRotationAngleChanged); + connect(m_label, &TextLabel::textWrapperChanged, this, &LabelWidget::labelTextWrapperChanged); + connect(m_label, &TextLabel::teXImageUpdated, this, &LabelWidget::labelTeXImageUpdated); + connect(m_label, &TextLabel::teXFontChanged, this, &LabelWidget::labelTeXFontChanged); + connect(m_label, &TextLabel::fontColorChanged, this, &LabelWidget::labelFontColorChanged); + connect(m_label, &TextLabel::backgroundColorChanged, this, &LabelWidget::labelBackgroundColorChanged); + connect(m_label, &TextLabel::positionChanged, this, &LabelWidget::labelPositionChanged); + connect(m_label, &TextLabel::horizontalAlignmentChanged, this, &LabelWidget::labelHorizontalAlignmentChanged); + connect(m_label, &TextLabel::verticalAlignmentChanged, this, &LabelWidget::labelVerticalAlignmentChanged); + connect(m_label, &TextLabel::rotationAngleChanged, this, &LabelWidget::labelRotationAngleChanged); 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, &TextLabel::visibleChanged, this, &LabelWidget::labelVisibleChanged); - connect( m_label, &TextLabel::visibleChanged, this, &LabelWidget::labelVisibleChanged); + connect(m_label, &TextLabel::visibleChanged, this, &LabelWidget::labelVisibleChanged); } /*! * 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; const Lock lock(m_initializing); 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().isEmpty()) text.clear(); else text = ui.teLabel->toHtml(); TextLabel::TextWrapper wrapper(text, false); for (auto* label : m_labelsList) { label->setText(wrapper); // Don't set FontColor, because the font color is already in the html code // of the text. The font color is used to change the color for unformated // text like from themes // label->setFontColor(ui.kcbFontColor->color()); // label->setBackgroundColor(ui.kcbBackgroundColor->color()); } } } /*! * \brief LabelWidget::charFormatChanged * \param format * Used to update the colors, font,... in the color font widgets to show the style of the selected text */ void LabelWidget::charFormatChanged(const QTextCharFormat& format) { if (m_initializing) return; if (ui.tbTexUsed->isChecked()) return; m_initializing = true; // 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 if (format.foreground().color().isValid()) ui.kcbFontColor->setColor(format.foreground().color()); else ui.kcbFontColor->setColor(m_label->fontColor()); if (format.background().color().isValid()) ui.kcbBackgroundColor->setColor(format.background().color()); else ui.kcbBackgroundColor->setColor(m_label->backgroundColor()); ui.kfontRequester->setFont(format.font()); m_initializing = false; } 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); //TODO: //for normal text we need to hide the background color because of QTBUG-25420 ui.kcbBackgroundColor->setVisible(checked); ui.lBackgroundColor->setVisible(checked); if (checked) { ui.tbTexUsed->setToolTip(i18n("Switch to TeX mode")); //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->fontColor()); ui.kcbBackgroundColor->setColor(m_label->backgroundColor()); } else { ui.tbTexUsed->setToolTip(i18n("Switch to text mode")); #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(QString()); } //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); 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; if (!m_teXEnabled && m_label->text().teXUsed) { //if no selection is done, apply the new color for the whole label, //apply to the currently selected part of the text only otherwise QTextCursor c = ui.teLabel->textCursor(); if (c.selectedText().isEmpty()) ui.teLabel->selectAll(); ui.teLabel->setTextColor(color); } else { for (auto* label : m_labelsList) label->setFontColor(color); } } void LabelWidget::backgroundColorChanged(const QColor& color) { if (m_initializing) return; if (!m_teXEnabled && m_label->text().teXUsed) { QTextCursor c = ui.teLabel->textCursor(); if (c.selectedText().isEmpty()) ui.teLabel->selectAll(); ui.teLabel->setTextBackgroundColor(color); } else { for (auto* label : m_labelsList) label->setBackgroundColor(color); } } void LabelWidget::fontSizeChanged(int value) { if (m_initializing) return; QTextCursor c = ui.teLabel->textCursor(); if (c.selectedText().isEmpty()) ui.teLabel->selectAll(); 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; QTextCursor c = ui.teLabel->textCursor(); if (c.selectedText().isEmpty()) ui.teLabel->selectAll(); if (checked) ui.teLabel->setFontWeight(QFont::Bold); else ui.teLabel->setFontWeight(QFont::Normal); } void LabelWidget::fontItalicChanged(bool checked) { if (m_initializing) return; QTextCursor c = ui.teLabel->textCursor(); if (c.selectedText().isEmpty()) ui.teLabel->selectAll(); ui.teLabel->setFontItalic(checked); } void LabelWidget::fontUnderlineChanged(bool checked) { if (m_initializing) return; QTextCursor c = ui.teLabel->textCursor(); if (c.selectedText().isEmpty()) ui.teLabel->selectAll(); ui.teLabel->setFontUnderline(checked); } void LabelWidget::fontStrikeOutChanged(bool checked) { if (m_initializing) return; QTextCursor c = ui.teLabel->textCursor(); if (c.selectedText().isEmpty()) ui.teLabel->selectAll(); QTextCharFormat format = ui.teLabel->currentCharFormat(); format.setFontStrikeOut(checked); ui.teLabel->setCurrentCharFormat(format); } void LabelWidget::fontSuperScriptChanged(bool checked) { if (m_initializing) return; QTextCursor c = ui.teLabel->textCursor(); if (c.selectedText().isEmpty()) ui.teLabel->selectAll(); 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; QTextCursor c = ui.teLabel->textCursor(); if (c.selectedText().isEmpty()) ui.teLabel->selectAll(); 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; QTextCursor c = ui.teLabel->textCursor(); if (c.selectedText().isEmpty()) ui.teLabel->selectAll(); // use format instead of using ui.teLabel->setFontFamily(font.family()); // because this calls after every command textChanged() which is inefficient QTextCharFormat format = ui.teLabel->currentCharFormat(); format.setFontFamily(font.family()); format.setFontPointSize(font.pointSize()); format.setFontItalic(font.italic()); format.setFontWeight(font.weight()); if (font.underline()) format.setUnderlineStyle(QTextCharFormat::UnderlineStyle::SingleUnderline); if (font.strikeOut()) // anytime true. don't know why format.setFontStrikeOut(font.strikeOut()); ui.teLabel->setCurrentCharFormat(format); } 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); QFont font = ui.teLabel->currentFont(); // use the system default size, otherwise the symbols might be hard to read // if the current label font size is too small font.setPointSize(QFont().pointSize()); selection.setCurrentFont(font); 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(); const QString configPath = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); const QString configFile = configPath + QLatin1String("/klanguageoverridesrc"); if (!QFile::exists(configFile)) { 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::SystemLocaleShortDate) ); m_dateTimeMenu->addAction( date.toString(Qt::SystemLocaleLongDate) ); m_dateTimeMenu->addAction( date.toString(Qt::RFC2822Date) ); 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::SystemLocaleShortDate) ); m_dateTimeMenu->addAction( time.toString(Qt::SystemLocaleLongDate) ); m_dateTimeMenu->addAction( time.toString(Qt::RFC2822Date) ); } else { //application language was changed: //determine the currently used language and use QLocale::toString() //to get the strings translated into the currently used language QSettings settings (configFile, QSettings::IniFormat); settings.beginGroup(QLatin1String("Language")); QByteArray languageCode; languageCode = settings.value(qAppName(), languageCode).toByteArray(); QLocale locale(QString::fromLatin1(languageCode.data())); QDate date = QDate::currentDate(); m_dateTimeMenu->addSeparator()->setText(i18n("Date")); m_dateTimeMenu->addAction( locale.toString(date, QLatin1String("ddd MMM d yyyy")) ); //Qt::TextDate m_dateTimeMenu->addAction( locale.toString(date, QLatin1String("yyyy-MM-dd")) ); //Qt::ISODate m_dateTimeMenu->addAction( locale.system().toString(date, QLocale::ShortFormat) ); //Qt::SystemLocaleShortDate //no LongFormat here since it would contain strings in system's language which (potentially) is not the current application language m_dateTimeMenu->addAction( locale.toString(date, QLatin1String("dd MMM yyyy")) ); //Qt::RFC2822Date QDateTime time = QDateTime::currentDateTime(); m_dateTimeMenu->addSeparator()->setText(i18n("Date and Time")); m_dateTimeMenu->addAction( locale.toString(time, QLatin1String("ddd MMM d hh:mm:ss yyyy")) ); //Qt::TextDate m_dateTimeMenu->addAction( locale.toString(time, QLatin1String("yyyy-MM-ddTHH:mm:ss")) ); //Qt::ISODate m_dateTimeMenu->addAction( locale.system().toString(time, QLocale::ShortFormat) ); //Qt::SystemLocaleShortDate //no LongFormat here since it would contain strings in system's language which (potentially) is not the current application language //TODO: RFC2822 requires time zone but Qt QLocale::toString() seems to ignore TZD (time zone designator) completely, //which works correctly with QDateTime::toString() m_dateTimeMenu->addAction( locale.toString(time, QLatin1String("dd MMM yyyy hh:mm:ss")) ); //Qt::RFC2822Date } 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) { auto 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) { if (m_initializing)return; const Lock lock(m_initializing); //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); } /*! * \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(QString()); } void LabelWidget::labelTeXFontChanged(const QFont& font) { m_initializing = true; ui.kfontRequesterTeX->setFont(font); ui.sbFontSize->setValue(font.pointSize()); m_initializing = false; } void LabelWidget::labelFontColorChanged(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::labelBackgroundColorChanged(const QColor color) { m_initializing = true; ui.kcbBackgroundColor->setColor(color); 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(const 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); ui.teLabel->selectAll(); // must be done to retrieve font ui.kfontRequester->setFont(ui.teLabel->currentFont()); } QTextCharFormat format = ui.teLabel->currentCharFormat(); // don't use colors from the textlabel, but ui.kcbFontColor->setColor(format.foreground().color()); ui.kcbBackgroundColor->setColor(format.background().color()); this->teXUsedChanged(m_label->text().teXUsed); ui.kfontRequesterTeX->setFont(format.font()); 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); }