diff --git a/CMakeLists.txt b/CMakeLists.txt
index 30581d22f..e23fba315 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,337 +1,334 @@
project(labplot2)
# minimum 3.2.0 for FindGSL.cmake
cmake_minimum_required(VERSION 3.2.0)
set(KF5_MIN_VERSION "5.16.0")
find_package(ECM 1.3.0 REQUIRED NO_MODULE)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH} ${ECM_KDE_MODULE_DIR})
# build type: "release", "debug", "debugfull"
string (TOLOWER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE)
find_package(Qt5 ${QT_MIN_VERSION} NO_MODULE REQUIRED COMPONENTS
Concurrent
Gui
PrintSupport
Sql
Svg
Widgets
Test
SerialPort
)
find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS
Archive
Completion
Config
ConfigWidgets
CoreAddons
DocTools
I18n
IconThemes
-# TODO: remove KDELibs4Support
- KDELibs4Support
- KIO
TextWidgets
WidgetsAddons
XmlGui
OPTIONAL_COMPONENTS
NewStuff
SyntaxHighlighting
)
IF (Qt5SerialPort_FOUND)
MESSAGE (STATUS "Found Qt5 SerialPort")
ELSE ()
MESSAGE (STATUS "Qt5 SerialPort not found")
ENDIF ()
IF (KF5NewStuff_FOUND)
MESSAGE (STATUS "Found KF5 new stuff")
add_definitions (-DHAVE_KF5_NEW_STUFF)
ELSE ()
MESSAGE (STATUS "KF5 new stuff not found")
ENDIF ()
IF (KF5SyntaxHighlighting_FOUND)
MESSAGE (STATUS "Found KF5 syntax highlighting")
add_definitions (-DHAVE_KF5_SYNTAX_HIGHLIGHTING)
ELSE ()
MESSAGE (STATUS "KF5 syntax highlighting not found")
ENDIF ()
find_package(BISON REQUIRED)
include(FeatureSummary)
include(ECMAddAppIcon)
include(ECMInstallIcons)
include(KDEInstallDirs)
include(KDECompilerSettings)
include(KDECMakeSettings)
### compiler flags ######################################
set (GENERIC_FLAGS "-Wall -Wextra -Wundef -Wpointer-arith -Wunreachable-code -Wunused -Wdeprecated-declarations -fno-omit-frame-pointer -fstack-protector")
set (GENERIC_GNU_FLAGS "-O2 -Wcast-align -Wswitch-enum -fvisibility=default")
set (GENERIC_C_FLAGS "-std=c99 ${GENERIC_FLAGS} -fno-exceptions")
# liborigin needs exceptions
set (GENERIC_CXX_FLAGS "-std=c++11 ${GENERIC_FLAGS} -fexceptions")
if ("${CMAKE_C_COMPILER_ID}" MATCHES "GNU")
message(STATUS "GNU C compiler detected, adding compile flags")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GENERIC_C_FLAGS} ${GENERIC_GNU_FLAGS}")
elseif ("${CMAKE_C_COMPILER_ID}" MATCHES "Clang")
message(STATUS "Clang C compiler detected, adding compile flags")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE ${GENERIC_C_FLAGS} ${GENERIC_GNU_FLAGS}")
elseif ("${CMAKE_C_COMPILER_ID}" MATCHES "Intel")
message(STATUS "Intel C compiler detected, adding compile flags")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE -O3 ${GENERIC_C_FLAGS}")
elseif ("${CMAKE_C_COMPILER_ID}" MATCHES "PGI")
message(STATUS "PGI C compiler detected, adding compile flags")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -D_GNU_SOURCE -O3 -D__GCC_ATOMIC_TEST_AND_SET_TRUEVAL=1 -Minform=inform -Mbounds -Mchkstk")
# " x" postfix to work around a bug in CMake that causes "MSVC" to translate to something completely different
elseif (("${CMAKE_C_COMPILER_ID} x" MATCHES "MSVC") OR MSVC)
message(STATUS "MSVC C compiler detected, adding compile flags")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -O2 -W3")
set(MSVC_FOUND TRUE)
else ()
message(STATUS "UNKNOWN C compiler, adding compile flags")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${GENERIC_C_FLAGS}")
endif()
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "GNU")
message(STATUS "GNU C++ compiler detected, adding compile flags")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GENERIC_CXX_FLAGS} ${GENERIC_GNU_FLAGS}")
elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")
message(STATUS "Clang C++ compiler detected, adding compile flags")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GNU_SOURCE ${GENERIC_CXX_FLAGS} ${GENERIC_GNU_FLAGS}")
elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Intel")
message(STATUS "Intel C++ compiler detected, adding compile flags")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GNU_SOURCE ${GENERIC_CXX_FLAGS}")
#-std=c++0x comes with cmake's general flags, depricated in icc, remove it
string(REPLACE "-std=c++0x" "" CMAKE_CXX_FLAGS ${CMAKE_CXX_FLAGS})
elseif ("${CMAKE_CXX_COMPILER_ID}" MATCHES "PGI")
message(STATUS "PGI C++ compiler detected, adding compile flags")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -D_GNU_SOURCE -O3 -std=c++11 -D__GCC_ATOMIC_TEST_AND_SET_TRUEVAL=1 -Minform=inform -Mbounds -Mchkstk")
# " x" postfix to work around a bug in CMake that causes "MSVC" to translate to something completely different
elseif (("${CMAKE_CXX_COMPILER_ID} x" MATCHES "MSVC") OR MSVC)
message(STATUS "MSVC C++ compiler detected, adding compile flags")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -W3")
set(MSVC_FOUND TRUE)
else ()
message(STATUS "UNKNOWN C++ compiler, adding compile flags")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${GENERIC_CXX_FLAGS}")
endif ()
##########################################################ESC[m
add_definitions (${QT_DEFINITIONS} ${QT_QTDBUS_DEFINITIONS})
include_directories (${QDBUS_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR})
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ${CMAKE_MODULE_PATH})
add_definitions (-DLVERSION=\"2.5.0\")
# add_definitions (-DLDEBUG='1')
set(BUILD_SHARED_LIBS true)
cmake_policy(SET CMP0002 OLD)
IF (CMAKE_VERSION VERSION_EQUAL "3.3" OR CMAKE_VERSION VERSION_GREATER "3.3")
cmake_policy(SET CMP0063 NEW)
ENDIF()
### Options ######################################
option(ENABLE_CANTOR "Build with Cantor support" ON)
option(ENABLE_FFTW "Build with FFTW support" ON)
option(ENABLE_HDF5 "Build with HDF5 support" ON)
option(ENABLE_NETCDF "Build with NetCDF support" ON)
option(ENABLE_FITS "Build with FITS support" ON)
option(ENABLE_LIBCERF "Build with libcerf support" ON)
option(ENABLE_LIBORIGIN "Build with liborigin support" ON)
option(ENABLE_TESTS "Build with tests" ON)
### OS macros ####################################
IF (WIN32)
add_definitions (-DHAVE_WINDOWS)
ENDIF ()
### GSL (required) ###############################
FIND_PACKAGE(GSL REQUIRED)
### liborigin (included) ###############################
IF (ENABLE_LIBORIGIN)
add_definitions (-DHAVE_LIBORIGIN)
IF (CMAKE_BUILD_TYPE STREQUAL "debug" OR CMAKE_BUILD_TYPE STREQUAL "debugfull")
MESSAGE (STATUS "Origin project import (through internal liborigin) enabled (parser logging enabled)")
SET (ENABLE_ORIGIN_PARSER_LOG TRUE)
ELSE ()
MESSAGE (STATUS "Origin project import (through internal liborigin) enabled (parser logging disabled)")
ENDIF ()
ELSE ()
MESSAGE (STATUS "Origin project import disabled")
ENDIF ()
### Cantorlibs (optional) ###############################
IF (ENABLE_CANTOR)
FIND_LIBRARY (CANTOR_LIBS cantorlibs)
FIND_PATH (CANTOR_INCLUDE_DIR cantor/worksheetaccess.h
/usr/include
/usr/local/include
)
IF (CANTOR_LIBS AND CANTOR_INCLUDE_DIR)
SET (CANTOR_LIBS_FOUND TRUE)
ELSE ()
SET (CANTOR_LIBS_FOUND FALSE)
SET (CANTOR_LIBS "")
ENDIF()
IF (CANTOR_LIBS_FOUND)
MESSAGE (STATUS "Found Cantor Library: ${CANTOR_INCLUDE_DIR} ${CANTOR_LIBS}")
add_definitions (-DHAVE_CANTOR_LIBS)
ELSE ()
MESSAGE (STATUS "Cantor Library not found.")
ENDIF ()
ELSE ()
MESSAGE (STATUS "Cantor Library DISABLED.")
ENDIF ()
### FFTW (optional) #####################################
IF (ENABLE_FFTW)
FIND_LIBRARY (FFTW_LIBRARIES fftw3
PATHS
/usr/lib
/usr/local/lib
)
FIND_PATH (FFTW_INCLUDE_DIR fftw3.h
/usr/include
/usr/local/include
)
IF (FFTW_LIBRARIES AND FFTW_INCLUDE_DIR)
SET (FFTW_FOUND TRUE)
ELSE ()
SET (FFTW_FOUND FALSE)
ENDIF ()
IF (FFTW_FOUND)
MESSAGE (STATUS "Found FFTW 3 Library: ${FFTW_INCLUDE_DIR} ${FFTW_LIBRARIES}")
add_definitions (-DHAVE_FFTW3)
ELSE ()
MESSAGE (STATUS "FFTW 3 Library not found.")
ENDIF ()
ELSE ()
MESSAGE (STATUS "FFTW 3 Library DISABLED.")
ENDIF ()
### HDF5 (optional) ##############################
IF (ENABLE_HDF5)
FIND_PACKAGE(HDF5 COMPONENTS C)
IF (HDF5_FOUND)
add_definitions (-DHAVE_HDF5)
include_directories (${HDF5_INCLUDE_DIRS})
ELSE ()
MESSAGE (STATUS "Hierarchical Data Format (HDF5) Library not found.")
ENDIF ()
ELSE ()
MESSAGE (STATUS "Hierarchical Data Format (HDF5) Library DISABLED.")
ENDIF ()
### NETCDF (optional) #############################
IF (ENABLE_NETCDF)
FIND_LIBRARY (NETCDF_LIBRARY netcdf
PATHS
/usr/lib
/usr/local/lib
)
FIND_PATH (NETCDF_INCLUDE_DIR netcdf.h
/usr/include
/usr/local/include
)
IF (NETCDF_LIBRARY AND NETCDF_INCLUDE_DIR)
SET (NETCDF_FOUND TRUE)
ELSE ()
SET (NETCDF_FOUND FALSE)
ENDIF ()
IF (NETCDF_FOUND)
MESSAGE (STATUS "Found Network Common Data Format (NetCDF) Library: ${NETCDF_INCLUDE_DIR} ${NETCDF_LIBRARY}")
add_definitions (-DHAVE_NETCDF)
ELSE ()
MESSAGE (STATUS "Network Common Data Format (NetCDF) Library not found.")
ENDIF ()
ELSE ()
MESSAGE (STATUS "Network Common Data Format (NetCDF) Library DISABLED.")
ENDIF ()
### FITS (optional) ###############################
IF (ENABLE_FITS)
FIND_LIBRARY (CFITSIO_LIBRARY cfitsio
PATHS
/usr/lib
/usr/local/lib
$ENV{CFITSIO}
)
FIND_PATH (CFITSIO_INCLUDE_DIR fitsio.h
/usr/include
/usr/include/cfitsio
/usr/local/include
$ENV{CFITSIO}
)
IF (CFITSIO_LIBRARY AND CFITSIO_INCLUDE_DIR)
SET (CFITSIO_FOUND TRUE)
ELSE ()
SET (CFITSIO_FOUND FALSE)
ENDIF ()
IF (CFITSIO_FOUND)
MESSAGE (STATUS "Found Flexible Image Transport System Data Format (FITS) Library: ${CFITSIO_INCLUDE_DIR} ${CFITSIO_LIBRARY}")
add_definitions (-DHAVE_FITS)
include_directories (${CFITSIO_INCLUDE_DIR})
ELSE ()
MESSAGE (STATUS "Flexible Image Transport System Data Format (FITS) Library not found.")
ENDIF ()
ELSE ()
MESSAGE (STATUS "Flexible Image Transport System Data Format (FITS) Library DISABLED.")
ENDIF ()
### LIBCERF (optional) #############################
IF (ENABLE_LIBCERF)
FIND_LIBRARY (LIBCERF_LIBRARY cerf
PATHS
/usr/lib
/usr/local/lib
)
FIND_PATH (LIBCERF_INCLUDE_DIR cerf.h
/usr/include
/usr/local/include
)
IF (LIBCERF_LIBRARY AND LIBCERF_INCLUDE_DIR)
SET (LIBCERF_FOUND TRUE)
ELSE ()
SET (LIBCERF_FOUND FALSE)
ENDIF ()
IF (LIBCERF_FOUND)
MESSAGE (STATUS "Found libcerf Library: ${LIBCERF_INCLUDE_DIR} ${LIBCERF_LIBRARY}")
add_definitions (-DHAVE_LIBCERF)
ELSE ()
MESSAGE (STATUS "libcerf Library not found.")
ENDIF ()
ELSE ()
MESSAGE (STATUS "libcerf Library DISABLED.")
ENDIF ()
#################################################
FIND_PATH (XLOCALE_INCLUDE_DIR xlocale.h
/usr/include
/usr/local/include
)
IF (XLOCALE_INCLUDE_DIR)
add_definitions (-DHAVE_XLOCALE)
include_directories (${XLOCALE_INCLUDE_DIR})
ENDIF()
add_subdirectory(data)
add_subdirectory(doc)
add_subdirectory(icons)
add_subdirectory(src)
IF (ENABLE_LIBORIGIN)
add_subdirectory(liborigin)
ENDIF ()
if (ENABLE_TESTS)
enable_testing(true)
add_subdirectory(tests)
endif()
install(FILES org.kde.labplot2.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR})
feature_summary(WHAT ALL)
diff --git a/ChangeLog b/ChangeLog
index b6af780c8..a1e1fd6f7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,204 +1,203 @@
-----2.5-----
New features:
* Support for reading and plotting of live-data
* Improved data fitting
* Automatically guess parameter of custom models
* Better result presentation
* Support different weight types
* Consider given x- and y-error when fitting (can be switched off)
* Show t statistics, P > |t| and confidence interval
* Calculate p-value for chi-square and F test in nonlinear fitting
* added fit models for most statistical distributions
* Improved theming
* Apply themes to worksheet and to all its children
* Respect theme settings also in plot legends and labels
- * Application option to set the default theme for new worksheets
* Allow to disable theming in worksheets and plots after a theme was selected
* Show currently active theme in the "Apply theme" button
* New application option in the settings for the default theme used for new worksheets.
* Support different data types
* auto detect integer and datetime data in import
* support number locale and datetime formats
* improved data type support in spreadsheets
* Import from SQL databases (tables or custom queries)
* Import Origin OPJ projects
* Much better support for Windows and macOS
* Syntax highlighting for LaTeX in the text label
* Allow to set the background color for LaTeX labels
* Support Hermite polynomials from GSL 2.4
* Support error functions and related functions from libcerf
* "Used in" sub-menu in column contex menu for faster navigation to the curves consuming the column
* Direct application of analysis functions (smoothing, interpolating, etc.) on the ploted data via curve's context menu
* Direct application of analysis functions on the data in the spreadsheet and plotting of the results via spreadsheet's context menu
* Drag columns in the project explorer and drop them on plots (either in a worksheet view or in the project explorer) to create curves
* "Show last N points" and "Show first N points" data ranges in cartesian plot
* Added CLI option --presenter to start LabPlot directly in the presenter mode
* Added CLI parameter to directly open project files (LabPlot or Origin)
* Allow drag&drop of projects files (LabPlot and Origin) on the main window to load the project
* Allow drag&drop of data files on the main window to import the data
* Show tooltips for the supported mathematical functions and constants in the expression text field
- * Automaticaly switch to the scientific representation for numbers bigger than 10^4 on the axis tick labels
+ * Automatically switch to the scientific representation for numbers bigger than 10^4 on the axis tick labels
* Automatically allow the latex typesetting in the application after the latex environment was installed later without making the user to go to the settings dialog
- * Allow to change the theme from within the application
+ * Allow to change the color scheme for the application
* Smooth and animated zooming in the worksheet view
* Allow to add text labels to plots
* Improved building with MSVC, Intel and PGI compiler
Performance improvements:
* Faster copy&paste in the spreadsheet
Bug fixes:
* Bug 379877 - masked rows in spreadsheet not restored in project
* Calculation of fit results corrected
* Axes now support values larger than FLT_MAX (~10^38) and smaller than FLT_MIN (~10^-38)
* When a LabPlot project is being droped in the main window, load the project directly instead of showing the import file dialog
* Correctly save and restore masked cells
* Don't crash if the rc-file was not found during the startup.
-----2.4-----
New features:
* Support themes for plots
* Import and editing of FITS data files
* Data reduction by removing data points using multiple algorithms
* Numerical differentiation and integration with several options
* Many new pre-defined fit models (Gompertz, Weibull, Log-Normal, Gumbel, etc.) sorted in categories
* Fit parameter now support fixed values, lower/upper limits and use Unicode
* Fit model and random number distribution formulas are now rendered with LaTeX
* Support user specified x range in all analysis functions
* Allow to enter complete LaTeX documents in text labels
* Configuration parameter to use different LaTex engines (LuaLaTex, XeLatex, pdfLaTex, LaTex)
* Disable LaTeX typesetting if no LaTex installation (and other required tools) were found at runtime
* Presenter mode for worksheets
* Support for Mac OS
* Support for Julia's vectors and tuples in CAS worksheets (requires Cantor v. 16.12 or higher)
* Allow to jump directly to the data source spreadsheet via XYCurve's context menu
* Select and delete multiple objects in project explorer
* Improved and extended internal parser for mathematical expressions
* Copy of worksheet elements as image to the clipboard via CTRL+C
Bug fixes:
* BUG: 361326 - Allow to select curves with overlapping bounding boxes
* Correctly load worksheet sizes from saved templates
* Fixed crash when removing columns in spreadsheet
* Fixed crash when fitting using GSL >= 2
* List of available functions corrected
* Constants are now available with full accuracy
* Windows: Import of files and open recent files fixed
-----2.3-----
New features:
* Integration of Cantor - Support for different open-source computer algebra systems
* Statistics on spreadsheets and matrices
* Export of spreadsheets and matrices to LaTeX tables
* Interpolation of data including different splines, cosine, exponential, cubic Hermite (Catmull-Rom, cardinal, Kochanek-Bartels) and rational functions
* Data smoothing using moving average (centered or lagged), percentile filter or Savitzky-Golay algorithm
* Fourier filter (low pass, high pass, band pass, band reject) with ideal, Butterworth, Chebychev I+II, Legendre or Bessel-Thomson filter
* Fourier transform with many window functions (Welch, Hann, Hamming, etc.) calculating magnitude, amplitude, power, phase, dB, etc. and supporting
one/two sided spectrum with or without shift and x scaling to frequency, index or period
* Filter and search capabilities in the drop down box for the selection of data sources
* Sigmoid function as a new pre-defined fit model
* Support for compiling on Microsoft Windows
Performance improvements:
* Faster generation of random values
* Global option to enable/disable the double-buffering for faster painting of curves (enabled on default)
Bug fixes:
* Save and restore last used setting in RandomValuesDialog
* Update axis title shape on title rotations correctly
* Save and restore custom column widths in the spreadsheet
* Fixed sporadic crashes during project close
-----2.2-----
New features:
* Datapicker - tool for extracting curves and data points from imported images
* Custom point on the plot with user-defined position and appearance
* Accept drag&drop events
* Support GSL 2.x
* Import and export dialogs save and restore their settings and sizes
Performance improvements:
* Faster rendering of the image view of the matrix
Bug fixes:
* BUG: 354744 - make setting of range auto scaling in CartesianPlot undo/redo-able
* Removed couple of hard coded sizes given in pixels for better user-experience on hidpi-displays
* Fixes the bug with disabled undo/redo-actions in after the undo-history was cleared
* Keep the information about the columns to be shown in the project explorer after project close
* Fixed some bugs in painting of the selection band on the worksheet
* Allow to open gz- and bz2-compressed LabPlot project files on the command line interface
-----2.1-----
New features:
* New Matrix view for handling matrix data.
* Workbook - a new container for grouping several objects of type Spreadsheet and/or Matrix.
* Import of binary, image, NetCDF and HDF data into spreadsheet or matrix.
* Visual HDF and NetCDF parser to view content of files and import data sets.
* Preview of all supported file types in import dialog.
* Transparently import compressed data files.
* In xy-curve the points may not be connected by the line if there are NANs in-between. This behaviour is controlled by the parameter "skip gaps".
* Multiplier of Pi format of the plot axis for periodical functions.
* New operations on columns in Spreadsheet - reverse, drop values and mask values.
* Formula used to generate the values in a column is stored and can be changed/adjusted in the formula dialog afterwards.
* Curve filling: the area below, under, left to or right to the curve can be filled.
* Support for multiple variables in "Function Values"-dialog - new data in the spreadsheet can be calculated out of multiple columns.
Performance improvements:
* Speeded up the creation of new columns during the import
Bug fixes:
* Fixed wrong behaviour when doing "zoom&select" in a plot and then deselecting the plot - it was not possible anymore to select the plot again on the worksheet.
-----2.0.2-----
New features:
* Plot 2D-curves defined by mathematical equations in cartesian and polar coordinates or via a parametric equation.
* Linear and non-linear regression analysis. Several predefined fit-models are provided. User-defined models are also possible.
* Besides fixed worksheet sizes (predefined sizes like A4 etc. or user-defined), complete view size can be used. All sizes are automatically adjusted on resize.
* Different axis arrow types.
* "select region and zoom in", "select x-region and zoom in", "select y-region and zoom in" functions for cartesian plot.
* Rounded border for several worksheet objects (plot area, legend etc.)
* Hover effect for axis, curve and text label.
* Added a MessageBox - ask befor deleting worksheet objects.
* Added three new types for drop lines - "zero baseline", "min baseline" and "max baseline"
* Fill the selection in Spreadsheet with a constant value provided by the user
* Fill columns with uniform and non-uniform random numbers, several distributions are available.
* Fill columns with function values
* Allow custom resolutions for PNG-export
* Export of the spreadsheet to a text file.
* Simultaneous zooming and navigation accross multiple plots.
* Implemented "Powers of 10/2/e" for the axis tick labels
Bug fixes:
* Don't crash when trying to create a plot in case no rc-file was installed.
* Don't allow to select unwanted objects in TreeViewComboBox in ImportDialog and XYCurveDock.
* Corrected painting of background images in plot area and legend.
* BUG: 330723 - fixed weird selection in spreadsheet.
* BUG: 330774 - fixed wrong positioning of axis on orientation changes.
* Update main window title when project name is changed
-----2.0.1-----
Bug fix release. Solved issues:
* Fixed wrong scaling of legend's text labels in pdf-export
* Fixed memory corruption in CartesianPlotDock that can lead to crashes
-----2.0.0-----
First stable release of LabPlot2. LabPlot2 is a complete rewrite of LabPlot1 and lacks in this release a lot of features available in the predecessor. On the other hand, the GUI and the usability is more superior as compared to LabPlot1 and there are several new features that were not available in LabPlot1.
Brief summary of features and capabilities of LabPlot2 implemented in the first release:
* project-based management of data
* created objects are organized in a tree and are visualized and accessible in the project explorer
* for a better management of objects, additional folders and sub-folders can be created within a project
* spreadsheet with very basic functionality is available for manual data entry
* "file data source" can be used to import a file and to let LabPlot2 watch for changes in that file
* external data from an ascii file can be also directly imported to a spreadsheet for further editing
* worksheet is the main object where plots, labels etc. are placed on
* several zooming functions for the worksheet
* only cartesian plot is implemented in the first release
* arbitrary number of freely positionable axes is possible
* xy-curve is implemented. As the data source for the x- and y-values columns either from a spreadsheet
or from a file data source can be used
* several zooming and "movement" functions are available for the plots which help to navigate through data
* legend for xy-plots
* a lot of properties of the worksheet elements can be edited in a very easy way in the corresponding dock widgets
* plots on the worksheet can be put into a horizontal, vertical or grid layouts
* export of worksheet (entire worksheet or current seleciton) to pdf, eps, png and svg
diff --git a/admin/SLOC.txt b/admin/SLOC.txt
index f1cff9750..d2e7b3e7e 100644
--- a/admin/SLOC.txt
+++ b/admin/SLOC.txt
@@ -1,171 +1,200 @@
Statistics generated using David A. Wheeler's 'SLOCCount'."
+-----2.5-----
+SLOC Directory SLOC-by-Language (Sorted)
+50012 backend cpp=45264,ansic=4251,yacc=480,python=17
+26974 kdefrontend cpp=26974
+8914 commonfrontend cpp=8914
+487 tools cpp=487
+152 cantor cpp=152
+0 doc (none)
+0 top_dir (none)
+
+
+Totals grouped by language (dominant language first):
+cpp: 81791 (94.51%)
+ansic: 4251 (4.91%)
+yacc: 480 (0.55%)
+python: 17 (0.02%)
+
+
+* Total Physical Source Lines of Code (SLOC) = 86,539
+* Development Effort Estimate, Person-Years (Person-Months) = 21.63 (259.59)
+ (Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05))
+* Schedule Estimate, Years (Months) = 1.72 (20.67)
+ (Basic COCOMO model, Months = 2.5 * (person-months**0.38))
+* Estimated Average Number of Developers (Effort/Schedule) = 12.56
+* Total Estimated Cost to Develop = $ 2,922,228
+ (average salary = $56,286/year, overhead = 2.40)
+
+
+
-----2.4-----
SLOC Directory SLOC-by-Language (Sorted)
41803 backend cpp=36475,ansic=4844,yacc=467,python=17
21807 kdefrontend cpp=21807
7633 commonfrontend cpp=7633
388 tools cpp=388
Totals grouped by language (dominant language first):
cpp: 66303 (92.56%)
ansic: 4844 (6.76%)
yacc: 467 (0.65%)
python: 17 (0.02%)
* Total Physical Source Lines of Code (SLOC) = 71,631
* Development Effort Estimate, Person-Years (Person-Months) = 17.74 (212.85)
(Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05))
* Schedule Estimate, Years (Months) = 1.60 (19.17)
(Basic COCOMO model, Months = 2.5 * (person-months**0.38))
* Estimated Average Number of Developers (Effort/Schedule) = 11.10
* Total Estimated Cost to Develop = $ 2,396,060
(average salary = $56,286/year, overhead = 2.40).
### kf5-version ###
SLOC Directory SLOC-by-Language (Sorted)
42159 backend cpp=36831,ansic=4844,yacc=467,python=17
22096 kdefrontend cpp=22096
7841 commonfrontend cpp=7841
390 tools cpp=390
152 cantor cpp=152
Totals grouped by language (dominant language first):
cpp: 67310 (92.66%)
ansic: 4844 (6.67%)
yacc: 467 (0.64%)
python: 17 (0.02%)
* Total Physical Source Lines of Code (SLOC) = 72,638
* Development Effort Estimate, Person-Years (Person-Months) = 18.00 (215.99)
(Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05))
* Schedule Estimate, Years (Months) = 1.61 (19.28)
(Basic COCOMO model, Months = 2.5 * (person-months**0.38))
* Estimated Average Number of Developers (Effort/Schedule) = 11.20
* Total Estimated Cost to Develop = $ 2,431,441
(average salary = $56,286/year, overhead = 2.40).
-----2.3-----
SLOC Directory SLOC-by-Language (Sorted)
36609 backend cpp=33601,ansic=2991,python=17
18102 kdefrontend cpp=18102
7092 commonfrontend cpp=7092
256 tools cpp=256
Totals grouped by language (dominant language first):
cpp: 59051 (95.15%)
ansic: 2991 (4.82%)
python: 17 (0.03%)
* Total Physical Source Lines of Code (SLOC) = 62,059
* Development Effort Estimate, Person-Years (Person-Months) = 15.26 (183.09)
(Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05))
* Schedule Estimate, Years (Months) = 1.51 (18.10)
(Basic COCOMO model, Months = 2.5 * (person-months**0.38))
* Estimated Average Number of Developers (Effort/Schedule) = 10.11
* Total Estimated Cost to Develop = $ 2,061,041
(average salary = $56,286/year, overhead = 2.40).
### kf5-version ###
SLOC Directory SLOC-by-Language (Sorted)
36952 backend cpp=33944,ansic=2991,python=17
18380 kdefrontend cpp=18380
7297 commonfrontend cpp=7297
256 tools cpp=256
152 cantor cpp=152
Totals grouped by language (dominant language first):
cpp: 60029 (95.23%)
ansic: 2991 (4.74%)
python: 17 (0.03%)
* Total Physical Source Lines of Code (SLOC) = 63,037
* Development Effort Estimate, Person-Years (Person-Months) = 15.51 (186.12)
(Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05))
* Schedule Estimate, Years (Months) = 1.52 (18.22)
(Basic COCOMO model, Months = 2.5 * (person-months**0.38))
* Estimated Average Number of Developers (Effort/Schedule) = 10.22
* Total Estimated Cost to Develop = $ 2,095,159
(average salary = $56,286/year, overhead = 2.40).
-----2.2-----
SLOC Directory SLOC-by-Language (Sorted)
32480 backend cpp=32480
15680 kdefrontend cpp=15680
6131 commonfrontend cpp=6131
256 tools cpp=256
Totals grouped by language (dominant language first):
cpp: 54547 (100.00%)
* Total Physical Source Lines of Code (SLOC) = 54,547
* Development Effort Estimate, Person-Years (Person-Months) = 13.32 (159.89)
(Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05))
* Schedule Estimate, Years (Months) = 1.43 (17.19)
(Basic COCOMO model, Months = 2.5 * (person-months**0.38))
* Estimated Average Number of Developers (Effort/Schedule) = 9.30
* Total Estimated Cost to Develop = $ 1,799,911
(average salary = $56,286/year, overhead = 2.40).
-----2.1-----
SLOC Directory SLOC-by-Language (Sorted)
28516 backend cpp=28516
13913 kdefrontend cpp=13913
4636 commonfrontend cpp=4636
256 tools cpp=256
Totals grouped by language (dominant language first):
cpp: 47321 (100.00%)
* Total Physical Source Lines of Code (SLOC) = 47,321
* Development Effort Estimate, Person-Years (Person-Months) = 11.48 (137.73)
(Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05))
* Schedule Estimate, Years (Months) = 1.35 (16.25)
(Basic COCOMO model, Months = 2.5 * (person-months**0.38))
* Estimated Average Number of Developers (Effort/Schedule) = 8.48
* Total Estimated Cost to Develop = $ 1,550,416
(average salary = $56,286/year, overhead = 2.40).
-----2.0-----
SLOC Directory SLOC-by-Language (Sorted)
23460 backend cpp=23460
13766 kdefrontend cpp=13559,yacc=207
3064 commonfrontend cpp=3064
137 tools cpp=137
Totals grouped by language (dominant language first):
cpp: 40220 (99.49%)
yacc: 207 (0.51%)
* Total Physical Source Lines of Code (SLOC) = 40,427
* Development Effort Estimate, Person-Years (Person-Months) = 9.73 (116.74)
(Basic COCOMO model, Person-Months = 2.4 * (KSLOC**1.05))
* Schedule Estimate, Years (Months) = 1.27 (15.26)
(Basic COCOMO model, Months = 2.5 * (person-months**0.38))
* Estimated Average Number of Developers (Effort/Schedule) = 7.65
* Total Estimated Cost to Develop = $ 1,314,155
(average salary = $56,286/year, overhead = 2.40).
diff --git a/compile-devel b/compile-devel
new file mode 100755
index 000000000..e4587737d
--- /dev/null
+++ b/compile-devel
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+BUILDDIR=build_devel
+
+#rm -rf $BUILDDIR
+
+mkdir $BUILDDIR
+cd $BUILDDIR
+
+cmake .. -DCMAKE_BUILD_TYPE=Debug -DENABLE_TESTS=OFF -DCMAKE_INSTALL_PREFIX=/usr && make -j 4
+#cmake .. -DCMAKE_BUILD_TYPE=Debug -DENABLE_LIBCERF=OFF -DCMAKE_INSTALL_PREFIX=/usr && make -j 4
+#cmake .. -DCMAKE_BUILD_TYPE=Debug -DENABLE_TESTS=OFF -DCMAKE_INSTALL_PREFIX=/usr && make -j 4
+
+#make install
diff --git a/create-dmg.sh b/create-dmg.sh
index 6ad5e8ccf..e4fbd861e 100755
--- a/create-dmg.sh
+++ b/create-dmg.sh
@@ -1,93 +1,93 @@
# Build it on Mac howto in script form
# but be aware, some frameworks need patching to have this working
# reference: https://cgit.kde.org/kate.git/tree/mac/emerge-deploy.sh
# errors fatal
set -e
NAME=labplot2
PNAME=LabPlot2
VERSION=2.5.0
PREFIX=kde/Applications
INPREFIX=$PREFIX/$PNAME.app/Contents
TMPDIR=LabPlot2
SIGNATURE="Stefan Gerlach"
#########################################
echo "CLEAN UP"
rm -rf $PREFIX/$PNAME.app
mkdir -pv $INPREFIX/{Frameworks,Resources,MacOS,PlugIns/iconengines,share/appdata,share/applications}
# application
cp -v labplot/build/src/$NAME.app/Contents/MacOS/$NAME $INPREFIX/MacOS
echo "Running macdeployqt ..."
# -verbose=3
macdeployqt $PREFIX/$PNAME.app -verbose=2
#########################################
echo "install files"
# splash
cp -v kde/share/$NAME/splash.png $INPREFIX/Resources/
# rc-file
# Standardlocation (QSP): ~/Library/Application\ Support/kxmlgui5/labplot2/labplot2ui.rc
# using hardcoded path:
cp -v kde/share/kxmlgui5/$NAME/${NAME}ui.rc $INPREFIX/Resources/
# themes
cp -vr kde/share/$NAME/themes $INPREFIX/Resources/
# gsl_distros, fit_models, colorchooser
cp -vr kde/share/$NAME/pics $INPREFIX/Resources/
# color schemes (needs patched kcolorschememanager.cpp)
cp -vr kde/share/$NAME/color-schemes $INPREFIX/Resources/color-schemes
# appdata
cp -v kde/share/metainfo/org.kde.labplot2.appdata.xml $INPREFIX/share/appdata/
cp -v kde/share/applications/org.kde.$NAME.desktop $INPREFIX/share/applications/
# cantor
-cp -v kde/Applications/cantor.app/Contents/MacOS/cantor $INPREFIX/MacOS
-cp -v kde/Applications/cantor_scripteditor.app/Contents/MacOS/cantor_scripteditor $INPREFIX/MacOS
+cp -v kde/Applications/KDE/cantor.app/Contents/MacOS/cantor $INPREFIX/MacOS
+cp -v kde/Applications/KDE/cantor_scripteditor.app/Contents/MacOS/cantor_scripteditor $INPREFIX/MacOS
cp -vr kde/plugins/cantor $INPREFIX/PlugIns
cp -v kde/lib/libcantor_config.dylib $INPREFIX/Frameworks/
cp -v kde/lib/libcantor_pythonbackend.dylib $INPREFIX/Frameworks/
cp -v kde/lib/libcantorlibs.18.07.70.dylib $INPREFIX/Frameworks/libcantorlibs.18.dylib
# icons
-cp -vf kde/bin/data/icontheme.rcc $INPREFIX/Resources/icontheme.rcc
+cp -vf kde/share/icontheme.rcc $INPREFIX/Resources/icontheme.rcc
# misc
cp /Applications/KDE/labplot2.app/Contents/Info.plist $INPREFIX
cp /Applications/KDE/labplot2.app/Contents/Resources/LABPLOT_SOURCE.icns $INPREFIX/Resources
### TODO
# package icon
# share/doc
##########################################
# fix for hdf5 lib
# install_name_tool -change /usr/local/Cellar/hdf5/1.8.17/lib/libhdf5.10.dylib /usr/local/opt/hdf5/1.8.17/lib/libhdf5.10.dylib /usr/local/opt/hdf5/1.8.17/lib/libhdf5_hl.10.dylib
###############################################
if [ -d ./$TMPDIR ]; then
rm -rf ./$TMPDIR/*
else
mkdir ./$TMPDIR
fi
mv $PREFIX/$PNAME.app ./$TMPDIR
ln -s /Applications ./$TMPDIR/Applications
## remove stuff we don't need or like
rm -rf $TMPDIR/$PNAME.app/Contents/Frameworks/QtTextToSpeech.framework
#rm -rf $TMPDIR/$PNAME.app/Contents/Plugins/bearer
###############################################
# create the final disk image
echo "BUILDING PACKAGE"
rm -f ./labplot-$VERSION.dmg
hdiutil create -srcfolder ./$TMPDIR -format UDBZ -fs HFS+ -imagekey zlib-level=9 ./labplot-$VERSION.dmg
diff --git a/data/pics/fit_models/pseudovoigt11.png b/data/pics/fit_models/pseudovoigt11.png
new file mode 100644
index 000000000..a32ffd2ea
Binary files /dev/null and b/data/pics/fit_models/pseudovoigt11.png differ
diff --git a/data/pics/fit_models/pseudovoigt12.png b/data/pics/fit_models/pseudovoigt12.png
new file mode 100644
index 000000000..258a40316
Binary files /dev/null and b/data/pics/fit_models/pseudovoigt12.png differ
diff --git a/data/pics/fit_models/pseudovoigt13.png b/data/pics/fit_models/pseudovoigt13.png
new file mode 100644
index 000000000..5c4da859b
Binary files /dev/null and b/data/pics/fit_models/pseudovoigt13.png differ
diff --git a/data/pics/fit_models/pseudovoigt14.png b/data/pics/fit_models/pseudovoigt14.png
new file mode 100644
index 000000000..4623d5df3
Binary files /dev/null and b/data/pics/fit_models/pseudovoigt14.png differ
diff --git a/data/pics/fit_models/voigt1.png b/data/pics/fit_models/voigt1.png
index f784ba41a..16a76e9e1 100644
Binary files a/data/pics/fit_models/voigt1.png and b/data/pics/fit_models/voigt1.png differ
diff --git a/data/pics/fit_models/voigt2.png b/data/pics/fit_models/voigt2.png
new file mode 100644
index 000000000..08029a39c
Binary files /dev/null and b/data/pics/fit_models/voigt2.png differ
diff --git a/data/pics/fit_models/voigt3.png b/data/pics/fit_models/voigt3.png
new file mode 100644
index 000000000..c998bf4b3
Binary files /dev/null and b/data/pics/fit_models/voigt3.png differ
diff --git a/data/pics/fit_models/voigt4.png b/data/pics/fit_models/voigt4.png
new file mode 100644
index 000000000..49981963b
Binary files /dev/null and b/data/pics/fit_models/voigt4.png differ
diff --git a/data/pics/sc-apps-labplot-matrix-new.svg b/data/pics/sc-apps-labplot-matrix-new.svg
index ddec4c161..fc4b35a99 100644
--- a/data/pics/sc-apps-labplot-matrix-new.svg
+++ b/data/pics/sc-apps-labplot-matrix-new.svg
@@ -1,3906 +1,3906 @@
diff --git a/data/pics/sc-apps-labplot-spreadsheet-new.svg b/data/pics/sc-apps-labplot-spreadsheet-new.svg
index ff1614de2..ead44bb80 100644
--- a/data/pics/sc-apps-labplot-spreadsheet-new.svg
+++ b/data/pics/sc-apps-labplot-spreadsheet-new.svg
@@ -1,3889 +1,3889 @@
diff --git a/data/pics/sc-apps-labplot-workbook-new.svg b/data/pics/sc-apps-labplot-workbook-new.svg
index cd401b555..b28734dd9 100644
--- a/data/pics/sc-apps-labplot-workbook-new.svg
+++ b/data/pics/sc-apps-labplot-workbook-new.svg
@@ -1,3884 +1,3884 @@
diff --git a/data/pics/sc-apps-labplot-worksheet-new.svg b/data/pics/sc-apps-labplot-worksheet-new.svg
index 95d0acfe3..651c32a85 100644
--- a/data/pics/sc-apps-labplot-worksheet-new.svg
+++ b/data/pics/sc-apps-labplot-worksheet-new.svg
@@ -1,3797 +1,3797 @@
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 161339abf..554f5ed39 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,395 +1,396 @@
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)
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/FITSOptionsWidget.cpp
${KDEFRONTEND_DIR}/datasources/JsonOptionsWidget.cpp
${KDEFRONTEND_DIR}/dockwidgets/AxisDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/NoteDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/CartesianPlotDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/CartesianPlotLegendDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/HistogramDock.cpp
${KDEFRONTEND_DIR}/dockwidgets/BarChartPlotDock.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/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/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
)
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/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/fitsoptionswidget.ui
${KDEFRONTEND_DIR}/ui/datasources/jsonoptionswidget.ui
${KDEFRONTEND_DIR}/ui/dockwidgets/axisdock.ui
${KDEFRONTEND_DIR}/ui/dockwidgets/cartesianplotdock.ui
${KDEFRONTEND_DIR}/ui/dockwidgets/cartesianplotlegenddock.ui
${KDEFRONTEND_DIR}/ui/dockwidgets/histogramdock.ui
${KDEFRONTEND_DIR}/ui/dockwidgets/histogramdockgeneraltab.ui
${KDEFRONTEND_DIR}/ui/dockwidgets/barchartplotdock.ui
${KDEFRONTEND_DIR}/ui/dockwidgets/columndock.ui
${KDEFRONTEND_DIR}/ui/dockwidgets/custompointdock.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/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/dropvalueswidget.ui
${KDEFRONTEND_DIR}/ui/spreadsheet/functionvalueswidget.ui
${KDEFRONTEND_DIR}/ui/spreadsheet/randomvalueswidget.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/AbstractScriptingEngine.cpp
${BACKEND_DIR}/core/AbstractScript.cpp
${BACKEND_DIR}/core/ScriptingEngineManager.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/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/FITSFilter.cpp
${BACKEND_DIR}/datasources/filters/QJsonModel.cpp
${BACKEND_DIR}/datasources/projects/ProjectParser.cpp
${BACKEND_DIR}/datasources/projects/LabPlotProjectParser.cpp
${BACKEND_DIR}/datasources/projects/OriginProjectParser.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/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/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/BarChartPlot.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}/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
)
set(NSL_SOURCES
${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}/datapicker/DatapickerView.cpp
${COMMONFRONTEND_DIR}/datapicker/DatapickerImageView.cpp
)
IF (${CANTOR_LIBS_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}
)
##############################################################################
INCLUDE_DIRECTORIES( . ${BACKEND_DIR}/gsl ${GSL_INCLUDE_DIR} )
set( LABPLOT_SRCS ${GUI_SOURCES} )
ki18n_wrap_ui( LABPLOT_SRCS ${UI_SOURCES} )
+# TODO: build without:
+add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0)
# 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)
-# TODO: remove KF5::KDELibs4Support
-target_link_libraries( labplot2lib KF5::KDELibs4Support KF5::Archive KF5::XmlGui Qt5::Svg Qt5::Core ${GSL_LIBRARIES} ${GSL_CBLAS_LIBRARIES} ${QT_QTSQL_LIBRARIES} )
+target_link_libraries( labplot2lib KF5::Archive KF5::Completion KF5::ConfigCore KF5::I18n KF5::IconThemes 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 (KF5SyntaxHighlighting_FOUND)
target_link_libraries( labplot2lib KF5::SyntaxHighlighting )
ENDIF ()
#TODO: KF5::NewStuff
IF (CANTOR_LIBS_FOUND)
target_link_libraries( labplot2lib ${CANTOR_LIBS} )
ENDIF ()
IF (HDF5_FOUND)
target_link_libraries( labplot2lib ${HDF5_LIBRARIES} )
ENDIF ()
IF (FFTW_FOUND)
target_link_libraries( labplot2lib ${FFTW_LIBRARIES} )
ENDIF ()
IF (NETCDF_FOUND)
target_link_libraries( labplot2lib ${NETCDF_LIBRARY} )
ENDIF ()
IF (CFITSIO_FOUND)
target_link_libraries( labplot2lib ${CFITSIO_LIBRARY} )
ENDIF ()
IF (LIBCERF_FOUND)
target_link_libraries( labplot2lib ${LIBCERF_LIBRARY} )
ENDIF ()
IF (ENABLE_LIBORIGIN)
target_link_libraries( labplot2lib liborigin-static )
ENDIF ()
# icons for the executable 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
)
# main executable
set(LABPLOT_SOURCE ${KDEFRONTEND_DIR}/LabPlot.cpp)
ecm_add_app_icon(LABPLOT_SOURCE ICONS ${LABPLOT_ICONS})
add_executable( labplot2 ${LABPLOT_SOURCE} )
target_link_libraries( labplot2 labplot2lib )
############## 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 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/cantorWorksheet/CantorWorksheet.cpp b/src/backend/cantorWorksheet/CantorWorksheet.cpp
index 2b2a80de1..79f57cad0 100644
--- a/src/backend/cantorWorksheet/CantorWorksheet.cpp
+++ b/src/backend/cantorWorksheet/CantorWorksheet.cpp
@@ -1,315 +1,315 @@
/***************************************************************************
File : CantorWorksheet.cpp
Project : LabPlot
Description : Aspect providing a Cantor Worksheets for Multiple backends
--------------------------------------------------------------------
Copyright : (C) 2015 Garvit Khatri (garvitdelhi@gmail.com)
Copyright : (C) 2016 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 "CantorWorksheet.h"
#include "VariableParser.h"
#include "backend/core/column/Column.h"
#include "backend/core/column/ColumnPrivate.h"
#include "backend/core/Project.h"
#include "commonfrontend/cantorWorksheet/CantorWorksheetView.h"
#include
#include
#include
#include
#include
#include
#include "cantor/cantor_part.h"
#include
#include
#include
CantorWorksheet::CantorWorksheet(AbstractScriptingEngine* engine, const QString &name, bool loading)
: AbstractPart(name),
scripted(engine),
m_view(nullptr),
m_backendName(name),
m_session(nullptr),
m_part(nullptr),
m_variableModel(nullptr),
m_worksheetAccess(nullptr) {
if(!loading)
init();
}
/*!
initializes Cantor's part and plugins
*/
bool CantorWorksheet::init(QByteArray* content) {
KPluginFactory* factory = KPluginLoader(QLatin1String("libcantorpart")).factory();
if (factory) {
m_part = factory->create(this, QVariantList() << m_backendName << QLatin1String("--noprogress"));
if (!m_part) {
qDebug() << "Could not create the Cantor Part.";
return false;
}
m_worksheetAccess = m_part->findChild(Cantor::WorksheetAccessInterface::Name);
//load worksheet content if available
if(content)
m_worksheetAccess->loadWorksheetFromByteArray(content);
connect(m_worksheetAccess, SIGNAL(sessionChanged()), this, SLOT(sessionChanged()));
//Cantor's session
m_session = m_worksheetAccess->session();
connect(m_session, SIGNAL(statusChanged(Cantor::Session::Status)), this, SIGNAL(statusChanged(Cantor::Session::Status)));
//variable model
m_variableModel = m_session->variableModel();
connect(m_variableModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(rowsInserted(QModelIndex,int,int)));
connect(m_variableModel, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)));
connect(m_variableModel, SIGNAL(modelReset()), this, SLOT(modelReset()));
//available plugins
Cantor::PanelPluginHandler* handler = m_part->findChild(QLatin1String("PanelPluginHandler"));
if(!handler) {
KMessageBox::error(view(), i18n("no PanelPluginHandle found for the Cantor Part."));
return false;
}
m_plugins = handler->plugins();
}
else {
//we can only get to this here if we open a project having Cantor content and Cantor plugins were not found.
//return false here, a proper error message will be created in load() and propagated further.
DEBUG("Failed to load cantor plugin");
return false;
}
return true;
}
//SLots
void CantorWorksheet::rowsInserted(const QModelIndex& parent, int first, int last) {
Q_UNUSED(parent)
for(int i = first; i <= last; ++i) {
const QString name = m_variableModel->data(m_variableModel->index(first, 0)).toString();
const QString value = m_variableModel->data(m_variableModel->index(first, 1)).toString();
VariableParser* parser = new VariableParser(m_backendName, value);
if(parser->isParsed()) {
Column* col = child(name);
if (col) {
col->replaceValues(0, parser->values());
} else {
col = new Column(name, parser->values());
col->setUndoAware(false);
addChild(col);
//TODO: Cantor currently ignores the order of variables in the worksheets
//and adds new variables at the last position in the model.
//Fix this in Cantor and switch to insertChildBefore here later.
//insertChildBefore(col, child(i));
}
} else {
//the already existing variable doesn't contain any numerical values -> remove it
Column* col = child(name);
if (col)
removeChild(col);
}
delete(parser);
}
project()->setChanged(true);
}
void CantorWorksheet::sessionChanged() {
//TODO: signal is never emitted in Cantor
project()->setChanged(true);
}
void CantorWorksheet::modelReset() {
for(int i = 0; i < childCount(); ++i)
child(i)->remove();
}
void CantorWorksheet::rowsAboutToBeRemoved(const QModelIndex & parent, int first, int last) {
Q_UNUSED(parent);
Q_UNUSED(first);
Q_UNUSED(last);
//TODO: Cantor removes rows from the model even when the variable was changed only.
//We don't want this behaviour since this removes the columns from the datasource in the curve.
//We need to fix/change this in Cantor.
return;
// Q_UNUSED(parent)
// for(int i = first; i <= last; ++i) {
// const QString name = m_variableModel->data(m_variableModel->index(first, 0)).toString();
// Column* column = child(name);
// if(column)
// column->remove();
// }
}
QList CantorWorksheet::getPlugins(){
return m_plugins;
}
KParts::ReadWritePart* CantorWorksheet::part() {
return m_part;
}
QIcon CantorWorksheet::icon() const {
if(m_session)
return QIcon::fromTheme(m_session->backend()->icon());
return QIcon();
}
QWidget* CantorWorksheet::view() const {
if (!m_partView) {
m_view = new CantorWorksheetView(const_cast(this));
m_view->setBaseSize(1500, 1500);
m_partView = m_view;
// connect(m_view, SIGNAL(statusInfo(QString)), this, SIGNAL(statusInfo(QString)));
}
return m_partView;
}
//! Return a new context menu.
/**
* The caller takes ownership of the menu.
*/
QMenu* CantorWorksheet::createContextMenu() {
QMenu* menu = AbstractPart::createContextMenu();
Q_ASSERT(menu);
emit requestProjectContextMenu(menu);
return menu;
}
QString CantorWorksheet::backendName() {
return this->m_backendName;
}
//TODO
bool CantorWorksheet::exportView() const {
return false;
}
bool CantorWorksheet::printView() {
m_part->action("file_print")->trigger();
return true;
}
bool CantorWorksheet::printPreview() const {
m_part->action("file_print_preview")->trigger();
return true;
}
//##############################################################################
//################## Serialization/Deserialization ###########################
//##############################################################################
//! Save as XML
void CantorWorksheet::save(QXmlStreamWriter* writer) const{
writer->writeStartElement("cantorWorksheet");
writeBasicAttributes(writer);
writeCommentElement(writer);
//general
writer->writeStartElement( "general" );
writer->writeAttribute( "backend_name", m_backendName);
//TODO: save worksheet settings
writer->writeEndElement();
//save the content of Cantor's worksheet
QByteArray content = m_worksheetAccess->saveWorksheetToByteArray();
writer->writeStartElement("worksheet");
writer->writeAttribute("content", content.toBase64());
writer->writeEndElement();
//save columns(variables)
for (auto* col : children(IncludeHidden))
col->save(writer);
writer->writeEndElement(); // close "cantorWorksheet" section
}
//! Load from XML
bool CantorWorksheet::load(XmlStreamReader* reader, bool preview) {
if(!reader->isStartElement() || reader->name() != "cantorWorksheet"){
reader->raiseError(i18n("no Cantor worksheet element found"));
return false;
}
if (!readBasicAttributes(reader))
return false;
- QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used");
+ KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used");
QXmlStreamAttributes attribs;
QString str;
bool rc = false;
while (!reader->atEnd()){
reader->readNext();
if (reader->isEndElement() && reader->name() == "cantorWorksheet")
break;
if (!reader->isStartElement())
continue;
if (reader->name() == "comment"){
if (!readCommentElement(reader))
return false;
} else if (!preview && reader->name() == "general"){
attribs = reader->attributes();
m_backendName = attribs.value("backend_name").toString().trimmed();
if(str.isEmpty())
- reader->raiseWarning(attributeWarning.arg("'backend_name'"));
+ reader->raiseWarning(attributeWarning.subs("backend_name").toString());
} else if (!preview && reader->name() == "worksheet"){
attribs = reader->attributes();
str = attribs.value("content").toString().trimmed();
if(str.isEmpty())
- reader->raiseWarning(attributeWarning.arg("'content'"));
+ reader->raiseWarning(attributeWarning.subs("content").toString());
QByteArray content = QByteArray::fromBase64(str.toAscii());
rc = init(&content);
if (!rc) {
QString msg = i18n("This project has Cantor content but no Cantor plugins were found. Please check your installation. The project will be closed.");
reader->raiseError(msg);
return false;
}
} else if(!preview && reader->name() == "column") {
Column* column = new Column("");
column->setUndoAware(false);
if (!column->load(reader, preview)) {
delete column;
return false;
}
addChild(column);
} else { // unknown element
reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString()));
if (!reader->skipToEndElement()) return false;
}
}
return true;
}
diff --git a/src/backend/cantorWorksheet/VariableParser.cpp b/src/backend/cantorWorksheet/VariableParser.cpp
index e4700312b..28fb393be 100644
--- a/src/backend/cantorWorksheet/VariableParser.cpp
+++ b/src/backend/cantorWorksheet/VariableParser.cpp
@@ -1,124 +1,123 @@
/***************************************************************************
File : VariableParser.h
Project : LabPlot
Description : Variable parser for different CAS backends
--------------------------------------------------------------------
Copyright : (C) 2015 Garvit Khatri (garvitdelhi@gmail.com)
Copyright : (C) 2016 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 "VariableParser.h"
#include
#include "backend/lib/trace.h"
#include
VariableParser::VariableParser(const QString& name, const QString& value)
: m_backendName(name), m_string(value), m_parsed(false) {
PERFTRACE("parsing variable");
if(m_backendName.compare(QStringLiteral("Maxima"), Qt::CaseInsensitive) == 0)
parseMaximaValues();
else if(m_backendName.compare(QStringLiteral("Python 3"), Qt::CaseInsensitive) == 0)
parsePythonValues();
else if(m_backendName.compare(QStringLiteral("Python 2"), Qt::CaseInsensitive) == 0)
parsePythonValues();
else if(m_backendName.compare(QStringLiteral("Sage"), Qt::CaseInsensitive) == 0)
parsePythonValues();
else if(m_backendName.compare(QStringLiteral("R"), Qt::CaseInsensitive) == 0)
parseRValues();
else if(m_backendName.compare(QStringLiteral("Julia"), Qt::CaseInsensitive) == 0)
parsePythonValues();
}
void VariableParser::parseMaximaValues() {
if(m_string.count(QStringLiteral("[")) < 2) {
m_string = m_string.replace(QStringLiteral("["), QStringLiteral(""));
m_string = m_string.replace(QStringLiteral("]"), QStringLiteral(""));
m_string = m_string.trimmed();
const QStringList valueStringList = m_string.split(QStringLiteral(","));
parseValues(valueStringList);
}
}
void VariableParser::parsePythonValues() {
QStringList valueStringList;
m_string = m_string.trimmed();
if (m_string.startsWith(QStringLiteral("array"))) {
//parse numpy arrays, string representation like array([1,2,3,4,5])
m_string = m_string.replace(QStringLiteral("array(["), QStringLiteral(""));
m_string = m_string.replace(QStringLiteral("])"), QStringLiteral(""));
} else if (m_string.startsWith(QStringLiteral("["))) {
//parse python's lists
m_string = m_string.replace(QStringLiteral("["), QStringLiteral(""));
m_string = m_string.replace(QStringLiteral("]"), QStringLiteral(""));
} else if(m_string.startsWith(QStringLiteral("("))) {
//parse python's tuples
m_string = m_string.replace(QStringLiteral("("), QStringLiteral(""));
m_string = m_string.replace(QStringLiteral(")"), QStringLiteral(""));
} else {
return;
}
if(m_string.count(QStringLiteral(","))>1)
valueStringList = m_string.split(QStringLiteral(","));
else
valueStringList = m_string.split(QStringLiteral(" "));
parseValues(valueStringList);
}
void VariableParser::parseRValues() {
- m_string = "[1] 1 2 3 4 5 6";
m_string = m_string.remove( QRegExp("\\[.*\\]"));
m_string = m_string.trimmed();
const QStringList valueStringList = m_string.split(QStringLiteral(" "));
parseValues(valueStringList);
}
bool VariableParser::isParsed() {
return m_parsed;
}
QVector< double > VariableParser::values() {
return m_values;
}
void VariableParser::parseValues(const QStringList& values) {
PERFTRACE("parsing variable values string list");
for(const QString& v : values) {
bool isNumber = false;
double value = v.trimmed().toDouble(&isNumber);
//accept the variable only if there is at least one numerical value in the array.
if(isNumber) {
if (!m_parsed)
m_parsed = true;
} else {
value = NAN;
}
m_values << value;
}
}
diff --git a/src/backend/core/AbstractAspect.cpp b/src/backend/core/AbstractAspect.cpp
index 34704525f..db37ba3d7 100644
--- a/src/backend/core/AbstractAspect.cpp
+++ b/src/backend/core/AbstractAspect.cpp
@@ -1,837 +1,837 @@
/***************************************************************************
File : AbstractAspect.cpp
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-2016 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 *
* *
***************************************************************************/
#include "backend/core/AbstractAspect.h"
#include "backend/core/AspectPrivate.h"
#include "backend/core/aspectcommands.h"
#include "backend/core/Project.h"
#include "backend/spreadsheet/Spreadsheet.h"
#include "backend/datapicker/DatapickerCurve.h"
#include "backend/lib/XmlStreamReader.h"
#include "backend/lib/SignallingUndoCommand.h"
#include "backend/lib/PropertyChangeCommand.h"
#include
/**
* \class AbstractAspect
* \brief Base class of all persistent objects in a Project.
*
* Before going into the details, it's useful to understand the ideas behind the
* \ref aspect "Aspect Framework".
*
* Aspects organize themselves into trees, where a parent takes ownership of its children. Usually,
* though not necessarily, a Project instance will sit at the root of the tree (without a Project
* ancestor, project() will return 0 and undo does not work). Children are organized using
* addChild(), removeChild(), child(), indexOfChild() and childCount() on the parent's side as well
* as the equivalent convenience methods index() and remove() on the child's side.
* In contrast to the similar feature of QObject, Aspect trees are fully undo/redo aware and provide
* signals around object adding/removal.
*
* AbstractAspect manages for every Aspect the properties #name, #comment, #captionSpec and
* #creationTime. All of these translate into the caption() as described in the documentation
* of setCaptionSpec().
*
* If an undoStack() can be found (usually it is managed by Project), changes to the properties
* as well as adding/removing children support multi-level undo/redo. In order to support undo/redo
* for problem-specific data in derived classes, make sure that all changes to your data are done
* by handing appropriate commands to exec().
*/
/**
* \enum AbstractAspect::ChildIndexFlag
* \brief Flags which control numbering scheme of children.
*/
/**
* \var AbstractAspect::IncludeHidden
* \brief Include aspects marked as "hidden" in numbering or listing children.
*/
/**
* \var AbstractAspect::Recursive
* \brief Recursively handle all descendents, not just immediate children.
*/
/**
* \var AbstractAspect::Compress
* \brief Remove all null pointers from the result list.
*/
////////////////////////////////////////////////////////////////////////////////////////////////////
// documentation of template and inline methods
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* \fn template < class T > T *AbstractAspect::ancestor() const
* \brief Return the closest ancestor of class T (or NULL if none found).
*/
/**
* \fn template < class T > QVector AbstractAspect::children(const ChildIndexFlags &flags=0) const
* \brief Return list of children inheriting from class T.
*
* Use AbstractAspect for T in order to get all children.
*/
/**
* \fn template < class T > T *AbstractAspect::child(int index, const ChildIndexFlags &flags=0) const
* \brief Return child identified by (0 based) index and class.
*
* Identifying objects by an index is inherently error-prone and confusing,
* given that the index can be based on different criteria (viz, counting
* only instances of specific classes and including/excluding hidden
* aspects). Therefore, it is recommended to avoid indices wherever possible
* and instead refer to aspects using AbstractAspect pointers.
*/
/**
* \fn template < class T > T *AbstractAspect::child(const QString &name) const
* \brief Get child by name and class.
*/
/**
* \fn template < class T > int AbstractAspect::childCount(const ChildIndexFlags &flags=0) const
* \brief Return the number of child Aspects inheriting from given class.
*/
/**
* \fn template < class T > int AbstractAspect::indexOfChild(const AbstractAspect * child, const ChildIndexFlags &flags=0) const
* \brief Return (0 based) index of child in the list of children inheriting from class T.
*/
/**
* \fn void AbstractAspect::aspectDescriptionAboutToChange(const AbstractAspect *aspect)
* \brief Emitted before the name, comment or caption spec is changed
*/
/**
* \fn void AbstractAspect::aspectDescriptionChanged(const AbstractAspect *aspect)
* \brief Emitted after the name, comment or caption spec have changed
*/
/**
* \fn void AbstractAspect::aspectAboutToBeAdded(const AbstractAspect *parent, const AbstractAspect *before, const AbstractAspect * child)
* \brief Emitted before a new child is inserted
*/
/**
* \fn void AbstractAspect::aspectAdded(const AbstractAspect *aspect)
* \brief Emitted after a new Aspect has been added to the tree
*/
/**
* \fn void AbstractAspect::aspectAboutToBeRemoved(const AbstractAspect *aspect)
* \brief Emitted before an aspect is removed from its parent
*/
/**
* \fn void AbstractAspect::aspectRemoved(const AbstractAspect *parent, const AbstractAspect * before, const AbstractAspect * child)
* \brief Emitted from the parent after removing a child
*/
/**
* \fn void AbstractAspect::aspectHiddenAboutToChange(const AbstractAspect *aspect)
* \brief Emitted before the hidden attribute is changed
*/
/**
* \fn void AbstractAspect::aspectHiddenChanged(const AbstractAspect *aspect)
* \brief Emitted after the hidden attribute has changed
*/
/**
* \fn void AbstractAspect::statusInfo(const QString &text)
* \brief Emitted whenever some aspect in the tree wants to give status information to the user
* \sa info(const QString&)
*/
/**
* \fn protected void AbstractAspect::info(const QString &text)
* \brief Implementations should call this whenever status information should be given to the user.
*
* This will cause statusInfo() to be emitted. Typically, this will cause the specified string
* to be displayed in a status bar, a log window or some similar non-blocking way so as not to
* disturb the workflow.
*/
/**
* \fn protected virtual void childSelected(const AbstractAspect*){}
* \brief called when a child's child aspect was selected in the model
*/
/**
* \fn protected virtual void childDeselected()
* \brief called when a child aspect was deselected in the model
*/
/**
* \fn protected virtual void childDeselected(const AbstractAspect*)
* \brief called when a child's child aspect was deselected in the model
*/
////////////////////////////////////////////////////////////////////////////////////////////////////
// start of AbstractAspect implementation
////////////////////////////////////////////////////////////////////////////////////////////////////
AbstractAspect::AbstractAspect(const QString &name)
: d(new AbstractAspectPrivate(this, name))
{
}
AbstractAspect::~AbstractAspect() {
delete d;
}
QString AbstractAspect::name() const {
return d->m_name;
}
void AbstractAspect::setName(const QString &value) {
if (value.isEmpty()) {
setName(QLatin1String("1"));
return;
}
if (value == d->m_name)
return;
QString new_name;
if (d->m_parent) {
new_name = d->m_parent->uniqueNameFor(value);
if (new_name != value)
info(i18n("Intended name \"%1\" was changed to \"%2\" in order to avoid name collision.", value, new_name));
} else {
new_name = value;
}
exec(new PropertyChangeCommand(i18n("%1: rename to %2", d->m_name, new_name),
&d->m_name, new_name),
"aspectDescriptionAboutToChange", "aspectDescriptionChanged", Q_ARG(const AbstractAspect*,this));
}
QString AbstractAspect::comment() const {
return d->m_comment;
}
void AbstractAspect::setComment(const QString& value) {
if (value == d->m_comment) return;
exec(new PropertyChangeCommand(i18n("%1: change comment", d->m_name),
&d->m_comment, value),
"aspectDescriptionAboutToChange", "aspectDescriptionChanged", Q_ARG(const AbstractAspect*,this));
}
void AbstractAspect::setCreationTime(const QDateTime& time) {
d->m_creation_time = time;
}
QDateTime AbstractAspect::creationTime() const {
return d->m_creation_time;
}
bool AbstractAspect::hidden() const {
return d->m_hidden;
}
/**
* \brief Set "hidden" property, i.e. whether to exclude this aspect from being shown in the explorer.
*/
void AbstractAspect::setHidden(bool value) {
if (value == d->m_hidden) return;
exec(new PropertyChangeCommand(i18n("%1: change hidden status", d->m_name),
&d->m_hidden, value),
"aspectHiddenAboutToChange", "aspectHiddenChanged", Q_ARG(const AbstractAspect*,this));
}
void AbstractAspect::setIsLoading(bool load) {
d->m_isLoading = load;
}
bool AbstractAspect::isLoading() const {
return d->m_isLoading;
}
/**
* \brief Return an icon to be used for decorating my views.
*/
QIcon AbstractAspect::icon() const {
return QIcon();
}
/**
* \brief Return a new context menu.
*
* The caller takes ownership of the menu.
*/
QMenu* AbstractAspect::createContextMenu() {
QMenu* menu = new QMenu();
menu->addSection(this->name());
//TODO: activate this again when the functionality is implemented
// menu->addAction( KStandardAction::cut(this) );
// menu->addAction(KStandardAction::copy(this));
// menu->addAction(KStandardAction::paste(this));
// menu->addSeparator();
menu->addAction(QIcon::fromTheme(QLatin1String("edit-rename")), i18n("Rename"), this, SIGNAL(renameRequested()));
//don't allow to delete data spreadsheets in the datapicker curves
if ( !(dynamic_cast(this) && dynamic_cast(this->parentAspect())) )
menu->addAction(QIcon::fromTheme(QLatin1String("edit-delete")), i18n("Delete"), this, SLOT(remove()));
return menu;
}
/**
* \brief Return my parent Aspect or 0 if I currently don't have one.
*/
AbstractAspect* AbstractAspect::parentAspect() const {
return d->m_parent;
}
void AbstractAspect::setParentAspect(AbstractAspect* parent) {
d->m_parent = parent;
}
/**
* \brief Return the folder the Aspect is contained in or 0 if there is none.
*
* The returned folder may be the aspect itself if it inherits Folder.
*/
Folder* AbstractAspect::folder() {
if(inherits("Folder")) return static_cast(this);
AbstractAspect* parent_aspect = parentAspect();
while(parent_aspect && !parent_aspect->inherits("Folder"))
parent_aspect = parent_aspect->parentAspect();
return static_cast(parent_aspect);
}
/**
* \brief Return whether the there is a path upwards to the given aspect
*
* This also returns true if other==this.
*/
bool AbstractAspect::isDescendantOf(AbstractAspect* other) {
if(other == this) return true;
AbstractAspect* parent_aspect = parentAspect();
while(parent_aspect) {
if(parent_aspect == other) return true;
parent_aspect = parent_aspect->parentAspect();
}
return false;
}
/**
* \brief Return the Project this Aspect belongs to, or 0 if it is currently not part of one.
*/
Project* AbstractAspect::project() {
return parentAspect() ? parentAspect()->project() : 0;
}
/**
* \brief Return the path that leads from the top-most Aspect (usually a Project) to me.
*/
QString AbstractAspect::path() const {
return parentAspect() ? parentAspect()->path() + QLatin1Char('/') + name() : QLatin1String("");
}
/**
* \brief Add the given Aspect to my list of children.
*/
void AbstractAspect::addChild(AbstractAspect* child) {
Q_CHECK_PTR(child);
QString new_name = uniqueNameFor(child->name());
beginMacro(i18n("%1: add %2", name(), new_name));
if (new_name != child->name()) {
info(i18n("Renaming \"%1\" to \"%2\" in order to avoid name collision.", child->name(), new_name));
child->setName(new_name);
}
exec(new AspectChildAddCmd(d, child, d->m_children.count()));
child->finalizeAdd();
endMacro();
}
/**
* \brief Add the given Aspect to my list of children without any checks and without putting this step onto the undo-stack
*/
void AbstractAspect::addChildFast(AbstractAspect* child) {
emit aspectAboutToBeAdded(this, 0, child); //TODO: before-pointer is 0 here, also in the commands classes. why?
d->insertChild(d->m_children.count(), child);
child->finalizeAdd();
emit aspectAdded(child);
}
/**
* \brief Insert the given Aspect at a specific position in my list of children.
*/
void AbstractAspect::insertChildBefore(AbstractAspect* child, AbstractAspect* before) {
Q_CHECK_PTR(child);
QString new_name = uniqueNameFor(child->name());
- beginMacro(i18n("%1: insert %2 before %3", name(), new_name, before ? before->name() : i18n("end")));
+ beginMacro(before ? i18n("%1: insert %2 before %3", name(), new_name, before->name()) : i18n("%1: insert %2 before end", name(), new_name));
if (new_name != child->name()) {
info(i18n("Renaming \"%1\" to \"%2\" in order to avoid name collision.", child->name(), new_name));
child->setName(new_name);
}
int index = d->indexOfChild(before);
if (index == -1)
index = d->m_children.count();
exec(new AspectChildAddCmd(d, child, index));
endMacro();
}
/**
* \brief Insert the given Aspect at a specific position in my list of children.without any checks and without putting this step onto the undo-stack
*/
void AbstractAspect::insertChildBeforeFast(AbstractAspect* child, AbstractAspect* before) {
connect(child, &AbstractAspect::selected, this, &AbstractAspect::childSelected);
connect(child, &AbstractAspect::deselected, this, &AbstractAspect::childDeselected);
int index = d->indexOfChild(before);
if (index == -1)
index = d->m_children.count();
emit aspectAboutToBeAdded(this, 0, child);
d->insertChild(index, child);
emit aspectAdded(child);
}
/**
* \brief Remove the given Aspect from my list of children.
*
* The ownership of the child is transferred to the undo command,
* i.e., the aspect is deleted by the undo command.
* \sa reparent()
*/
void AbstractAspect::removeChild(AbstractAspect* child) {
Q_ASSERT(child->parentAspect() == this);
beginMacro(i18n("%1: remove %2", name(), child->name()));
exec(new AspectChildRemoveCmd(d, child));
endMacro();
}
/**
* \brief Remove all child Aspects.
*/
void AbstractAspect::removeAllChildren() {
beginMacro(i18n("%1: remove all children", name()));
QVector children_list = children();
QVector::const_iterator i = children_list.constBegin();
AbstractAspect *current = 0, *nextSibling = 0;
if (i != children_list.constEnd()) {
current = *i;
if (++i != children_list.constEnd())
nextSibling = *i;
}
while (current) {
emit aspectAboutToBeRemoved(current);
exec(new AspectChildRemoveCmd(d, current));
emit aspectRemoved(this, nextSibling, current);
current = nextSibling;
if (i != children_list.constEnd() && ++i != children_list.constEnd())
nextSibling = *i;
else
nextSibling = 0;
}
endMacro();
}
/**
* \brief Move a child to another parent aspect and transfer ownership.
*/
void AbstractAspect::reparent(AbstractAspect* newParent, int newIndex) {
Q_ASSERT(parentAspect() != NULL);
Q_ASSERT(newParent != NULL);
int max_index = newParent->childCount(IncludeHidden);
if (newIndex == -1)
newIndex = max_index;
Q_ASSERT(newIndex >= 0 && newIndex <= max_index);
AbstractAspect* old_parent = parentAspect();
int old_index = old_parent->indexOfChild(this, IncludeHidden);
AbstractAspect* old_sibling = old_parent->child(old_index+1, IncludeHidden);
AbstractAspect* new_sibling = newParent->child(newIndex, IncludeHidden);
//TODO check/test this!
emit aspectAboutToBeRemoved(this);
emit newParent->aspectAboutToBeAdded(newParent, new_sibling, this);
exec(new AspectChildReparentCmd(parentAspect()->d, newParent->d, this, newIndex));
emit old_parent->aspectRemoved(old_parent, old_sibling, this);
emit aspectAdded(this);
endMacro();
}
QVector AbstractAspect::children(const char* className, ChildIndexFlags flags) {
QVector result;
for (auto* child : children()) {
if (flags & IncludeHidden || !child->hidden()) {
if ( child->inherits(className) || !(flags & Compress)) {
result << child;
if (flags & Recursive){
result << child->children(className, flags);
}
}
}
}
return result;
}
const QVector AbstractAspect::children() const {
return d->m_children;
}
/**
* \brief Remove me from my parent's list of children.
*/
void AbstractAspect::remove() {
if(parentAspect())
parentAspect()->removeChild(this);
}
/*!
* returns the list of all parent aspects (folders and sub-folders)
*/
QVector AbstractAspect::dependsOn() const {
QVector aspects;
if (parentAspect())
aspects << parentAspect() << parentAspect()->dependsOn();
return aspects;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//! \name serialize/deserialize
//@{
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* \fn virtual void AbstractAspect::save(QXmlStreamWriter *) const
* \brief Save as XML
*/
/**
* \fn virtual bool AbstractAspect::load(XmlStreamReader *)
* \brief Load from XML
*
* XmlStreamReader supports errors as well as warnings. If only
* warnings (non-critial errors) occur, this function must return
* the reader at the end element corresponding to the current
* element at the time the function was called.
*
* This function is normally intended to be called directly
* after the ctor. If you want to call load on an aspect that
* has been altered, you must make sure beforehand that
* it is in the same state as after creation, e.g., remove
* all its child aspects.
*
* \return false on error
*/
/**
* \brief Save the comment to XML
*/
void AbstractAspect::writeCommentElement(QXmlStreamWriter * writer) const{
writer->writeStartElement(QLatin1String("comment"));
writer->writeCharacters(comment());
writer->writeEndElement();
}
/**
* \brief Load comment from an XML element
*/
bool AbstractAspect::readCommentElement(XmlStreamReader * reader){
setComment(reader->readElementText());
return true;
}
/**
* \brief Save name and creation time to XML
*/
void AbstractAspect::writeBasicAttributes(QXmlStreamWriter* writer) const {
writer->writeAttribute(QLatin1String("creation_time") , creationTime().toString(QLatin1String("yyyy-dd-MM hh:mm:ss:zzz")));
writer->writeAttribute(QLatin1String("name"), name());
}
/**
* \brief Load name and creation time from XML
*
* \return false on error
*/
bool AbstractAspect::readBasicAttributes(XmlStreamReader* reader){
const QXmlStreamAttributes& attribs = reader->attributes();
// name
QString str = attribs.value(QLatin1String("name")).toString();
if(str.isEmpty())
reader->raiseWarning(i18n("Attribute 'name' is missing or empty."));
d->m_name = str;
// creation time
str = attribs.value(QLatin1String("creation_time")).toString();
if(str.isEmpty()) {
reader->raiseWarning(i18n("Invalid creation time for '%1'. Using current time.", name()));
d->m_creation_time = QDateTime::currentDateTime();
} else {
QDateTime creation_time = QDateTime::fromString(str, QLatin1String("yyyy-dd-MM hh:mm:ss:zzz"));
if (creation_time.isValid())
d->m_creation_time = creation_time;
else
d->m_creation_time = QDateTime::currentDateTime();
}
return true;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//@}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
//! \name undo related
//@{
////////////////////////////////////////////////////////////////////////////////////////////////////
void AbstractAspect::setUndoAware(bool b) {
d->m_undoAware = b;
}
/**
* \brief Return the undo stack of the Project, or 0 if this Aspect is not part of a Project.
*
* It's also possible to construct undo-enabled Aspect trees without Project.
* The only requirement is that the root Aspect reimplements undoStack() to get the
* undo stack from somewhere (the default implementation just delegates to parentAspect()).
*/
QUndoStack* AbstractAspect::undoStack() const {
return parentAspect() ? parentAspect()->undoStack() : 0;
}
/**
* \brief Execute the given command, pushing it on the undoStack() if available.
*/
void AbstractAspect::exec(QUndoCommand* cmd) {
Q_CHECK_PTR(cmd);
if (d->m_undoAware) {
QUndoStack *stack = undoStack();
if (stack)
stack->push(cmd);
else {
cmd->redo();
delete cmd;
}
if (project())
project()->setChanged(true);
} else {
cmd->redo();
delete cmd;
}
}
/**
* \brief Execute command and arrange for signals to be sent before/after it is redone or undone.
*
* \arg \c command The command to be executed.
* \arg \c preChangeSignal The name of the signal to be triggered before re-/undoing the command.
* \arg \c postChangeSignal The name of the signal to be triggered after re-/undoing the command.
* \arg val0,val1,val2,val3 Arguments to the signals; to be given using Q_ARG().
*
* Signal arguments are given using the macro Q_ARG(typename, const value&). Since
* the variable given as "value" will likely be out of scope when the signals are emitted, a copy
* needs to be created. This uses QMetaType, which means that (non-trivial) argument types need to
* be registered using qRegisterMetaType() before giving them to exec() (in particular, this also
* goes for pointers to custom data types).
*
* \sa SignallingUndoCommand
*/
void AbstractAspect::exec(QUndoCommand* command,
const char* preChangeSignal, const char* postChangeSignal,
QGenericArgument val0, QGenericArgument val1, QGenericArgument val2, QGenericArgument val3) {
beginMacro(command->text());
exec(new SignallingUndoCommand(QLatin1String("change signal"), this,
preChangeSignal, postChangeSignal, val0, val1, val2, val3));
exec(command);
exec(new SignallingUndoCommand(QLatin1String("change signal"), this,
postChangeSignal, preChangeSignal, val0, val1, val2, val3));
endMacro();
}
/**
* \brief Begin an undo stack macro (series of commands)
*/
void AbstractAspect::beginMacro(const QString& text) {
if (!d->m_undoAware)
return;
QUndoStack* stack = undoStack();
if (stack)
stack->beginMacro(text);
}
/**
* \brief End the current undo stack macro
*/
void AbstractAspect::endMacro() {
if (!d->m_undoAware)
return;
QUndoStack* stack = undoStack();
if (stack)
stack->endMacro();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//@}
////////////////////////////////////////////////////////////////////////////////////////////////////
/*!
* this function is called when the selection in ProjectExplorer was changed.
* forwards the selection/deselection to the parent aspect via emitting a signal.
*/
void AbstractAspect::setSelected(bool s){
if (s)
emit selected(this);
else
emit deselected(this);
}
void AbstractAspect::childSelected(const AbstractAspect* aspect) {
//forward the signal to the highest possible level in the parent-child hierarchy
//e.g. axis of a plot was selected. Don't include parent aspects here that do not
//need to react on the selection of children: e.g. Folder or XYFitCurve with
//the child column for calculated residuals
if (aspect->parentAspect() != 0
&& !aspect->parentAspect()->inherits("Folder")
&& !aspect->parentAspect()->inherits("XYFitCurve")
&& !aspect->parentAspect()->inherits("CantorWorksheet"))
emit aspect->parentAspect()->selected(aspect);
}
void AbstractAspect::childDeselected(const AbstractAspect* aspect) {
//forward the signal to the highest possible level in the parent-child hierarchy
//e.g. axis of a plot was selected. Don't include parent aspects here that do not
//need to react on the deselection of children: e.g. Folder or XYFitCurve with
//the child column for calculated residuals
if (aspect->parentAspect() != 0
&& !aspect->parentAspect()->inherits("Folder")
&& !aspect->parentAspect()->inherits("XYFitCurve")
&& !aspect->parentAspect()->inherits("CantorWorksheet"))
emit aspect->parentAspect()->deselected(aspect);
}
/**
* \brief Make the specified name unique among my children by incrementing a trailing number.
*/
QString AbstractAspect::uniqueNameFor(const QString& current_name) const {
QStringList child_names;
for (auto* child : children())
child_names << child->name();
if (!child_names.contains(current_name))
return current_name;
QString base = current_name;
int last_non_digit;
for (last_non_digit = base.size()-1; last_non_digit>=0 &&
base[last_non_digit].category() == QChar::Number_DecimalDigit; --last_non_digit)
base.chop(1);
if (last_non_digit >=0 && base[last_non_digit].category() != QChar::Separator_Space)
base.append(" ");
int new_nr = current_name.rightRef(current_name.size() - base.size()).toInt();
QString new_name;
do
new_name = base + QString::number(++new_nr);
while (child_names.contains(new_name));
return new_name;
}
void AbstractAspect::connectChild(AbstractAspect* child) {
connect(child, &AbstractAspect::aspectDescriptionAboutToChange, this, &AbstractAspect::aspectDescriptionAboutToChange);
connect(child, &AbstractAspect::aspectDescriptionChanged, this, &AbstractAspect::aspectDescriptionChanged);
connect(child, &AbstractAspect::aspectAboutToBeAdded, this, &AbstractAspect::aspectAboutToBeAdded);
connect(child, &AbstractAspect::aspectAdded, this, &AbstractAspect::aspectAdded);
connect(child, &AbstractAspect::aspectAboutToBeRemoved, this, &AbstractAspect::aspectAboutToBeRemoved);
connect(child, &AbstractAspect::aspectRemoved, this, &AbstractAspect::aspectRemoved);
connect(child, &AbstractAspect::aspectHiddenAboutToChange, this, &AbstractAspect::aspectHiddenAboutToChange);
connect(child, &AbstractAspect::aspectHiddenChanged, this, &AbstractAspect::aspectHiddenChanged);
connect(child, &AbstractAspect::statusInfo, this, &AbstractAspect::statusInfo);
connect(child, &AbstractAspect::selected, this, &AbstractAspect::childSelected);
connect(child, &AbstractAspect::deselected, this, &AbstractAspect::childDeselected);
}
//##############################################################################
//###################### Private implementation ###############################
//##############################################################################
AbstractAspectPrivate::AbstractAspectPrivate(AbstractAspect* owner, const QString& name)
: m_name(name.isEmpty() ? QLatin1String("1") : name), m_hidden(false), q(owner), m_parent(0),
m_undoAware(true), m_isLoading(false)
{
m_creation_time = QDateTime::currentDateTime();
}
AbstractAspectPrivate::~AbstractAspectPrivate() {
for(auto* child : m_children)
delete child;
}
void AbstractAspectPrivate::insertChild(int index, AbstractAspect* child) {
m_children.insert(index, child);
// Always remove from any previous parent before adding to a new one!
// Can't handle this case here since two undo commands have to be created.
Q_ASSERT(child->parentAspect() == 0);
child->setParentAspect(q);
q->connectChild(child);
}
int AbstractAspectPrivate::indexOfChild(const AbstractAspect* child) const {
for(int i=0; isetParentAspect(0);
return index;
}
diff --git a/src/backend/core/AbstractColumn.cpp b/src/backend/core/AbstractColumn.cpp
index 25fa9ba00..c328889bd 100644
--- a/src/backend/core/AbstractColumn.cpp
+++ b/src/backend/core/AbstractColumn.cpp
@@ -1,710 +1,710 @@
/***************************************************************************
File : AbstractColumn.cpp
Project : LabPlot
Description : Interface definition for data with column logic
--------------------------------------------------------------------
Copyright : (C) 2007,2008 Tilman Benkert (thzs@gmx.net)
Copyright : (C) 2017 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 "backend/core/AbstractColumn.h"
#include "backend/core/AbstractColumnPrivate.h"
#include "backend/core/abstractcolumncommands.h"
#include "backend/lib/XmlStreamReader.h"
#include "backend/lib/SignallingUndoCommand.h"
#include
#include
-#include
+#include
#include
/**
* \class AbstractColumn
* \brief Interface definition for data with column logic
*
* This is an abstract base class for column-based data,
* i.e. mathematically a vector or technically a 1D array or list.
* It only defines the interface but has no data members itself.
*
* Classes derived from this are typically table columns or outputs
* of filters which can be chained between table columns and plots.
* From the point of view of the plot functions there will be no difference
* between a table column and a filter output since both use this interface.
*
* Classes derived from this will either store a
* vector with entries of one certain data type, e.g. double, QString,
* QDateTime, or generate such values on demand. To determine the data
* type of a class derived from this, use the columnMode() function.
* AbstractColumn defines all access functions for all supported data
* types but only those corresponding to the return value of columnMode()
* will return a meaningful value. Calling functions not belonging to
* the data type of the column is safe, but will do nothing (writing
* function) or return some default value (reading functions).
*
* This class also defines all signals which indicate a data change.
* Any class whose output values are subject to change over time must emit
* the according signals. These signals notify any object working with the
* column before and after a change of the column.
* In some cases it will be necessary for a class using
* the column to connect aboutToBeDestroyed(), to react
* to a column's deletion, e.g. a filter's reaction to a
* table deletion.
*
* All writing functions have a "do nothing" standard implementation to
* make deriving a read-only class very easy without bothering about the
* writing interface.
*/
/**
* \brief Ctor
*
* \param name the column name (= aspect name)
*/
AbstractColumn::AbstractColumn(const QString &name) : AbstractAspect(name),
d( new AbstractColumnPrivate(this) ) {
}
AbstractColumn::~AbstractColumn() {
emit aboutToBeDestroyed(this);
delete d;
}
QStringList AbstractColumn::dateFormats() {
QStringList dates;
dates << "yyyy-MM-dd";
dates << "yyyy/MM/dd";
dates << "dd/MM/yyyy";
dates << "dd/MM/yy";
dates << "dd.MM.yyyy";
dates << "dd.MM.yy";
dates << "MM/yyyy";
dates << "dd.MM.";
dates << "yyyyMMdd";
return dates;
}
QStringList AbstractColumn::timeFormats() {
QStringList times;
times << "hh";
times << "hh ap";
times << "hh:mm";
times << "hh:mm ap";
times << "hh:mm:ss";
times << "hh:mm:ss.zzz";
times << "hh:mm:ss:zzz";
times << "mm:ss.zzz";
times << "hhmmss";
return times;
}
QStringList AbstractColumn::dateTimeFormats() {
QStringList dateTimes;
// any combination of date and times
for (const auto& d : dateFormats())
dateTimes << d;
for (const auto& t : timeFormats())
dateTimes << t;
for (const auto& d : dateFormats())
for (const auto& t : timeFormats())
dateTimes << d + ' ' + t;
return dateTimes;
}
/**
* \brief Convenience method for mode-dependent icon
*/
QIcon AbstractColumn::iconForMode(ColumnMode mode) {
switch (mode) {
case AbstractColumn::Numeric:
case AbstractColumn::Integer:
break;
case AbstractColumn::Text:
return QIcon::fromTheme("draw-text");
case AbstractColumn::DateTime:
case AbstractColumn::Month:
case AbstractColumn::Day:
return QIcon::fromTheme("chronometer");
}
return QIcon::fromTheme("x-shape-text");
}
/**
* \fn bool AbstractColumn::isReadOnly() const
* \brief Return whether the object is read-only
*/
/**
* \fn AbstractColumn::ColumnMode AbstractColumn::columnMode() const
* \brief Return the column mode
*
* This function is most used by tables but can also be used
* by plots. The column mode specifies how to interpret
* the values in the column additional to the data type.
*/
/**
* \brief Set the column mode
*
* This sets the column mode and, if
* necessary, converts it to another datatype.
*/
void AbstractColumn::setColumnMode(AbstractColumn::ColumnMode) {}
/**
* \brief Copy another column of the same type
*
* This function will return false if the data type
* of 'other' is not the same as the type of 'this'.
* Use a filter to convert a column to another type.
*/
bool AbstractColumn::copy(const AbstractColumn *other) {
Q_UNUSED(other)
return false;
}
/**
* \brief Copies part of another column of the same type
*
* This function will return false if the data type
* of 'other' is not the same as the type of 'this'.
* \param source pointer to the column to copy
* \param source_start first row to copy in the column to copy
* \param destination_start first row to copy in
* \param num_rows the number of rows to copy
*/
bool AbstractColumn::copy(const AbstractColumn *source, int source_start, int destination_start, int num_rows) {
Q_UNUSED(source)
Q_UNUSED(source_start)
Q_UNUSED(destination_start)
Q_UNUSED(num_rows)
return false;
}
/**
* \fn int AbstractColumn::rowCount() const
* \brief Return the data vector size
*/
/**
* \brief Insert some empty (or initialized with invalid values) rows
*/
void AbstractColumn::insertRows(int before, int count) {
beginMacro( i18np("%1: insert 1 row", "%1: insert %2 rows", name(), count) );
exec(new SignallingUndoCommand("pre-signal", this, "rowsAboutToBeInserted", "rowsRemoved",
Q_ARG(const AbstractColumn*,this), Q_ARG(int,before), Q_ARG(int,count)));
handleRowInsertion(before, count);
exec(new SignallingUndoCommand("post-signal", this, "rowsInserted", "rowsAboutToBeRemoved",
Q_ARG(const AbstractColumn*,this), Q_ARG(int,before), Q_ARG(int,count)));
endMacro();
}
void AbstractColumn::handleRowInsertion(int before, int count) {
exec(new AbstractColumnInsertRowsCmd(this, before, count));
}
/**
* \brief Remove 'count' rows starting from row 'first'
*/
void AbstractColumn::removeRows(int first, int count) {
beginMacro( i18np("%1: remove 1 row", "%1: remove %2 rows", name(), count) );
exec(new SignallingUndoCommand("change signal", this, "rowsAboutToBeRemoved", "rowsInserted",
Q_ARG(const AbstractColumn*,this), Q_ARG(int,first), Q_ARG(int,count)));
handleRowRemoval(first, count);
exec(new SignallingUndoCommand("change signal", this, "rowsRemoved", "rowsAboutToBeInserted",
Q_ARG(const AbstractColumn*,this), Q_ARG(int,first), Q_ARG(int,count)));
endMacro();
}
void AbstractColumn::handleRowRemoval(int first, int count) {
exec(new AbstractColumnRemoveRowsCmd(this, first, count));
}
/**
* \fn AbstractColumn::PlotDesignation AbstractColumn::plotDesignation() const
* \brief Return the column plot designation
*/
/**
* \brief Set the column plot designation
*/
void AbstractColumn::setPlotDesignation(AbstractColumn::PlotDesignation pd) {
Q_UNUSED(pd)
}
/**
* \brief Clear the whole column
*/
void AbstractColumn::clear() {}
/**
* \brief Convenience method for mode-independent testing of validity
*/
bool AbstractColumn::isValid(int row) const {
switch (columnMode()) {
case AbstractColumn::Numeric:
return !std::isnan(valueAt(row));
case AbstractColumn::Integer: // there is no invalid integer
return true;
case AbstractColumn::Text:
return !textAt(row).isNull();
case AbstractColumn::DateTime:
case AbstractColumn::Month:
case AbstractColumn::Day:
return dateTimeAt(row).isValid();
}
return false;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//! \name IntervalAttribute related functions
//@{
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* \brief Return whether a certain row is masked
*/
bool AbstractColumn::isMasked(int row) const {
return d->m_masking.isSet(row);
}
/**
* \brief Return whether a certain interval of rows is fully masked
*/
bool AbstractColumn::isMasked(const Interval& i) const {
return d->m_masking.isSet(i);
}
/**
* \brief Return all intervals of masked rows
*/
QVector< Interval > AbstractColumn::maskedIntervals() const {
return d->m_masking.intervals();
}
/**
* \brief Clear all masking information
*/
void AbstractColumn::clearMasks() {
exec(new AbstractColumnClearMasksCmd(d),
"maskingAboutToChange", "maskingChanged", Q_ARG(const AbstractColumn*,this));
}
/**
* \brief Set an interval masked
*
* \param i the interval
* \param mask true: mask, false: unmask
*/
void AbstractColumn::setMasked(const Interval& i, bool mask) {
exec(new AbstractColumnSetMaskedCmd(d, i, mask),
"maskingAboutToChange", "maskingChanged", Q_ARG(const AbstractColumn*,this));
}
/**
* \brief Overloaded function for convenience
*/
void AbstractColumn::setMasked(int row, bool mask) {
setMasked(Interval(row,row), mask);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//@}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
//! \name Formula related functions
//@{
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* \brief Return the formula associated with row 'row'
*/
QString AbstractColumn::formula(int row) const {
Q_UNUSED(row);
return QString();
}
/**
* \brief Return the intervals that have associated formulas
*
* This can be used to make a list of formulas with their intervals.
* Here is some example code:
*
* \code
* QStringList list;
* QVector< Interval > intervals = my_column.formulaIntervals();
* foreach(Interval interval, intervals)
* list << QString(interval.toString() + ": " + my_column.formula(interval.start()));
* \endcode
*/
QVector< Interval > AbstractColumn::formulaIntervals() const {
return QVector< Interval >();
}
/**
* \brief Set a formula string for an interval of rows
*/
void AbstractColumn::setFormula(const Interval& i, const QString& formula) {
Q_UNUSED(i) Q_UNUSED(formula)
}
/**
* \brief Overloaded function for convenience
*/
void AbstractColumn::setFormula(int row, const QString& formula) {
Q_UNUSED(row) Q_UNUSED(formula)
}
/**
* \brief Clear all formulas
*/
void AbstractColumn::clearFormulas() {};
////////////////////////////////////////////////////////////////////////////////////////////////////
//@}
////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////
//! \name type specific functions
//@{
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* \brief Return the content of row 'row'.
*
* Use this only when columnMode() is Text
*/
QString AbstractColumn::textAt(int row) const {
Q_UNUSED(row);
return "";
}
/**
* \brief Set the content of row 'row'
*
* Use this only when columnMode() is Text
*/
void AbstractColumn::setTextAt(int row, const QString& new_value) {
Q_UNUSED(row) Q_UNUSED(new_value)
}
/**
* \brief Replace a range of values
*
* Use this only when columnMode() is Text
*/
void AbstractColumn::replaceTexts(int first, const QVector& new_values) {
Q_UNUSED(first) Q_UNUSED(new_values)
};
/**
* \brief Return the date part of row 'row'
*
* Use this only when columnMode() is DateTime, Month or Day
*/
QDate AbstractColumn::dateAt(int row) const {
Q_UNUSED(row);
return QDate();
}
/**
* \brief Set the content of row 'row'
*
* Use this only when columnMode() is DateTime, Month or Day
*/
void AbstractColumn::setDateAt(int row, QDate new_value) {
Q_UNUSED(row) Q_UNUSED(new_value)
};
/**
* \brief Return the time part of row 'row'
*
* Use this only when columnMode() is DateTime, Month or Day
*/
QTime AbstractColumn::timeAt(int row) const {
Q_UNUSED(row);
return QTime();
}
/**
* \brief Set the content of row 'row'
*
* Use this only when columnMode() is DateTime, Month or Day
*/
void AbstractColumn::setTimeAt(int row, QTime new_value) {
Q_UNUSED(row) Q_UNUSED(new_value)
}
/**
* \brief Return the QDateTime in row 'row'
*
* Use this only when columnMode() is DateTime, Month or Day
*/
QDateTime AbstractColumn::dateTimeAt(int row) const {
Q_UNUSED(row);
return QDateTime();
}
/**
* \brief Set the content of row 'row'
*
* Use this only when columnMode() is DateTime, Month or Day
*/
void AbstractColumn::setDateTimeAt(int row, const QDateTime& new_value) {
Q_UNUSED(row) Q_UNUSED(new_value)
};
/**
* \brief Replace a range of values
*
* Use this only when columnMode() is DateTime, Month or Day
*/
void AbstractColumn::replaceDateTimes(int first, const QVector& new_values) {
Q_UNUSED(first) Q_UNUSED(new_values)
};
/**
* \brief Return the double value in row 'row'
*
* Use this only when columnMode() is Numeric
*/
double AbstractColumn::valueAt(int row) const {
Q_UNUSED(row);
return NAN;
}
/**
* \brief Set the content of row 'row'
*
* Use this only when columnMode() is Numeric
*/
void AbstractColumn::setValueAt(int row, const double new_value) {
Q_UNUSED(row) Q_UNUSED(new_value)
};
/**
* \brief Replace a range of values
*
* Use this only when columnMode() is Numeric
*/
void AbstractColumn::replaceValues(int first, const QVector& new_values) {
Q_UNUSED(first) Q_UNUSED(new_values)
}
/**
* \brief Return the integer value in row 'row'
*
* Use this only when columnMode() is Integer
*/
int AbstractColumn::integerAt(int row) const {
Q_UNUSED(row);
return 42;
}
/**
* \brief Set the content of row 'row'
*
* Use this only when columnMode() is Integer
*/
void AbstractColumn::setIntegerAt(int row, const int new_value) {
Q_UNUSED(row) Q_UNUSED(new_value)
};
/**
* \brief Replace a range of values
*
* Use this only when columnMode() is Integer
*/
void AbstractColumn::replaceInteger(int first, const QVector& new_values) {
Q_UNUSED(first) Q_UNUSED(new_values)
}
/**********************************************************************/
double AbstractColumn::minimum(int count) const {
Q_UNUSED(count);
return -INFINITY;
}
double AbstractColumn::maximum(int count) const {
Q_UNUSED(count);
return INFINITY;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//@}
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* \fn void AbstractColumn::plotDesignationAboutToChange(const AbstractColumn *source)
* \brief Column plot designation will be changed
*
* 'source' is always the this pointer of the column that
* emitted this signal. This way it's easier to use
* one handler for lots of columns.
*/
/**
* \fn void AbstractColumn::plotDesignationChanged(const AbstractColumn *source)
* \brief Column plot designation changed
*
* 'source' is always the this pointer of the column that
* emitted this signal. This way it's easier to use
* one handler for lots of columns.
*/
/**
* \fn void AbstractColumn::modeAboutToChange(const AbstractColumn *source)
* \brief Column mode (possibly also the data type) will be changed
*
* 'source' is always the this pointer of the column that
* emitted this signal. This way it's easier to use
* one handler for lots of columns.
*/
/**
* \fn void AbstractColumn::modeChanged(const AbstractColumn *source)
* \brief Column mode (possibly also the data type) changed
*
* 'source' is always the this pointer of the column that
* emitted this signal. This way it's easier to use
* one handler for lots of columns.
*/
/**
* \fn void AbstractColumn::dataAboutToChange(const AbstractColumn *source)
* \brief Data of the column will be changed
*
* 'source' is always the this pointer of the column that
* emitted this signal. This way it's easier to use
* one handler for lots of columns.
*/
/**
* \fn void AbstractColumn::dataChanged(const AbstractColumn *source)
* \brief Data of the column has changed
*
* Important: When data has changed also the number
* of rows in the column may have changed without
* any other signal emission.
* 'source' is always the this pointer of the column that
* emitted this signal. This way it's easier to use
* one handler for lots of columns.
*/
/**
* \fn void AbstractColumn::rowsAboutToBeInserted(const AbstractColumn *source, int before, int count)
* \brief Rows will be inserted
*
* \param source the column that emitted the signal
* \param before the row to insert before
* \param count the number of rows to be inserted
*/
/**
* \fn void AbstractColumn::rowsInserted(const AbstractColumn *source, int before, int count)
* \brief Rows have been inserted
*
* \param source the column that emitted the signal
* \param before the row to insert before
* \param count the number of rows to be inserted
*/
/**
* \fn void AbstractColumn::rowsAboutToBeRemoved(const AbstractColumn *source, int first, int count)
* \brief Rows will be deleted
*
* \param source the column that emitted the signal
* \param first the first row to be deleted
* \param count the number of rows to be deleted
*/
/**
* \fn void AbstractColumn::rowsRemoved(const AbstractColumn *source, int first, int count)
* \brief Rows have been deleted
*
* \param source the column that emitted the signal
* \param first the first row that was deleted
* \param count the number of deleted rows
*/
/**
* \fn void AbstractColumn::maskingAboutToChange(const AbstractColumn *source)
* \brief Rows are about to be masked or unmasked
*/
/**
* \fn void AbstractColumn::maskingChanged(const AbstractColumn *source)
* \brief Rows have been masked or unmasked
*/
/**
* \fn void AbstractColumn::aboutToBeDestroyed(const AbstractColumn *source)
* \brief Emitted shortl before this column is deleted
*
* \param source the object emitting this signal
*
* This is needed by AbstractFilter.
*/
/**
* \brief Read XML mask element
*/
bool AbstractColumn::XmlReadMask(XmlStreamReader *reader) {
Q_ASSERT(reader->isStartElement() && reader->name() == "mask");
bool ok1, ok2;
int start, end;
start = reader->readAttributeInt("start_row", &ok1);
end = reader->readAttributeInt("end_row", &ok2);
if(!ok1 || !ok2) {
reader->raiseError(i18n("invalid or missing start or end row"));
return false;
}
setMasked(Interval(start,end));
if (!reader->skipToEndElement()) return false;
return true;
}
/**
* \brief Write XML mask element
*/
void AbstractColumn::XmlWriteMask(QXmlStreamWriter *writer) const {
for (const auto& interval: maskedIntervals()) {
writer->writeStartElement("mask");
writer->writeAttribute("start_row", QString::number(interval.start()));
writer->writeAttribute("end_row", QString::number(interval.end()));
writer->writeEndElement();
}
}
diff --git a/src/backend/core/AbstractFilter.cpp b/src/backend/core/AbstractFilter.cpp
index ff9da2d55..f201e4891 100644
--- a/src/backend/core/AbstractFilter.cpp
+++ b/src/backend/core/AbstractFilter.cpp
@@ -1,351 +1,351 @@
/***************************************************************************
File : AbstractFilter.cpp
Project : LabPlot
--------------------------------------------------------------------
Copyright : (C) 2007 by Knut Franke, Tilman Benkert
Email (use @ for *) : knut.franke*gmx.de, thzs*gmx.net
Description : Base class for all analysis operations.
***************************************************************************/
/***************************************************************************
* *
* 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 "AbstractFilter.h"
-#include
+#include
#include "backend/core/AbstractColumn.h"
#include "backend/lib/macros.h"
/**
* \class AbstractFilter
* \brief Base class for all analysis operations.
*
* AbstractFilter provides an abstraction for analysis operations. It is modelled on an
* electronic filtering circuit: From the outside, a filter appears as a black box with
* a number of input and output ports (obviously, those numbers do not necessarily agree).
*
* \section using Using AbstractFilter
* You can connect one AbstractColumn to each input port using
* input(int port, AbstractColumn* source). Every output(int port) is realized
* again by an AbstractColumn, which you can connect to as many other filters, tables
* or plots as you like.
* Ownership of the data sources always stays with the class which is providing the data,
* that is, neither input() nor output() transfer ownership.
*
* Furthermore, you can use inputCount() and outputCount() to query the number of
* input and output ports, respectively and you can obtain label strings for inputs (via
* inputLabel()) and outputs (via AbstractColumn::label()). This allows generic filter
* handling routines to be written, which is important for using filters provided by plugins.
*
* Its simplicity of use notwithstanding, AbstractFilter provides a powerful and versatile
* basis also for analysis operations that would not commonly be referred to as "filter".
* An example of such a more advanced filter implementation is StatisticsFilter.
*
* \section subclassing Subclassing AbstractFilter
* The main design goal was to make implementing new filters as easy as possible.
* Filters with only one output port can subclass AbstractSimpleFilter, which is even easier
* to use. Filters with more than one output port have to subclass
* AbstractFilter directly, which is slightly more involved, because in
* addition to data transfer between these classes the signals defined by AbstractColumn
* have to be handled on both inputs and outputs. Signals from data sources connected to the input
* ports are automatically connected to a matching set of virtual methods, which can be
* reimplemented by subclasses to handle these events.
*
* While AbstractFilter handles the tedious part of connecting a data source to an input port,
* its subclasses are given a chance to reject such connections (e.g., based on the data type
* of the source) by reimplementing inputAcceptable().
*
* \sa AbstractSimpleFilter
*/
/**
* \fn AbstractFilter::inputCount() const
* \brief Return the number of input ports supported by the filter or -1 if any number of inputs is acceptable.
*/
/**
* \fn AbstractFilter::outputCount() const
* \brief Return the number of output ports provided by the filter.
*
* %Note that this number need not be static, but can be dynamically determined, for example
* based on the inputs provided to the filter.
*/
/**
* \brief Return the index of the highest input port that is connected.
*
* Note that this is different from both the number of ports that could be connected,
* inputCount(), and the number of ports that actually have been connected, which are
* not necessarily sequential. In conjunction with input(int), this method can be used to
* traverse the connected inputs.
*/
int AbstractFilter::highestConnectedInput() const {
return m_inputs.count() - 1;
}
/**
* \brief Connect the provided data source to the specified input port.
* \param port the port number to which to connect
* \param source the data source to connect to the input port
* \returns true if the connection was accepted, false otherwise.
*
* The port number is checked for validity against inputCount() and both port number and data
* source are passed to inputAcceptable() for review. If both checks succeed, the
* source is recorded in #m_inputs.
* If applicable, the previously connected data source is disconnected before replacing it.
*
* You can also use this method to disconnect an input without replacing it with a new one by
* calling it with source=0.
*
* \sa inputAcceptable(), #m_inputs
*/
bool AbstractFilter::input(int port, const AbstractColumn* source) {
// DEBUG("AbstractFilter::input()");
if (port < 0 || (inputCount() >= 0 && port >= inputCount())) return false;
if (source && !inputAcceptable(port, source)) return false;
if (port >= m_inputs.size()) m_inputs.resize(port+1);
const AbstractColumn* old_input = m_inputs.value(port);
if (source == old_input) return true;
if (old_input) {
disconnect(old_input, 0, this, 0);
// replace input, notifying the filter implementation of the changes
inputDescriptionAboutToChange(old_input);
inputPlotDesignationAboutToChange(old_input);
inputMaskingAboutToChange(old_input);
inputDataAboutToChange(old_input);
if(source && source->columnMode() != old_input->columnMode())
inputModeAboutToChange(old_input);
}
if (!source)
inputAboutToBeDisconnected(old_input);
m_inputs[port] = source;
if (source) { // we have a new source
// DEBUG(" new source");
if(old_input && source->columnMode() != old_input->columnMode())
inputModeAboutToChange(source);
inputDataChanged(source);
inputMaskingChanged(source);
inputPlotDesignationChanged(source);
inputDescriptionChanged(source);
// connect the source's signals
connect(source, &AbstractColumn::aspectDescriptionAboutToChange, this,
static_cast(&AbstractFilter::inputDescriptionAboutToChange));
connect(source, &AbstractColumn::aspectDescriptionChanged, this,
static_cast(&AbstractFilter::inputDescriptionChanged));
connect(source, &AbstractColumn::plotDesignationAboutToChange, this, &AbstractFilter::inputPlotDesignationAboutToChange);
connect(source, &AbstractColumn::plotDesignationChanged, this, &AbstractFilter::inputPlotDesignationChanged);
connect(source, &AbstractColumn::modeAboutToChange, this, &AbstractFilter::inputModeAboutToChange);
connect(source, &AbstractColumn::modeChanged, this, &AbstractFilter::inputModeChanged);
connect(source, &AbstractColumn::dataAboutToChange, this, &AbstractFilter::inputDataAboutToChange);
connect(source, &AbstractColumn::dataChanged, this, &AbstractFilter::inputDataChanged);
connect(source, &AbstractColumn::rowsAboutToBeInserted, this, &AbstractFilter::inputRowsAboutToBeInserted);
connect(source, &AbstractColumn::rowsInserted, this, &AbstractFilter::inputRowsInserted);
connect(source, &AbstractColumn::rowsAboutToBeRemoved, this, &AbstractFilter::inputRowsAboutToBeRemoved);
connect(source, &AbstractColumn::rowsRemoved, this, &AbstractFilter::inputRowsRemoved);
connect(source, &AbstractColumn::maskingAboutToChange, this, &AbstractFilter::inputMaskingAboutToChange);
connect(source, &AbstractColumn::maskingChanged, this, &AbstractFilter::inputMaskingChanged);
connect(source, &AbstractColumn::aboutToBeDestroyed, this, &AbstractFilter::inputAboutToBeDestroyed);
} else { // source==0, that is, the input port has been disconnected
// DEBUG(" no source");
// try to shrink m_inputs
int num_connected_inputs = m_inputs.size();
while (m_inputs.at(num_connected_inputs-1) == 0) {
num_connected_inputs--;
if(!num_connected_inputs) break;
}
m_inputs.resize(num_connected_inputs);
}
return true;
}
/**
* \brief Connect all outputs of the provided filter to the corresponding inputs of this filter.
* \returns true if all connections were accepted, false otherwise
*
* Overloaded method provided for convenience.
*/
bool AbstractFilter::input(const AbstractFilter* sources) {
if (!sources) return false;
bool result = true;
for (int i = 0; i < sources->outputCount(); ++i)
if (!input(i, sources->output(i)))
result = false;
return result;
}
/**
* \brief Return the input currently connected to the specified port, or 0.
*/
const AbstractColumn* AbstractFilter::input(int port) const {
return m_inputs.value(port);
}
/**
* \brief Return the label associated to the given input port.
*
* Default labels are In1, In2, ... (or translated equivalents), but implementations can
* reimplement this method to produce more meaningful labels.
*
* Output ports are implicitly labeled through AbstractAspect::name().
*/
QString AbstractFilter::inputLabel(int port) const {
return i18nc("default labels of filter input ports", "In%1", port + 1);
}
/**
* \fn AbstractColumn *AbstractFilter::output(int port=0)
* \brief Get the data source associated with the specified output port.
*
* The returned pointer may be 0 even for valid port numbers, for example if not all required
* input ports have been connected.
*/
/**
* \fn const AbstractColumn *AbstractFilter::output(int port=0) const
* \brief Overloaded method for const access.
*/
/**
* \brief Return the input port to which the column is connected or -1 if it's not connected
*/
int AbstractFilter::portIndexOf(const AbstractColumn* column) {
for (int i = 0; i < m_inputs.size(); ++i)
if(m_inputs.at(i) == column) return i;
return -1;
}
/**
* \brief Give implementations a chance to reject connections to their input ports.
*
* If not reimplemented, all connections to ports within [0, inputCount()-1] will be accepted.
*/
bool AbstractFilter::inputAcceptable(int port, const AbstractColumn* source) {
Q_UNUSED(port); Q_UNUSED(source); return true;
}
/**
* \brief Called whenever an input is disconnected or deleted.
*
* This is only to notify implementations of the event, the default implementation is a
* no-op.
*/
void AbstractFilter::inputAboutToBeDisconnected(const AbstractColumn* source) {
Q_UNUSED(source);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//!\name signal handlers
//@{
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* \brief Name and/or comment of an input will be changed.
*
* \param source is always the this pointer of the column that emitted the signal.
*/
void AbstractFilter::inputDescriptionAboutToChange(const AbstractColumn* source) {
Q_UNUSED(source);
}
void AbstractFilter::inputDescriptionAboutToChange(const AbstractAspect* aspect) {
const AbstractColumn* col = qobject_cast(aspect);
if (col) inputDescriptionAboutToChange(col);
}
/**
* \brief Name and/or comment of an input changed.
*
* \param source is always the this pointer of the column that emitted the signal.
*/
void AbstractFilter::inputDescriptionChanged(const AbstractColumn* source) {
Q_UNUSED(source);
}
void AbstractFilter::inputDescriptionChanged(const AbstractAspect* aspect) {
const AbstractColumn* col = qobject_cast(aspect);
if (col && m_inputs.contains(col)) inputDescriptionChanged(col);
}
/**
* \brief The plot designation of an input is about to change.
*
* \param source is always the this pointer of the column that emitted the signal.
*/
void AbstractFilter::inputPlotDesignationAboutToChange(const AbstractColumn* source) {
Q_UNUSED(source);
}
/**
* \brief The plot designation of an input changed.
*
* \param source is always the this pointer of the column that emitted the signal.
*/
void AbstractFilter::inputPlotDesignationChanged(const AbstractColumn* source) {
Q_UNUSED(source);
}
/**
* \brief The display mode and possibly the data type of an input is about to change.
*
* \param source is always the this pointer of the column that emitted the signal.
*/
void AbstractFilter::inputModeAboutToChange(const AbstractColumn* source) {
Q_UNUSED(source);
}
/**
* \brief The display mode and possibly the data type has changed.
*
* \param source is always the this pointer of the column that emitted the signal.
*/
void AbstractFilter::inputModeChanged(const AbstractColumn* source) {
Q_UNUSED(source);
}
/**
* \brief The data of an input is about to change.
*
* \param source is always the this pointer of the column that emitted the signal.
*/
void AbstractFilter::inputDataAboutToChange(const AbstractColumn* source) {
Q_UNUSED(source);
}
/**
* \brief The data of an input has changed.
*
* \param source is always the this pointer of the column that emitted the signal.
*/
void AbstractFilter::inputDataChanged(const AbstractColumn* source) {
Q_UNUSED(source);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//@}
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* \var AbstractFilter::m_inputs
* \brief The data sources connected to my input ports.
*/
diff --git a/src/backend/core/AbstractPart.cpp b/src/backend/core/AbstractPart.cpp
index e03001715..a4f1b7842 100644
--- a/src/backend/core/AbstractPart.cpp
+++ b/src/backend/core/AbstractPart.cpp
@@ -1,138 +1,138 @@
/***************************************************************************
File : AbstractPart.cpp
Project : LabPlot
Description : Base class of Aspects with MDI windows as views.
--------------------------------------------------------------------
Copyright : (C) 2008 Knut Franke (knut.franke@gmx.de)
Copyright : (C) 2012 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 "backend/core/AbstractPart.h"
#include "commonfrontend/core/PartMdiView.h"
#include "backend/core/Workbook.h"
#include "backend/datapicker/Datapicker.h"
#include "backend/spreadsheet/Spreadsheet.h"
#include "backend/datapicker/DatapickerCurve.h"
#include
#include
-#include
+#include
/**
* \class AbstractPart
* \brief Base class of Aspects with MDI windows as views (AspectParts).
*/
AbstractPart::AbstractPart(const QString& name) : AbstractAspect(name),
m_mdiWindow(nullptr), m_partView(nullptr) {
}
AbstractPart::~AbstractPart() {
if (m_mdiWindow)
delete m_mdiWindow;
}
/**
* \fn QWidget *AbstractPart::view() const
* \brief Construct a primary view on me.
*
* The caller receives ownership of the view.
*
* This method may be called multiple times during the life time of a Part, or it might not get
* called at all. Parts must not depend on the existence of a view for their operation.
*/
/**
* \brief Wrap the view() into a PartMdiView.
*
* A new view is only created the first time this method is called;
* after that, a pointer to the pre-existing view is returned.
*/
PartMdiView* AbstractPart::mdiSubWindow() const {
if (!m_mdiWindow)
m_mdiWindow = new PartMdiView(const_cast(this));
return m_mdiWindow;
}
bool AbstractPart::hasMdiSubWindow() const {
return m_mdiWindow;
}
/*!
* this function is called when PartMdiView, the mdi-subwindow-wrapper of the actual view,
* is closed (=deleted) in MainWindow. Makes sure that the view also gets deleted.
*/
void AbstractPart::deleteView() const {
//if the parent is a Workbook or Datapicker, the actual view was already deleted when QTabWidget was deleted.
//here just set the pointer to 0.
if (dynamic_cast(parentAspect()) || dynamic_cast(parentAspect())
|| dynamic_cast(parentAspect()->parentAspect())) {
m_partView = nullptr;
return;
}
if (m_partView) {
delete m_partView;
m_partView = nullptr;
m_mdiWindow = nullptr;
}
}
/**
* \brief Return AbstractAspect::createContextMenu() plus operations on the primary view.
*/
QMenu* AbstractPart::createContextMenu() {
QMenu* menu = AbstractAspect::createContextMenu();
Q_ASSERT(menu);
menu->addSeparator();
if (m_mdiWindow) {
menu->addAction(QIcon::fromTheme("document-export-database"), i18n("Export"), this, SIGNAL(exportRequested()));
menu->addAction(QIcon::fromTheme("document-print"), i18n("Print"), this, SIGNAL(printRequested()));
menu->addAction(QIcon::fromTheme("document-print-preview"), i18n("Print Preview"), this, SIGNAL(printPreviewRequested()));
menu->addSeparator();
const QStyle *widget_style = m_mdiWindow->style();
if(m_mdiWindow->windowState() & (Qt::WindowMinimized | Qt::WindowMaximized)) {
QAction* action = menu->addAction(i18n("&Restore"), m_mdiWindow, SLOT(showNormal()));
action->setIcon(widget_style->standardIcon(QStyle::SP_TitleBarNormalButton));
}
if(!(m_mdiWindow->windowState() & Qt::WindowMinimized)) {
QAction* action = menu->addAction(i18n("Mi&nimize"), m_mdiWindow, SLOT(showMinimized()));
action->setIcon(widget_style->standardIcon(QStyle::SP_TitleBarMinButton));
}
if(!(m_mdiWindow->windowState() & Qt::WindowMaximized)) {
QAction* action = menu->addAction(i18n("Ma&ximize"), m_mdiWindow, SLOT(showMaximized()));
action->setIcon(widget_style->standardIcon(QStyle::SP_TitleBarMaxButton));
}
} else {
//data spreadsheets in the datapicker curves cannot be hidden/minimized, don't show this menu entry
if ( !(dynamic_cast(this) && dynamic_cast(this->parentAspect())) )
menu->addAction(i18n("Show"), this, SIGNAL(showRequested()));
}
return menu;
}
diff --git a/src/backend/core/AbstractScriptingEngine.cpp b/src/backend/core/AbstractScriptingEngine.cpp
index 49a06b4c5..77ede193e 100644
--- a/src/backend/core/AbstractScriptingEngine.cpp
+++ b/src/backend/core/AbstractScriptingEngine.cpp
@@ -1,225 +1,225 @@
/***************************************************************************
File : AbstractScriptingEngine.cpp
Project : SciDAVis
--------------------------------------------------------------------
Copyright : (C) 2006,2008 by Knut Franke
Email (use @ for *) : knut.franke*gmx.de
Description : Implementations of generic scripting classes
***************************************************************************/
/***************************************************************************
* *
* 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 "AbstractScriptingEngine.h"
#include
-#include
+#include
/**
* \class AbstractScriptingEngine
* \brief An interpreter for evaluating scripting code.
*
* AbstractScriptingEngine objects represent a running interpreter, possibly with global
* variables, and are responsible for generating AbstractScript objects (which do
* the actual evaluation of code).
*
* The class also keeps a static list of available interpreters and instantiates
* them on demand.
*/
/**
* \brief Only initialize general information here.
*
* All AbstractScriptingEngine subclasses are instantiated at startup (more precisely: when
* instantiating ScriptingEngineManager). Therefor, loading the actual interpreter is done
* in initialize().
*/
AbstractScriptingEngine::AbstractScriptingEngine(const char *lang_name)
{
setObjectName(lang_name);
m_initialized=false;
m_refcount=0;
}
/**
* \fn void AbstractScriptingEngine::initialize()
* \brief Initialize the scripting environment.
*
* Don't forget to set m_initialized to true in implementations after a successful
* initialization.
*/
/**
* \brief initialization of the interpreter may fail; or there could be other errors setting up the environment
*/
bool AbstractScriptingEngine::initialized() const {
return m_initialized;
}
/**
* \brief whether asynchronuous execution is enabled (if supported by the implementation)
*/
bool AbstractScriptingEngine::isRunning() const {
return false;
}
/**
* \fn AbstractScript *AbstractScriptingEngine::makeScript(const QString &code, QObject *context, const QString &name)
* \brief Instantiate the AbstractScript subclass matching the AbstractScriptingEngine subclass.
*/
/**
* \brief If an exception / error occurred, return a nicely formated stack backtrace.
*/
QString AbstractScriptingEngine::stackTraceString() {
return QString();
}
/**
* \brief Clear the global environment.
*
* What exactly happens depends on the implementation.
*/
void AbstractScriptingEngine::clear() {}
/**
* \brief If the implementation supports asynchronuos execution, deactivate it.
*/
void AbstractScriptingEngine::stopExecution() {}
/**
* \brief If the implementation supports asynchronuos execution, activate it.
*/
void AbstractScriptingEngine::startExecution() {}
/**
* \brief Return a list of supported mathematical functions.
*
* These should be imported into the global namespace.
*/
const QStringList AbstractScriptingEngine::mathFunctions() const {
return QStringList();
}
/**
* \brief Return a documentation string for the given mathematical function.
*/
const QString AbstractScriptingEngine::mathFunctionDoc(const QString&) const {
return QString();
}
/**
* \brief Return a list of file extensions commonly used for this language.
*/
const QStringList AbstractScriptingEngine::fileExtensions() const {
return QStringList();
}
/**
* \brief Construct a filter expression from fileExtensions(), suitable for QFileDialog.
*/
const QString AbstractScriptingEngine::nameAndPatterns() const
{
QStringList extensions = fileExtensions();
if (extensions.isEmpty())
return "";
else
return i18n("%1 Source (*.%2)", objectName(), extensions.join(" *."));
}
/**
* \brief Increase the reference count.
*
* This should only be called by scripted and Script to avoid memory leaks.
*/
void AbstractScriptingEngine::incref()
{
m_refcount++;
}
/**
* \brief Decrease the reference count.
*
* This should only be called by scripted and Script to avoid segfaults.
*/
void AbstractScriptingEngine::decref()
{
m_refcount--;
if (m_refcount==0)
delete this;
}
/**
* \fn void AbstractScriptingEngine::error(const QString &message, const QString &scriptName, int lineNumber)
* \brief signal an error condition / exception
*/
/**
* \fn void AbstractScriptingEngine::print(const QString &output)
* \brief output that is not handled by a Script
*/
/**
* \var AbstractScriptingEngine::m_initialized
* \brief whether the interpreter has been successfully initialized
*/
/**
* \var AbstractScriptingEngine::m_refcount
* \brief the reference counter
*/
/******************************************************************************\
*Helper classes for managing instances of AbstractScriptingEngine subclasses.*
\******************************************************************************/
/**
* \class ScriptingChangeEvent
* \brief notify an object that it should update its scripting environment (see class scripted)
*/
/**
* \class scripted
* \brief Interface for maintaining a reference to the current AbstractScriptingEngine
*
* Every class that wants to use a AbstractScriptingEngine should subclass this one and
* implement slot customEvent(QEvent*) such that it forwards any
* AbstractScriptingChangeEvents to scripted::scriptingChangeEvent.
*/
scripted::scripted(AbstractScriptingEngine *engine)
{
if (engine)
engine->incref();
m_scripting_engine = engine;
}
scripted::~scripted()
{
if (m_scripting_engine)
m_scripting_engine->decref();
}
void scripted::scriptingChangeEvent(ScriptingChangeEvent *sce)
{
m_scripting_engine->decref();
sce->scriptingEngine()->incref();
m_scripting_engine = sce->scriptingEngine();
}
diff --git a/src/backend/core/AbstractSimpleFilter.cpp b/src/backend/core/AbstractSimpleFilter.cpp
index 7ccd515c5..f89222c81 100644
--- a/src/backend/core/AbstractSimpleFilter.cpp
+++ b/src/backend/core/AbstractSimpleFilter.cpp
@@ -1,409 +1,409 @@
/***************************************************************************
File : AbstractSimpleFilter.cpp
Project : AbstractColumn
--------------------------------------------------------------------
Copyright : (C) 2007,2008 by Knut Franke, Tilman Benkert
Email (use @ for *) : knut.franke*gmx.de, thzs*gmx.net
Description : Simplified filter interface for filters with
only one output port.
***************************************************************************/
/***************************************************************************
* *
* 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 "AbstractSimpleFilter.h"
#include "backend/lib/XmlStreamReader.h"
#include
#include
#include
-#include
+#include
/**
* \class AbstractSimpleFilter
* \brief Simplified filter interface for filters with only one output port.
*
* This class is only meant to simplify implementation of a restricted subtype of filter.
* It should not be instantiated directly. You should always either derive from
* AbstractFilter or (if necessary) provide an actual (non-abstract) implementation.
*
* The trick here is that, in a sense, the filter is its own output port. This means you
* can implement a complete filter in only one class and don't have to coordinate data
* transfer between a filter class and a data source class.
* Additionally, AbstractSimpleFilter offers some useful convenience methods which make writing
* filters as painless as possible.
*
* For the data type of the output, all types supported by AbstractColumn (currently double, QString and
* QDateTime) are supported.
*
* \section tutorial1 Tutorial, Step 1
* The simplest filter you can write assumes there's also only one input port and rows on the
* input correspond 1:1 to rows in the output. All you need to specify is what data type you
* want to have (in this example double) on the input port and how to compute the output values:
*
* \code
* 01 #include "AbstractSimpleFilter.h"
* 02 class TutorialFilter1 : public AbstractSimpleFilter
* 03 {
* 04 protected:
* 05 virtual bool inputAcceptable(int, AbstractColumn *source) {
* 06 return (source->columnMode() == AbstractColumn::Numeric);
* 07 }
* 08 public:
* 09 virtual AbstractColumn::ColumnMode columnMode() const { return AbstractColumn::Numeric; }
* 10
* 11 virtual double valueAt(int row) const {
* 12 if (!m_inputs.value(0)) return 0.0;
* 13 double input_value = m_inputs.value(0)->valueAt(row);
* 14 return input_value * input_value;
* 15 }
* 16 };
* \endcode
*
* This filter reads an input value (line 13) and returns its square (line 14).
* Reimplementing inputAcceptable() makes sure that the data source really is of type
* double (lines 5 to 7). Otherwise, the source will be rejected by AbstractFilter::input().
* The output type is reported by reimplementing columnMode() (line 09).
* Before you actually use m_inputs.value(0), make sure that the input port has
* been connected to a data source (line 12).
* Otherwise line 13 would result in a crash. That's it, we've already written a
* fully-functional filter!
*
* Equivalently, you can write 1:1-filters for QString or QDateTime inputs by checking for
* AbstractColumn::TypeQString or AbstractColumn::TypeQDateTime in line 6. You would then use
* AbstractColumn::textAt(row) or AbstractColumn::dateTimeAt(row) in line 13 to access the input data.
* For QString output, you need to implement AbstractColumn::textAt(row).
* For QDateTime output, you have to implement three methods:
* \code
* virtual QDateTime dateTimeAt(int row) const;
* virtual QDate dateAt(int row) const;
* virtual QTime timeAt(int row) const;
* \endcode
*
* \section tutorial2 Tutorial, Step 2
* Now for something slightly more interesting: a filter that uses only every second row of its
* input. We no longer have a 1:1 correspondence between input and output rows, so we'll have
* to do a bit more work in order to have everything come out as expected.
* We'll use double-typed input and output again:
* \code
* 01 #include "AbstractSimpleFilter.h"
* 02 class TutorialFilter2 : public AbstractSimpleFilter
* 03 {
* 04 protected:
* 05 virtual bool inputAcceptable(int, AbstractColumn *source) {
* 06 return (source->columnMode() == AbstractColumn::Numeric);
* 07 }
* 08 public:
* 09 virtual AbstractColumn::ColumnMode columnMode() const { return AbstractColumn::Numeric; }
* \endcode
* Even rows (including row 0) get dropped, odd rows are renumbered:
* \code
* 10 public:
* 11 virtual double valueAt(int row) const {
* 12 if (!m_inputs.value(0)) return 0.0;
* 13 return m_inputs.value(0)->valueAt(2*row + 1);
* 14 }
* \endcode
*/
// TODO: should simple filters have a name argument?
/**
* \brief Ctor
*/
AbstractSimpleFilter::AbstractSimpleFilter()
: AbstractFilter("SimpleFilter"), m_output_column(new SimpleFilterColumn(this)) {
addChild(m_output_column);
}
/**
* \brief Default to one input port.
*/
int AbstractSimpleFilter::inputCount() const {
return 1;
}
/**
* \brief We manage only one output port (don't override unless you really know what you are doing).
*/
int AbstractSimpleFilter::outputCount() const {
return 1;
}
/**
* \brief Copy plot designation of input port 0.
*/
AbstractColumn::PlotDesignation AbstractSimpleFilter::plotDesignation() const {
return m_inputs.value(0) ? m_inputs.at(0)->plotDesignation() : AbstractColumn::NoDesignation;
}
/**
* \brief Return the column mode
*
* This function is most used by tables but can also be used
* by plots. The column mode specifies how to interpret
* the values in the column additional to the data type.
*/
AbstractColumn::ColumnMode AbstractSimpleFilter::columnMode() const {
// calling this function while m_input is empty is a sign of very bad code
// nevertheless it will return some rather meaningless value to
// avoid crashes
return m_inputs.value(0) ? m_inputs.at(0)->columnMode() : AbstractColumn::Text;
}
/**
* \brief Return the content of row 'row'.
*
* Use this only when columnMode() is Text
*/
QString AbstractSimpleFilter::textAt(int row) const {
return m_inputs.value(0) ? m_inputs.at(0)->textAt(row) : QString();
}
/**
* \brief Return the date part of row 'row'
*
* Use this only when columnMode() is DateTime, Month or Day
*/
QDate AbstractSimpleFilter::dateAt(int row) const {
return m_inputs.value(0) ? m_inputs.at(0)->dateAt(row) : QDate();
}
/**
* \brief Return the time part of row 'row'
*
* Use this only when columnMode() is DateTime, Month or Day
*/
QTime AbstractSimpleFilter::timeAt(int row) const {
return m_inputs.value(0) ? m_inputs.at(0)->timeAt(row) : QTime();
}
/**
* \brief Set the content of row 'row'
*
* Use this only when columnMode() is DateTime, Month or Day
*/
QDateTime AbstractSimpleFilter::dateTimeAt(int row) const {
return m_inputs.value(0) ? m_inputs.at(0)->dateTimeAt(row) : QDateTime();
}
/**
* \brief Return the double value in row 'row'
*
* Use this only when columnMode() is Numeric
*/
double AbstractSimpleFilter::valueAt(int row) const {
return m_inputs.value(0) ? m_inputs.at(0)->valueAt(row) : 0.0;
}
/**
* \brief Return the integer value in row 'row'
*
* Use this only when columnMode() is Integer
*/
int AbstractSimpleFilter::integerAt(int row) const {
return m_inputs.value(0) ? m_inputs.at(0)->integerAt(row) : 0;
}
/**
* \brief Number of output rows == number of input rows
*
* ... unless overridden in a subclass.
*/
int AbstractSimpleFilter::rowCount() const {
return m_inputs.value(0) ? m_inputs.at(0)->rowCount() : 0;
}
/**
* \brief Rows that will change when the given input interval changes.
*
* This implementation assumes a 1:1 correspondence between input and output rows, but can be
* overridden in subclasses.
*/
QList< Interval > AbstractSimpleFilter::dependentRows(const Interval& inputRange) const {
return QList< Interval >() << inputRange;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//!\name signal handlers
//@{
////////////////////////////////////////////////////////////////////////////////////////////////////
void AbstractSimpleFilter::inputPlotDesignationAboutToChange(const AbstractColumn*) {
emit m_output_column->plotDesignationAboutToChange(m_output_column);
}
void AbstractSimpleFilter::inputPlotDesignationChanged(const AbstractColumn*) {
emit m_output_column->plotDesignationChanged(m_output_column);
}
void AbstractSimpleFilter::inputModeAboutToChange(const AbstractColumn*) {
emit m_output_column->dataAboutToChange(m_output_column);
}
void AbstractSimpleFilter::inputModeChanged(const AbstractColumn*) {
emit m_output_column->dataChanged(m_output_column);
}
void AbstractSimpleFilter::inputDataAboutToChange(const AbstractColumn*) {
emit m_output_column->dataAboutToChange(m_output_column);
}
void AbstractSimpleFilter::inputDataChanged(const AbstractColumn*) {
emit m_output_column->dataChanged(m_output_column);
}
void AbstractSimpleFilter::inputRowsAboutToBeInserted(const AbstractColumn * source, int before, int count) {
Q_UNUSED(source);
Q_UNUSED(count);
foreach (const Interval& output_range, dependentRows(Interval(before, before)))
emit m_output_column->rowsAboutToBeInserted(m_output_column, output_range.start(), output_range.size());
}
void AbstractSimpleFilter::inputRowsInserted(const AbstractColumn * source, int before, int count) {
Q_UNUSED(source);
Q_UNUSED(count);
foreach (const Interval& output_range, dependentRows(Interval(before, before)))
emit m_output_column->rowsInserted(m_output_column, output_range.start(), output_range.size());
}
void AbstractSimpleFilter::inputRowsAboutToBeRemoved(const AbstractColumn * source, int first, int count) {
Q_UNUSED(source);
foreach (const Interval& output_range, dependentRows(Interval(first, first+count-1)))
emit m_output_column->rowsAboutToBeRemoved(m_output_column, output_range.start(), output_range.size());
}
void AbstractSimpleFilter::inputRowsRemoved(const AbstractColumn * source, int first, int count) {
Q_UNUSED(source);
foreach (const Interval& output_range, dependentRows(Interval(first, first+count-1)))
emit m_output_column->rowsRemoved(m_output_column, output_range.start(), output_range.size());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//@}
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* \brief Return a pointer to #m_output_column on port 0 (don't override unless you really know what you are doing).
*/
AbstractColumn *AbstractSimpleFilter::output(int port) {
return port == 0 ? static_cast(m_output_column) : 0;
}
const AbstractColumn *AbstractSimpleFilter::output(int port) const {
return port == 0 ? static_cast(m_output_column) : 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//! \name serialize/deserialize
//@{
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* \brief Save to XML
*/
void AbstractSimpleFilter::save(QXmlStreamWriter * writer) const {
writer->writeStartElement("simple_filter");
writeBasicAttributes(writer);
writeExtraAttributes(writer);
writer->writeAttribute("filter_name", metaObject()->className());
writeCommentElement(writer);
writer->writeEndElement();
}
/**
* \brief Override this in derived classes if they have other attributes than filter_name
*/
void AbstractSimpleFilter::writeExtraAttributes(QXmlStreamWriter * writer) const {
Q_UNUSED(writer)
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//@}
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* \brief Load from XML
*/
bool AbstractSimpleFilter::load(XmlStreamReader* reader, bool preview) {
Q_UNUSED(preview); //TODO
if(reader->isStartElement() && reader->name() == "simple_filter") {
if (!readBasicAttributes(reader)) return false;
QXmlStreamAttributes attribs = reader->attributes();
QString str = attribs.value(reader->namespaceUri().toString(), "filter_name").toString();
if(str != metaObject()->className()) {
reader->raiseError(i18n("incompatible filter type"));
return false;
}
// read child elements
while (!reader->atEnd()) {
reader->readNext();
if (reader->isEndElement()) break;
if (reader->isStartElement()) {
if (reader->name() == "comment") {
if (!readCommentElement(reader)) return false;
} else { // unknown element
reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString()));
if (!reader->skipToEndElement()) return false;
}
}
}
} else
reader->raiseError(i18n("no simple filter element found"));
return !reader->hasError();
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//! \class SimpleFilterColumn
////////////////////////////////////////////////////////////////////////////////////////////////////
AbstractColumn::ColumnMode SimpleFilterColumn::columnMode() const {
return m_owner->columnMode();
}
QString SimpleFilterColumn::textAt(int row) const {
return m_owner->textAt(row);
}
QDate SimpleFilterColumn::dateAt(int row) const {
return m_owner->dateAt(row);
}
QTime SimpleFilterColumn::timeAt(int row) const {
return m_owner->timeAt(row);
}
QDateTime SimpleFilterColumn::dateTimeAt(int row) const {
return m_owner->dateTimeAt(row);
}
double SimpleFilterColumn::valueAt(int row) const {
return m_owner->valueAt(row);
}
int SimpleFilterColumn::integerAt(int row) const {
return m_owner->integerAt(row);
}
diff --git a/src/backend/core/AspectTreeModel.cpp b/src/backend/core/AspectTreeModel.cpp
index b816d7dd8..e727ccefa 100644
--- a/src/backend/core/AspectTreeModel.cpp
+++ b/src/backend/core/AspectTreeModel.cpp
@@ -1,499 +1,499 @@
/***************************************************************************
File : AspectTreeModel.h
Project : LabPlot
Description : Represents a tree of AbstractAspect objects as a Qt item model.
--------------------------------------------------------------------
Copyright : (C) 2007-2009 by Knut Franke (knut.franke@gmx.de)
Copyright : (C) 2007-2009 by Tilman Benkert (thzs@gmx.net)
Copyright : (C) 2011-2016 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 "backend/core/AbstractAspect.h"
#include "backend/core/column/Column.h"
#include "backend/worksheet/WorksheetElement.h"
#include "backend/core/AspectTreeModel.h"
#include
#include
#include
#include
#include
-#include
+#include
/**
* \class AspectTreeModel
* \brief Represents a tree of AbstractAspect objects as a Qt item model.
*
* This class is an adapter between an AbstractAspect hierarchy and Qt's view classes.
*
* It represents children of an Aspect as rows in the model, with the fixed columns
* Name (AbstractAspect::name()), Type (the class name), Created (AbstractAspect::creationTime())
* and Comment (AbstractAspect::comment()). Name is decorated using AbstractAspect::icon().
* The tooltip for all columns is generated from AbstractAspect::caption().
*
* Name and Comment are editable.
*
* For views which support this (currently ProjectExplorer), the menu created by
* AbstractAspect::createContextMenu() is made available via the custom role ContextMenuRole.
*/
/**
* \enum AspectTreeModel::CustomDataRole
* \brief Custom data roles used in addition to Qt::ItemDataRole
*/
/**
* \var AspectTreeModel::ContextMenuRole
* \brief pointer to a new context menu for an Aspect
*/
/**
* \fn QModelIndex AspectTreeModel::modelIndexOfAspect(const AbstractAspect *aspect, int column=0) const
* \brief Convenience wrapper around QAbstractItemModel::createIndex().
*/
AspectTreeModel::AspectTreeModel(AbstractAspect* root, QObject* parent)
: QAbstractItemModel(parent),
m_root(root),
m_readOnly(false),
m_folderSelectable(true),
m_numericColumnsOnly(false),
m_nonEmptyNumericColumnsOnly(false),
m_showPlotDesignation(false),
m_filterCaseSensitivity(Qt::CaseInsensitive),
m_matchCompleteWord(false) {
connect(m_root, &AbstractAspect::aspectDescriptionChanged, this, &AspectTreeModel::aspectDescriptionChanged);
connect(m_root, &AbstractAspect::aspectAboutToBeAdded, this, &AspectTreeModel::aspectAboutToBeAdded);
connect(m_root, &AbstractAspect::aspectAboutToBeRemoved, this, &AspectTreeModel::aspectAboutToBeRemoved);
connect(m_root, &AbstractAspect::aspectAdded, this, &AspectTreeModel::aspectAdded);
connect(m_root, &AbstractAspect::aspectRemoved, this, &AspectTreeModel::aspectRemoved);
connect(m_root, &AbstractAspect::aspectHiddenAboutToChange, this, &AspectTreeModel::aspectHiddenAboutToChange);
connect(m_root, &AbstractAspect::aspectHiddenChanged, this, &AspectTreeModel::aspectHiddenChanged);
}
/*!
\c list contains the class names of the aspects, that can be selected in the corresponding model view.
*/
void AspectTreeModel::setSelectableAspects(QList list) {
m_selectableAspects=list;
}
void AspectTreeModel::setReadOnly(bool readOnly) {
m_readOnly = readOnly;
}
void AspectTreeModel::enableNumericColumnsOnly(bool value) {
m_numericColumnsOnly = value;
}
void AspectTreeModel::enableNonEmptyNumericColumnsOnly(bool value) {
m_nonEmptyNumericColumnsOnly = value;
}
void AspectTreeModel::enableShowPlotDesignation(bool value) {
m_showPlotDesignation = value;
}
QModelIndex AspectTreeModel::index(int row, int column, const QModelIndex &parent) const {
if (!hasIndex(row, column, parent))
return QModelIndex();
if(!parent.isValid()) {
if(row != 0) return QModelIndex();
return createIndex(row, column, m_root);
}
AbstractAspect *parent_aspect = static_cast(parent.internalPointer());
AbstractAspect *child_aspect = parent_aspect->child(row);
if (!child_aspect) return QModelIndex();
return createIndex(row, column, child_aspect);
}
QModelIndex AspectTreeModel::parent(const QModelIndex &index) const {
if (!index.isValid())
return QModelIndex();
AbstractAspect *parent_aspect = static_cast(index.internalPointer())->parentAspect();
if (!parent_aspect) return QModelIndex();
return modelIndexOfAspect(parent_aspect);
}
int AspectTreeModel::rowCount(const QModelIndex &parent) const {
if (!parent.isValid())
return 1;
AbstractAspect *parent_aspect = static_cast(parent.internalPointer());
return parent_aspect->childCount();
}
int AspectTreeModel::columnCount(const QModelIndex &parent) const {
Q_UNUSED(parent);
return 4;
}
QVariant AspectTreeModel::headerData(int section, Qt::Orientation orientation, int role) const {
if(orientation != Qt::Horizontal)
return QVariant();
switch(role) {
case Qt::DisplayRole:
switch(section) {
case 0:
return i18n("Name");
case 1:
return i18n("Type");
case 2:
return i18n("Created");
case 3:
return i18n("Comment");
default:
return QVariant();
}
default:
return QVariant();
}
}
QVariant AspectTreeModel::data(const QModelIndex &index, int role) const {
if (!index.isValid())
return QVariant();
AbstractAspect* aspect = static_cast(index.internalPointer());
switch(role) {
case Qt::DisplayRole:
case Qt::EditRole:
switch(index.column()) {
case 0: {
const Column* column = dynamic_cast(aspect);
if (column) {
QString name = aspect->name();
if (m_numericColumnsOnly && !(column->columnMode() == AbstractColumn::Numeric || column->columnMode() == AbstractColumn::Integer))
- name += QLatin1String(" (") + i18n("non-numeric data") + QLatin1Char(')');
+ name = i18n("%1 (non-numeric data)", name);
else if (m_nonEmptyNumericColumnsOnly && !column->hasValues())
- name += QLatin1String(" (") + i18n("no values") + QLatin1Char(')');
+ name = i18n("%1 (no values)", name);
if (m_showPlotDesignation) {
QString designation;
switch(column->plotDesignation()) {
case AbstractColumn::NoDesignation:
break;
case AbstractColumn::X:
designation = QLatin1String(" [X]");
break;
case AbstractColumn::Y:
designation = QLatin1String(" [Y]");
break;
case AbstractColumn::Z:
designation = QLatin1String(" [Z]");
break;
case AbstractColumn::XError:
designation = QLatin1String(" [") + i18n("X-error") + QLatin1Char(']');
break;
case AbstractColumn::XErrorPlus:
designation = QLatin1String(" [") + i18n("X-error +") + QLatin1Char(']');
break;
case AbstractColumn::XErrorMinus:
designation = QLatin1String(" [") + i18n("X-error -") + QLatin1Char(']');
break;
case AbstractColumn::YError:
designation = QLatin1String(" [") + i18n("Y-error") + QLatin1Char(']');
break;
case AbstractColumn::YErrorPlus:
designation = QLatin1String(" [") + i18n("Y-error +") + QLatin1Char(']');
break;
case AbstractColumn::YErrorMinus:
designation = QLatin1String(" [") + i18n("Y-error -") + QLatin1Char(']');
break;
}
name += QLatin1Char('\t') + designation;
}
return name;
} else
return aspect->name();
}
case 1:
if (aspect->metaObject()->className() != QLatin1String("CantorWorksheet"))
return aspect->metaObject()->className();
else
return QLatin1String("CAS Worksheet");
case 2:
return aspect->creationTime().toString();
case 3:
return aspect->comment().replace('\n', ' ').simplified();
default:
return QVariant();
}
case Qt::ToolTipRole:
if (aspect->comment().isEmpty())
return QLatin1String("") + aspect->name() + QLatin1String("");
else
return QLatin1String("") + aspect->name() + QLatin1String("
") + aspect->comment().replace(QLatin1Char('\n'), QLatin1String(" "));
case Qt::DecorationRole:
return index.column() == 0 ? aspect->icon() : QIcon();
case Qt::ForegroundRole: {
const WorksheetElement* we = qobject_cast(aspect);
if (we) {
if (!we->isVisible())
return QVariant( QApplication::palette().color(QPalette::Disabled,QPalette::Text ) );
}
return QVariant( QApplication::palette().color(QPalette::Active,QPalette::Text ) );
}
default:
return QVariant();
}
}
Qt::ItemFlags AspectTreeModel::flags(const QModelIndex &index) const {
if (!index.isValid())
return 0;
Qt::ItemFlags result;
AbstractAspect *aspect = static_cast(index.internalPointer());
if (!m_selectableAspects.isEmpty()) {
foreach(const char * classString, m_selectableAspects) {
if (aspect->inherits(classString)) {
result = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
if( index!=this->index(0,0,QModelIndex()) && !m_filterString.isEmpty() ) {
if (this->containsFilterString(aspect))
result = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
else
result &= ~Qt::ItemIsEnabled;
}
break;
} else
result &= ~Qt::ItemIsEnabled;
}
} else {
//default case: the list for the selectable aspects is empty and all aspects are selectable.
// Apply filter, if available. Indices, that don't match the filter are not selectable.
//Don't apply any filter to the very first index in the model - this top index corresponds to the project item.
if ( index!=this->index(0,0,QModelIndex()) && !m_filterString.isEmpty() ) {
if (this->containsFilterString(aspect))
result = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
else
result = Qt::ItemIsSelectable;
} else
result = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
}
//the columns "name" and "description" are editable
if (!m_readOnly) {
if (index.column() == 0 || index.column() == 3)
result |= Qt::ItemIsEditable;
}
const Column* column = dynamic_cast(aspect);
if (column) {
//allow to drag and drop columns for the faster creation of curves in the plots.
//TODO: allow drag&drop later for other objects too, once we implement copy and paste in the project explorer
result = result |Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled;
if (m_numericColumnsOnly && !(column->columnMode() == AbstractColumn::Numeric || column->columnMode() == AbstractColumn::Integer))
result &= ~Qt::ItemIsEnabled;
if (m_nonEmptyNumericColumnsOnly && !column->hasValues())
result &= ~Qt::ItemIsEnabled;
}
return result;
}
void AspectTreeModel::aspectDescriptionChanged(const AbstractAspect *aspect) {
emit dataChanged(modelIndexOfAspect(aspect), modelIndexOfAspect(aspect, 3));
}
void AspectTreeModel::aspectAboutToBeAdded(const AbstractAspect *parent, const AbstractAspect *before, const AbstractAspect *child) {
Q_UNUSED(child);
int index = parent->indexOfChild(before);
if (index == -1)
index = parent->childCount();
beginInsertRows(modelIndexOfAspect(parent), index, index);
}
void AspectTreeModel::aspectAdded(const AbstractAspect *aspect) {
endInsertRows();
AbstractAspect * parent = aspect->parentAspect();
emit dataChanged(modelIndexOfAspect(parent), modelIndexOfAspect(parent, 3));
connect(aspect, &AbstractAspect::renameRequested, this, &AspectTreeModel::renameRequestedSlot);
connect(aspect, &AbstractAspect::childAspectSelectedInView, this, &AspectTreeModel::aspectSelectedInView);
connect(aspect, &AbstractAspect::childAspectDeselectedInView, this, &AspectTreeModel::aspectDeselectedInView);
//add signal-slot connects for all children, too
for (const auto* child : aspect->children(AbstractAspect::Recursive)) {
connect(child, &AbstractAspect::renameRequested, this, &AspectTreeModel::renameRequestedSlot);
connect(child, &AbstractAspect::childAspectSelectedInView, this, &AspectTreeModel::aspectSelectedInView);
connect(child, &AbstractAspect::childAspectDeselectedInView, this, &AspectTreeModel::aspectDeselectedInView);
}
}
void AspectTreeModel::aspectAboutToBeRemoved(const AbstractAspect *aspect) {
AbstractAspect * parent = aspect->parentAspect();
int index = parent->indexOfChild(aspect);
beginRemoveRows(modelIndexOfAspect(parent), index, index);
}
void AspectTreeModel::aspectRemoved() {
endRemoveRows();
}
void AspectTreeModel::aspectHiddenAboutToChange(const AbstractAspect * aspect) {
for (AbstractAspect * i = aspect->parentAspect(); i; i = i->parentAspect())
if (i->hidden())
return;
if (aspect->hidden())
aspectAboutToBeAdded(aspect->parentAspect(), aspect, aspect);
else
aspectAboutToBeRemoved(aspect);
}
void AspectTreeModel::aspectHiddenChanged(const AbstractAspect *aspect) {
for (AbstractAspect * i = aspect->parentAspect(); i; i = i->parentAspect())
if (i->hidden())
return;
if (aspect->hidden())
aspectRemoved();
else
aspectAdded(aspect);
}
bool AspectTreeModel::setData(const QModelIndex &index, const QVariant &value, int role) {
if (!index.isValid() || role != Qt::EditRole) return false;
AbstractAspect *aspect = static_cast(index.internalPointer());
switch (index.column()) {
case 0:
aspect->setName(value.toString());
break;
case 3:
aspect->setComment(value.toString());
break;
default:
return false;
}
emit dataChanged(index, index);
return true;
}
QModelIndex AspectTreeModel::modelIndexOfAspect(const AbstractAspect* aspect, int column) const {
AbstractAspect* parent = aspect->parentAspect();
return createIndex(parent ? parent->indexOfChild(aspect) : 0,
column, const_cast(aspect));
}
/*!
returns the model index of an aspect defined via its path.
*/
QModelIndex AspectTreeModel::modelIndexOfAspect(const QString& path, int column) const {
//determine the aspect out of aspect path
AbstractAspect* aspect = nullptr;
auto children = m_root->children("AbstractAspect", AbstractAspect::Recursive);
for (auto* child: children) {
if (child->path() == path) {
aspect = child;
break;
}
}
//return the model index of the aspect
if (aspect)
return modelIndexOfAspect(aspect, column);
else
return QModelIndex();
}
void AspectTreeModel::setFilterString(const QString & s) {
m_filterString=s;
QModelIndex topLeft = this->index(0,0, QModelIndex());
QModelIndex bottomRight = this->index(this->rowCount()-1,3, QModelIndex());
emit dataChanged(topLeft, bottomRight);
}
void AspectTreeModel::setFilterCaseSensitivity(Qt::CaseSensitivity cs) {
m_filterCaseSensitivity = cs;
}
void AspectTreeModel::setFilterMatchCompleteWord(bool b) {
m_matchCompleteWord = b;
}
bool AspectTreeModel::containsFilterString(const AbstractAspect* aspect) const {
if (m_matchCompleteWord) {
if (aspect->name().compare(m_filterString, m_filterCaseSensitivity) == 0)
return true;
} else {
if (aspect->name().contains(m_filterString, m_filterCaseSensitivity))
return true;
}
//check for the occurrence of the filter string in the names of the parents
if ( aspect->parentAspect() )
return this->containsFilterString(aspect->parentAspect());
else
return false;
//TODO make this optional
// //check for the occurrence of the filter string in the names of the children
// foreach(const AbstractAspect * child, aspect->children()){
// if ( this->containsFilterString(child) )
// return true;
// }
}
//##############################################################################
//################################# SLOTS ####################################
//##############################################################################
void AspectTreeModel::renameRequestedSlot() {
AbstractAspect* aspect = qobject_cast(QObject::sender());
if (aspect)
emit renameRequested(modelIndexOfAspect(aspect));
}
void AspectTreeModel::aspectSelectedInView(const AbstractAspect* aspect) {
if (aspect->hidden()) {
//a hidden aspect was selected in the view (e.g. plot title in WorksheetView)
//select the parent aspect first, if available
AbstractAspect* parent = aspect->parentAspect();
if (parent)
emit indexSelected(modelIndexOfAspect(parent));
//emit also this signal, so the GUI can handle this selection.
emit hiddenAspectSelected(aspect);
} else
emit indexSelected(modelIndexOfAspect(aspect));
//deselect the root item when one of the children was selected in the view
//in order to avoid multiple selection with the project item (if selected) in the project explorer
emit indexDeselected(modelIndexOfAspect(m_root));
}
void AspectTreeModel::aspectDeselectedInView(const AbstractAspect* aspect) {
if (aspect->hidden()) {
AbstractAspect* parent = aspect->parentAspect();
if (parent)
emit indexDeselected(modelIndexOfAspect(parent));
} else
emit indexDeselected(modelIndexOfAspect(aspect));
}
diff --git a/src/backend/core/Project.cpp b/src/backend/core/Project.cpp
index f3d51c9b6..9078a91de 100644
--- a/src/backend/core/Project.cpp
+++ b/src/backend/core/Project.cpp
@@ -1,457 +1,457 @@
/***************************************************************************
File : Project.cpp
Project : LabPlot
Description : Represents a LabPlot project.
--------------------------------------------------------------------
Copyright : (C) 2011-2014 Alexander Semke (alexander.semke@web.de)
Copyright : (C) 2007-2008 Tilman Benkert (thzs@gmx.net)
Copyright : (C) 2007 Knut Franke (knut.franke@gmx.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 "backend/core/Project.h"
#include "backend/lib/XmlStreamReader.h"
#include "backend/datasources/LiveDataSource.h"
#include "backend/spreadsheet/Spreadsheet.h"
#include "backend/worksheet/Worksheet.h"
#include "backend/worksheet/plots/cartesian/CartesianPlot.h"
#include "backend/worksheet/plots/cartesian/XYEquationCurve.h"
#include "backend/worksheet/plots/cartesian/XYDataReductionCurve.h"
#include "backend/worksheet/plots/cartesian/XYDifferentiationCurve.h"
#include "backend/worksheet/plots/cartesian/XYIntegrationCurve.h"
#include "backend/worksheet/plots/cartesian/XYInterpolationCurve.h"
#include "backend/worksheet/plots/cartesian/XYSmoothCurve.h"
#include "backend/worksheet/plots/cartesian/XYFitCurve.h"
#include "backend/worksheet/plots/cartesian/XYFourierFilterCurve.h"
#include "backend/worksheet/plots/cartesian/XYFourierTransformCurve.h"
#include "backend/worksheet/plots/cartesian/Axis.h"
#include "backend/datapicker/DatapickerCurve.h"
#include
#include
#include
#include
#include
#include
#include
#include
-#include
+#include
#include
/**
* \class Project
* \brief Represents a project.
* \ingroup core
* Project represents the root node of all objects created during the runtime of the program.
* Manages also the undo stack.
*/
/**
* \enum Project::MdiWindowVisibility
* \brief MDI subwindow visibility setting
*/
/**
* \var Project::folderOnly
* \brief only show MDI windows corresponding to Parts in the current folder
*/
/**
* \var Project::foldAndSubfolders
* \brief show MDI windows corresponding to Parts in the current folder and its subfolders
*/
/**
* \var Project::allMdiWindows
* \brief show MDI windows for all Parts in the project simultaneously
*/
class Project::Private {
public:
Private() :
mdiWindowVisibility(Project::folderOnly),
scriptingEngine(0),
version(LVERSION),
author(QString(qgetenv("USER"))),
modificationTime(QDateTime::currentDateTime()),
changed(false) {
}
QUndoStack undo_stack;
MdiWindowVisibility mdiWindowVisibility;
AbstractScriptingEngine* scriptingEngine;
QString fileName;
QString version;
QString author;
QDateTime modificationTime;
bool changed;
};
Project::Project() : Folder(i18n("Project")), d(new Private()) {
//load default values for name, comment and author from config
KConfig config;
KConfigGroup group = config.group("Project");
d->author = group.readEntry("Author", QString());
//we don't have direct access to the members name and comment
//->temporaly disable the undo stack and call the setters
setUndoAware(false);
setIsLoading(true);
setName(group.readEntry("Name", i18n("Project")));
setComment(group.readEntry("Comment", QString()));
setUndoAware(true);
setIsLoading(false);
d->changed = false;
// TODO: intelligent engine choosing
// Q_ASSERT(ScriptingEngineManager::instance()->engineNames().size() > 0);
// QString engine_name = ScriptingEngineManager::instance()->engineNames()[0];
// d->scriptingEngine = ScriptingEngineManager::instance()->engine(engine_name);
connect(this, &Project::aspectDescriptionChanged,this, &Project::descriptionChanged);
}
Project::~Project() {
//if the project is being closed and the live data sources still continue reading the data,
//the dependend objects (columns, etc.), which are already deleted maybe here, are still being notified about the changes.
//->stop reading the live data sources prior to deleting all objects.
for (auto* lds : children())
lds->pauseReading();
//if the project is being closed, in Worksheet the scene items are being removed and the selection in the view can change.
//don't react on these changes since this can lead crashes (worksheet object is already in the destructor).
//->notify all worksheets about the project being closed.
for (auto* w : children(AbstractAspect::Recursive))
w->setIsClosing();
d->undo_stack.clear();
delete d;
}
QUndoStack* Project::undoStack() const {
return &d->undo_stack;
}
QMenu* Project::createContextMenu() {
QMenu* menu = new QMenu(); // no remove action from AbstractAspect in the project context menu
emit requestProjectContextMenu(menu);
return menu;
}
QMenu* Project::createFolderContextMenu(const Folder* folder) {
QMenu* menu = const_cast(folder)->AbstractAspect::createContextMenu();
emit requestFolderContextMenu(folder, menu);
return menu;
}
void Project::setMdiWindowVisibility(MdiWindowVisibility visibility) {
d->mdiWindowVisibility = visibility;
emit mdiWindowVisibilityChanged();
}
Project::MdiWindowVisibility Project::mdiWindowVisibility() const {
return d->mdiWindowVisibility;
}
AbstractScriptingEngine* Project::scriptingEngine() const {
return d->scriptingEngine;
}
CLASS_D_ACCESSOR_IMPL(Project, QString, fileName, FileName, fileName)
BASIC_D_ACCESSOR_IMPL(Project, QString, version, Version, version)
CLASS_D_ACCESSOR_IMPL(Project, QString, author, Author, author)
CLASS_D_ACCESSOR_IMPL(Project, QDateTime, modificationTime, ModificationTime, modificationTime)
void Project::setChanged(const bool value) {
if (isLoading())
return;
if (value)
emit changed();
d->changed = value;
}
bool Project ::hasChanged() const {
return d->changed ;
}
void Project::descriptionChanged(const AbstractAspect* aspect) {
if (isLoading())
return;
if (this != aspect)
return;
d->changed = true;
emit changed();
}
void Project::navigateTo(const QString& path) {
emit requestNavigateTo(path);
}
bool Project::isLabPlotProject(const QString& fileName) {
return fileName.endsWith(QStringLiteral(".lml"), Qt::CaseInsensitive) || fileName.endsWith(QStringLiteral(".lml.gz"), Qt::CaseInsensitive)
|| fileName.endsWith(QStringLiteral(".lml.bz2"), Qt::CaseInsensitive) || fileName.endsWith(QStringLiteral(".lml.xz"), Qt::CaseInsensitive);
}
QString Project::supportedExtensions() {
static const QString extensions = "*.lml *.lml.gz *.lml.bz2 *.lml.xz *.LML *.LML.GZ *.LML.BZ2 *.LML.XZ";
return extensions;
}
//##############################################################################
//################## Serialization/Deserialization ###########################
//##############################################################################
/**
* \brief Save as XML
*/
void Project::save(QXmlStreamWriter* writer) const {
//set the version and the modification time to the current values
d->version = LVERSION;
d->modificationTime = QDateTime::currentDateTime();
writer->setAutoFormatting(true);
writer->writeStartDocument();
writer->writeDTD("");
writer->writeStartElement("project");
writer->writeAttribute("version", version());
writer->writeAttribute("fileName", fileName());
writer->writeAttribute("modificationTime", modificationTime().toString("yyyy-dd-MM hh:mm:ss:zzz"));
writer->writeAttribute("author", author());
writeBasicAttributes(writer);
writeCommentElement(writer);
//save all children
for (auto* child : children(IncludeHidden)) {
writer->writeStartElement("child_aspect");
child->save(writer);
writer->writeEndElement();
}
//save the state of the views (visible, maximized/minimized/geometry)
//and the state of the project explorer (expanded items, currently selected item)
emit requestSaveState(writer);
writer->writeEndElement();
writer->writeEndDocument();
}
bool Project::load(const QString& filename, bool preview) {
QIODevice *file;
// first try gzip compression, because projects can be gzipped and end with .lml
if (filename.endsWith(QLatin1String(".lml"), Qt::CaseInsensitive))
file = new KCompressionDevice(filename,KFilterDev::compressionTypeForMimeType("application/x-gzip"));
else // opens filename using file ending
file = new KFilterDev(filename);
if (file == 0)
file = new QFile(filename);
if (!file->open(QIODevice::ReadOnly)) {
KMessageBox::error(0, i18n("Sorry. Could not open file for reading."));
return false;
}
char c;
bool rc = file->getChar(&c);
if (!rc) {
KMessageBox::error(0, i18n("The project file is empty."), i18n("Error opening project"));
file->close();
delete file;
return false;
}
file->seek(0);
//parse XML
XmlStreamReader reader(file);
setIsLoading(true);
rc = this->load(&reader, preview);
setIsLoading(false);
if (rc == false) {
RESET_CURSOR;
QString msg_text = reader.errorString();
KMessageBox::error(0, msg_text, i18n("Error when opening the project"));
return false;
}
if (reader.hasWarnings()) {
QString msg = i18n("The following problems occurred when loading the project file:\n");
const QStringList& warnings = reader.warningStrings();
for (const auto& str : warnings)
msg += str + '\n';
qWarning() << msg;
//TODO: show warnings in a kind of "log window" but not in message box
// KMessageBox::error(this, msg, i18n("Project loading partly failed"));
}
file->close();
delete file;
return true;
}
/**
* \brief Load from XML
*/
bool Project::load(XmlStreamReader* reader, bool preview) {
while (!(reader->isStartDocument() || reader->atEnd()))
reader->readNext();
if(!(reader->atEnd())) {
if (!reader->skipToNextTag())
return false;
if (reader->name() == "project") {
QString version = reader->attributes().value("version").toString();
if(version.isEmpty())
reader->raiseWarning(i18n("Attribute 'version' is missing."));
else
d->version = version;
if (!readBasicAttributes(reader)) return false;
if (!readProjectAttributes(reader)) return false;
while (!reader->atEnd()) {
reader->readNext();
if (reader->isEndElement()) break;
if (reader->isStartElement()) {
if (reader->name() == "comment") {
if (!readCommentElement(reader))
return false;
} else if(reader->name() == "child_aspect") {
if (!readChildAspectElement(reader, preview))
return false;
} else if(reader->name() == "state") {
//load the state of the views (visible, maximized/minimized/geometry)
//and the state of the project explorer (expanded items, currently selected item)
emit requestLoadState(reader);
} else {
reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString()));
if (!reader->skipToEndElement()) return false;
}
}
}
//wait until all columns are decoded from base64-encoded data
QThreadPool::globalInstance()->waitForDone();
//everything is read now.
//restore the pointer to the data sets (columns) in xy-curves etc.
QVector curves = children(AbstractAspect::Recursive);
QVector axes = children(AbstractAspect::Recursive);
QVector dataPickerCurves = children(AbstractAspect::Recursive);
if (!curves.isEmpty() || !axes.isEmpty()) {
QVector columns = children(AbstractAspect::Recursive);
//XY-curves
for (auto* curve : curves) {
if (!curve) continue;
curve->suppressRetransform(true);
XYEquationCurve* equationCurve = dynamic_cast(curve);
XYAnalysisCurve* analysisCurve = dynamic_cast(curve);
if (equationCurve) {
//curves defined by a mathematical equations recalculate their own columns on load again.
if (!preview)
equationCurve->recalculate();
} else if (analysisCurve) {
RESTORE_COLUMN_POINTER(analysisCurve, xDataColumn, XDataColumn);
RESTORE_COLUMN_POINTER(analysisCurve, yDataColumn, YDataColumn);
XYFitCurve* fitCurve = dynamic_cast(curve);
if (fitCurve) {
RESTORE_COLUMN_POINTER(fitCurve, xErrorColumn, XErrorColumn);
RESTORE_COLUMN_POINTER(fitCurve, yErrorColumn, YErrorColumn);
}
} else {
RESTORE_COLUMN_POINTER(curve, xColumn, XColumn);
RESTORE_COLUMN_POINTER(curve, yColumn, YColumn);
RESTORE_COLUMN_POINTER(curve, valuesColumn, ValuesColumn);
RESTORE_COLUMN_POINTER(curve, xErrorPlusColumn, XErrorPlusColumn);
RESTORE_COLUMN_POINTER(curve, xErrorMinusColumn, XErrorMinusColumn);
RESTORE_COLUMN_POINTER(curve, yErrorPlusColumn, YErrorPlusColumn);
RESTORE_COLUMN_POINTER(curve, yErrorMinusColumn, YErrorMinusColumn);
}
if (dynamic_cast(curve))
RESTORE_POINTER(dynamic_cast(curve), dataSourceCurve, DataSourceCurve, XYCurve, curves);
curve->suppressRetransform(false);
}
//Axes
for (auto* axis : axes) {
if (!axis) continue;
RESTORE_COLUMN_POINTER(axis, majorTicksColumn, MajorTicksColumn);
RESTORE_COLUMN_POINTER(axis, minorTicksColumn, MinorTicksColumn);
}
for (auto* dataPickerCurve : dataPickerCurves) {
if (!dataPickerCurve) continue;
RESTORE_COLUMN_POINTER(dataPickerCurve, posXColumn, PosXColumn);
RESTORE_COLUMN_POINTER(dataPickerCurve, posYColumn, PosYColumn);
RESTORE_COLUMN_POINTER(dataPickerCurve, plusDeltaXColumn, PlusDeltaXColumn);
RESTORE_COLUMN_POINTER(dataPickerCurve, minusDeltaXColumn, MinusDeltaXColumn);
RESTORE_COLUMN_POINTER(dataPickerCurve, plusDeltaYColumn, PlusDeltaYColumn);
RESTORE_COLUMN_POINTER(dataPickerCurve, minusDeltaYColumn, MinusDeltaYColumn);
}
}
} else // no project element
reader->raiseError(i18n("no project element found"));
} else // no start document
reader->raiseError(i18n("no valid XML document found"));
if (!preview) {
for (auto* plot : children(AbstractAspect::Recursive)) {
plot->setIsLoading(false);
plot->retransform();
}
}
emit loaded();
return !reader->hasError();
}
bool Project::readProjectAttributes(XmlStreamReader* reader) {
QXmlStreamAttributes attribs = reader->attributes();
QString str = attribs.value(reader->namespaceUri().toString(), "fileName").toString();
if(str.isEmpty()) {
reader->raiseError(i18n("Project file name missing."));
return false;
}
d->fileName = str;
str = attribs.value(reader->namespaceUri().toString(), "modificationTime").toString();
QDateTime modificationTime = QDateTime::fromString(str, "yyyy-dd-MM hh:mm:ss:zzz");
if(str.isEmpty() || !modificationTime.isValid()) {
reader->raiseWarning(i18n("Invalid project modification time. Using current time."));
d->modificationTime = QDateTime::currentDateTime();
} else
d->modificationTime = modificationTime;
str = attribs.value(reader->namespaceUri().toString(), "author").toString();
d->author = str;
return true;
}
diff --git a/src/backend/core/Workbook.cpp b/src/backend/core/Workbook.cpp
index 764af7c92..d612e51ea 100644
--- a/src/backend/core/Workbook.cpp
+++ b/src/backend/core/Workbook.cpp
@@ -1,229 +1,229 @@
/***************************************************************************
File : Workbook.h
Project : LabPlot
Description : Aspect providing a container for storing data
in form of spreadsheets and matrices
--------------------------------------------------------------------
Copyright : (C) 2015 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 "Workbook.h"
#include "backend/spreadsheet/Spreadsheet.h"
#include "backend/matrix/Matrix.h"
#include "backend/lib/XmlStreamReader.h"
#include "commonfrontend/workbook/WorkbookView.h"
#include "kdefrontend/spreadsheet/ExportSpreadsheetDialog.h"
-#include
+#include
/**
* \class Workbook
* \brief Top-level container for Spreadsheet and Matrix.
* \ingroup backend
*/
Workbook::Workbook(AbstractScriptingEngine* engine, const QString& name)
: AbstractPart(name), scripted(engine), m_view(nullptr) {
}
QIcon Workbook::icon() const {
return QIcon::fromTheme("labplot-workbook");
}
/*!
* Returns a new context menu. The caller takes ownership of the menu.
*/
QMenu* Workbook::createContextMenu() {
QMenu *menu = AbstractPart::createContextMenu();
Q_ASSERT(menu);
emit requestProjectContextMenu(menu);
return menu;
}
QWidget* Workbook::view() const {
if (!m_partView) {
m_view = new WorkbookView(const_cast(this));
m_partView = m_view;
}
return m_partView;
}
bool Workbook::exportView() const {
Spreadsheet* s = currentSpreadsheet();
bool ret = false;
if (s)
ret = s->exportView();
else {
Matrix* m = currentMatrix();
if (m)
ret = m->exportView();
}
return ret;
}
bool Workbook::printView() {
Spreadsheet* s = currentSpreadsheet();
bool ret = false;
if (s)
ret = s->printView();
else {
Matrix* m = currentMatrix();
if (m)
ret = m->printView();
}
return ret;
}
bool Workbook::printPreview() const {
Spreadsheet* s = currentSpreadsheet();
bool ret = false;
if (s)
ret = s->printPreview();
else {
Matrix* m = currentMatrix();
if (m)
ret = m->printPreview();
}
return ret;
}
Spreadsheet* Workbook::currentSpreadsheet() const {
if (!m_view)
return nullptr;
int index = m_view->currentIndex();
if(index != -1) {
AbstractAspect* aspect = child(index);
return dynamic_cast(aspect);
}
return nullptr;
}
Matrix* Workbook::currentMatrix() const {
if (!m_view)
return nullptr;
int index = reinterpret_cast(m_view)->currentIndex();
if(index != -1) {
AbstractAspect* aspect = child(index);
return dynamic_cast(aspect);
}
return nullptr;
}
/*!
this slot is called when a workbook child is selected in the project explorer.
emits \c workbookItemSelected() to forward this event to the \c WorkbookView
in order to select the corresponding tab.
*/
void Workbook::childSelected(const AbstractAspect* aspect) {
int index = indexOfChild(aspect);
emit workbookItemSelected(index);
}
/*!
this slot is called when a worksheet element is deselected in the project explorer.
*/
void Workbook::childDeselected(const AbstractAspect* aspect) {
Q_UNUSED(aspect);
}
/*!
* Emits the signal to select or to deselect the workbook item (spreadsheet or matrix) with the index \c index
* 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 WorkbookView when the current tab was changed
*/
void Workbook::setChildSelectedInView(int index, bool selected) {
AbstractAspect* aspect = child(index);
if (selected) {
emit childAspectSelectedInView(aspect);
//deselect the workbook in the project explorer, if a child (spreadsheet or matrix) was selected.
//prevents unwanted multiple selection with workbook if it was selected before.
emit childAspectDeselectedInView(this);
} else {
emit childAspectDeselectedInView(aspect);
//deselect also all children that were potentially selected before (columns of a spreadsheet)
for (auto* child : aspect->children())
emit childAspectDeselectedInView(child);
}
}
//##############################################################################
//################## Serialization/Deserialization ###########################
//##############################################################################
//! Save as XML
void Workbook::save(QXmlStreamWriter* writer) const {
writer->writeStartElement( "workbook" );
writeBasicAttributes(writer);
writeCommentElement(writer);
//serialize all children
for (auto* aspect : children())
aspect->save(writer);
writer->writeEndElement(); // close "workbook" section
}
//! Load from XML
bool Workbook::load(XmlStreamReader* reader, bool preview) {
if(!reader->isStartElement() || reader->name() != "workbook") {
reader->raiseError(i18n("no workbook element found"));
return false;
}
if (!readBasicAttributes(reader))
return false;
while (!reader->atEnd()) {
reader->readNext();
if (reader->isEndElement() && reader->name() == "workbook")
break;
if (!reader->isStartElement())
continue;
if(reader->name() == "spreadsheet") {
Spreadsheet* spreadsheet = new Spreadsheet(0, "spreadsheet", true);
if (!spreadsheet->load(reader, preview)) {
delete spreadsheet;
return false;
} else
addChild(spreadsheet);
} else if (reader->name() == "matrix") {
Matrix* matrix = new Matrix(0, i18n("matrix"), true);
if (!matrix->load(reader, preview)) {
delete matrix;
return false;
} else
addChild(matrix);
} else { // unknown element
reader->raiseWarning(i18n("unknown workbook element '%1'", reader->name().toString()));
if (!reader->skipToEndElement()) return false;
}
}
return true;
}
diff --git a/src/backend/core/abstractcolumncommands.cpp b/src/backend/core/abstractcolumncommands.cpp
index 5ccaf2f70..cac63ab58 100644
--- a/src/backend/core/abstractcolumncommands.cpp
+++ b/src/backend/core/abstractcolumncommands.cpp
@@ -1,258 +1,258 @@
/***************************************************************************
File : abstractcolumncommands.cpp
Project : LabPlot
Description : Commands to be called by AbstractColumn to modify AbstractColumnPrivate
--------------------------------------------------------------------
Copyright : (C) 2007-2009 Tilman Benkert (thzs@gmx.net)
Copyright : (C) 2010 Knut Franke (knut.franke@gmx.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 "abstractcolumncommands.h"
-#include
+#include
/** ***************************************************************************
* \class AbstractColumnClearMasksCmd
* \brief Clear masking information
** ***************************************************************************/
/**
* \var AbstractColumnClearMasksCmd::m_col
* \brief The private column data to modify
*/
/**
* \var AbstractColumnClearMasksCmd::m_masking
* \brief The old masks
*/
/**
* \var AbstractColumnClearMasksCmd::m_copied
* \brief A status flag
*/
/**
* \brief Ctor
*/
AbstractColumnClearMasksCmd::AbstractColumnClearMasksCmd(AbstractColumnPrivate* col, QUndoCommand* parent)
: QUndoCommand( parent ), m_col(col)
{
setText(i18n("%1: clear masks", col->name()));
m_copied = false;
}
/**
* \brief Dtor
*/
AbstractColumnClearMasksCmd::~AbstractColumnClearMasksCmd()
{
}
/**
* \brief Execute the command
*/
void AbstractColumnClearMasksCmd::redo()
{
if(!m_copied)
{
m_masking = m_col->m_masking;
m_copied = true;
}
m_col->m_masking.clear();
emit m_col->owner()->dataChanged(m_col->owner());
}
/**
* \brief Undo the command
*/
void AbstractColumnClearMasksCmd::undo()
{
m_col->m_masking = m_masking;
emit m_col->owner()->dataChanged(m_col->owner());
}
/** ***************************************************************************
* \class AbstractColumnSetMaskedCmd
* \brief Mark an interval of rows as masked
** ***************************************************************************/
/**
* \var AbstractColumnSetMaskedCmd::m_col
* \brief The private AbstractColumn data to modify
*/
/**
* \var AbstractColumnSetMaskedCmd::m_interval
* \brief The interval
*/
/**
* \var AbstractColumnSetMaskedCmd::m_masked
* \brief Mask/unmask flag
*/
/**
* \var AbstractColumnSetMaskedCmd::m_masking
* \brief Interval attribute backup
*/
/**
* \var AbstractColumnSetMaskedCmd::m_copied
* \brief A status flag
*/
/**
* \brief Ctor
*/
AbstractColumnSetMaskedCmd::AbstractColumnSetMaskedCmd(AbstractColumnPrivate * col, const Interval& interval, bool masked, QUndoCommand * parent )
: QUndoCommand( parent ), m_col(col), m_interval(interval), m_masked(masked)
{
if(masked)
setText(i18n("%1: mask cells", col->name()));
else
setText(i18n("%1: unmask cells", col->name()));
m_copied = false;
}
/**
* \brief Dtor
*/
AbstractColumnSetMaskedCmd::~AbstractColumnSetMaskedCmd()
{
}
/**
* \brief Execute the command
*/
void AbstractColumnSetMaskedCmd::redo()
{
if(!m_copied)
{
m_masking = m_col->m_masking;
m_copied = true;
}
m_col->m_masking.setValue(m_interval, m_masked);
emit m_col->owner()->dataChanged(m_col->owner());
}
/**
* \brief Undo the command
*/
void AbstractColumnSetMaskedCmd::undo()
{
m_col->m_masking = m_masking;
emit m_col->owner()->dataChanged(m_col->owner());
}
/** ***************************************************************************
* \class AbstractColumnInsertRowsCmd
* \brief Insert empty rows into a column
** ***************************************************************************/
/**
* \var AbstractColumnInsertRowsCmd::m_col
* \brief Private object of AbstractColumn to be modified.
*/
/**
* \var AbstractColumnInsertRowsCmd::m_before
* \brief Row number before which to insert the new rows.
*/
/**
* \var AbstractColumnInsertRowsCmd::m_count
* \brief Number of rows to be inserted.
*/
/**
* \brief Ctor
*/
AbstractColumnInsertRowsCmd::AbstractColumnInsertRowsCmd(AbstractColumn *col, int before,
int count, QUndoCommand *parent) :
QUndoCommand(parent),
m_col(col->d),
m_before(before),
m_count(count) {
}
/**
* \brief Dtor
*/
AbstractColumnInsertRowsCmd::~AbstractColumnInsertRowsCmd() {
}
void AbstractColumnInsertRowsCmd::redo() {
m_col->m_masking.insertRows(m_before, m_count);
}
void AbstractColumnInsertRowsCmd::undo() {
m_col->m_masking.removeRows(m_before, m_count);
}
/** ***************************************************************************
* \class AbstractColumnRemoveRowsCmd
* \brief Remove rows from a column
*
* See AbstractColumnInsertRowsCmd for a discussion of the design.
** ***************************************************************************/
/**
* \var AbstractColumnRemoveRowsCmd::m_col
* \brief Private object of AbstractColumn to be modified.
*/
/**
* \var AbstractColumnRemoveRowsCmd::m_first
* \brief First row number to be removed.
*/
/**
* \var AbstractColumnRemoveRowsCmd::m_count
* \brief Number of rows to be removed.
*/
/**
* \brief Ctor
*/
AbstractColumnRemoveRowsCmd::AbstractColumnRemoveRowsCmd(AbstractColumn *col, int first,
int count, QUndoCommand *parent) :
QUndoCommand(parent),
m_col(col->d),
m_first(first),
m_count(count) {
}
/**
* \brief Dtor
*/
AbstractColumnRemoveRowsCmd::~AbstractColumnRemoveRowsCmd() {
}
void AbstractColumnRemoveRowsCmd::redo() {
m_masking = m_col->m_masking;
m_col->m_masking.removeRows(m_first, m_count);
}
void AbstractColumnRemoveRowsCmd::undo() {
m_col->m_masking = m_masking;
}
diff --git a/src/backend/core/aspectcommands.h b/src/backend/core/aspectcommands.h
index 300f4e853..9ff39bb8a 100644
--- a/src/backend/core/aspectcommands.h
+++ b/src/backend/core/aspectcommands.h
@@ -1,130 +1,130 @@
/***************************************************************************
File : aspectcommands.h
Project : LabPlot
--------------------------------------------------------------------
Copyright : (C) 2007-2010 by Knut Franke (knut.franke@gmx.de)
Copyright : (C) 2007-2009 Tilman Benkert(thzs@gmx.net)
Copyright : (C) 2013-2017 by Alexander Semke (alexander.semke@web.de)
Description : Undo commands used by AbstractAspect.
Only meant to be used within AbstractAspect.cpp
***************************************************************************/
/***************************************************************************
* *
* 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 ASPECTCOMMANDS_H
#define ASPECTCOMMANDS_H
#include "AspectPrivate.h"
#include
-#include
+#include
class AspectChildRemoveCmd : public QUndoCommand {
public:
AspectChildRemoveCmd(AbstractAspectPrivate* target, AbstractAspect* child)
: m_target(target), m_child(child), m_index(-1) {
// , m_removed(false) {
setText(i18n("%1: remove %2", m_target->m_name, m_child->name()));
}
~AspectChildRemoveCmd() override {
//TODO: this makes labplot crashing on project close/save.
// if (m_removed)
// delete m_child;
}
// calling redo transfers ownership of m_child to the undo command
void redo() override {
AbstractAspect* nextSibling;
if (m_child == m_target->m_children.last())
nextSibling = 0;
else
nextSibling = m_target->m_children.at(m_target->indexOfChild(m_child) + 1);
emit m_target->q->aspectAboutToBeRemoved(m_child);
m_index = m_target->removeChild(m_child);
emit m_target->q->aspectRemoved(m_target->q, nextSibling, m_child);
// m_removed = true;
}
// calling undo transfers ownership of m_child back to its parent aspect
void undo() override {
Q_ASSERT(m_index != -1); // m_child must be a child of m_target->q
emit m_target->q->aspectAboutToBeAdded(m_target->q, 0, m_child);
m_target->insertChild(m_index, m_child);
emit m_target->q->aspectAdded(m_child);
// m_removed = false;
}
protected:
AbstractAspectPrivate* m_target;
AbstractAspect* m_child;
int m_index;
// bool m_removed;
};
class AspectChildAddCmd : public AspectChildRemoveCmd {
public:
AspectChildAddCmd(AbstractAspectPrivate* target, AbstractAspect* child, int index)
: AspectChildRemoveCmd(target, child) {
setText(i18n("%1: add %2", m_target->m_name, m_child->name()));
m_index = index;
// m_removed = true;
}
void redo() override {
AspectChildRemoveCmd::undo();
}
void undo() override {
AspectChildRemoveCmd::redo();
}
};
class AspectChildReparentCmd : public QUndoCommand {
public:
AspectChildReparentCmd(AbstractAspectPrivate* target, AbstractAspectPrivate* new_parent,
AbstractAspect* child, int new_index)
: m_target(target), m_new_parent(new_parent), m_child(child), m_index(-1), m_new_index(new_index) {
setText(i18n("%1: move %2 to %3.", m_target->m_name, m_child->name(), m_new_parent->m_name));
}
// calling redo transfers ownership of m_child to the new parent aspect
void redo() override {
m_index = m_target->removeChild(m_child);
m_new_parent->insertChild(m_new_index, m_child);
}
// calling undo transfers ownership of m_child back to its previous parent aspect
void undo() override {
Q_ASSERT(m_index != -1);
m_new_parent->removeChild(m_child);
m_target->insertChild(m_index, m_child);
}
protected:
AbstractAspectPrivate * m_target;
AbstractAspectPrivate * m_new_parent;
AbstractAspect* m_child;
int m_index;
int m_new_index;
};
#endif
diff --git a/src/backend/core/column/Column.cpp b/src/backend/core/column/Column.cpp
index 16c0d5280..903460002 100644
--- a/src/backend/core/column/Column.cpp
+++ b/src/backend/core/column/Column.cpp
@@ -1,1219 +1,1219 @@
/***************************************************************************
File : Column.cpp
Project : LabPlot
Description : Aspect that manages a column
--------------------------------------------------------------------
Copyright : (C) 2007-2009 Tilman Benkert (thzs@gmx.net)
Copyright : (C) 2013-2017 Alexander Semke (alexander.semke@web.de)
Copyright : (C) 2017 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 "backend/core/column/Column.h"
#include "backend/core/column/ColumnPrivate.h"
#include "backend/core/column/ColumnStringIO.h"
#include "backend/core/column/columncommands.h"
#include "backend/core/Project.h"
#include "backend/lib/XmlStreamReader.h"
#include "backend/core/datatypes/String2DateTimeFilter.h"
#include "backend/core/datatypes/DateTime2StringFilter.h"
#include "backend/worksheet/plots/cartesian/XYCurve.h"
#include "backend/worksheet/plots/cartesian/XYAnalysisCurve.h"
extern "C" {
#include
}
#include
#include
#include
#include
#include
-#include
+#include
/**
* \class Column
* \brief Aspect that manages a column
*
* This class represents a column, i.e., (mathematically) a 1D vector of
* values with a header. It provides a public reading and (undo aware) writing
* interface as defined in AbstractColumn. A column
* can have one of currently three data types: double, QString, or
* QDateTime. The string representation of the values can differ depending
* on the mode of the column.
*
* Column inherits from AbstractAspect and is intended to be a child
* of the corresponding Spreadsheet in the aspect hierarchy. Columns don't
* have a view as they are intended to be displayed inside a spreadsheet.
*/
Column::Column(const QString& name, AbstractColumn::ColumnMode mode)
: AbstractColumn(name), d(new ColumnPrivate(this, mode)) {
init();
}
/**
* \brief Common part of ctors
*/
void Column::init() {
m_string_io = new ColumnStringIO(this);
d->inputFilter()->input(0, m_string_io);
d->outputFilter()->input(0, this);
d->inputFilter()->setHidden(true);
d->outputFilter()->setHidden(true);
addChild(d->inputFilter());
addChild(d->outputFilter());
m_suppressDataChangedSignal = false;
m_usedInActionGroup = new QActionGroup(this);
connect(m_usedInActionGroup, &QActionGroup::triggered, this, &Column::navigateTo);
}
Column::~Column() {
delete m_string_io;
delete d;
}
QMenu* Column::createContextMenu() {
QMenu* menu = AbstractAspect::createContextMenu();
QAction* firstAction = menu->actions().at(1);
//add actions available in SpreadsheetView
emit requestProjectContextMenu(menu);
//"Used in" menu containing all curves where the column is used
QMenu* usedInMenu = new QMenu(i18n("Used in"));
usedInMenu->setIcon(QIcon::fromTheme("go-next-view"));
//remove previously added actions
for (auto* action: m_usedInActionGroup->actions())
m_usedInActionGroup->removeAction(action);
//add curves where the column is currently in use
QVector curves = project()->children(AbstractAspect::Recursive);
for (const auto* curve: curves) {
bool used = false;
const XYAnalysisCurve* analysisCurve = dynamic_cast(curve);
if (analysisCurve) {
if (analysisCurve->dataSourceType() == XYAnalysisCurve::DataSourceSpreadsheet && (analysisCurve->xDataColumn() == this || analysisCurve->yDataColumn() == this) )
used = true;
} else {
if (curve->xColumn() == this || curve->yColumn() == this)
used = true;
}
if (used) {
QAction* action = new QAction(curve->icon(), curve->name(), m_usedInActionGroup);
action->setData(curve->path());
usedInMenu->addAction(action);
}
}
menu->insertSeparator(firstAction);
menu->insertMenu(firstAction, usedInMenu);
menu->insertSeparator(firstAction);
return menu;
}
void Column::navigateTo(QAction* action) {
project()->navigateTo(action->data().toString());
}
/*!
*
*/
void Column::setSuppressDataChangedSignal(bool b) {
m_suppressDataChangedSignal = b;
}
/**
* \brief Set the column mode
*
* This sets the column mode and, if
* necessary, converts it to another datatype.
*/
void Column::setColumnMode(AbstractColumn::ColumnMode mode) {
if (mode == columnMode())
return;
DEBUG("Column::setColumnMode()");
beginMacro(i18n("%1: change column type", name()));
auto* old_input_filter = d->inputFilter();
auto* old_output_filter = d->outputFilter();
exec(new ColumnSetModeCmd(d, mode));
if (d->inputFilter() != old_input_filter) {
removeChild(old_input_filter);
addChild(d->inputFilter());
d->inputFilter()->input(0, m_string_io);
}
if (d->outputFilter() != old_output_filter) {
removeChild(old_output_filter);
addChild(d->outputFilter());
d->outputFilter()->input(0, this);
}
endMacro();
DEBUG("Column::setColumnMode() DONE");
}
void Column::setColumnModeFast(AbstractColumn::ColumnMode mode) {
if (mode == columnMode())
return;
auto* old_input_filter = d->inputFilter();
auto* old_output_filter = d->outputFilter();
exec(new ColumnSetModeCmd(d, mode));
if (d->inputFilter() != old_input_filter) {
removeChild(old_input_filter);
addChildFast(d->inputFilter());
d->inputFilter()->input(0, m_string_io);
}
if (d->outputFilter() != old_output_filter) {
removeChild(old_output_filter);
addChildFast(d->outputFilter());
d->outputFilter()->input(0, this);
}
}
/**
* \brief Copy another column of the same type
*
* This function will return false if the data type
* of 'other' is not the same as the type of 'this'.
* Use a filter to convert a column to another type.
*/
bool Column::copy(const AbstractColumn* other) {
Q_CHECK_PTR(other);
if (other->columnMode() != columnMode()) return false;
exec(new ColumnFullCopyCmd(d, other));
return true;
}
/**
* \brief Copies a part of another column of the same type
*
* This function will return false if the data type
* of 'other' is not the same as the type of 'this'.
* \param other pointer to the column to copy
* \param src_start first row to copy in the column to copy
* \param dest_start first row to copy in
* \param num_rows the number of rows to copy
*/
bool Column::copy(const AbstractColumn* source, int source_start, int dest_start, int num_rows) {
Q_CHECK_PTR(source);
if (source->columnMode() != columnMode()) return false;
exec(new ColumnPartialCopyCmd(d, source, source_start, dest_start, num_rows));
return true;
}
/**
* \brief Insert some empty (or initialized with zero) rows
*/
void Column::handleRowInsertion(int before, int count) {
AbstractColumn::handleRowInsertion(before, count);
exec(new ColumnInsertRowsCmd(d, before, count));
if (!m_suppressDataChangedSignal)
emit dataChanged(this);
d->statisticsAvailable = false;;
d->hasValuesAvailable = false;
}
/**
* \brief Remove 'count' rows starting from row 'first'
*/
void Column::handleRowRemoval(int first, int count) {
AbstractColumn::handleRowRemoval(first, count);
exec(new ColumnRemoveRowsCmd(d, first, count));
if (!m_suppressDataChangedSignal)
emit dataChanged(this);
d->statisticsAvailable = false;;
d->hasValuesAvailable = false;
}
/**
* \brief Set the column plot designation
*/
void Column::setPlotDesignation(AbstractColumn::PlotDesignation pd) {
if (pd != plotDesignation())
exec(new ColumnSetPlotDesignationCmd(d, pd));
}
/**
* \brief Get width
*/
int Column::width() const {
return d->width();
}
/**
* \brief Set width
*/
void Column::setWidth(int value) {
d->setWidth(value);
}
/**
* \brief Clear the whole column
*/
void Column::clear() {
exec(new ColumnClearCmd(d));
}
////////////////////////////////////////////////////////////////////////////////
//@}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//! \name Formula related functions
//@{
////////////////////////////////////////////////////////////////////////////////
/**
* \brief Returns the formula used to generate column values
*/
QString Column:: formula() const {
return d->formula();
}
const QStringList& Column::formulaVariableNames() const {
return d->formulaVariableNames();
}
const QStringList& Column::formulaVariableColumnPathes() const {
return d->formulaVariableColumnPathes();
}
/**
* \brief Sets the formula used to generate column values
*/
void Column::setFormula(const QString& formula, const QStringList& variableNames, const QStringList& columnPathes) {
exec(new ColumnSetGlobalFormulaCmd(d, formula, variableNames, columnPathes));
}
/**
* \brief Set a formula string for an interval of rows
*/
void Column::setFormula(const Interval& i, const QString& formula) {
exec(new ColumnSetFormulaCmd(d, i, formula));
}
/**
* \brief Overloaded function for convenience
*/
void Column::setFormula(int row, const QString& formula) {
setFormula(Interval(row, row), formula);
}
/**
* \brief Clear all formulas
*/
void Column::clearFormulas() {
exec(new ColumnClearFormulasCmd(d));
}
////////////////////////////////////////////////////////////////////////////////
//@}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
//! \name type specific functions
//@{
////////////////////////////////////////////////////////////////////////////////
/**
* \brief Set the content of row 'row'
*
* Use this only when columnMode() is Text
*/
void Column::setTextAt(int row, const QString& new_value) {
DEBUG("Column::setTextAt()");
d->statisticsAvailable = false;;
exec(new ColumnSetTextCmd(d, row, new_value));
}
/**
* \brief Replace a range of values
*
* Use this only when columnMode() is Text
*/
void Column::replaceTexts(int first, const QVector& new_values) {
DEBUG("Column::replaceTexts()");
if (!new_values.isEmpty()) { //TODO: do we really need this check?
d->statisticsAvailable = false;;
exec(new ColumnReplaceTextsCmd(d, first, new_values));
}
}
/**
* \brief Set the content of row 'row'
*
* Use this only when columnMode() is DateTime, Month or Day
*/
void Column::setDateAt(int row, QDate new_value) {
d->statisticsAvailable = false;;
setDateTimeAt(row, QDateTime(new_value, timeAt(row)));
}
/**
* \brief Set the content of row 'row'
*
* Use this only when columnMode() is DateTime, Month or Day
*/
void Column::setTimeAt(int row, QTime new_value) {
d->statisticsAvailable = false;;
setDateTimeAt(row, QDateTime(dateAt(row), new_value));
}
/**
* \brief Set the content of row 'row'
*
* Use this only when columnMode() is DateTime, Month or Day
*/
void Column::setDateTimeAt(int row, const QDateTime& new_value) {
d->statisticsAvailable = false;;
exec(new ColumnSetDateTimeCmd(d, row, new_value));
}
/**
* \brief Replace a range of values
*
* Use this only when columnMode() is DateTime, Month or Day
*/
void Column::replaceDateTimes(int first, const QVector& new_values) {
if (!new_values.isEmpty()) {
d->statisticsAvailable = false;;
exec(new ColumnReplaceDateTimesCmd(d, first, new_values));
}
}
/**
* \brief Set the content of row 'row'
*
* Use this only when columnMode() is Numeric
*/
void Column::setValueAt(int row, const double new_value) {
// DEBUG("Column::setValueAt()");
d->statisticsAvailable = false;;
d->hasValuesAvailable = false;
exec(new ColumnSetValueCmd(d, row, new_value));
}
/**
* \brief Replace a range of values
*
* Use this only when columnMode() is Numeric
*/
void Column::replaceValues(int first, const QVector& new_values) {
DEBUG("Column::replaceValues()");
if (!new_values.isEmpty()) {
d->statisticsAvailable = false;;
d->hasValuesAvailable = false;
exec(new ColumnReplaceValuesCmd(d, first, new_values));
}
}
/**
* \brief Set the content of row 'row'
*
* Use this only when columnMode() is Integer
*/
void Column::setIntegerAt(int row, const int new_value) {
DEBUG("Column::setIntegerAt()");
d->statisticsAvailable = false;;
d->hasValuesAvailable = false;
exec(new ColumnSetIntegerCmd(d, row, new_value));
}
/**
* \brief Replace a range of values
*
* Use this only when columnMode() is Integer
*/
void Column::replaceInteger(int first, const QVector& new_values) {
DEBUG("Column::replaceInteger()");
if (!new_values.isEmpty()) {
d->statisticsAvailable = false;;
d->hasValuesAvailable = false;
exec(new ColumnReplaceIntegersCmd(d, first, new_values));
}
}
const Column::ColumnStatistics& Column::statistics() {
if (!d->statisticsAvailable)
calculateStatistics();
return d->statistics;
}
void Column::calculateStatistics() {
d->statistics = ColumnStatistics();
ColumnStatistics& statistics = d->statistics;
// TODO: support other data types?
QVector* rowValues = reinterpret_cast*>(data());
size_t notNanCount = 0;
double val;
double columnSum = 0.0;
double columnProduct = 1.0;
double columnSumNeg = 0.0;
double columnSumSquare = 0.0;
statistics.minimum = INFINITY;
statistics.maximum = -INFINITY;
QMap frequencyOfValues;
QVector rowData;
rowData.reserve(rowValues->size());
for (int row = 0; row < rowValues->size(); ++row) {
val = rowValues->value(row);
if (std::isnan(val) || isMasked(row))
continue;
if (val < statistics.minimum)
statistics.minimum = val;
if (val > statistics.maximum)
statistics.maximum = val;
columnSum+= val;
columnSumNeg += (1.0 / val);
columnSumSquare += pow(val, 2.0);
columnProduct *= val;
if (frequencyOfValues.contains(val))
frequencyOfValues.operator [](val)++;
else
frequencyOfValues.insert(val, 1);
++notNanCount;
rowData.push_back(val);
}
if (notNanCount == 0) {
d->statisticsAvailable = true;
return;
}
if (rowData.size() < rowValues->size())
rowData.squeeze();
statistics.arithmeticMean = columnSum / notNanCount;
statistics.geometricMean = pow(columnProduct, 1.0 / notNanCount);
statistics.harmonicMean = notNanCount / columnSumNeg;
statistics.contraharmonicMean = columnSumSquare / columnSum;
double columnSumVariance = 0;
double columnSumMeanDeviation = 0.0;
double columnSumMedianDeviation = 0.0;
double sumForCentralMoment_r3 = 0.0;
double sumForCentralMoment_r4 = 0.0;
gsl_sort(rowData.data(), 1, notNanCount);
statistics.median = (notNanCount%2) ? rowData.at((int)((notNanCount-1)/2)) :
(rowData.at((int)((notNanCount-1)/2)) + rowData.at((int)(notNanCount/2)))/2.0;
QVector absoluteMedianList;
absoluteMedianList.reserve((int)notNanCount);
absoluteMedianList.resize((int)notNanCount);
int idx = 0;
for(int row = 0; row < rowValues->size(); ++row) {
val = rowValues->value(row);
if (std::isnan(val) || isMasked(row) )
continue;
columnSumVariance += pow(val - statistics.arithmeticMean, 2.0);
sumForCentralMoment_r3 += pow(val - statistics.arithmeticMean, 3.0);
sumForCentralMoment_r4 += pow(val - statistics.arithmeticMean, 4.0);
columnSumMeanDeviation += fabs( val - statistics.arithmeticMean );
absoluteMedianList[idx] = fabs(val - statistics.median);
columnSumMedianDeviation += absoluteMedianList[idx];
idx++;
}
statistics.meanDeviationAroundMedian = columnSumMedianDeviation / notNanCount;
statistics.medianDeviation = (notNanCount%2) ? absoluteMedianList.at((int)((notNanCount-1)/2)) :
(absoluteMedianList.at((int)((notNanCount-1)/2)) + absoluteMedianList.at((int)(notNanCount/2)))/2.0;
const double centralMoment_r3 = sumForCentralMoment_r3 / notNanCount;
const double centralMoment_r4 = sumForCentralMoment_r4 / notNanCount;
statistics.variance = columnSumVariance / notNanCount;
statistics.standardDeviation = sqrt(statistics.variance);
statistics.skewness = centralMoment_r3 / pow(statistics.standardDeviation, 3.0);
statistics.kurtosis = (centralMoment_r4 / pow(statistics.standardDeviation, 4.0)) - 3.0;
statistics.meanDeviation = columnSumMeanDeviation / notNanCount;
double entropy = 0.0;
for (const auto& v: frequencyOfValues) {
const double frequencyNorm = static_cast(v) / notNanCount;
entropy += (frequencyNorm * log2(frequencyNorm));
}
statistics.entropy = -entropy;
d->statisticsAvailable = true;
}
//////////////////////////////////////////////////////////////////////////////////////////////
void* Column::data() const {
return d->data();
}
/*!
* return \c true if the column has numeric values, \false otherwise.
*/
bool Column::hasValues() const {
if (columnMode() == AbstractColumn::Numeric) {
if (d->hasValuesAvailable) {
return d->hasValues;
} else {
bool foundValues = false;
for (int row = 0; row < rowCount(); ++row) {
if (!std::isnan(valueAt(row))) {
foundValues = true;
break;
}
}
d->hasValues = foundValues;
d->hasValuesAvailable = true;
return d->hasValues;
}
} else if (columnMode() == AbstractColumn::Integer)
return true; //integer column has always valid values
else
return false; //non-numerical column
}
//TODO: support all data types
/**
* \brief Return the content of row 'row'.
*
* Use this only when columnMode() is Text
*/
QString Column::textAt(int row) const {
return d->textAt(row);
}
/**
* \brief Return the date part of row 'row'
*
* Use this only when columnMode() is DateTime, Month or Day
*/
QDate Column::dateAt(int row) const {
return d->dateAt(row);
}
/**
* \brief Return the time part of row 'row'
*
* Use this only when columnMode() is DateTime, Month or Day
*/
QTime Column::timeAt(int row) const {
return d->timeAt(row);
}
/**
* \brief Return the QDateTime in row 'row'
*
* Use this only when columnMode() is DateTime, Month or Day
*/
QDateTime Column::dateTimeAt(int row) const {
return d->dateTimeAt(row);
}
/**
* \brief Return the double value in row 'row'
*/
double Column::valueAt(int row) const {
return d->valueAt(row);
}
/**
* \brief Return the int value in row 'row'
*/
int Column::integerAt(int row) const {
return d->integerAt(row);
}
/*
* call this function if the data of the column was changed directly via the data()-pointer
* and not via the setValueAt() in order to emit the dataChanged-signal.
* This is used e.g. in \c XYFitCurvePrivate::recalculate()
*/
void Column::setChanged() {
if (!m_suppressDataChangedSignal)
emit dataChanged(this);
d->statisticsAvailable = false;;
d->hasValuesAvailable = false;
}
////////////////////////////////////////////////////////////////////////////////
//@}
////////////////////////////////////////////////////////////////////////////////
/**
* \brief Return an icon to be used for decorating the views and spreadsheet column headers
*/
QIcon Column::icon() const {
return iconForMode(columnMode());
}
////////////////////////////////////////////////////////////////////////////////////////////////////
//! \name serialize/deserialize
//@{
////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* \brief Save the column as XML
*/
void Column::save(QXmlStreamWriter* writer) const {
writer->writeStartElement("column");
writeBasicAttributes(writer);
writer->writeAttribute("designation", QString::number(plotDesignation()));
writer->writeAttribute("mode", QString::number(columnMode()));
writer->writeAttribute("width", QString::number(width()));
//save the formula used to generate column values, if available
if (!formula().isEmpty() ) {
writer->writeStartElement("formula");
writer->writeTextElement("text", formula());
writer->writeStartElement("variableNames");
for (const auto& name: formulaVariableNames())
writer->writeTextElement("name", name);
writer->writeEndElement();
writer->writeStartElement("columnPathes");
for (const auto& path: formulaVariableColumnPathes())
writer->writeTextElement("path", path);
writer->writeEndElement();
writer->writeEndElement();
}
writeCommentElement(writer);
writer->writeStartElement("input_filter");
d->inputFilter()->save(writer);
writer->writeEndElement();
writer->writeStartElement("output_filter");
d->outputFilter()->save(writer);
writer->writeEndElement();
XmlWriteMask(writer);
//TODO: formula in cells is not implemented yet
// QVector< Interval > formulas = formulaIntervals();
// foreach(const Interval& interval, formulas) {
// writer->writeStartElement("formula");
// writer->writeAttribute("start_row", QString::number(interval.start()));
// writer->writeAttribute("end_row", QString::number(interval.end()));
// writer->writeCharacters(formula(interval.start()));
// writer->writeEndElement();
// }
int i;
switch(columnMode()) {
case AbstractColumn::Numeric: {
const char* data = reinterpret_cast(static_cast< QVector* >(d->data())->constData());
size_t size = d->rowCount() * sizeof(double);
writer->writeCharacters(QByteArray::fromRawData(data, (int)size).toBase64());
break;
}
case AbstractColumn::Integer: {
const char* data = reinterpret_cast(static_cast< QVector* >(d->data())->constData());
size_t size = d->rowCount() * sizeof(int);
writer->writeCharacters(QByteArray::fromRawData(data, (int)size).toBase64());
break;
}
case AbstractColumn::Text:
for (i = 0; i < rowCount(); ++i) {
writer->writeStartElement("row");
writer->writeAttribute("index", QString::number(i));
writer->writeCharacters(textAt(i));
writer->writeEndElement();
}
break;
case AbstractColumn::DateTime:
case AbstractColumn::Month:
case AbstractColumn::Day:
for (i = 0; i < rowCount(); ++i) {
writer->writeStartElement("row");
writer->writeAttribute("index", QString::number(i));
writer->writeCharacters(dateTimeAt(i).toString("yyyy-dd-MM hh:mm:ss:zzz"));
writer->writeEndElement();
}
break;
}
writer->writeEndElement(); // "column"
}
//TODO: extra header
class DecodeColumnTask : public QRunnable {
public:
DecodeColumnTask(ColumnPrivate* priv, const QString& content) {
m_private = priv;
m_content = content;
};
void run() override {
QByteArray bytes = QByteArray::fromBase64(m_content.toAscii());
if (m_private->columnMode() == AbstractColumn::Numeric) {
QVector* data = new QVector(bytes.size()/(int)sizeof(double));
memcpy(data->data(), bytes.data(), bytes.size());
m_private->replaceData(data);
} else {
QVector* data = new QVector(bytes.size()/(int)sizeof(int));
memcpy(data->data(), bytes.data(), bytes.size());
m_private->replaceData(data);
}
}
private:
ColumnPrivate* m_private;
QString m_content;
};
/**
* \brief Load the column from XML
*/
bool Column::load(XmlStreamReader* reader, bool preview) {
if (reader->isStartElement() && reader->name() != "column") {
reader->raiseError(i18n("no column element found"));
return false;
}
if (!readBasicAttributes(reader))
return false;
- QString attributeWarning = i18n("Attribute '%1' missing or empty, default value is used");
+ KLocalizedString attributeWarning = ki18n("Attribute '%1' missing or empty, default value is used");
QXmlStreamAttributes attribs = reader->attributes();
QString str = attribs.value("designation").toString();
if (str.isEmpty())
- reader->raiseWarning(attributeWarning.arg("'designation'"));
+ reader->raiseWarning(attributeWarning.subs("designation").toString());
else
d->setPlotDesignation( AbstractColumn::PlotDesignation(str.toInt()) );
str = attribs.value("mode").toString();
if (str.isEmpty())
- reader->raiseWarning(attributeWarning.arg("'mode'"));
+ reader->raiseWarning(attributeWarning.subs("mode").toString());
else
setColumnModeFast( AbstractColumn::ColumnMode(str.toInt()) );
str = attribs.value("width").toString();
if (str.isEmpty())
- reader->raiseWarning(attributeWarning.arg("'width'"));
+ reader->raiseWarning(attributeWarning.subs("width").toString());
else
d->setWidth(str.toInt());
// read child elements
while (!reader->atEnd()) {
reader->readNext();
if (reader->isEndElement()) break;
if (reader->isStartElement()) {
bool ret_val = true;
if (reader->name() == "comment")
ret_val = readCommentElement(reader);
else if (reader->name() == "input_filter")
ret_val = XmlReadInputFilter(reader);
else if (reader->name() == "output_filter")
ret_val = XmlReadOutputFilter(reader);
else if (reader->name() == "mask")
ret_val = XmlReadMask(reader);
else if (reader->name() == "formula")
ret_val = XmlReadFormula(reader);
else if (reader->name() == "row")
ret_val = XmlReadRow(reader);
else { // unknown element
reader->raiseWarning(i18n("unknown element '%1'", reader->name().toString()));
if (!reader->skipToEndElement()) return false;
}
if (!ret_val)
return false;
}
if (!preview) {
QString content = reader->text().toString().trimmed();
if (!content.isEmpty() && ( columnMode() == AbstractColumn::Numeric || columnMode() == AbstractColumn::Integer)) {
DecodeColumnTask* task = new DecodeColumnTask(d, content);
QThreadPool::globalInstance()->start(task);
}
}
}
return !reader->error();
}
/**
* \brief Read XML input filter element
*/
bool Column::XmlReadInputFilter(XmlStreamReader* reader) {
Q_ASSERT(reader->isStartElement() == true && reader->name() == "input_filter");
if (!reader->skipToNextTag()) return false;
if (!d->inputFilter()->load(reader, false)) return false;
if (!reader->skipToNextTag()) return false;
Q_ASSERT(reader->isEndElement() == true && reader->name() == "input_filter");
return true;
}
/**
* \brief Read XML output filter element
*/
bool Column::XmlReadOutputFilter(XmlStreamReader* reader) {
Q_ASSERT(reader->isStartElement() == true && reader->name() == "output_filter");
if (!reader->skipToNextTag()) return false;
if (!d->outputFilter()->load(reader, false)) return false;
if (!reader->skipToNextTag()) return false;
Q_ASSERT(reader->isEndElement() == true && reader->name() == "output_filter");
return true;
}
/**
* \brief Read XML formula element
*/
bool Column::XmlReadFormula(XmlStreamReader* reader) {
QString formula;
QStringList variableNames;
QStringList columnPathes;
while (reader->readNext()) {
if (reader->isEndElement()) break;
if (reader->name() == "text")
formula = reader->readElementText();
else if (reader->name() == "variableNames") {
while (reader->readNext()) {
if (reader->name() == "variableNames" && reader->isEndElement()) break;
if (reader->isStartElement())
variableNames << reader->readElementText();
}
} else if (reader->name() == "columnPathes") {
while (reader->readNext()) {
if (reader->name() == "columnPathes" && reader->isEndElement()) break;
if (reader->isStartElement())
columnPathes << reader->readElementText();
}
}
}
setFormula(formula, variableNames, columnPathes);
return true;
}
//TODO: read cell formula, not implemented yet
// bool Column::XmlReadFormula(XmlStreamReader* reader)
// {
// Q_ASSERT(reader->isStartElement() && reader->name() == "formula");
//
// bool ok1, ok2;
// int start, end;
// start = reader->readAttributeInt("start_row", &ok1);
// end = reader->readAttributeInt("end_row", &ok2);
// if(!ok1 || !ok2)
// {
// reader->raiseError(i18n("invalid or missing start or end row"));
// return false;
// }
// setFormula(Interval(start,end), reader->readElementText());
//
// return true;
// }
/**
* \brief Read XML row element
*/
bool Column::XmlReadRow(XmlStreamReader* reader) {
Q_ASSERT(reader->isStartElement() == true && reader->name() == "row");
// QXmlStreamAttributes attribs = reader->attributes();
bool ok;
int index = reader->readAttributeInt("index", &ok);
if (!ok) {
reader->raiseError(i18n("invalid or missing row index"));
return false;
}
QString str = reader->readElementText();
switch (columnMode()) {
case AbstractColumn::Numeric: {
double value = str.toDouble(&ok);
if(!ok) {
reader->raiseError(i18n("invalid row value"));
return false;
}
setValueAt(index, value);
break;
}
case AbstractColumn::Integer: {
int value = str.toInt(&ok);
if(!ok) {
reader->raiseError(i18n("invalid row value"));
return false;
}
setIntegerAt(index, value);
break;
}
case AbstractColumn::Text:
setTextAt(index, str);
break;
case AbstractColumn::DateTime:
case AbstractColumn::Month:
case AbstractColumn::Day:
QDateTime date_time = QDateTime::fromString(str,"yyyy-dd-MM hh:mm:ss:zzz");
setDateTimeAt(index, date_time);
break;
}
return true;
}
////////////////////////////////////////////////////////////////////////////////
//@}
////////////////////////////////////////////////////////////////////////////////
/**
* \brief Return whether the object is read-only
*/
bool Column::isReadOnly() const {
return false;
}
/**
* \brief Return the column mode
*
* This function is mostly used by spreadsheets but can also be used
* by plots. The column mode specifies how to interpret
* the values in the column additional to the data type.
*/
AbstractColumn::ColumnMode Column::columnMode() const {
return d->columnMode();
}
/**
* \brief Return the data vector size
*
* This returns the number of rows that actually contain data.
* Rows beyond this can be masked etc. but should be ignored by filters,
* plots etc.
*/
int Column::rowCount() const {
return d->rowCount();
}
/**
* \brief Return the column plot designation
*/
AbstractColumn::PlotDesignation Column::plotDesignation() const {
return d->plotDesignation();
}
AbstractSimpleFilter* Column::outputFilter() const {
return d->outputFilter();
}
/**
* \brief Return a wrapper column object used for String I/O.
*/
ColumnStringIO* Column::asStringColumn() const {
return m_string_io;
}
////////////////////////////////////////////////////////////////////////////////
//! \name IntervalAttribute related functions
//@{
////////////////////////////////////////////////////////////////////////////////
/**
* \brief Return the formula associated with row 'row'
*/
QString Column::formula(int row) const {
return d->formula(row);
}
/**
* \brief Return the intervals that have associated formulas
*
* This can be used to make a list of formulas with their intervals.
* Here is some example code:
*
* \code
* QStringList list;
* QVector< Interval > intervals = my_column.formulaIntervals();
* foreach(Interval interval, intervals)
* list << QString(interval.toString() + ": " + my_column.formula(interval.start()));
* \endcode
*/
QVector< Interval > Column::formulaIntervals() const {
return d->formulaIntervals();
}
void Column::handleFormatChange() {
DEBUG("Column::handleFormatChange() mode = " << ENUM_TO_STRING(AbstractColumn, ColumnMode, columnMode()));
if (columnMode() == AbstractColumn::DateTime) {
auto* input_filter = static_cast(d->inputFilter());
auto* output_filter = static_cast(d->outputFilter());
DEBUG("change format " << input_filter->format().toStdString() << " to " << output_filter->format().toStdString());
input_filter->setFormat(output_filter->format());
}
emit aspectDescriptionChanged(this); // the icon for the type changed
if (!m_suppressDataChangedSignal)
emit dataChanged(this); // all cells must be repainted
d->statisticsAvailable = false;;
d->hasValuesAvailable = false;
DEBUG("Column::handleFormatChange() DONE");
}
/*!
* calculates the minimal value in the column.
* for \c count = 0, the minimum of all elements is returned.
* for \c count > 0, the minimum of the first \count elements is returned.
* for \c count < 0, the minimum of the last \count elements is returned.
*/
double Column::minimum(int count) const {
double min = INFINITY;
if (count == 0 && d->statisticsAvailable)
min = const_cast(this)->statistics().minimum;
else {
ColumnMode mode = columnMode();
int start, end;
if (count == 0) {
start = 0;
end = rowCount();
} else if (count > 0) {
start = 0;
end = qMin(rowCount(), count);
} else {
start = qMax(rowCount() + count, 0);
end = rowCount();
}
switch (mode) {
case Numeric: {
QVector* vec = static_cast*>(data());
for (int row = start; row < end; ++row) {
const double val = vec->at(row);
if (std::isnan(val))
continue;
if (val < min)
min = val;
}
break;
}
case Integer: {
QVector* vec = static_cast*>(data());
for (int row = start; row < end; ++row) {
const int val = vec->at(row);
if (val < min)
min = val;
}
break;
}
case Text:
case DateTime:
case Day:
case Month:
default:
break;
}
}
return min;
}
/*!
* calculates the maximal value in the column.
* for \c count = 0, the maximum of all elements is returned.
* for \c count > 0, the maximum of the first \count elements is returned.
* for \c count < 0, the maximum of the last \count elements is returned.
*/
double Column::maximum(int count) const {
double max = -INFINITY;
if (count == 0 && d->statisticsAvailable)
max = const_cast(this)->statistics().maximum;
else {
ColumnMode mode = columnMode();
int start, end;
if (count == 0) {
start = 0;
end = rowCount();
} else if (count > 0) {
start = 0;
end = qMin(rowCount(), count);
} else {
start = qMax(rowCount() + count, 0);
end = rowCount();
}
switch (mode) {
case Numeric: {
QVector* vec = static_cast*>(data());
for (int row = start; row < end; ++row) {
const double val = vec->at(row);
if (std::isnan(val))
continue;
if (val > max)
max = val;
}
break;
}
case Integer: {
QVector* vec = static_cast*>(data());
for (int row = start; row < end; ++row) {
const int val = vec->at(row);
if (val > max)
max = val;
}
break;
}
case Text:
case DateTime:
case Day:
case Month:
default:
break;
}
}
return max;
}
diff --git a/src/backend/core/column/columncommands.cpp b/src/backend/core/column/columncommands.cpp
index 3a626d0eb..cd1b4af59 100644
--- a/src/backend/core/column/columncommands.cpp
+++ b/src/backend/core/column/columncommands.cpp
@@ -1,1200 +1,1200 @@
/***************************************************************************
File : columncommands.cpp
Project : AbstractColumn
Description : Commands to be called by Column to modify ColumnPrivate
--------------------------------------------------------------------
Copyright : (C) 2007,2008 Tilman Benkert (thzs@gmx.net)
Copyright : (C) 2010 by Knut Franke (knut.franke@gmx.de)
Copyright : (C) 2009-2017 Alexander Semke (alexander.semke@web.de)
Copyright : (C) 2017 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 "columncommands.h"
#include "ColumnPrivate.h"
-#include
+#include
#include
/** ***************************************************************************
* \class ColumnSetModeCmd
* \brief Set the column mode
** ***************************************************************************/
/**
* \var ColumnSetModeCmd::m_col
* \brief The private column data to modify
*/
/**
* \var ColumnSetModeCmd::m_old_mode
* \brief The previous mode
*/
/**
* \var ColumnSetModeCmd::m_mode
* \brief The new mode
*/
/**
* \var ColumnSetModeCmd::m_old_data
* \brief Pointer to old data
*/
/**
* \var ColumnSetModeCmd::m_new_data
* \brief Pointer to new data
*/
/**
* \var ColumnSetModeCmd::m_new_in_filter
* \brief The new input filter
*/
/**
* \var ColumnSetModeCmd::m_new_out_filter
* \brief The new output filter
*/
/**
* \var ColumnSetModeCmd::m_old_in_filter
* \brief The old input filter
*/
/**
* \var ColumnSetModeCmd::m_old_out_filter
* \brief The old output filter
*/
/**
* \var ColumnSetModeCmd::m_undone
* \brief Flag indicating whether this command has been undone (and not redone).
*/
/**
* \var ColumnSetModeCmd::m_excecuted
* \brief Flag indicating whether the command has been executed at least once.
*/
/**
* \brief Ctor
*/
ColumnSetModeCmd::ColumnSetModeCmd(ColumnPrivate* col, AbstractColumn::ColumnMode mode, QUndoCommand* parent)
: QUndoCommand(parent), m_col(col), m_mode(mode) {
setText(i18n("%1: change column type", col->name()));
m_undone = false;
m_executed = false;
}
/**
* \brief Dtor
*/
ColumnSetModeCmd::~ColumnSetModeCmd() {
if(m_undone) {
if(m_new_data != m_old_data)
switch (m_mode) {
case AbstractColumn::Numeric:
delete static_cast*>(m_new_data);
break;
case AbstractColumn::Integer:
delete static_cast*>(m_new_data);
break;
case AbstractColumn::Text:
delete static_cast*>(m_new_data);
break;
case AbstractColumn::DateTime:
case AbstractColumn::Month:
case AbstractColumn::Day:
delete static_cast*>(m_new_data);
break;
}
} else {
if(m_new_data != m_old_data)
switch (m_old_mode) {
case AbstractColumn::Numeric:
delete static_cast*>(m_old_data);
break;
case AbstractColumn::Integer:
delete static_cast*>(m_old_data);
break;
case AbstractColumn::Text:
delete static_cast*>(m_old_data);
break;
case AbstractColumn::DateTime:
case AbstractColumn::Month:
case AbstractColumn::Day:
delete static_cast*>(m_old_data);
break;
}
}
}
/**
* \brief Execute the command
*/
void ColumnSetModeCmd::redo() {
if(!m_executed) {
// save old values
m_old_mode = m_col->columnMode();
m_old_data = m_col->data();
m_old_in_filter = m_col->inputFilter();
m_old_out_filter = m_col->outputFilter();
// do the conversion
m_col->setColumnMode(m_mode);
// save new values
m_new_data = m_col->data();
m_new_in_filter = m_col->inputFilter();
m_new_out_filter = m_col->outputFilter();
m_executed = true;
} else {
// set to saved new values
m_col->replaceModeData(m_mode, m_new_data, m_new_in_filter, m_new_out_filter);
}
m_undone = false;
}
/**
* \brief Undo the command
*/
void ColumnSetModeCmd::undo() {
// reset to old values
m_col->replaceModeData(m_old_mode, m_old_data, m_old_in_filter, m_old_out_filter);
m_undone = true;
}
/** ***************************************************************************
* \class ColumnFullCopyCmd
* \brief Copy a complete column
** ***************************************************************************/
/**
* \var ColumnFullCopyCmd::m_col
* \brief The private column data to modify
*/
/**
* \var ColumnFullCopyCmd::m_src
* \brief The column to copy
*/
/**
* \var ColumnFullCopyCmd::m_backup
* \brief A backup column
*/
/**
* \var ColumnFullCopyCmd::m_backup_owner
* \brief A dummy owner for the backup column
*
* This is needed because a ColumnPrivate must have an owner. We want access
* to the ColumnPrivate object to access its data pointer for fast data
* replacement without too much copying.
*/
/**
* \brief Ctor
*/
ColumnFullCopyCmd::ColumnFullCopyCmd(ColumnPrivate* col, const AbstractColumn* src, QUndoCommand* parent)
: QUndoCommand(parent), m_col(col), m_src(src), m_backup(0), m_backup_owner(0) {
setText(i18n("%1: change cell values", col->name()));
}
/**
* \brief Dtor
*/
ColumnFullCopyCmd::~ColumnFullCopyCmd() {
delete m_backup;
delete m_backup_owner;
}
/**
* \brief Execute the command
*/
void ColumnFullCopyCmd::redo() {
if(m_backup == 0) {
m_backup_owner = new Column("temp", m_src->columnMode());
m_backup = new ColumnPrivate(m_backup_owner, m_src->columnMode());
m_backup->copy(m_col);
m_col->copy(m_src);
} else {
// swap data of orig. column and backup
void * data_temp = m_col->data();
m_col->replaceData(m_backup->data());
m_backup->replaceData(data_temp);
}
}
/**
* \brief Undo the command
*/
void ColumnFullCopyCmd::undo() {
// swap data of orig. column and backup
void * data_temp = m_col->data();
m_col->replaceData(m_backup->data());
m_backup->replaceData(data_temp);
}
/** ***************************************************************************
* \class ColumnPartialCopyCmd
* \brief Copy parts of a column
** ***************************************************************************/
/**
* \var ColumnPartialCopyCmd::m_col
* \brief The private column data to modify
*/
/**
* \var ColumnPartialCopyCmd::m_src
* \brief The column to copy
*/
/**
* \var ColumnPartialCopyCmd::m_col_backup
* \brief A backup of the original column
*/
/**
* \var ColumnPartialCopyCmd::m_src_backup
* \brief A backup of the source column
*/
/**
* \var ColumnPartialCopyCmd::m_col_backup_owner
* \brief A dummy owner for the backup column
*
* This is needed because a ColumnPrivate must have an owner and
* we must have a ColumnPrivate object as backup.
* Using a Column object as backup would lead to an inifinite loop.
*/
/**
* \var ColumnPartialCopyCmd::m_src_backup_owner
* \brief A dummy owner for the source backup column
*
* This is needed because a ColumnPrivate must have an owner and
* we must have a ColumnPrivate object as backup.
* Using a Column object as backup would lead to an inifinite loop.
*/
/**
* \var ColumnPartialCopyCmd::m_src_start
* \brief Start index in source column
*/
/**
* \var ColumnPartialCopyCmd::m_dest_start
* \brief Start index in destination column
*/
/**
* \var ColumnPartialCopyCmd::m_num_rows
* \brief Number of rows to copy
*/
/**
* \var ColumnPartialCopyCmd::m_old_row_count
* \brief Previous number of rows in the destination column
*/
/**
* \brief Ctor
*/
ColumnPartialCopyCmd::ColumnPartialCopyCmd(ColumnPrivate* col, const AbstractColumn* src, int src_start, int dest_start, int num_rows, QUndoCommand* parent)
: QUndoCommand(parent), m_col(col), m_src(src), m_col_backup(nullptr), m_src_backup(nullptr), m_col_backup_owner(nullptr), m_src_backup_owner(nullptr),
m_src_start(src_start), m_dest_start(dest_start), m_num_rows(num_rows), m_old_row_count(0) {
setText(i18n("%1: change cell values", col->name()));
}
/**
* \brief Dtor
*/
ColumnPartialCopyCmd::~ColumnPartialCopyCmd() {
delete m_src_backup;
delete m_col_backup;
delete m_src_backup_owner;
delete m_col_backup_owner;
}
/**
* \brief Execute the command
*/
void ColumnPartialCopyCmd::redo() {
if(m_src_backup == 0) {
// copy the relevant rows of source and destination column into backup columns
m_src_backup_owner = new Column("temp", m_col->columnMode());
m_src_backup = new ColumnPrivate(m_src_backup_owner, m_col->columnMode());
m_src_backup->copy(m_src, m_src_start, 0, m_num_rows);
m_col_backup_owner = new Column("temp", m_col->columnMode());
m_col_backup = new ColumnPrivate(m_col_backup_owner, m_col->columnMode());
m_col_backup->copy(m_col, m_dest_start, 0, m_num_rows);
m_old_row_count = m_col->rowCount();
}
m_col->copy(m_src_backup, 0, m_dest_start, m_num_rows);
}
/**
* \brief Undo the command
*/
void ColumnPartialCopyCmd::undo() {
m_col->copy(m_col_backup, 0, m_dest_start, m_num_rows);
m_col->resizeTo(m_old_row_count);
m_col->replaceData(m_col->data());
}
/** ***************************************************************************
* \class ColumnInsertRowsCmd
* \brief Insert empty rows
** ***************************************************************************/
/**
* \var ColumnInsertRowsCmd::m_col
* \brief The private column data to modify
*/
/**
* \brief Ctor
*/
ColumnInsertRowsCmd::ColumnInsertRowsCmd(ColumnPrivate* col, int before, int count, QUndoCommand* parent)
: QUndoCommand(parent), m_col(col), m_before(before), m_count(count) {
}
/**
* \brief Execute the command
*/
void ColumnInsertRowsCmd::redo() {
m_col->insertRows(m_before, m_count);
}
/**
* \brief Undo the command
*/
void ColumnInsertRowsCmd::undo() {
m_col->removeRows(m_before, m_count);
}
/** ***************************************************************************
* \class ColumnRemoveRowsCmd
* \brief Remove consecutive rows from a column
** ***************************************************************************/
/**
* \var ColumnRemoveRowsCmd::m_col
* \brief The private column data to modify
*/
/**
* \var ColumnRemoveRowsCmd::m_data_row_count
* \brief Number of removed rows actually containing data
*/
/**
* \var ColumnRemoveRowsCmd::m_old_size
* \brief The number of rows before the removal
*/
/**
* \var ColumnRemoveRowsCmd::m_backup
* \brief Column saving the removed rows
*/
/**
* \var ColumnRemoveRowsCmd::m_backup_owner
* \brief A dummy owner for the backup column
*
* This is needed because a ColumnPrivate must have an owner. We want access
* to the ColumnPrivate object to access its data pointer for fast data
* replacement without too much copying.
*/
/**
* \var ColumnRemoveRowsCmd::m_formulas
* \brief Backup of the formula attribute
*/
/**
* \brief Ctor
*/
ColumnRemoveRowsCmd::ColumnRemoveRowsCmd(ColumnPrivate* col, int first, int count, QUndoCommand* parent)
: QUndoCommand(parent), m_col(col), m_first(first), m_count(count),
m_data_row_count(0), m_old_size(0), m_backup(0), m_backup_owner(nullptr) {
}
/**
* \brief Dtor
*/
ColumnRemoveRowsCmd::~ColumnRemoveRowsCmd() {
delete m_backup;
delete m_backup_owner;
}
/**
* \brief Execute the command
*/
void ColumnRemoveRowsCmd::redo() {
if(m_backup == 0) {
if(m_first >= m_col->rowCount())
m_data_row_count = 0;
else if(m_first + m_count > m_col->rowCount())
m_data_row_count = m_col->rowCount() - m_first;
else
m_data_row_count = m_count;
m_old_size = m_col->rowCount();
m_backup_owner = new Column("temp", m_col->columnMode());
m_backup = new ColumnPrivate(m_backup_owner, m_col->columnMode());
m_backup->copy(m_col, m_first, 0, m_data_row_count);
m_formulas = m_col->formulaAttribute();
}
m_col->removeRows(m_first, m_count);
}
/**
* \brief Undo the command
*/
void ColumnRemoveRowsCmd::undo() {
m_col->insertRows(m_first, m_count);
m_col->copy(m_backup, 0, m_first, m_data_row_count);
m_col->resizeTo(m_old_size);
m_col->replaceFormulas(m_formulas);
}
/** ***************************************************************************
* \class ColumnSetPlotDesignationCmd
* \brief Sets a column's plot designation
** ***************************************************************************/
/**
* \var ColumnSetPlotDesignationCmd::m_col
* \brief The private column data to modify
*/
/**
* \var ColumnSetPlotDesignation::m_new_pd
* \brief New plot designation
*/
/**
* \var ColumnSetPlotDesignation::m_old_pd
* \brief Old plot designation
*/
/**
* \brief Ctor
*/
ColumnSetPlotDesignationCmd::ColumnSetPlotDesignationCmd(ColumnPrivate* col, AbstractColumn::PlotDesignation pd, QUndoCommand* parent)
: QUndoCommand(parent), m_col(col), m_new_pd(pd) {
setText(i18n("%1: set plot designation", col->name()));
}
/**
* \brief Execute the command
*/
void ColumnSetPlotDesignationCmd::redo() {
m_old_pd = m_col->plotDesignation();
m_col->setPlotDesignation(m_new_pd);
}
/**
* \brief Undo the command
*/
void ColumnSetPlotDesignationCmd::undo() {
m_col->setPlotDesignation(m_old_pd);
}
/** ***************************************************************************
* \class ColumnClearCmd
* \brief Clear the column
** ***************************************************************************/
/**
* \var ColumnClearCmd::m_col
* \brief The private column data to modify
*/
/**
* \var ColumnClearCmd::m_data
* \brief Pointer to the old data pointer
*/
/**
* \var ColumnClearCmd::m_empty_data
* \brief Pointer to an empty data vector
*/
/**
* \var ColumnClearCmd::m_undone
* \brief Status flag
*/
/**
* \brief Ctor
*/
ColumnClearCmd::ColumnClearCmd(ColumnPrivate* col, QUndoCommand* parent)
: QUndoCommand(parent), m_col(col) {
setText(i18n("%1: clear column", col->name()));
m_empty_data = 0;
m_data = 0;
m_undone = false;
}
/**
* \brief Dtor
*/
ColumnClearCmd::~ColumnClearCmd() {
if(m_undone) {
if (!m_empty_data) return;
switch(m_col->columnMode()) {
case AbstractColumn::Numeric:
delete static_cast*>(m_empty_data);
break;
case AbstractColumn::Integer:
delete static_cast*>(m_empty_data);
break;
case AbstractColumn::Text:
delete static_cast*>(m_empty_data);
break;
case AbstractColumn::DateTime:
case AbstractColumn::Month:
case AbstractColumn::Day:
delete static_cast*>(m_empty_data);
break;
}
} else {
if (!m_data) return;
switch(m_col->columnMode()) {
case AbstractColumn::Numeric:
delete static_cast*>(m_data);
break;
case AbstractColumn::Integer:
delete static_cast*>(m_data);
break;
case AbstractColumn::Text:
delete static_cast*>(m_data);
break;
case AbstractColumn::DateTime:
case AbstractColumn::Month:
case AbstractColumn::Day:
delete static_cast*>(m_data);
break;
}
}
}
/**
* \brief Execute the command
*/
void ColumnClearCmd::redo() {
if(!m_empty_data) {
const int rowCount = m_col->rowCount();
switch(m_col->columnMode()) {
case AbstractColumn::Numeric: {
QVector* vec = new QVector(rowCount);
m_empty_data = vec;
for (int i = 0; i < rowCount; ++i)
vec->operator[](i) = NAN;
break;
}
case AbstractColumn::Integer: {
QVector* vec = new QVector(rowCount);
m_empty_data = vec;
for (int i = 0; i < rowCount; ++i)
vec->operator[](i) = 0;
break;
}
case AbstractColumn::DateTime:
case AbstractColumn::Month:
case AbstractColumn::Day:
m_empty_data = new QVector();
for (int i = 0; i < rowCount; ++i)
static_cast< QVector*>(m_empty_data)->append(QDateTime());
break;
case AbstractColumn::Text:
m_empty_data = new QVector();
for (int i = 0; i < rowCount; ++i)
static_cast*>(m_empty_data)->append(QString());
break;
}
m_data = m_col->data();
}
m_col->replaceData(m_empty_data);
m_undone = false;
}
/**
* \brief Undo the command
*/
void ColumnClearCmd::undo() {
m_col->replaceData(m_data);
m_undone = true;
}
/** ***************************************************************************
* \class ColumSetGlobalFormulaCmd
* \brief Set the formula for the entire column (global formula)
** ***************************************************************************/
ColumnSetGlobalFormulaCmd::ColumnSetGlobalFormulaCmd(ColumnPrivate* col, const QString& formula, const QStringList& variableNames, const QStringList& variableColumns)
: QUndoCommand(), m_col(col), m_newFormula(formula), m_newVariableNames(variableNames), m_newVariableColumnPathes(variableColumns), m_copied(false) {
setText(i18n("%1: set formula", col->name()));
}
void ColumnSetGlobalFormulaCmd::redo() {
if(!m_copied) {
m_formula = m_col->formula();
m_variableNames = m_col->formulaVariableNames();
m_variableColumnPathes = m_col->formulaVariableColumnPathes();
m_copied = true;
}
m_col->setFormula(m_newFormula, m_newVariableNames, m_newVariableColumnPathes);
}
void ColumnSetGlobalFormulaCmd::undo() {
m_col->setFormula(m_formula, m_variableNames, m_variableColumnPathes);
}
/** ***************************************************************************
* \class ColumSetFormulaCmd
* \brief Set the formula for a given interval
** ***************************************************************************/
/**
* \var ColumnSetFormulaCmd::m_col
* \brief The private column data to modify
*/
/**
* \var ColumnSetFormulaCmd::m_interval
* \brief The interval
*/
/**
* \var ColumnSetFormulaCmd::m_formula
* \brief The new formula
*/
/**
* \var ColumnSetFormulaCmd::m_formulas
* \brief Interval attribute backup
*/
/**
* \var ColumnSetFormulaCmd::m_copied
* \brief A status flag
*/
/**
* \brief Ctor
*/
ColumnSetFormulaCmd::ColumnSetFormulaCmd(ColumnPrivate* col, const Interval& interval, const QString& formula, QUndoCommand* parent)
: QUndoCommand(parent), m_col(col), m_interval(interval), m_newFormula(formula), m_copied(false) {
setText(i18n("%1: set cell formula", col->name()));
}
void ColumnSetFormulaCmd::redo() {
if(!m_copied) {
m_formulas = m_col->formulaAttribute();
m_copied = true;
}
m_col->setFormula(m_interval, m_newFormula);
}
void ColumnSetFormulaCmd::undo() {
m_col->replaceFormulas(m_formulas);
}
/** ***************************************************************************
* \class ColumnClearFormulasCmd
* \brief Clear all associated formulas
** ***************************************************************************/
/**
* \var ColumClearFormulasCmd::m_col
* \brief The private column data to modify
*/
/**
* \var ColumnClearFormulasCmd::m_formulas
* \brief The old formulas
*/
/**
* \var ColumnClearFormulasCmd::m_copied
* \brief A status flag
*/
/**
* \brief Ctor
*/
ColumnClearFormulasCmd::ColumnClearFormulasCmd(ColumnPrivate* col, QUndoCommand* parent)
: QUndoCommand(parent), m_col(col) {
setText(i18n("%1: clear all formulas", col->name()));
m_copied = false;
}
/**
* \brief Execute the command
*/
void ColumnClearFormulasCmd::redo() {
if(!m_copied) {
m_formulas = m_col->formulaAttribute();
m_copied = true;
}
m_col->clearFormulas();
}
/**
* \brief Undo the command
*/
void ColumnClearFormulasCmd::undo() {
m_col->replaceFormulas(m_formulas);
}
/** ***************************************************************************
* \class ColumnSetTextCmd
* \brief Set the text for a string cell
** ***************************************************************************/
/**
* \var ColumnSetTextCmd::m_col
* \brief The private column data to modify
*/
/**
* \var ColumnSetTextCmd::m_row
* \brief The row to modify
*/
/**
* \var ColumnSetTextCmd::m_new_value
* \brief The new value
*/
/**
* \var ColumnSetTextCmd::m_old_value
* \brief The old value
*/
/**
* \var ColumnSetTextCmd::m_row_count
* \brief The old number of rows
*/
/**
* \brief Ctor
*/
ColumnSetTextCmd::ColumnSetTextCmd(ColumnPrivate* col, int row, const QString& new_value, QUndoCommand* parent)
: QUndoCommand(parent), m_col(col), m_row(row), m_new_value(new_value), m_row_count(0) {
setText(i18n("%1: set text for row %2", col->name(), row));
}
/**
* \brief Execute the command
*/
void ColumnSetTextCmd::redo() {
m_old_value = m_col->textAt(m_row);
m_row_count = m_col->rowCount();
m_col->setTextAt(m_row, m_new_value);
}
/**
* \brief Undo the command
*/
void ColumnSetTextCmd::undo() {
m_col->setTextAt(m_row, m_old_value);
m_col->resizeTo(m_row_count);
m_col->replaceData(m_col->data());
}
/** ***************************************************************************
* \class ColumnSetValueCmd
* \brief Set the value for a double cell
** ***************************************************************************/
/**
* \var ColumnSetValueCmd::m_col
* \brief The private column data to modify
*/
/**
* \var ColumnSetValueCmd::m_row
* \brief The row to modify
*/
/**
* \var ColumnSetValueCmd::m_new_value
* \brief The new value
*/
/**
* \var ColumnSetValueCmd::m_old_value
* \brief The old value
*/
/**
* \var ColumnSetValueCmd::m_row_count
* \brief The old number of rows
*/
/**
* \brief Ctor
*/
ColumnSetValueCmd::ColumnSetValueCmd(ColumnPrivate* col, int row, double new_value, QUndoCommand* parent)
: QUndoCommand(parent), m_col(col), m_row(row), m_new_value(new_value), m_old_value(0.0) {
setText(i18n("%1: set value for row %2", col->name(), row));
}
/**
* \brief Execute the command
*/
void ColumnSetValueCmd::redo() {
m_old_value = m_col->valueAt(m_row);
m_row_count = m_col->rowCount();
m_col->setValueAt(m_row, m_new_value);
}
/**
* \brief Undo the command
*/
void ColumnSetValueCmd::undo() {
m_col->setValueAt(m_row, m_old_value);
m_col->resizeTo(m_row_count);
m_col->replaceData(m_col->data());
}
/** ***************************************************************************
* \class ColumnSetIntegerCmd
* \brief Set the value for a int cell
** ***************************************************************************/
ColumnSetIntegerCmd::ColumnSetIntegerCmd(ColumnPrivate* col, int row, int new_value, QUndoCommand* parent)
: QUndoCommand(parent), m_col(col), m_row(row), m_new_value(new_value), m_old_value(0), m_row_count(0) {
DEBUG("ColumnSetIntegerCmd::ColumnSetIntegerCmd()");
setText(i18n("%1: set value for row %2", col->name(), row));
}
/**
* \brief Execute the command
*/
void ColumnSetIntegerCmd::redo() {
m_old_value = m_col->integerAt(m_row);
m_row_count = m_col->rowCount();
m_col->setIntegerAt(m_row, m_new_value);
}
/**
* \brief Undo the command
*/
void ColumnSetIntegerCmd::undo() {
m_col->setIntegerAt(m_row, m_old_value);
m_col->resizeTo(m_row_count);
m_col->replaceData(m_col->data());
}
/** ***************************************************************************
* \class ColumnSetDataTimeCmd
* \brief Set the value of a date-time cell
** ***************************************************************************/
/**
* \var ColumnSetDateTimeCmd::m_col
* \brief The private column data to modify
*/
/**
* \var ColumnSetDateTimeCmd::m_row
* \brief The row to modify
*/
/**
* \var ColumnSetDateTimeCmd::m_new_value
* \brief The new value
*/
/**
* \var ColumnSetDateTimeCmd::m_old_value
* \brief The old value
*/
/**
* \var ColumnSetDateTimeCmd::m_row_count
* \brief The old number of rows
*/
/**
* \brief Ctor
*/
ColumnSetDateTimeCmd::ColumnSetDateTimeCmd(ColumnPrivate* col, int row, const QDateTime& new_value, QUndoCommand* parent)
: QUndoCommand(parent), m_col(col), m_row(row), m_new_value(new_value), m_row_count(0) {
setText(i18n("%1: set value for row %2", col->name(), row));
}
/**
* \brief Execute the command
*/
void ColumnSetDateTimeCmd::redo() {
m_old_value = m_col->dateTimeAt(m_row);
m_row_count = m_col->rowCount();
m_col->setDateTimeAt(m_row, m_new_value);
}
/**
* \brief Undo the command
*/
void ColumnSetDateTimeCmd::undo() {
m_col->setDateTimeAt(m_row, m_old_value);
m_col->resizeTo(m_row_count);
m_col->replaceData(m_col->data());
}
/** ***************************************************************************
* \class ColumnReplaceTextsCmd
* \brief Replace a range of strings in a string column
** ***************************************************************************/
/**
* \var ColumnReplaceTextsCmd::m_col
* \brief The private column data to modify
*/
/**
* \var ColumnReplaceTextsCmd::m_first
* \brief The first row to replace
*/
/**
* \var ColumnReplaceTextsCmd::m_new_values
* \brief The new values
*/
/**
* \var ColumnReplaceTextsCmd::m_old_values
* \brief The old values
*/
/**
* \var ColumnReplaceTextsCmd::m_copied
* \brief Status flag
*/
/**
* \var ColumnReplaceTextsCmd::m_row_count
* \brief The old number of rows
*/
/**
* \brief Ctor
*/
ColumnReplaceTextsCmd::ColumnReplaceTextsCmd(ColumnPrivate* col, int first, const QVector& new_values, QUndoCommand* parent)
: QUndoCommand(parent), m_col(col), m_first(first), m_new_values(new_values), m_copied(false), m_row_count(0) {
setText(i18n("%1: replace the texts for rows %2 to %3", col->name(), first, first + new_values.count() - 1));
}
/**
* \brief Execute the command
*/
void ColumnReplaceTextsCmd::redo() {
if(!m_copied) {
m_old_values = static_cast*>(m_col->data())->mid(m_first, m_new_values.count());
m_row_count = m_col->rowCount();
m_copied = true;
}
m_col->replaceTexts(m_first, m_new_values);
}
/**
* \brief Undo the command
*/
void ColumnReplaceTextsCmd::undo() {
m_col->replaceTexts(m_first, m_old_values);
m_col->resizeTo(m_row_count);
m_col->replaceData(m_col->data());
}
/** ***************************************************************************
* \class ColumnReplaceValuesCmd
* \brief Replace a range of doubles in a double column
** ***************************************************************************/
/**
* \var ColumnReplaceValuesCmd::m_col
* \brief The private column data to modify
*/
/**
* \var ColumnReplaceValuesCmd::m_first
* \brief The first row to replace
*/
/**
* \var ColumnReplaceValuesCmd::m_new_values
* \brief The new values
*/
/**
* \var ColumnReplaceValuesCmd::m_old_values
* \brief The old values
*/
/**
* \var ColumnReplaceValuesCmd::m_copied
* \brief Status flag
*/
/**
* \var ColumnReplaceValuesCmd::m_row_count
* \brief The old number of rows
*/
/**
* \brief Ctor
*/
ColumnReplaceValuesCmd::ColumnReplaceValuesCmd(ColumnPrivate* col, int first, const QVector& new_values, QUndoCommand* parent)
: QUndoCommand(parent), m_col(col), m_first(first), m_new_values(new_values), m_row_count(0) {
setText(i18n("%1: replace the values for rows %2 to %3", col->name(), first, first + new_values.count() -1));
m_copied = false;
}
/**
* \brief Execute the command
*/
void ColumnReplaceValuesCmd::redo() {
if(!m_copied) {
m_old_values = static_cast