diff --git a/CalligraProducts.cmake b/CalligraProducts.cmake index 189a7dd6c1d..6d759ece513 100644 --- a/CalligraProducts.cmake +++ b/CalligraProducts.cmake @@ -1,644 +1,646 @@ ### DEFINITION OF PRODUCTS, FEATURES AND PRODUCTSETS #################################################### # When building Calligra a lot of different things are created and installed. To # describe them and their internal dependencies the concepts of "product", # "feature" and "product set" are used. # A "product" is the smallest functional unit which can be created in the build # and which is useful on its own when installed. Examples are e.g. libraries, # plugins or executables. Products have external and internal required # dependencies at build-time. Internal dependencies are noted in terms of other # products or features (see below) and could be e.g. other libraries to link # against or build tools needed to generate source files. # A product gets defined by setting an identifier, a descriptive fullname and # the needed internal build-time requirements. Any other product or feature # listed as requirement must have been defined before. # A "feature" is not a standalone product, but adds abilities to one or multiple # given products. One examples is e.g. scriptability. Features have external and # internal required dependencies at build-time. Internal dependencies are noted # in terms of other products or features and could be e.g. other libraries to # link against or build tools needed to generate source files. # A feature gets defined by setting an identifier, a descriptive fullname and # the needed internal build-time requirements. Any other product or feature # listed as requirement must have been defined before. # A "productset" is a selection of products and features which should be build # together. The products and features can be either essential or optional to the # set. If essential (REQUIRES), the whole productset will not be build if a # product or feature is missing another internal or external dependency. If # optional (OPTIONAL), the rest of the set will still be build in that case. # The products and features to include in a set can be listed directly or # indirectly: they can be named explicitely, but also by including other # productsets in a set, whose products and features will then be part of the # first set as well. # Products, features and productsets can be listed as dependencies in multiple # product sets. As with dependencies for products or features, they must have # been defined before. # Products, features and product sets are in the same namespace, so a given # identifier can be only used either for a product or for a feature or for a # product set. # The ids of products and features (but not sets) are used to generate cmake # variables SHOULD_BUILD_${ID}, which then are used to control what is build and # how. ############################################# #### Product definitions #### ############################################# # For defining new products see end of this file, "How to add another product?" # IDEA: also add headers/sdk for all the libs ("_DEVEL"?) # IDEA: note external deps for products, so they are only checked if needed # There can be required or optional external deps, required will also result # in automatic disabling of product building # TODO: some products have multiple optional requirements, but need at least one. # See APP_CONVERTER, FILEMANAGER_* # building tools calligra_define_product(BUILDTOOL_RNG2CPP "rng2cpp") # Calligra-independent utility libs calligra_define_product(LIB_KOVECTORIMAGE "libkovectorimage") # calligra libs calligra_define_product(LIB_CALLIGRA "Calligra core libs" REQUIRES BUILDTOOL_RNG2CPP) calligra_define_product(LIB_KOMAIN "Lib for one-file-per-window apps" REQUIRES LIB_CALLIGRA) calligra_define_product(LIB_KOPAGEAPP "Lib for paged documents" REQUIRES LIB_CALLIGRA LIB_KOMAIN) calligra_define_product(LIB_KOODF2 "libkoodf2" REQUIRES LIB_CALLIGRA) calligra_define_product(LIB_KOODFREADER "libkoodfreader" REQUIRES LIB_KOODF2 LIB_CALLIGRA) calligra_define_product(LIB_MSO "libmso" REQUIRES LIB_CALLIGRA) calligra_define_product(LIB_KOMSOOXML "libkomsooxml" REQUIRES LIB_CALLIGRA LIB_KOODF2 LIB_KOMAIN) # features calligra_define_feature(FEATURE_SCRIPTING UNMAINTAINED "Scripting feature") calligra_define_feature(FEATURE_RDF UNMAINTAINED "RDF feature") # plugins calligra_define_product(PLUGIN_TEXTSHAPE "Text shape plugin" REQUIRES LIB_CALLIGRA) # parts calligra_define_product(PART_WORDS "Words engine" REQUIRES LIB_CALLIGRA LIB_KOMAIN PLUGIN_TEXTSHAPE) calligra_define_product(PART_STAGE "Stage engine" REQUIRES LIB_CALLIGRA LIB_KOMAIN LIB_KOPAGEAPP) calligra_define_product(PART_SHEETS "Sheets engine" REQUIRES LIB_CALLIGRA LIB_KOMAIN) calligra_define_product(PART_QTQUICK "QtQuick Plugin that provides Calligra components" REQUIRES PART_WORDS PART_STAGE)# SHEETS_PART) calligra_define_product(PART_COMPONENTS "QtQuick2 Plugin that provides Calligra components" REQUIRES PART_WORDS PART_STAGE PART_SHEETS) # apps calligra_define_product(APP_WORDS "Words app (for Desktop)" REQUIRES PART_WORDS) calligra_define_product(APP_STAGE "Stage app (for Desktop)" UNMAINTAINED REQUIRES PART_STAGE) calligra_define_product(APP_SHEETS "Sheets app (for Desktop)" REQUIRES PART_SHEETS) calligra_define_product(APP_KARBON "Karbon app (for Desktop)" REQUIRES LIB_CALLIGRA LIB_KOMAIN) calligra_define_product(APP_FLOW "Flow app (for Desktop)" UNPORTED REQUIRES LIB_CALLIGRA LIB_KOMAIN LIB_KOPAGEAPP) calligra_define_product(APP_PLAN "Plan app (for Desktop)" REQUIRES LIB_CALLIGRA LIB_KOMAIN) calligra_define_product(APP_BRAINDUMP "Braindump app (for Desktop)" UNMAINTAINED REQUIRES LIB_CALLIGRA LIB_KOMAIN) calligra_define_product(DOC "Calligra Documentations" STAGING) # staging apps calligra_define_product(APP_GEMINI "The Calligra Gemini application" REQUIRES PART_COMPONENTS) # TODO: this needs to be split up by app products # extras calligra_define_product(APP_CONVERTER "Format converter for commandline" REQUIRES LIB_CALLIGRA LIB_KOMAIN) calligra_define_product(FILEMANAGER_PROPERTIES "Plugin for the KDE file properties dialog" REQUIRES LIB_CALLIGRA) calligra_define_product(FILEMANAGER_THUMBNAIL "Plugins for KDE filesystem thumbnailing" REQUIRES LIB_CALLIGRA LIB_KOMAIN) calligra_define_product(FILEMANAGER_QUICKPRINT "Plugin for the filemanager adding a \"Print\" action") calligra_define_product(FILEMANAGER_TEMPLATES "File templates for filemanager") calligra_define_product(OKULAR_GENERATOR_ODP "Plugin for Okular adding support for ODP" REQUIRES PART_STAGE) calligra_define_product(OKULAR_GENERATOR_ODT "Plugin for Okular adding support for ODT" REQUIRES PART_WORDS) # more plugins calligra_define_product(PLUGIN_COLORENGINES "Colorengine plugins" REQUIRES LIB_CALLIGRA) calligra_define_product(PLUGIN_MUSICSHAPE "Music shape plugin" REQUIRES LIB_CALLIGRA) calligra_define_product(PLUGIN_SPACENAVIGATOR "SpaceNavigator input plugin" REQUIRES LIB_CALLIGRA) calligra_define_product(PLUGIN_ARTISTICTEXTSHAPE "Artistic shape plugin" REQUIRES LIB_CALLIGRA) calligra_define_product(PLUGIN_DOCKERS "Default dockers plugin" REQUIRES LIB_CALLIGRA) calligra_define_product(PLUGIN_TEXTEDITING "Textediting plugins" REQUIRES LIB_CALLIGRA) calligra_define_product(PLUGIN_DEFAULTTOOLS "Default Flake tools plugin" REQUIRES LIB_CALLIGRA) calligra_define_product(PLUGIN_PATHSHAPES "Path shape plugins" REQUIRES LIB_CALLIGRA) calligra_define_product(PLUGIN_VARIABLES "Text variables plugin" REQUIRES LIB_CALLIGRA) calligra_define_product(PLUGIN_CHARTSHAPE "Chart shape plugin" REQUIRES LIB_CALLIGRA LIB_KOMAIN) calligra_define_product(PLUGIN_PICTURESHAPE "Picture shape plugin" REQUIRES LIB_CALLIGRA) calligra_define_product(PLUGIN_PLUGINSHAPE "Plugin shape plugin" REQUIRES LIB_CALLIGRA LIB_KOMAIN) calligra_define_product(PLUGIN_FORMULASHAPE "Formula shape plugin" REQUIRES LIB_CALLIGRA LIB_KOMAIN) calligra_define_product(PLUGIN_VIDEOSHAPE "Plugin for handling videos in Calligra" REQUIRES LIB_CALLIGRA) calligra_define_product(PLUGIN_VECTORSHAPE "Vectorgraphic shape plugin" REQUIRES LIB_CALLIGRA LIB_KOVECTORIMAGE) calligra_define_product(PLUGIN_SEMANTICITEMS "Semantic items plugins" REQUIRES FEATURE_RDF LIB_CALLIGRA) calligra_define_product(PLUGIN_SHAPEFILTEREFFECTS "Default shape filtereffects plugin" REQUIRES LIB_CALLIGRA) calligra_define_product(PLUGIN_STENCILSDOCKER "Stencils docker plugin" REQUIRES LIB_CALLIGRA) calligra_define_product(PLUGIN_KARBONPLUGINS "Semantic items plugins" REQUIRES LIB_CALLIGRA) calligra_define_product(PLUGIN_CALLIGRAGEMINI_GIT "Git support plugin for Calligra Gemini") # staging plugins calligra_define_product(PLUGIN_THREEDSHAPE "3D shape plugin" STAGING REQUIRES LIB_CALLIGRA) # Sheets filters calligra_define_product(FILTER_XLSX_TO_ODS "XLSX to ODS filter" REQUIRES LIB_KOMSOOXML PART_SHEETS) calligra_define_product(FILTER_XLS_TO_SHEETS "Sheets XLS import filter" REQUIRES LIB_MSO LIB_KOMSOOXML PART_SHEETS) calligra_define_product(FILTER_SHEETS_TO_XLS "Sheets XLS export filter" REQUIRES LIB_MSO LIB_KOMSOOXML PART_SHEETS) calligra_define_product(FILTER_CSV_TO_SHEETS "Sheets CSV import filter" REQUIRES PART_SHEETS) calligra_define_product(FILTER_SHEETS_TO_CSV "Sheets CSV export filter" REQUIRES PART_SHEETS) calligra_define_product(FILTER_APPLIXSPREAD_TO_KSPREAD "Applix Spreadsheet to KSpread filter" REQUIRES PART_SHEETS) calligra_define_product(FILTER_DBASE_TO_KSPREAD "dBASE to KSpread filter" REQUIRES LIB_KOMAIN) calligra_define_product(FILTER_GNUMERIC_TO_SHEETS "Sheets GNUMERIC import filter" REQUIRES PART_SHEETS) calligra_define_product(FILTER_SHEETS_TO_GNUMERIC "Sheets GNUMERIC import filter" REQUIRES PART_SHEETS) calligra_define_product(FILTER_OPENCALC_TO_SHEETS "Sheets OpenOffice.org Calc import filter" REQUIRES PART_SHEETS) calligra_define_product(FILTER_SHEETS_TO_OPENCALC "Sheets OpenOffice.org Calc export filter" REQUIRES PART_SHEETS) calligra_define_product(FILTER_QUATTROPRO_TO_SHEETS "Sheets Quattro Pro import filter" REQUIRES PART_SHEETS) calligra_define_product(FILTER_HTML_TO_ODS "HTML to ODS filter" REQUIRES LIB_KOMAIN) calligra_define_product(FILTER_SHEETS_TO_HTML "Sheets HTML export filter" REQUIRES PART_SHEETS) calligra_define_product(FILTER_KSPREAD_TO_LATEX "KSpread to LaTeX filter" REQUIRES LIB_KOMAIN) # Flow filters calligra_define_product(FILTER_VISIO_TO_ODG "Visio to ODG filter" REQUIRES LIB_KOMAIN) calligra_define_product(FILTER_WPG_TO_ODG "WPG to ODG filter" REQUIRES LIB_KOMAIN) # Stage filters calligra_define_product(FILTER_KEY_TO_ODP "Apple Keynote to ODP filter" REQUIRES LIB_KOMAIN) calligra_define_product(FILTER_KPR_TO_ODP "KPresenter to ODP filter" REQUIRES LIB_KOMAIN) calligra_define_product(FILTER_PPT_TO_ODP "PPT to OPD filter" REQUIRES LIB_MSO LIB_KOMAIN) calligra_define_product(FILTER_PPTX_TO_ODP "PPTX to ODP filter" REQUIRES LIB_KOMSOOXML LIB_KOODF2 LIB_KOMAIN) # Words filters calligra_define_product(FILTER_DOC_TO_ODT "DOC to ODT filter" REQUIRES LIB_MSO LIB_KOMSOOXML LIB_KOMAIN) calligra_define_product(FILTER_DOCX_TO_ODT "DOCX to ODT filter" REQUIRES LIB_KOMSOOXML LIB_KOODF2 LIB_KOMAIN) calligra_define_product(FILTER_ODT_TO_DOCX "ODT to DOCX filter" REQUIRES LIB_KOODFREADER LIB_KOODF2 LIB_KOMAIN) calligra_define_product(FILTER_WORDPERFECT_TO_ODT "Word Perfect to ODT filter" REQUIRES LIB_KOMAIN) calligra_define_product(FILTER_WORKS_TO_ODT "MS Works to ODT filter" REQUIRES LIB_KOMAIN) calligra_define_product(FILTER_APPLIXWORD_TO_ODT "Applixword to ODT filter" REQUIRES LIB_KOMAIN) calligra_define_product(FILTER_ASCII_TO_WORDS "Words ASCII import filter" REQUIRES PART_WORDS LIB_KOODF2 LIB_KOMAIN) calligra_define_product(FILTER_ODT_TO_ASCII "ODT to ASCII filter" REQUIRES LIB_KOODFREADER LIB_KOMAIN) calligra_define_product(FILTER_RTF_TO_ODT "RTF to ODT filter" REQUIRES LIB_KOMAIN) calligra_define_product(FILTER_ODT_TO_MOBI "Mobi export filter" REQUIRES LIB_KOMAIN) calligra_define_product(FILTER_ODT_TO_EPUB2 "ODT Epub2 export filter" REQUIRES LIB_KOVECTORIMAGE LIB_KOMAIN) calligra_define_product(FILTER_ODT_TO_HTML "ODT HTML export filter" REQUIRES LIB_KOVECTORIMAGE LIB_KOMAIN) calligra_define_product(FILTER_ODT_TO_WIKI "ODT Wiki export filter" REQUIRES LIB_KOODFREADER LIB_KOODF2 LIB_KOMAIN) # Karbon filters calligra_define_product(FILTER_EPS_TO_SVG_AI "EPS to SVG/AI filter" REQUIRES LIB_KOMAIN) calligra_define_product(FILTER_XFIG_TO_ODG "XFig to ODG filter" REQUIRES LIB_KOMAIN) calligra_define_product(FILTER_PDF_TO_SVG "PDF to SVG filter" REQUIRES LIB_KOMAIN) calligra_define_product(FILTER_WPG_TO_SVG "WPG to SVG filter" REQUIRES LIB_KOMAIN) calligra_define_product(FILTER_KARBON_TO_IMAGE "Karbon image export filter" REQUIRES APP_KARBON) calligra_define_product(FILTER_KARBON_TO_SVG "Karbon SVG export filter" REQUIRES APP_KARBON) calligra_define_product(FILTER_SVG_TO_KARBON "Karbon SVG import filter" REQUIRES APP_KARBON) calligra_define_product(FILTER_KARBON_TO_WMF "Karbon WMF export filter" REQUIRES APP_KARBON) calligra_define_product(FILTER_WMF_TO_SVG "WMF to SVG filter" REQUIRES LIB_KOVECTORIMAGE LIB_KOMAIN) calligra_define_product(FILTER_KARBON1X_TO_KARBON "Karbon 1.x import filter" REQUIRES APP_KARBON) # meta apps calligra_define_product(APP_CALLIGRA "General Calligra app starter" REQUIRES LIB_CALLIGRA LIB_KOMAIN) # more extras calligra_define_product(OKULAR_GENERATOR_PPT "Plugin for Okular extended with support for PPT" REQUIRES OKULAR_GENERATOR_ODP FILTER_PPT_TO_ODP) calligra_define_product(OKULAR_GENERATOR_PPTX "Plugin for Okular extended with support for PPTX" REQUIRES OKULAR_GENERATOR_ODP FILTER_PPTX_TO_ODP) calligra_define_product(OKULAR_GENERATOR_DOC "Plugin for Okular extended with support for DOC" REQUIRES OKULAR_GENERATOR_ODT FILTER_DOC_TO_ODT) calligra_define_product(OKULAR_GENERATOR_DOCX "Plugin for Okular extended with support for DOCX" REQUIRES OKULAR_GENERATOR_ODT FILTER_DOCX_TO_ODT) +calligra_define_product(OKULAR_GENERATOR_RTF "Plugin for Okular extended with support for RTF" REQUIRES OKULAR_GENERATOR_ODT FILTER_RTF_TO_ODT) calligra_define_product(OKULAR_GENERATOR_WORDPERFECT "Plugin for Okular extended with support for WORDPERFECT" REQUIRES OKULAR_GENERATOR_ODT FILTER_WORDPERFECT_TO_ODT) # developer utils calligra_define_product(APP_SLIDECOMPARE "slidecompare" REQUIRES LIB_CALLIGRA LIB_KOMAIN FILTER_PPT_TO_ODP) calligra_define_product(APP_DEVTOOLS "Tools for developers") calligra_define_product(APP_CSTESTER "cstester" REQUIRES PART_SHEETS PART_STAGE PART_WORDS) # development calligra_define_product(DEVEL_HEADERS "Headers of libraries" UNPORTED) ############################################# #### Product set definitions #### ############################################# # For defining new productsets see end of this file, # "How to add another productset?" # filter sets calligra_define_productset(FILTERS_SHEETS_IMPORT "All Sheets import filters" OPTIONAL FILTER_XLSX_TO_ODS FILTER_XLS_TO_SHEETS FILTER_CSV_TO_SHEETS FILTER_APPLIXSPREAD_TO_KSPREAD FILTER_DBASE_TO_KSPREAD FILTER_GNUMERIC_TO_SHEETS FILTER_OPENCALC_TO_SHEETS FILTER_QUATTROPRO_TO_SHEETS FILTER_HTML_TO_ODS ) calligra_define_productset(FILTERS_SHEETS_EXPORT "All Sheets export filters" OPTIONAL FILTER_SHEETS_TO_XLS FILTER_SHEETS_TO_CSV FILTER_SHEETS_TO_GNUMERIC FILTER_SHEETS_TO_OPENCALC FILTER_SHEETS_TO_HTML FILTER_KSPREAD_TO_LATEX ) calligra_define_productset(FILTERS_SHEETS "All Sheets filters" OPTIONAL FILTERS_SHEETS_IMPORT FILTERS_SHEETS_EXPORT ) calligra_define_productset(FILTERS_FLOW_IMPORT "All Flow import filters" OPTIONAL FILTER_VISIO_TO_ODG FILTER_WPG_TO_ODG ) #calligra_define_productset(FILTERS_FLOW_EXPORT "All Flow export filters" OPTIONAL ) noone currently calligra_define_productset(FILTERS_FLOW "All Flow filters" OPTIONAL FILTERS_FLOW_IMPORT # FILTERS_FLOW_EXPORT ) calligra_define_productset(FILTERS_STAGE_IMPORT "All Stage import filters" OPTIONAL FILTER_KEY_TO_ODP FILTER_KPR_TO_ODP FILTER_PPT_TO_ODP FILTER_PPTX_TO_ODP ) #calligra_define_productset(FILTERS_STAGE_EXPORT "All Stage export filters" OPTIONAL ) noone currently calligra_define_productset(FILTERS_STAGE "All Stage filters" OPTIONAL FILTERS_STAGE_IMPORT # FILTERS_STAGE_EXPORT ) calligra_define_productset(FILTERS_WORDS_IMPORT "All Words import filters" OPTIONAL FILTER_DOC_TO_ODT FILTER_DOCX_TO_ODT FILTER_WORDPERFECT_TO_ODT FILTER_WORKS_TO_ODT FILTER_APPLIXWORD_TO_ODT FILTER_ASCII_TO_WORDS FILTER_RTF_TO_ODT ) calligra_define_productset(FILTERS_WORDS_EXPORT "All Words export filters" OPTIONAL FILTER_ODT_TO_ASCII FILTER_ODT_TO_MOBI FILTER_ODT_TO_EPUB2 FILTER_ODT_TO_HTML FILTER_ODT_TO_DOCX FILTER_ODT_TO_WIKI ) calligra_define_productset(FILTERS_WORDS "All Words filters" OPTIONAL FILTERS_WORDS_IMPORT FILTERS_WORDS_EXPORT ) calligra_define_productset(FILTERS_KARBON_IMPORT "All Karbon import filters" OPTIONAL FILTER_EPS_TO_SVG_AI FILTER_XFIG_TO_ODG FILTER_PDF_TO_SVG FILTER_WPG_TO_SVG FILTER_SVG_TO_KARBON FILTER_WMF_TO_SVG FILTER_KARBON1X_TO_KARBON ) calligra_define_productset(FILTERS_KARBON_EXPORT "All Karbon export filters" OPTIONAL FILTER_KARBON_TO_IMAGE FILTER_KARBON_TO_SVG FILTER_KARBON_TO_WMF ) calligra_define_productset(FILTERS_KARBON "All Karbon filters" OPTIONAL FILTERS_KARBON_IMPORT FILTERS_KARBON_EXPORT ) # filemanager calligra_define_productset(FILEMANAGER "Extensions for the filemanager" OPTIONAL FILEMANAGER_PROPERTIES FILEMANAGER_QUICKPRINT FILEMANAGER_TEMPLATES FILEMANAGER_THUMBNAIL ) # apps calligra_define_productset(BRAINDUMP "Full Braindump (for Desktop)" REQUIRES APP_BRAINDUMP OPTIONAL # plugins PLUGIN_ARTISTICTEXTSHAPE PLUGIN_CHARTSHAPE PLUGIN_DEFAULTTOOLS PLUGIN_DOCKERS PLUGIN_FORMULASHAPE PLUGIN_MUSICSHAPE PLUGIN_PATHSHAPES PLUGIN_PICTURESHAPE PLUGIN_PLUGINSHAPE PLUGIN_TEXTEDITING PLUGIN_TEXTSHAPE PLUGIN_THREEDSHAPE PLUGIN_VARIABLES PLUGIN_VECTORSHAPE PLUGIN_VIDEOSHAPE ) calligra_define_productset(FLOW "Full Flow (for Desktop)" REQUIRES APP_FLOW OPTIONAL # extras FILEMANAGER # plugins PLUGIN_ARTISTICTEXTSHAPE PLUGIN_CHARTSHAPE PLUGIN_DEFAULTTOOLS PLUGIN_DOCKERS PLUGIN_FORMULASHAPE PLUGIN_PATHSHAPES PLUGIN_PICTURESHAPE PLUGIN_PLUGINSHAPE PLUGIN_TEXTEDITING PLUGIN_TEXTSHAPE PLUGIN_VARIABLES PLUGIN_VECTORSHAPE # filters FILTERS_FLOW ) calligra_define_productset(KARBON "Full Karbon (for Desktop)" REQUIRES APP_KARBON PLUGIN_KARBONPLUGINS PLUGIN_STENCILSDOCKER PLUGIN_SHAPEFILTEREFFECTS OPTIONAL # extras FILEMANAGER # plugins PLUGIN_ARTISTICTEXTSHAPE PLUGIN_CHARTSHAPE PLUGIN_DEFAULTTOOLS PLUGIN_DOCKERS PLUGIN_FORMULASHAPE PLUGIN_PATHSHAPES PLUGIN_PICTURESHAPE PLUGIN_PLUGINSHAPE PLUGIN_TEXTEDITING PLUGIN_TEXTSHAPE PLUGIN_VARIABLES PLUGIN_VECTORSHAPE # filters FILTERS_KARBON ) calligra_define_productset(PLAN "Full Plan (for Desktop)" REQUIRES APP_PLAN OPTIONAL FEATURE_SCRIPTING ) calligra_define_productset(SHEETS "Full Sheets (for Desktop)" REQUIRES APP_SHEETS OPTIONAL # extras FILEMANAGER # feature FEATURE_SCRIPTING # plugins PLUGIN_ARTISTICTEXTSHAPE PLUGIN_CHARTSHAPE PLUGIN_DEFAULTTOOLS PLUGIN_DOCKERS PLUGIN_FORMULASHAPE PLUGIN_PATHSHAPES PLUGIN_PICTURESHAPE PLUGIN_PLUGINSHAPE PLUGIN_TEXTEDITING PLUGIN_TEXTSHAPE PLUGIN_VARIABLES PLUGIN_VECTORSHAPE # filters FILTERS_SHEETS ) calligra_define_productset(STAGE "Full Stage (for Desktop)" REQUIRES APP_STAGE OPTIONAL # extras FILEMANAGER # plugins PLUGIN_ARTISTICTEXTSHAPE PLUGIN_CHARTSHAPE PLUGIN_DEFAULTTOOLS PLUGIN_DOCKERS PLUGIN_FORMULASHAPE PLUGIN_PATHSHAPES PLUGIN_PICTURESHAPE PLUGIN_PLUGINSHAPE PLUGIN_TEXTEDITING PLUGIN_TEXTSHAPE PLUGIN_VARIABLES PLUGIN_VECTORSHAPE PLUGIN_VIDEOSHAPE # filters FILTERS_STAGE ) calligra_define_productset(WORDS "Full Words (for Desktop)" REQUIRES APP_WORDS OPTIONAL # extras FILEMANAGER # plugins PLUGIN_ARTISTICTEXTSHAPE PLUGIN_CHARTSHAPE PLUGIN_DEFAULTTOOLS PLUGIN_DOCKERS PLUGIN_FORMULASHAPE PLUGIN_PATHSHAPES PLUGIN_PICTURESHAPE PLUGIN_PLUGINSHAPE PLUGIN_SEMANTICITEMS PLUGIN_TEXTEDITING PLUGIN_TEXTSHAPE PLUGIN_VARIABLES PLUGIN_VECTORSHAPE # filters FILTERS_WORDS ) calligra_define_productset(GEMINI "Calligra for 2:1 devices" REQUIRES APP_GEMINI OPTIONAL # plugins PLUGIN_ARTISTICTEXTSHAPE PLUGIN_CALLIGRAGEMINI_GIT PLUGIN_CHARTSHAPE PLUGIN_DEFAULTTOOLS PLUGIN_DOCKERS PLUGIN_FORMULASHAPE PLUGIN_PATHSHAPES PLUGIN_PICTURESHAPE PLUGIN_PLUGINSHAPE PLUGIN_TEXTEDITING PLUGIN_TEXTSHAPE PLUGIN_VARIABLES PLUGIN_VECTORSHAPE PLUGIN_VIDEOSHAPE # filters FILTERS_WORDS FILTERS_STAGE ) # okular support calligra_define_productset(OKULAR "Okular generators" OPTIONAL OKULAR_GENERATOR_ODP OKULAR_GENERATOR_PPT OKULAR_GENERATOR_PPTX OKULAR_GENERATOR_ODT OKULAR_GENERATOR_DOC OKULAR_GENERATOR_DOCX + OKULAR_GENERATOR_RTF OKULAR_GENERATOR_WORDPERFECT ) # How to add another product? # =========================== # # 1. Define the product by a call of calligra_define_product, # e.g. # # calligra_define_product(MYPRODUCT "title of product") # # For the product id use a proper prefix (LIB_, PLUGIN_, FILTER_, APP_, PART_, # ...), whatever is appropriate. # # 2. Extend that call with a REQUIRES argument section, if the product has # hard internal build-time dependencies on other products or features. # Products/features that are listed as dependencies have to be defined before # (see also the API doc in cmake/modules/CalligraProductSetMacros.cmake) # E.g. # # calligra_define_product(MYPRODUCT "title of product" REQUIRES P1 P2) # # 3. Add a rule when to not build the product, in the section "Detect which # products/features can be compiled" of the toplevel CMakeLists.txt. Each # product should have their own boolean expression when to set the build flag # to FALSE, e.g. # # if (PLATFORMX OR NOT EXTERNAL_DEP_X_FOUND) # set(SHOULD_BUILD_MYPRODUCT FALSE) # endif () # # 4. Wrap everything belonging to the product with the build flag of the product. # Ideally this is done around subdirectory inclusions, results in easier code. # e.g. # # if (SHOULD_BUILD_MYPRODUCT) # add_subdirectory(myproduct) # endif () # # 5. Tag the product as STAGING, if it is not yet ready for release, but already # integrated in the master branch, e.g. # # calligra_define_product(MYPRODUCT "title of product" STAGING REQUIRES P1) # # 6. Add the product to all products, features and product sets which have this # product as REQUIRED or OPTIONAL dependency. # # # How to add another feature? # =========================== # # 1. Define the feature by a call of calligra_define_feature, # e.g. # # calligra_define_feature(MYFEATURE "title of feature") # # For the feature id use a proper prefix (FEATURE_, ...), whatever is # appropriate. # # 2. Extend that call with a REQUIRES argument section, if the feature has # hard internal build-time dependencies on other products or features. # Products or features that are listed as dependencies have to be defined # before # (see also the API doc in cmake/modules/CalligraProductSetMacros.cmake) # E.g. # # calligra_define_feature(MYFEATURE "title of feature" REQUIRES P1 F1) # # 3. Add a rule when to not build the feature, in the section "Detect which # products/features can be compiled" of the toplevel CMakeLists.txt. Each # feature should have their own boolean expression when to set the build flag # to FALSE, e.g. # # if (PLATFORMX OR NOT EXTERNAL_DEP_X_FOUND) # set(SHOULD_BUILD_MYFEATURE FALSE) # endif () # # 4. Wrap everything belonging to the feature with the build flag of the feature. # Ideally this is done around subdirectory inclusions, results in easier code. # e.g. # # if (SHOULD_BUILD_MYFEATURE) # add_subdirectory(myproduct) # endif () # # 5. Tag the feature as STAGING, if it is not yet ready for release, but already # integrated in the master branch, e.g. # # calligra_define_product(MYFEATURE "title of feature" STAGING REQUIRES P1 F1) # # 6. Add the feature to all products, features and product sets which have this # product as REQUIRED or OPTIONAL dependency. # # # How to add another productset? # ============================== # # There are two possible places to put a productset definition. The first is to # add it to this file, which should be done for more generic sets that are # useful for many people. The second is a file of its own, in the directory # "cmake/productsets", which should be done for more special ones or for those # which should not be added to the repository. # The file must be named with the name of the productset in lowercase and have # the extension ".cmake". # # 1. Define the productset by a call of calligra_define_productset, # e.g. # # calligra_define_productset(MYPRODUCTSET "title of productset") # # 2. Extend that call with REQUIRES or OPTIONAL argument sections, if the productset # has hard or soft internal dependencies on other products, features or # productsets. # Products, features or productsets that are listed as dependencies have to # be defined before # (see also the API doc in cmake/modules/CalligraProductSetMacros.cmake) # E.g. # # calligra_define_productset(MYPRODUCT "title of product" # REQUIRES P1 P2 F1 PS1 # OPTIONAL P3 F2 PS2) # # 3. Add the productset to all product sets which have this product set as # REQUIRED or OPTIONAL dependency. # # Example for a file-based productset definition: # You want a productset "MYWORDS". For that you add a file named # "mywords.cmake" into the directory "cmake/productsets", with the content: # --- 8< --- # calligra_define_productset(MYWORDS "My Words" # REQUIRES # APP_WORDS # PLUGIN_DEFAULTTOOLS # PLUGIN_DOCKERS # PLUGIN_PATHSHAPES # PLUGIN_VARIABLES # PLUGIN_TEXTSHAPE # PLUGIN_PLUGINSHAPE # PLUGIN_FORMULASHAPE # ) # --- 8< --- diff --git a/README.PACKAGERS b/README.PACKAGERS index 804b9142b56..633e028d7a4 100644 --- a/README.PACKAGERS +++ b/README.PACKAGERS @@ -1,201 +1,201 @@ Packaging Information for Calligra Suite ---------------------------------------- We recommend building several binary packages from the Calligra source. Splitting Calligra into packages: * gives users a better choice of which components they have installed; * allows users to install just the applications without unnecessary dependencies; * helps to reduce packaging conflicts for users with non-standard package selections. Table Of Contents ----------------- 1. Recommended cmake flags 2. Calligra plugins 3. Calligra libraries 4. IMPORTANT On using CPU vector capabilities in Calligra Libs 5. Okular plugins 5.1. Runtime dependency of the Okular ODP plugin 5.2. Support for PPT, PPTX and more runtime dependency 5.3. Support for DOC, DOCX, WPD and more runtime dependency 6. Adding unmaintained applications/modules 1. Recommended cmake flags ========================== For alpha and beta packages, please build with debug output enabled, but for production packages the -DCMAKE_CXX_FLAGS="-DKDE_NO_DEBUG_OUTPUT" is recommended. A significant performance increase will be the result. All modules may not be ready for production release. It is recommended to specify -DRELEASE_BUILD=true to exclude these modules. (If you want these packages to be included anyway, specify -DRELEASE_BUILD=false.) NOTE: If RELEASE_BUILD is not set, CMAKE_BUILD_TYPE is considered. If it is set to Release, RelWithDebInfo or MinSizeRel, the modules are also excluded. 2. Calligra plugins =================== Calligra applications can use plugins, placed in plugins/ subdirectory. Some of them are very special and/or have extra dependencies so should be packaged separately. Suggested Name Details ------------------------------------------------------------------------ calligra-semanticitems Description: Calligra semantic item plugins Contents: calligra_semanticitem_contact.so calligra_semanticitem_contact.desktop calligra_semanticitem_event.so calligra_semanticitem_event.desktop calligra_semanticitem_location.so calligra_semanticitem_location.desktop Translation File: calligra_semanticitem_contact.mo calligra_semanticitem_contact.mo calligra_semanticitem_location.mo Dependencies: libkdepimlibs (kabc, kcalcore, akonadi) libmarblewidget-qt5 or marble (provides libmarblewidget) If Calligra is build with support for RDF enabled, it is STRONGLY RECOMMENDED to separate the semantic item plugins coming from "plugins/semanticitems" into their own package. That package should not be an automatic dependency on any other Calligra package, but only installed explicitely by the user. Reason are the external dependencies with these plugins, especially kdepimlibs. So if e.g. a user just installs the Braindump package, this should not pull in all the things that KDEPIM/Akonadi pulls in. 3. Calligra libraries ===================== Calligra share common functionality within libraries, placed in libs/ subdirectory. Calligra libraries should be placed in a single package, separate from Calligra applications. Below is the list of the libraries. Name Conditional compilation flag Globally used in Calligra? Public API (headers installed) (default: NO) (default: YES) (default: YES) ------------------------------------------------------------------------------------------ flake kokross NO kopageapp koplugin NO kotext komain koodf kordf Words/Author pigment kowidgets 4. IMPORTANT On using CPU vector capabilities in Calligra Libs ======================================================================== IN BRIEF: 1) Intall Vc library [1] and don't forget to activate PACKAGERS_BUILD=ON option when building a package. 2) Vc libary should be present on the building system only, it need not be installed on all the client systems. Pigment can make use of the vector capabilities of the user's CPU. To make it possible Vc library [1] should be present in the host system. This is a static library and fully included into the final Pigment binary, so it is not necessary to have it installed in the client system. The code generation is generally controlled by two factors: the presence of the Vc library and a special cmake option 'PACKAGERS_BUILD'. Consider three cases: 1) Vc library is not present. PACKAGERS_BUILD=. The calligra is build with default compiller options. The resulting binary is non-optimized and portable among different CPU architectures. 2) Vc library is present. PACKAGERS_BUILD=OFF (default). All the calligra binaries are optimized for the host CPU. This is the most efficient type of build of Calligra. But be careful, because such binaries are not portable among different CPU architectures! Using this build for packages distributed to many users will most probably result in SIGILL crashes on the client system. Use this option for private builds only. 3) Vc library is present. PACKAGERS_BUILD=ON. This option disables CPU optimizations for the most of Calligra, but gnerates several versions of the code for its hottest parts. The specific implementation of the code is chosen on the fly when Calligra starts. This version is a bit slower than 2) but much faster than 1) and is *portable* among all the CPU architectures. Use this type of build for building distributable packages. [1] - http://code.compeng.uni-frankfurt.de/projects/vc 5. Okular plugins ================= 5.1. Runtime dependency of the Okular ODP plugin ------------------------------------------------ OKULAR_GENERATOR_ODP, the ODP plugin for Okular, in extras/okularodpgenerator, has a runtime dependency on the Stage engine, PART_STAGE. 5.2. Support for PPT, PPTX and more runtime dependency ------------------------------------------------------ The Okular ODP plugin also supports the formats PPT and PPTX, if the respective filters FILTER_PPT_TO_ODP and FILTER_PPTX_TO_ODP are built, by the products OKULAR_GENERATOR_PPT and OKULAR_GENERATOR_PPTX. In that case the files * libokularGenerator_{powerpoint,pptx}_calligra.desktop * okular{Powerpoint,Pptx}_calligra.desktop * okularApplication_{powerpoint,pptx}_calligra.desktop will be installed from the filters/stage/{powerpoint,pptx}. So with those desktop files and the Okular ODP plugin installed there is a runtime dependency on the Stage engine and the respective filters. 5.3. Support for DOC, DOCX, WPD and more runtime dependency ----------------------------------------------------------- -The Okular ODT plugin also supports the formats DOC, DOCX and WPD, if the -respective filters FILTER_DOC_TO_ODT, FILTER_DOCX_TO_ODT and -FILTER_WORDPERFECT_TO_ODT are built, by the products OKULAR_GENERATOR_DOC, -OKULAR_GENERATOR_DOCX and OKULAR_GENERATOR_WORDPERFECT. +The Okular ODT plugin also supports the formats DOC, DOCX, RTF and WPD, if the +respective filters FILTER_DOC_TO_ODT, FILTER_DOCX_TO_ODT, FILTER_RTF_TO_ODT +and FILTER_WORDPERFECT_TO_ODT are built, by the products OKULAR_GENERATOR_DOC, +OKULAR_GENERATOR_DOCX, OKULAR_GENERATOR_RTF and OKULAR_GENERATOR_WORDPERFECT. In that case the files -* libokularGenerator_{doc,docx,wpd}_calligra.desktop -* okular{Doc,Doxx,Wpd}_calligra.desktop -* okularApplication_{doc,docx,wpd}_calligra.desktop -will be installed from the filters/word/{msword-odf,docx/import,wordperfect/import}. +* libokularGenerator_{doc,docx,rtf,wpd}_calligra.desktop +* okular{Doc,Doxx,Rtf,Wpd}_calligra.desktop +* okularApplication_{doc,docx,rtf,wpd}_calligra.desktop +will be installed from the filters/word/{msword-odf,docx/import,rtf/import,wordperfect/import}. So with those desktop files and the Okular ODT plugin installed there is a runtime dependency on the respective filters. 6. Adding unmaintained applications/modules =========================================== A few applications and modules do not have maintainers. So there is noone to handle bugs experienced by users or discuss new features/improvements. Still those applications & modules are usable for certain needs. If you want to provide the users of your packages also with those unmaintained applications and modules, you will need to create a custom patch to the file CalligraProducts.cmake and remove the respective "UNMAINTAINED" tags from the product definitions. When doing so, please state the unmaintained state in the package description, so the users are aware about the lack of support at the current time. diff --git a/extras/okularodpgenerator/okularApplication_odp_calligra.desktop b/extras/okularodpgenerator/okularApplication_odp_calligra.desktop index 78a15f8ec17..c3786e27e9f 100644 --- a/extras/okularodpgenerator/okularApplication_odp_calligra.desktop +++ b/extras/okularodpgenerator/okularApplication_odp_calligra.desktop @@ -1,95 +1,95 @@ [Desktop Entry] MimeType=application/vnd.oasis.opendocument.presentation;application/vnd.oasis.opendocument.presentation-template; Terminal=false Name=okular Name[bg]=okular Name[bs]=okular Name[ca]=Okular Name[ca@valencia]=Okular Name[cs]=Okular Name[da]=okular Name[de]=Okular Name[el]=okular Name[en_GB]=okular Name[es]=okular Name[et]=Okular Name[eu]=Okular Name[fi]=okular Name[fr]=Okular Name[ga]=okular Name[gl]=Okular Name[hu]=Okular Name[ia]=Okular Name[is]=okular Name[it]=Okular Name[ja]=Okular Name[kk]=Okular Name[ko]=okular Name[lt]=okular Name[mr]=ओक्युलर Name[nb]=okular Name[nds]=Okular Name[nl]=okular Name[pl]=okular Name[pt]=okular Name[pt_BR]=Okular Name[ru]=Okular Name[sk]=okular Name[sl]=okular Name[sv]=okular Name[tr]=okular Name[ug]=okular Name[uk]=Okular Name[wa]=okular Name[x-test]=xxokularxx Name[zh_CN]=Okular Name[zh_TW]=okular GenericName=Document Viewer GenericName[bg]=Преглед на доументи GenericName[bs]=Preglednik Dokumenta GenericName[ca]=Visualitzador de documents GenericName[ca@valencia]=Visualitzador de documents GenericName[cs]=Prohlížeč dokumentů GenericName[da]=Dokumentfremviser GenericName[de]=Dokumentenbetrachter GenericName[el]=Προβολέας εγγράφων GenericName[en_GB]=Document Viewer GenericName[eo]=Dokumenta rigardilo GenericName[es]=Visor de documentos GenericName[et]=Dokumendinäitaja GenericName[eu]=Dokumentu-ikustailea GenericName[fi]=Asiakirjakatselin GenericName[fr]=Afficheur de documents GenericName[ga]=Amharcán Cáipéisí GenericName[gl]=Visualizador de documentos GenericName[hu]=Dokumentummegjelenítő GenericName[ia]=Visor de documento GenericName[it]=Visore di documenti GenericName[ja]=文書ビューア GenericName[kk]=Құжатты қарау құралы GenericName[ko]=문서 뷰어 GenericName[lt]=Dokumentų žiūryklė GenericName[mr]=दस्तऐवज प्रदर्शक GenericName[nb]=Dokumentviser GenericName[nds]=Dokmentkieker GenericName[nl]=Documentenviewer GenericName[pl]=Przeglądarka dokumentów GenericName[pt]=Visualizador de Documentos GenericName[pt_BR]=Visualizador de documentos GenericName[ru]=Просмотр документов GenericName[sk]=Prehliadač dokumentov GenericName[sl]=Pregledovalnik dokumentov GenericName[sv]=Dokumentvisare GenericName[tr]=Belge Görüntüleyici GenericName[ug]=پۈتۈك كۆرگۈ GenericName[uk]=Перегляд документів GenericName[wa]=Håyneu di documints GenericName[x-test]=xxDocument Viewerxx GenericName[zh_CN]=文档查看器 GenericName[zh_TW]=文件檢視器 -Exec=okular %U %i -caption %c +Exec=okular %U %i -qwindowtitle %c Icon=okular Type=Application InitialPreference=3 Categories=Qt;KDE;Graphics;Viewer; NoDisplay=true diff --git a/extras/okularodtgenerator/okularApplication_odt_calligra.desktop b/extras/okularodtgenerator/okularApplication_odt_calligra.desktop index fcccab622a3..12cf536a507 100644 --- a/extras/okularodtgenerator/okularApplication_odt_calligra.desktop +++ b/extras/okularodtgenerator/okularApplication_odt_calligra.desktop @@ -1,95 +1,95 @@ [Desktop Entry] MimeType=application/vnd.oasis.opendocument.text;application/vnd.oasis.opendocument.text-template; Terminal=false Name=okular Name[bg]=okular Name[bs]=okular Name[ca]=Okular Name[ca@valencia]=Okular Name[cs]=Okular Name[da]=okular Name[de]=Okular Name[el]=okular Name[en_GB]=okular Name[es]=okular Name[et]=Okular Name[eu]=Okular Name[fi]=okular Name[fr]=Okular Name[ga]=okular Name[gl]=Okular Name[hu]=Okular Name[ia]=Okular Name[is]=okular Name[it]=Okular Name[ja]=Okular Name[kk]=Okular Name[ko]=okular Name[lt]=okular Name[mr]=ओक्युलर Name[nb]=okular Name[nds]=Okular Name[nl]=okular Name[pl]=okular Name[pt]=okular Name[pt_BR]=Okular Name[ru]=Okular Name[sk]=okular Name[sl]=okular Name[sv]=okular Name[tr]=okular Name[ug]=okular Name[uk]=Okular Name[wa]=okular Name[x-test]=xxokularxx Name[zh_CN]=Okular Name[zh_TW]=okular GenericName=Document Viewer GenericName[bg]=Преглед на доументи GenericName[bs]=Preglednik Dokumenta GenericName[ca]=Visualitzador de documents GenericName[ca@valencia]=Visualitzador de documents GenericName[cs]=Prohlížeč dokumentů GenericName[da]=Dokumentfremviser GenericName[de]=Dokumentenbetrachter GenericName[el]=Προβολέας εγγράφων GenericName[en_GB]=Document Viewer GenericName[eo]=Dokumenta rigardilo GenericName[es]=Visor de documentos GenericName[et]=Dokumendinäitaja GenericName[eu]=Dokumentu-ikustailea GenericName[fi]=Asiakirjakatselin GenericName[fr]=Afficheur de documents GenericName[ga]=Amharcán Cáipéisí GenericName[gl]=Visualizador de documentos GenericName[hu]=Dokumentummegjelenítő GenericName[ia]=Visor de documento GenericName[it]=Visore di documenti GenericName[ja]=文書ビューア GenericName[kk]=Құжатты қарау құралы GenericName[ko]=문서 뷰어 GenericName[lt]=Dokumentų žiūryklė GenericName[mr]=दस्तऐवज प्रदर्शक GenericName[nb]=Dokumentviser GenericName[nds]=Dokmentkieker GenericName[nl]=Documentenviewer GenericName[pl]=Przeglądarka dokumentów GenericName[pt]=Visualizador de Documentos GenericName[pt_BR]=Visualizador de documentos GenericName[ru]=Просмотр документов GenericName[sk]=Prehliadač dokumentov GenericName[sl]=Pregledovalnik dokumentov GenericName[sv]=Dokumentvisare GenericName[tr]=Belge Görüntüleyici GenericName[ug]=پۈتۈك كۆرگۈ GenericName[uk]=Перегляд документів GenericName[wa]=Håyneu di documints GenericName[x-test]=xxDocument Viewerxx GenericName[zh_CN]=文档查看器 GenericName[zh_TW]=文件檢視器 -Exec=okular %U %i -caption %c +Exec=okular %U %i -qwindowtitle %c Icon=okular Type=Application InitialPreference=3 Categories=Qt;KDE;Graphics;Viewer; NoDisplay=true diff --git a/filters/stage/powerpoint/CMakeLists.txt b/filters/stage/powerpoint/CMakeLists.txt index 54ccb11a82a..d33e30d3df6 100644 --- a/filters/stage/powerpoint/CMakeLists.txt +++ b/filters/stage/powerpoint/CMakeLists.txt @@ -1,41 +1,43 @@ -add_subdirectory( tests ) +if(BUILD_TESTING) + add_subdirectory( tests ) +endif() include_directories( ../../libmso ${KOMAIN_INCLUDES} ${KOTEXT_INCLUDES} ) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${KDE4_ENABLE_EXCEPTIONS} -fPIC") add_library(ppttoodplib STATIC PptDebug.cpp PptToOdp.cpp ParsedPresentation.cpp DateTimeFormat.cpp pptstyle.cpp) target_link_libraries(ppttoodplib PUBLIC koodf mso) add_executable(ppttoodp ppttoodpmain.cpp) target_link_libraries(ppttoodp ppttoodplib) add_library(calligra_filter_ppt2odp MODULE PowerPointImport.cpp) calligra_filter_desktop_to_json(calligra_filter_ppt2odp calligra_filter_ppt2odp.desktop) target_link_libraries(calligra_filter_ppt2odp ppttoodplib komain) install(TARGETS calligra_filter_ppt2odp DESTINATION ${PLUGIN_INSTALL_DIR}/calligra/formatfilters) if(SHOULD_BUILD_FILEMANAGER_THUMBNAIL) install( FILES stage_powerpoint_thumbnail.desktop DESTINATION ${SERVICES_INSTALL_DIR}) endif() if(SHOULD_BUILD_OKULAR_GENERATOR_PPT) kcoreaddons_add_plugin(okularGenerator_powerpoint_calligra JSON libokularGenerator_ppt.json INSTALL_NAMESPACE "okular/generators" SOURCES OkularPptGeneratorPlugin.cpp ) target_link_libraries( okularGenerator_powerpoint_calligra kookularGenerator_odp ) install( FILES okularPowerpoint_calligra.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) install( PROGRAMS okularApplication_powerpoint_calligra.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) endif() diff --git a/filters/stage/powerpoint/okularApplication_powerpoint_calligra.desktop b/filters/stage/powerpoint/okularApplication_powerpoint_calligra.desktop index e42519cf513..2ee03dedd18 100644 --- a/filters/stage/powerpoint/okularApplication_powerpoint_calligra.desktop +++ b/filters/stage/powerpoint/okularApplication_powerpoint_calligra.desktop @@ -1,95 +1,95 @@ [Desktop Entry] MimeType=application/vnd.ms-powerpoint; Terminal=false Name=okular Name[bg]=okular Name[bs]=okular Name[ca]=Okular Name[ca@valencia]=Okular Name[cs]=Okular Name[da]=okular Name[de]=Okular Name[el]=okular Name[en_GB]=okular Name[es]=okular Name[et]=Okular Name[eu]=Okular Name[fi]=okular Name[fr]=Okular Name[ga]=okular Name[gl]=Okular Name[hu]=Okular Name[ia]=Okular Name[is]=okular Name[it]=Okular Name[ja]=Okular Name[kk]=Okular Name[ko]=okular Name[lt]=okular Name[mr]=ओक्युलर Name[nb]=okular Name[nds]=Okular Name[nl]=okular Name[pl]=okular Name[pt]=okular Name[pt_BR]=Okular Name[ru]=Okular Name[sk]=okular Name[sl]=okular Name[sv]=okular Name[tr]=okular Name[ug]=okular Name[uk]=Okular Name[wa]=okular Name[x-test]=xxokularxx Name[zh_CN]=Okular Name[zh_TW]=okular GenericName=Document Viewer GenericName[bg]=Преглед на доументи GenericName[bs]=Preglednik Dokumenta GenericName[ca]=Visualitzador de documents GenericName[ca@valencia]=Visualitzador de documents GenericName[cs]=Prohlížeč dokumentů GenericName[da]=Dokumentfremviser GenericName[de]=Dokumentenbetrachter GenericName[el]=Προβολέας εγγράφων GenericName[en_GB]=Document Viewer GenericName[eo]=Dokumenta rigardilo GenericName[es]=Visor de documentos GenericName[et]=Dokumendinäitaja GenericName[eu]=Dokumentu-ikustailea GenericName[fi]=Asiakirjakatselin GenericName[fr]=Afficheur de documents GenericName[ga]=Amharcán Cáipéisí GenericName[gl]=Visualizador de documentos GenericName[hu]=Dokumentummegjelenítő GenericName[ia]=Visor de documento GenericName[it]=Visore di documenti GenericName[ja]=文書ビューア GenericName[kk]=Құжатты қарау құралы GenericName[ko]=문서 뷰어 GenericName[lt]=Dokumentų žiūryklė GenericName[mr]=दस्तऐवज प्रदर्शक GenericName[nb]=Dokumentviser GenericName[nds]=Dokmentkieker GenericName[nl]=Documentenviewer GenericName[pl]=Przeglądarka dokumentów GenericName[pt]=Visualizador de Documentos GenericName[pt_BR]=Visualizador de documentos GenericName[ru]=Просмотр документов GenericName[sk]=Prehliadač dokumentov GenericName[sl]=Pregledovalnik dokumentov GenericName[sv]=Dokumentvisare GenericName[tr]=Belge Görüntüleyici GenericName[ug]=پۈتۈك كۆرگۈ GenericName[uk]=Перегляд документів GenericName[wa]=Håyneu di documints GenericName[x-test]=xxDocument Viewerxx GenericName[zh_CN]=文档查看器 GenericName[zh_TW]=文件檢視器 -Exec=okular %U %i -caption %c +Exec=okular %U %i -qwindowtitle %c Icon=okular Type=Application InitialPreference=3 Categories=Qt;KDE;Graphics;Viewer; NoDisplay=true diff --git a/filters/stage/pptx/okularApplication_pptx_calligra.desktop b/filters/stage/pptx/okularApplication_pptx_calligra.desktop index 43ca373352f..fc8d2c1746c 100644 --- a/filters/stage/pptx/okularApplication_pptx_calligra.desktop +++ b/filters/stage/pptx/okularApplication_pptx_calligra.desktop @@ -1,95 +1,95 @@ [Desktop Entry] MimeType=application/vnd.openxmlformats-officedocument.presentationml.presentation; Terminal=false Name=okular Name[bg]=okular Name[bs]=okular Name[ca]=Okular Name[ca@valencia]=Okular Name[cs]=Okular Name[da]=okular Name[de]=Okular Name[el]=okular Name[en_GB]=okular Name[es]=okular Name[et]=Okular Name[eu]=Okular Name[fi]=okular Name[fr]=Okular Name[ga]=okular Name[gl]=Okular Name[hu]=Okular Name[ia]=Okular Name[is]=okular Name[it]=Okular Name[ja]=Okular Name[kk]=Okular Name[ko]=okular Name[lt]=okular Name[mr]=ओक्युलर Name[nb]=okular Name[nds]=Okular Name[nl]=okular Name[pl]=okular Name[pt]=okular Name[pt_BR]=Okular Name[ru]=Okular Name[sk]=okular Name[sl]=okular Name[sv]=okular Name[tr]=okular Name[ug]=okular Name[uk]=Okular Name[wa]=okular Name[x-test]=xxokularxx Name[zh_CN]=Okular Name[zh_TW]=okular GenericName=Document Viewer GenericName[bg]=Преглед на доументи GenericName[bs]=Preglednik Dokumenta GenericName[ca]=Visualitzador de documents GenericName[ca@valencia]=Visualitzador de documents GenericName[cs]=Prohlížeč dokumentů GenericName[da]=Dokumentfremviser GenericName[de]=Dokumentenbetrachter GenericName[el]=Προβολέας εγγράφων GenericName[en_GB]=Document Viewer GenericName[eo]=Dokumenta rigardilo GenericName[es]=Visor de documentos GenericName[et]=Dokumendinäitaja GenericName[eu]=Dokumentu-ikustailea GenericName[fi]=Asiakirjakatselin GenericName[fr]=Afficheur de documents GenericName[ga]=Amharcán Cáipéisí GenericName[gl]=Visualizador de documentos GenericName[hu]=Dokumentummegjelenítő GenericName[ia]=Visor de documento GenericName[it]=Visore di documenti GenericName[ja]=文書ビューア GenericName[kk]=Құжатты қарау құралы GenericName[ko]=문서 뷰어 GenericName[lt]=Dokumentų žiūryklė GenericName[mr]=दस्तऐवज प्रदर्शक GenericName[nb]=Dokumentviser GenericName[nds]=Dokmentkieker GenericName[nl]=Documentenviewer GenericName[pl]=Przeglądarka dokumentów GenericName[pt]=Visualizador de Documentos GenericName[pt_BR]=Visualizador de documentos GenericName[ru]=Просмотр документов GenericName[sk]=Prehliadač dokumentov GenericName[sl]=Pregledovalnik dokumentov GenericName[sv]=Dokumentvisare GenericName[tr]=Belge Görüntüleyici GenericName[ug]=پۈتۈك كۆرگۈ GenericName[uk]=Перегляд документів GenericName[wa]=Håyneu di documints GenericName[x-test]=xxDocument Viewerxx GenericName[zh_CN]=文档查看器 GenericName[zh_TW]=文件檢視器 -Exec=okular %U %i -caption %c +Exec=okular %U %i -qwindowtitle %c Icon=okular Type=Application InitialPreference=3 Categories=Qt;KDE;Graphics;Viewer; NoDisplay=true diff --git a/filters/words/docx/import/okularApplication_docx_calligra.desktop b/filters/words/docx/import/okularApplication_docx_calligra.desktop index 5d9358149b6..631dc24aa7f 100644 --- a/filters/words/docx/import/okularApplication_docx_calligra.desktop +++ b/filters/words/docx/import/okularApplication_docx_calligra.desktop @@ -1,95 +1,95 @@ [Desktop Entry] MimeType=application/vnd.openxmlformats-officedocument.wordprocessingml.document;application/vnd.openxmlformats-officedocument.wordprocessingml.template; Terminal=false Name=okular Name[bg]=okular Name[bs]=okular Name[ca]=Okular Name[ca@valencia]=Okular Name[cs]=Okular Name[da]=okular Name[de]=Okular Name[el]=okular Name[en_GB]=okular Name[es]=okular Name[et]=Okular Name[eu]=Okular Name[fi]=okular Name[fr]=Okular Name[ga]=okular Name[gl]=Okular Name[hu]=Okular Name[ia]=Okular Name[is]=okular Name[it]=Okular Name[ja]=Okular Name[kk]=Okular Name[ko]=okular Name[lt]=okular Name[mr]=ओक्युलर Name[nb]=okular Name[nds]=Okular Name[nl]=okular Name[pl]=okular Name[pt]=okular Name[pt_BR]=Okular Name[ru]=Okular Name[sk]=okular Name[sl]=okular Name[sv]=okular Name[tr]=okular Name[ug]=okular Name[uk]=Okular Name[wa]=okular Name[x-test]=xxokularxx Name[zh_CN]=Okular Name[zh_TW]=okular GenericName=Document Viewer GenericName[bg]=Преглед на доументи GenericName[bs]=Preglednik Dokumenta GenericName[ca]=Visualitzador de documents GenericName[ca@valencia]=Visualitzador de documents GenericName[cs]=Prohlížeč dokumentů GenericName[da]=Dokumentfremviser GenericName[de]=Dokumentenbetrachter GenericName[el]=Προβολέας εγγράφων GenericName[en_GB]=Document Viewer GenericName[eo]=Dokumenta rigardilo GenericName[es]=Visor de documentos GenericName[et]=Dokumendinäitaja GenericName[eu]=Dokumentu-ikustailea GenericName[fi]=Asiakirjakatselin GenericName[fr]=Afficheur de documents GenericName[ga]=Amharcán Cáipéisí GenericName[gl]=Visualizador de documentos GenericName[hu]=Dokumentummegjelenítő GenericName[ia]=Visor de documento GenericName[it]=Visore di documenti GenericName[ja]=文書ビューア GenericName[kk]=Құжатты қарау құралы GenericName[ko]=문서 뷰어 GenericName[lt]=Dokumentų žiūryklė GenericName[mr]=दस्तऐवज प्रदर्शक GenericName[nb]=Dokumentviser GenericName[nds]=Dokmentkieker GenericName[nl]=Documentenviewer GenericName[pl]=Przeglądarka dokumentów GenericName[pt]=Visualizador de Documentos GenericName[pt_BR]=Visualizador de documentos GenericName[ru]=Просмотр документов GenericName[sk]=Prehliadač dokumentov GenericName[sl]=Pregledovalnik dokumentov GenericName[sv]=Dokumentvisare GenericName[tr]=Belge Görüntüleyici GenericName[ug]=پۈتۈك كۆرگۈ GenericName[uk]=Перегляд документів GenericName[wa]=Håyneu di documints GenericName[x-test]=xxDocument Viewerxx GenericName[zh_CN]=文档查看器 GenericName[zh_TW]=文件檢視器 -Exec=okular %U %i -caption %c +Exec=okular %U %i -qwindowtitle %c Icon=okular Type=Application InitialPreference=3 Categories=Qt;KDE;Graphics;Viewer; NoDisplay=true diff --git a/filters/words/msword-odf/okularApplication_doc_calligra.desktop b/filters/words/msword-odf/okularApplication_doc_calligra.desktop index 1002e9996a6..7df27b1bed9 100644 --- a/filters/words/msword-odf/okularApplication_doc_calligra.desktop +++ b/filters/words/msword-odf/okularApplication_doc_calligra.desktop @@ -1,95 +1,95 @@ [Desktop Entry] MimeType=application/msword; Terminal=false Name=okular Name[bg]=okular Name[bs]=okular Name[ca]=Okular Name[ca@valencia]=Okular Name[cs]=Okular Name[da]=okular Name[de]=Okular Name[el]=okular Name[en_GB]=okular Name[es]=okular Name[et]=Okular Name[eu]=Okular Name[fi]=okular Name[fr]=Okular Name[ga]=okular Name[gl]=Okular Name[hu]=Okular Name[ia]=Okular Name[is]=okular Name[it]=Okular Name[ja]=Okular Name[kk]=Okular Name[ko]=okular Name[lt]=okular Name[mr]=ओक्युलर Name[nb]=okular Name[nds]=Okular Name[nl]=okular Name[pl]=okular Name[pt]=okular Name[pt_BR]=Okular Name[ru]=Okular Name[sk]=okular Name[sl]=okular Name[sv]=okular Name[tr]=okular Name[ug]=okular Name[uk]=Okular Name[wa]=okular Name[x-test]=xxokularxx Name[zh_CN]=Okular Name[zh_TW]=okular GenericName=Document Viewer GenericName[bg]=Преглед на доументи GenericName[bs]=Preglednik Dokumenta GenericName[ca]=Visualitzador de documents GenericName[ca@valencia]=Visualitzador de documents GenericName[cs]=Prohlížeč dokumentů GenericName[da]=Dokumentfremviser GenericName[de]=Dokumentenbetrachter GenericName[el]=Προβολέας εγγράφων GenericName[en_GB]=Document Viewer GenericName[eo]=Dokumenta rigardilo GenericName[es]=Visor de documentos GenericName[et]=Dokumendinäitaja GenericName[eu]=Dokumentu-ikustailea GenericName[fi]=Asiakirjakatselin GenericName[fr]=Afficheur de documents GenericName[ga]=Amharcán Cáipéisí GenericName[gl]=Visualizador de documentos GenericName[hu]=Dokumentummegjelenítő GenericName[ia]=Visor de documento GenericName[it]=Visore di documenti GenericName[ja]=文書ビューア GenericName[kk]=Құжатты қарау құралы GenericName[ko]=문서 뷰어 GenericName[lt]=Dokumentų žiūryklė GenericName[mr]=दस्तऐवज प्रदर्शक GenericName[nb]=Dokumentviser GenericName[nds]=Dokmentkieker GenericName[nl]=Documentenviewer GenericName[pl]=Przeglądarka dokumentów GenericName[pt]=Visualizador de Documentos GenericName[pt_BR]=Visualizador de documentos GenericName[ru]=Просмотр документов GenericName[sk]=Prehliadač dokumentov GenericName[sl]=Pregledovalnik dokumentov GenericName[sv]=Dokumentvisare GenericName[tr]=Belge Görüntüleyici GenericName[ug]=پۈتۈك كۆرگۈ GenericName[uk]=Перегляд документів GenericName[wa]=Håyneu di documints GenericName[x-test]=xxDocument Viewerxx GenericName[zh_CN]=文档查看器 GenericName[zh_TW]=文件檢視器 -Exec=okular %U %i -caption %c +Exec=okular %U %i -qwindowtitle %c Icon=okular Type=Application InitialPreference=3 Categories=Qt;KDE;Graphics;Viewer; NoDisplay=true diff --git a/filters/words/rtf/import/CMakeLists.txt b/filters/words/rtf/import/CMakeLists.txt index 9eb31194730..227d1f1e2d3 100644 --- a/filters/words/rtf/import/CMakeLists.txt +++ b/filters/words/rtf/import/CMakeLists.txt @@ -1,16 +1,31 @@ add_subdirectory(3rdparty) include_directories( 3rdparty/rtf-qt/src/ ) set(rtf2odt_PART_SRCS rtfimport.cpp ) add_library(calligra_filter_rtf2odt MODULE ${rtf2odt_PART_SRCS}) calligra_filter_desktop_to_json(calligra_filter_rtf2odt calligra_filter_rtf2odt.desktop) target_link_libraries(calligra_filter_rtf2odt RtfReader komain) install(TARGETS calligra_filter_rtf2odt DESTINATION ${PLUGIN_INSTALL_DIR}/calligra/formatfilters) if(SHOULD_BUILD_FILEMANAGER_THUMBNAIL) install( FILES words_rtf_thumbnail.desktop DESTINATION ${SERVICES_INSTALL_DIR}) endif() + +if(SHOULD_BUILD_OKULAR_GENERATOR_RTF) + kcoreaddons_add_plugin(okularGenerator_rtf_calligra + JSON libokularGenerator_rtf.json + INSTALL_NAMESPACE "okular/generators" + SOURCES OkularRtfGeneratorPlugin.cpp + ) + + target_link_libraries( okularGenerator_rtf_calligra + kookularGenerator_odt + ) + + install( FILES okularRtf_calligra.desktop DESTINATION ${SERVICES_INSTALL_DIR} ) + install( PROGRAMS okularApplication_rtf_calligra.desktop DESTINATION ${XDG_APPS_INSTALL_DIR} ) +endif() diff --git a/filters/words/rtf/import/OkularRtfGeneratorPlugin.cpp b/filters/words/rtf/import/OkularRtfGeneratorPlugin.cpp new file mode 100644 index 00000000000..00d000d8aa6 --- /dev/null +++ b/filters/words/rtf/import/OkularRtfGeneratorPlugin.cpp @@ -0,0 +1,24 @@ +/* This file is part of the KDE project + Copyright (C) 2016 Friedrich W. H. Kossebau + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public License + along with this library; see the file COPYING.LIB. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include + +OKULAR_EXPORT_PLUGIN(OkularOdtGenerator, "libokularGenerator_rtf.json") + +#include "OkularRtfGeneratorPlugin.moc" diff --git a/filters/words/rtf/import/libokularGenerator_rtf.json b/filters/words/rtf/import/libokularGenerator_rtf.json new file mode 100644 index 00000000000..99e74e0c4d6 --- /dev/null +++ b/filters/words/rtf/import/libokularGenerator_rtf.json @@ -0,0 +1,56 @@ +{ + "KPlugin": { + "Authors": [ + { + "Email": "sven.langkamp@gmail.com", + "Name": "Sven Langkamp", + "Name[x-test]": "xxSven Langkampxx" + }, + { + "Email": "kossebau@kde.org", + "Name": "Friedrich W. H. Kossebau", + "Name[x-test]": "xxFriedrich W. H. Kossebauxx" + } + ], + "Copyright": "Copyright 2012-2016 Sven Langkamp, Friedrich W. H. Kossebau", + "Copyright[fi]": "Copyright 2012–2016 Sven Langkamp, Friedrich W. H. Kossebau", + "Copyright[gl]": "© 2012-2016 Sven Langkamp, Friedrich W. H. Kossebau", + "Copyright[uk]": "© Sven Langkamp, Friedrich W. H. Kossebau, 2012–2016", + "Copyright[x-test]": "xxCopyright 2012-2016 Sven Langkamp, Friedrich W. H. Kossebauxx", + "Description": "RTF file renderer", + "Description[ca@valencia]": "Representació de fitxers RTF", + "Description[ca]": "Representació de fitxers RTF", + "Description[de]": "Renderer für RTF-Dateien", + "Description[es]": "Motor de visualización para archivos RTF", + "Description[it]": "Presentatore file RTF", + "Description[nl]": "RTF-bestandsweergave", + "Description[pt]": "Visualizador de ficheiros RTF", + "Description[sv]": "RTF-filåtergivning", + "Description[uk]": "Засіб показу вмісту файлів RTF", + "Description[x-test]": "xxRTF file rendererxx", + "Id": "okularGenerator_docx_calligra", + "License": "GPL", + "MimeTypes": [ + "application/rtf", + "text/rtf" + ], + "Name": "RTF Backend", + "Name[ca@valencia]": "Dorsal pels RTF", + "Name[ca]": "Dorsal pels RTF", + "Name[de]": "RTF-Backend", + "Name[es]": "Motor RTF", + "Name[it]": "Motore RTF", + "Name[nl]": "RTF-backend", + "Name[pt]": "Infra-Estrutura para RTF", + "Name[sv]": "RTF-gränssnitt", + "Name[uk]": "Модуль обробки RTF", + "Name[x-test]": "xxRTF Backendxx", + "ServiceTypes": [ + "okular/Generator" + ], + "Version": "2.99.5" + }, + "X-KDE-Priority": 1, + "X-KDE-okularAPIVersion": 1, + "X-KDE-okularHasInternalSettings": false +} diff --git a/filters/stage/powerpoint/okularApplication_powerpoint_calligra.desktop b/filters/words/rtf/import/okularApplication_rtf_calligra.desktop similarity index 97% copy from filters/stage/powerpoint/okularApplication_powerpoint_calligra.desktop copy to filters/words/rtf/import/okularApplication_rtf_calligra.desktop index e42519cf513..fbcc714f31d 100644 --- a/filters/stage/powerpoint/okularApplication_powerpoint_calligra.desktop +++ b/filters/words/rtf/import/okularApplication_rtf_calligra.desktop @@ -1,95 +1,95 @@ [Desktop Entry] -MimeType=application/vnd.ms-powerpoint; +MimeType=text/rtf;application/rtf; Terminal=false Name=okular Name[bg]=okular Name[bs]=okular Name[ca]=Okular Name[ca@valencia]=Okular Name[cs]=Okular Name[da]=okular Name[de]=Okular Name[el]=okular Name[en_GB]=okular Name[es]=okular Name[et]=Okular Name[eu]=Okular Name[fi]=okular Name[fr]=Okular Name[ga]=okular Name[gl]=Okular Name[hu]=Okular Name[ia]=Okular Name[is]=okular Name[it]=Okular Name[ja]=Okular Name[kk]=Okular Name[ko]=okular Name[lt]=okular Name[mr]=ओक्युलर Name[nb]=okular Name[nds]=Okular Name[nl]=okular Name[pl]=okular Name[pt]=okular Name[pt_BR]=Okular Name[ru]=Okular Name[sk]=okular Name[sl]=okular Name[sv]=okular Name[tr]=okular Name[ug]=okular Name[uk]=Okular Name[wa]=okular Name[x-test]=xxokularxx Name[zh_CN]=Okular Name[zh_TW]=okular GenericName=Document Viewer GenericName[bg]=Преглед на доументи GenericName[bs]=Preglednik Dokumenta GenericName[ca]=Visualitzador de documents GenericName[ca@valencia]=Visualitzador de documents GenericName[cs]=Prohlížeč dokumentů GenericName[da]=Dokumentfremviser GenericName[de]=Dokumentenbetrachter GenericName[el]=Προβολέας εγγράφων GenericName[en_GB]=Document Viewer GenericName[eo]=Dokumenta rigardilo GenericName[es]=Visor de documentos GenericName[et]=Dokumendinäitaja GenericName[eu]=Dokumentu-ikustailea GenericName[fi]=Asiakirjakatselin GenericName[fr]=Afficheur de documents GenericName[ga]=Amharcán Cáipéisí GenericName[gl]=Visualizador de documentos GenericName[hu]=Dokumentummegjelenítő GenericName[ia]=Visor de documento GenericName[it]=Visore di documenti GenericName[ja]=文書ビューア GenericName[kk]=Құжатты қарау құралы GenericName[ko]=문서 뷰어 GenericName[lt]=Dokumentų žiūryklė GenericName[mr]=दस्तऐवज प्रदर्शक GenericName[nb]=Dokumentviser GenericName[nds]=Dokmentkieker GenericName[nl]=Documentenviewer GenericName[pl]=Przeglądarka dokumentów GenericName[pt]=Visualizador de Documentos GenericName[pt_BR]=Visualizador de documentos GenericName[ru]=Просмотр документов GenericName[sk]=Prehliadač dokumentov GenericName[sl]=Pregledovalnik dokumentov GenericName[sv]=Dokumentvisare GenericName[tr]=Belge Görüntüleyici GenericName[ug]=پۈتۈك كۆرگۈ GenericName[uk]=Перегляд документів GenericName[wa]=Håyneu di documints GenericName[x-test]=xxDocument Viewerxx GenericName[zh_CN]=文档查看器 GenericName[zh_TW]=文件檢視器 -Exec=okular %U %i -caption %c +Exec=okular %U %i -qwindowtitle %c Icon=okular Type=Application InitialPreference=3 Categories=Qt;KDE;Graphics;Viewer; NoDisplay=true diff --git a/filters/words/rtf/import/okularRtf_calligra.desktop b/filters/words/rtf/import/okularRtf_calligra.desktop new file mode 100644 index 00000000000..9f2a539b43f --- /dev/null +++ b/filters/words/rtf/import/okularRtf_calligra.desktop @@ -0,0 +1,49 @@ +[Desktop Entry] +Icon=okular +Name=okular +Name[bg]=okular +Name[bs]=okular +Name[ca]=Okular +Name[ca@valencia]=Okular +Name[cs]=Okular +Name[da]=okular +Name[de]=Okular +Name[el]=okular +Name[en_GB]=okular +Name[es]=okular +Name[et]=Okular +Name[eu]=Okular +Name[fi]=okular +Name[fr]=Okular +Name[ga]=okular +Name[gl]=Okular +Name[hu]=Okular +Name[ia]=Okular +Name[is]=okular +Name[it]=Okular +Name[ja]=Okular +Name[kk]=Okular +Name[ko]=okular +Name[lt]=okular +Name[mr]=ओक्युलर +Name[nb]=okular +Name[nds]=Okular +Name[nl]=okular +Name[pl]=okular +Name[pt]=okular +Name[pt_BR]=Okular +Name[ru]=Okular +Name[sk]=okular +Name[sl]=okular +Name[sv]=okular +Name[tr]=okular +Name[ug]=okular +Name[uk]=Okular +Name[wa]=okular +Name[x-test]=xxokularxx +Name[zh_CN]=Okular +Name[zh_TW]=okular +ServiceTypes=KParts/ReadOnlyPart +X-KDE-Library=okularpart +Type=Service +MimeType=text/rtf;application/rtf; diff --git a/filters/words/wordperfect/import/okularApplication_wpd_calligra.desktop b/filters/words/wordperfect/import/okularApplication_wpd_calligra.desktop index 5f1a8f22e96..024efeacadf 100644 --- a/filters/words/wordperfect/import/okularApplication_wpd_calligra.desktop +++ b/filters/words/wordperfect/import/okularApplication_wpd_calligra.desktop @@ -1,95 +1,95 @@ [Desktop Entry] MimeType=application/vnd.wordperfect; Terminal=false Name=okular Name[bg]=okular Name[bs]=okular Name[ca]=Okular Name[ca@valencia]=Okular Name[cs]=Okular Name[da]=okular Name[de]=Okular Name[el]=okular Name[en_GB]=okular Name[es]=okular Name[et]=Okular Name[eu]=Okular Name[fi]=okular Name[fr]=Okular Name[ga]=okular Name[gl]=Okular Name[hu]=Okular Name[ia]=Okular Name[is]=okular Name[it]=Okular Name[ja]=Okular Name[kk]=Okular Name[ko]=okular Name[lt]=okular Name[mr]=ओक्युलर Name[nb]=okular Name[nds]=Okular Name[nl]=okular Name[pl]=okular Name[pt]=okular Name[pt_BR]=Okular Name[ru]=Okular Name[sk]=okular Name[sl]=okular Name[sv]=okular Name[tr]=okular Name[ug]=okular Name[uk]=Okular Name[wa]=okular Name[x-test]=xxokularxx Name[zh_CN]=Okular Name[zh_TW]=okular GenericName=Document Viewer GenericName[bg]=Преглед на доументи GenericName[bs]=Preglednik Dokumenta GenericName[ca]=Visualitzador de documents GenericName[ca@valencia]=Visualitzador de documents GenericName[cs]=Prohlížeč dokumentů GenericName[da]=Dokumentfremviser GenericName[de]=Dokumentenbetrachter GenericName[el]=Προβολέας εγγράφων GenericName[en_GB]=Document Viewer GenericName[eo]=Dokumenta rigardilo GenericName[es]=Visor de documentos GenericName[et]=Dokumendinäitaja GenericName[eu]=Dokumentu-ikustailea GenericName[fi]=Asiakirjakatselin GenericName[fr]=Afficheur de documents GenericName[ga]=Amharcán Cáipéisí GenericName[gl]=Visualizador de documentos GenericName[hu]=Dokumentummegjelenítő GenericName[ia]=Visor de documento GenericName[it]=Visore di documenti GenericName[ja]=文書ビューア GenericName[kk]=Құжатты қарау құралы GenericName[ko]=문서 뷰어 GenericName[lt]=Dokumentų žiūryklė GenericName[mr]=दस्तऐवज प्रदर्शक GenericName[nb]=Dokumentviser GenericName[nds]=Dokmentkieker GenericName[nl]=Documentenviewer GenericName[pl]=Przeglądarka dokumentów GenericName[pt]=Visualizador de Documentos GenericName[pt_BR]=Visualizador de documentos GenericName[ru]=Просмотр документов GenericName[sk]=Prehliadač dokumentov GenericName[sl]=Pregledovalnik dokumentov GenericName[sv]=Dokumentvisare GenericName[tr]=Belge Görüntüleyici GenericName[ug]=پۈتۈك كۆرگۈ GenericName[uk]=Перегляд документів GenericName[wa]=Håyneu di documints GenericName[x-test]=xxDocument Viewerxx GenericName[zh_CN]=文档查看器 GenericName[zh_TW]=文件檢視器 -Exec=okular %U %i -caption %c +Exec=okular %U %i -qwindowtitle %c Icon=okular Type=Application InitialPreference=3 Categories=Qt;KDE;Graphics;Viewer; NoDisplay=true diff --git a/flow/part/org.kde.calligraflow.desktop b/flow/part/org.kde.calligraflow.desktop index 9bafccffaca..4ae95f50de8 100644 --- a/flow/part/org.kde.calligraflow.desktop +++ b/flow/part/org.kde.calligraflow.desktop @@ -1,107 +1,107 @@ [Desktop Entry] Type=Application Name=Calligra Flow Name[bs]=Calligra Flow Name[ca]=Calligra Flow Name[ca@valencia]=Calligra Flow Name[cs]=Calligra Flow Name[da]=Calligra Flow Name[de]=Calligra Flow Name[el]=Calligra Flow Name[en_GB]=Calligra Flow Name[es]=Calligra Flow Name[et]=Calligra Flow Name[eu]=Calligra Flow Name[fi]=Calligra Flow Name[fr]=Calligra Flow Name[gl]=Calligra Flow Name[hu]=Calligra Flow Name[it]=Calligra Flow Name[ja]=Calligra Flow Name[kk]=Calligra Flow Name[lt]=Calligra Flow Name[nb]=Calligra Flow Name[nds]=Calligra-Flow Name[nl]=Calligra Flow Name[pl]=Schematy Calligra Name[pt]=Calligra Flow Name[pt_BR]=Calligra Flow Name[ru]=Calligra Flow Name[sk]=Calligra Flow Name[sl]=Calligra Flow Name[sv]=Calligra Flow Name[tr]=Calligra Flow Name[ug]=Calligra Flow Name[uk]=Calligra Flow Name[x-test]=xxCalligra Flowxx Name[zh_CN]=Calligra Flow Name[zh_TW]=Calligra Flow Exec=calligraflow %u GenericName=Flowchart & Diagram Editing GenericName[bg]=Редактиране на блоксхеми и диаграми GenericName[bs]=Flowchart i Dijagram editovanje GenericName[ca]=Editor de diagrames de fluxos GenericName[ca@valencia]=Editor de diagrames de fluxos GenericName[cs]=Editor nákresů a diagramů GenericName[cy]=Golygu Siartiau Llif & Diagram GenericName[da]=Flydediagrammer & diagramredigering GenericName[de]=Flussdiagramme & Diagrammbearbeitung GenericName[el]=Επεξεργασία διαγραμμάτων ροής & απλών διαγραμμάτων GenericName[en_GB]=Flowchart & Diagram Editing GenericName[eo]=Desegnilo por fluo- kaj aliaj diagramoj GenericName[es]=Edición de diagramas de flujo y esquemas GenericName[et]=Skeemide redigeerimine GenericName[eu]=Fluxu-diagramen eta diagramen edizioa GenericName[fa]=روندنما و ویرایش نمودار GenericName[fi]=Kaavioiden piirtäminen GenericName[fr]=Conception de diagrammes et de tableaux GenericName[fy]=Bewurkje fan (stroom)diagrammen GenericName[ga]=Eagarthóireacht Sreabhchairteacha agus Léaráidí GenericName[gl]=Editor de diagramas GenericName[he]=עריכת תרשימי זרימה ודיאגרמות GenericName[hi]=फ्लोचार्ट व डायग्राम संपादन GenericName[hne]=फ्लोचार्ट अउ डायग्राम संपादन GenericName[hr]=Uređivannje dijagrama i prikaza protoka GenericName[hu]=Folyamatábra- és diagramkészítő GenericName[is]=Flæðirits og skýringamynda vinnsla GenericName[it]=Editor di diagrammi di flusso GenericName[ja]=フローチャート & ダイアグラム編集 GenericName[kk]=Блок-сұлба мен Диаграммаларды өңдеу GenericName[ko]=순서도와 다이어그램 편집 GenericName[lv]=Plūsmkaršu un diagrammu rediģēšana GenericName[ms]=Pengeditan Carta Aliran & Rajah GenericName[nb]=Flytskjema- og diagramredigering GenericName[nds]=Afloopdiagrammen un Diagrammbewerken GenericName[ne]=फ्लो चित्रपट र चित्र सम्पादन GenericName[nl]=Bewerken van (stroom)diagrammen GenericName[pl]=Edycja diagramów oraz schematów przepływów GenericName[pt]=Fluxogramas e Edição de Diagramas GenericName[pt_BR]=Edição de fluxogramas e diagramas GenericName[ru]=Схемы GenericName[sk]= Vývojové diagramy GenericName[sl]=Urejanje grafov in diagramov poteka GenericName[sv]=Flödesscheman och diagramredigering GenericName[ta]=பாய்வு நிரல்பட மற்றும் வரிபட தொகுப்பு GenericName[tg]=Ҷадвалҳо ва диаграмма GenericName[tr]=Diyagram Düzenleyici GenericName[ug]=ئېقىن رەسىم ۋە چېرتيوژ تەھرىرلەش GenericName[uk]=Редагування діаграм та блок-схем GenericName[uz]=Sxema va diagrammalarni tahrirlash GenericName[uz@cyrillic]=Схема ва диаграммаларни таҳрирлаш GenericName[wa]=Aspougnaedje di grafikes matematikes eyet d' diyagrames GenericName[xh]=Umzobo wokulandelana kweenkqubo & Uhlelo lomzobo GenericName[x-test]=xxFlowchart & Diagram Editingxx GenericName[zh_CN]=流程图和图表编辑 GenericName[zh_TW]=流程圖與圖表編輯 MimeType=application/vnd.oasis.opendocument.graphics;application/vnd.visio; X-KDE-ServiceTypes=Calligra/Application Icon=calligraflow X-KDE-NativeMimeType=application/vnd.oasis.opendocument.graphics # application/vnd.oasis.opendocument.graphics is not on this list because Karbon has the default; # Flow will be used only in absence of Karbon X-Calligra-DefaultMimeTypes=application/vnd.oasis.opendocument.graphics;application/vnd.visio; X-DocPath=http://userbase.kde.org/Special:MyLanguage/Flow X-KDE-StartupNotify=true X-DBUS-StartupType=Multi X-DBUS-ServiceName=org.calligra.flow -Categories=Qt;KDE;Office; +Categories=Qt;KDE;Office;FlowChart; diff --git a/karbon/data/org.kde.karbon.appdata.xml b/karbon/data/org.kde.karbon.appdata.xml index e439039d8f4..d06b1e2ec2d 100644 --- a/karbon/data/org.kde.karbon.appdata.xml +++ b/karbon/data/org.kde.karbon.appdata.xml @@ -1,395 +1,395 @@ org.kde.karbon.desktop CC0-1.0 GPL-2.0+ Karbon Karbon Karbon Karbon Karbon Karbon Karbon Karbon Karbon Karbon Karbon Karbon Karbon Karbon Karbon Karbon Karbon Karbon Karbon Karbon Karbon Karbon Karbon Karbon xxKarbonxx Karbon Scalable Graphics Grafika koja se može skalirati Gràfics escalables Gràfics escalables Škálovatelná grafika Skalierbare Vektorgrafik Scalable Graphics Gráficos escalables Vektorgraafika Skaalautuva grafiikka Graphisme vectoriel Gráficos de tamaño variábel Graphicos scalabile Grafica scalabile 画像の寸法 Vectorafbeeldingen Skalowalna grafika Gráficos Vectoriais Imagens vetoriais Škálovateľná grafika Skalbar grafik Програма для роботи з масштабованою графікою xxScalable Graphicsxx 可缩放图像

Karbon is a vector drawing application with a user interface that is easy to use, highly customizable and extensible. That makes Karbon a great application for users starting to explore the world of vector graphics as well as for artists wanting to create breathtaking vector art.

Karbon je vektorska aplikacija za crtanje s korisničkim sučeljem koje je jednostavno za korištenje , vrlo prilagodljivo i proširivo . To čini Karbon odličnom aplikacijom za korisnike koji počinju istraživati ​​svijet vektorske grafike , kao i za umjetnike koji žele stvoriti prekrasnu vektorsku umjetnost .

El Karbon és una aplicació de dibuix vectorial amb una interfície d'usuari senzilla d'utilitzar, que es pot personalitzar i ampliar molt. Això converteix el Karbon en una gran aplicació per usuaris que comencin a explorar el món dels gràfics vectorials i també pels artistes que volen crear art vectorial que deixi bocabadat.

El Karbon és una aplicació de dibuix vectorial amb una interfície d'usuari senzilla d'utilitzar, que es pot personalitzar i ampliar molt. Això converteix el Karbon en una gran aplicació per usuaris que comencen a explorar el món dels gràfics vectorials i també pels artistes que volen crear art vectorial que deixi bocabadat.

Karbon is a vector drawing application with a user interface that is easy to use, highly customisable and extensible. That makes Karbon a great application for users starting to explore the world of vector graphics as well as for artists wanting to create breathtaking vector art.

Karbon es una aplicación de dibujo vectorial que dispone de una interfaz de usuario intuitiva, altamente personalizable y extensible. Todo ello hace que Karbon sea una gran aplicación para aquellos usuarios que empiezan a explorar el mundo de los gráficos vectoriales, así como para los artistas que desean crear impresionantes piezas de arte vectorial.

Karbon on vektorgraafikarakendus hõlpsasti kasutatava, äärmiselt kohandatava ja laiendatava kasutajaliidesega. See muudab Karboni imeheaks abivahendiks algajale, kes alles uudistab vektorgraafika maailma, aga ka kunstnikele, kes soovivad luua hingematvalt kaunist vektorgraafikat.

Karbon on vektorigrafiikkasovellus, jonka käyttöliittymä on helppo ja laajasti mukautettavissa ja laajennettavissa. Karbon on näin loistava sovellus vektorigrafiikan maailmaan vasta tutustuville käyttäjille samoin kuin henkeäsalpaavaa vektoritaidetta luoville taiteilijoille.

Karbon est une application de dessin vectoriel avec une interface facile à utiliser, personnalisable et extensible. Cela fait de Karbon une application majeure pour les utilisateurs s'initiant au dessin vectoriel et pour les artistes voulant créer des œuvres à couper le souffle.

Karbon é un aplicativo de debuxo vectorial con unha interface de usuario fácil de usar, personalizar e ampliar. Iso fai de Karbon un gran aplicativo para usuarios que comezan a explorar o mundo das imaxes vectoriais, pero tamén para artistas que queren crear impresionantes obras de arte vectoriais.

Karbon è un'applicazione di disegno vettoriale con un'interfaccia utente facile da utilizzare, altamente personalizzabile ed estendibile. Ciò rende Karbon un'ottima applicazione per gli utenti che iniziano a esplorare il mondo della grafica vettoriale così come per gli artisti che vogliono creare straordinarie opere vettoriali.

Karbon is een toepassing voor tekenen met vectoren met een gebruikersinterface dat gemakkelijk is te gebruiken, zeer goed aan te passen en uit te breiden. Dit maakt Karbon een geweldige toepassing voor gebruikers die de wereld van grafiek met vectoren aan het verkennen zijn evenals voor kunstenaars die adembenemende vectorkunst willen maken.

Karbon jest programem do rysowania wektorowego z układem sterowania, który jest łatwy w użyciu, wysoce dostosowywalny i rozszerzalny. To czyni Karbon wspaniałym programem dla użytkowników zaczynających poznawanie świata grafiki wektorowej, a artystów zachęca do tworzenie sztuki wektorowej zapierającej dech w piersiach.

O Karbon é uma aplicação de desenho vectorial com uma interface simples de usar, facilmente configurável e modular. Isto torna o Karbon uma óptima aplicação para os utilizadores que começam a explorar o mundo dos gráficos vectoriais, assim como para os artistas que desejam criar arte vectorial espectacular.

Karbon é um aplicativo de desenho vetorial com uma interface simples de usar, facilmente configurável e expansível. Isso torna o Karbon um ótimo aplicativo para usuários que começam a explorar o mundo dos gráficos vetoriais, assim como para artistas que desejam criar arte vetorial espetacular.

Karbon je vektorová kresliaca aplikácia s používateľským rozhraním jednoduchým na používanie, veľmi prispôsobiteľná a rozšíriteľná. Toto robí z Karbonu výbornú aplikáciu pre používateľov, ktorí začínajú objavovať sveet vektorovej grafiky, ako aj pre umelcov, ktorí chcú vytvárať dych vyrážajúce vektorové umenie.

Karbon är ett vektorritprogram med ett användargränssnitt som är lätt att använda, ytterst anpassningsbart och utökningsbart. Det gör Karbon till ett utmärkt program för användare som börjar utforska vektorgrafikvärlden samt för konstnärer som vill skapa hänförande vektorkonst.

Karbon — програма для векторного малювання з простим, гнучким та придатним до розширення інтерфейсом користувача. Це робить Karbon чудовою програмою для користувачів, які починають знайомитися зі світом векторної графіки, а також для художників, які хочуть створити захопливі векторні твори.

xxKarbon is a vector drawing application with a user interface that is easy to use, highly customizable and extensible. That makes Karbon a great application for users starting to explore the world of vector graphics as well as for artists wanting to create breathtaking vector art.xx

Karbon 是一个矢量绘图程序,具有易用,高度可定制和可扩展的用户界面。它是一个理想的工具,帮助用户探索矢量图形,帮助艺术家创作令人惊叹的矢量艺术作品。

Features:

Svojstva:

Característiques:

Característiques:

Vlastnosti:

Funktionen:

Features:

Características:

Omadused:

Ominaisuuksia:

Fonctionnalités :

Funcionalidades:

Characteristicas

Funzionalità:

機能:

Mogelijkheden:

Możliwości:

Funcionalidades:

Funcionalidades:

Funkcie:

Funktioner:

Можливості:

xxFeatures:xx

功能:

  • Loading support for ODG, SVG, WPG, WMF, EPS/PS
  • Učitavanje podrške za ODG, SVG, WPG, WMF, EPS/PS
  • Permet llegir ODG, SVG, WPG, WMF, EPS/PS
  • Permet llegir ODG, SVG, WPG, WMF, EPS/PS
  • Unterstützung für das Laden von ODG, SVG, WPG, WMF, EPS/PS
  • Loading support for ODG, SVG, WPG, WMF, EPS/PS
  • Admite la carga de ODG, SVG, WPG, WMF, EPS/PS
  • ODG, SVG, WPG, WMF, EPS/PS laadimise toetamine
  • Lukutuki ODG-, SVG-, WPG-, WMF- ja EPS/PS-muodoista
  • Lecture des formats ODG, SVG, WPG, WMF, EPS/PS
  • Capacidade para cargar ODG, SVG, WPG, WMF e EPS/PS.
  • Cargante supporto pro ODG, SVG, WPG, WMF, EPS/PS
  • Supporto per il caricamento di ODG, SVG, WPG, WMF, EPS/PS
  • 読み込み可能な形式: ODG, SVG, WPG, WMF, EPS/PS
  • Ondersteuning voor laden van ODG, SVG, WPG, WMF, EPS/PS
  • Obsługa wczytywania dla ODG, SVG, WPG, WMF, EPS/PS
  • Suporte do carregamento de imagens em ODG, SVG, WPG, WMF, EPS/PS
  • Suporte para carregamento de arquivos ODG, SVG, WPG, WMF e EPS/PS
  • Podpora čítania pre ODG, SVG, WPG, WMF, EPS/PS
  • Stöd för att läsa in ODG, SVG, WPG, WMF, EPS/PS
  • Підтримка завантаження даних ODG, SVG, WPG, WMF, EPS/PS.
  • xxLoading support for ODG, SVG, WPG, WMF, EPS/PSxx
  • 加载支持 ODG,SVG,WPG,WMF,EPS/PS
  • Writing support for ODG, SVG, PNG, PDF, WMF
  • Pisanje podrške za ODG, SVG, WPG, WMF, EPS/PS
  • Permet gravar ODG, SVG, PNG, PDF, WMF
  • Permet gravar ODG, SVG, PNG, PDF, WMF
  • Unterstützung für das Schreiben von ODG, SVG, PNG, PDF, WMF
  • Writing support for ODG, SVG, PNG, PDF, WMF
  • Admite la escritura de ODG, SVG, PNG, PDF, WMF
  • ODG, SVG, PNG, PDF, WMF kirjutamine toetamine
  • Kirjoitustuki ODG-, SVG-, PNG-, PDF- ja WMF-muotoihin
  • Écriture des formats ODG, SVG, PNG, PDF, WMF
  • Capacidade de xeración de ODG, SVG, PNG, PDF e WMF.
  • Supporto de scriber pro ODG, SVG, PNG, PDF, WMF
  • Supporto per la scrittura di ODG, SVG, PNG, PDF, WMF
  • 書き込み可能な形式: ODG, SVG, PNG, PDF, WMF
  • Ondersteuning voor schrijven van ODG, SVG, PNG, PDF, WMF
  • Obsługa zapisywania dla ODG, SVG, PNG, PDF, WMF
  • Suporte da gravação de imagens em ODG, SVG, PNG, PDF, WMF
  • Suporte para gravação de arquivos ODG, SVG, PNG, PDF e WMF
  • Podpora zápisu pre ODG, SVG, PNG, PDF, WMF
  • Stöd för att skriva ut ODG, SVG, PNG, PDF, WMF
  • Підтримка запису у форматах ODG, SVG, PNG, PDF, WMF.
  • xxWriting support for ODG, SVG, PNG, PDF, WMFxx
  • 写入支持 ODG,SVG,PNG,PDF,WMF
  • Customizable user interface with freely placeable toolbars and dockers
  • Prilagodljiv korisnički interfejs sa gdje se moze slobodno staviti alatne trake i dockers
  • Interfície d'usuari que es pot personalitzar que permet situar lliurement barres d'eines i acobladors
  • Interfície d'usuari que es pot personalitzar que permet situar lliurement barres d'eines i acobladors
  • Anpassungsfähige Bedienungsoberfläche mit frei platzierbaren Werkzeugleisten und andockbaren Fenstern
  • Customisable user interface with freely placeable toolbars and dockers
  • Interfaz de usuario personalizable con barras de herramientas y paneles que se pueden posicionar libremente en cualquier lugar.
  • Kohandatav kasutajaliides vabalt liigutatavate tööriistaribade ja dokkidega
  • Mukautettava käyttöliittymä, jonka työkalurivit ja telakat ovat vapaasti sijoitettavissa
  • Interface utilisateur personnalisable avec des barres d'outil librement déplaçables.
  • Interface de usuario personalizábel con barras de ferramentas e docas que poden recolocarse libremente.
  • Interfacie de usator personalisabile con barra de instrumentos e stivatores (dockers) liberemente positionabile
  • Interfaccia utente personalizzabile con barre degli strumenti posizionabili liberamente e aree di aggancio
  • Aan te passen gebruikersinterface met vrij te plaatsen werkbalken en verankeringen
  • Układ sterowania z dowolnie umieszczanymi paskami narzędzi i dokowaniami
  • Interface de utilizador configurável com barras e áreas acopláveis livres
  • Interface de usuário configurável com barras de ferramentas e áreas acopláveis que podem ser facilmente ajustadas
  • Nastaviteľné používateľské rozhranie s voľne umiestniteľnými panelmi nástrojov a dockermi
  • Anpassningsbart användargränssnitt med verktygsrader och paneler som kan placeras fritt
  • Придатний до налаштовування інтерфейс користувача з довільним розташуванням панелей інструментів та бічних панелей.
  • xxCustomizable user interface with freely placeable toolbars and dockersxx
  • 可自定义用户界面,包含可自由放置的工具栏和停靠栏
  • Layer docker for easy handling of complex documents including preview thumbnails, support for grouping shapes via drag and drop, controlling visibility of shapes or locking
  • Sloj lučki radnik za jednostavno rukovanje složenim dokumentima , uključujući pregled sličica , podršku za grupiranje oblika putem drag and drop , kontroliranje vidljivost oblika ili zaključavanje
  • Acoblador de capa per a una gestió senzilla de documents complexos incloent la vista prèvia de miniatures, permet agrupar formes via arrossegar i deixar anar, controlar la visibilitat de les formes o el bloqueig.
  • Acoblador de capa per a una gestió senzilla de documents complexos incloent la vista prèvia de miniatures, permet agrupar formes via arrossegar i deixar anar, controlar la visibilitat de les formes o el bloqueig.
  • Layer docker for easy handling of complex documents including preview thumbnails, support for grouping shapes via drag and drop, controlling visibility of shapes or locking
  • Panel de capas para un sencillo manejo de documentos complejos, que incluye vista previa de miniaturas y agrupación de figuras mediante la operación de arrastrar y soltar, controlando la visibilidad de las figuras o su bloqueo.
  • Kihtide dokk keerukamate dokumentide hõlpsaks töötlemiseks, kaasa arvatud pisipiltide eelvaatlus, kujundite rühmitamise toetamine ainult lohistamisega, kujundite nähtavuse ja lukustamise määramine
  • Monimutkaisten tiedostojen helppoon hallintaan tasotelakka, joka sisältää esikatselukuvat, muotojen ryhmittelyn tuen vetämällä ja pudottamalla sekä muotojen näkyvyyden ja lukitsemisen hallinnan
  • Conteneurs de calques pour la manipulation facile de documents complexes incluant les vignettes d'aperçu, la prise en charge du groupement de formes par glisser-déposer, contrôle de la visibilité des formes et verrouillage
  • Doca de capas para xestionar facilmente documentos complexos. Inclúe miniaturas de vista previa, permite agrupar formas arrastrando e soltando, controlar a visibilidade das formas ou bloquealas.
  • Area di aggancio di livelli per una facile gestione di documenti complessi incluse miniature di anteprima, supporto di forme raggruppate tramite il trascinamento e rilascio, controllo della visibilità delle forme e blocco
  • Laagverankering voor gemakkelijke behandeling van complexe documenten inclusief voorbeeldminiaturen, ondersteuning voor groeperen van vormen via slepen en laten vallen, besturing van zichtbaarheid van vormen of vastzetten
  • Dokowanie warstw dla łatwiejszej obsługi złożonych dokumentów zawierający: podgląd miniatur, obsługę grupowania kształtów poprzez przeciągnij i upuść, sterowanie widocznością kształtów czy blokowanie.
  • Área de camadas para o tratamento de documentos complexos, incluindo miniaturas da antevisão, suporte para o agrupamento de formas por arrastamento, o controlo da visibilidade das formas ou o seu bloqueio
  • Área de camadas para tratamento de documentos complexos, incluindo miniaturas, suporte a agrupamento de formas com arrastar e soltar, controle de visibilidade das formas ou seu bloqueio
  • Docker vrstiev pre ľahkú manipuláciu so zložitými dokumentami vrátane náhľadu miniatúr, podpora pre zoskupovanie tvarov cez drag and drop, ovládanie viditeľnosti tvarov alebo zamykanie
  • Lagerpanel för enkel hantering av komplexa dokument inklusive miniatyrbilder för förhandsgranskning, stöd för att gruppera former via drag och släpp, styra eller låsning synlighet av former
  • Панель шарів для полегшення обробки складних документів з мініатюрами об’єктів, підтримка групування форм перетягуванням зі скиданням, керування показом форм та блокуванням.
  • xxLayer docker for easy handling of complex documents including preview thumbnails, support for grouping shapes via drag and drop, controlling visibility of shapes or lockingxx
  • 图层停靠栏用于方便处理复杂文档,包括缩略图预览,支持拖拽组合形状,控制形状的可见性或锁定。
  • Advanced path editing tool with great on-canvas editing capabilities
  • Alat za napredno uređivanje puta s velikom mogućnosti uređivanja na platnu
  • Eina avançada d'edició de camins amb una gran capacitat d'edició en el llenç
  • Eina avançada d'edició de camins amb una gran capacitat d'edició en el llenç
  • Advanced path editing tool with great on-canvas editing capabilities
  • Herramienta de edición avanzada de rutas con grandes capacidades de edición en el lienzo
  • Täiustatud kompleksjoone muutmise tööriist suurepäraste otse lõuendil redigeerimise võimalustega
  • Edistynyt polunmuokkaustyökalu, jolla on erinomaiset muokkausominaisuudet
  • Outil d'édition de chemin avancé avec des capacités d'édition dans les canevas performantes
  • Ferramenta avanzada de edición de camiños cunha gran funcionalidade de edición sobre o lenzo.
  • Strumento avanzato di modifica di tracciati con grandi capacità di modifica sul posto
  • Geavanceerde padbewerkingsgereedschap met prima bewerkingsmiddelen op het werkblad
  • Zaawansowane narzędzia edytowania ścieżki z szerokimi możliwościami edytowania na płótnie
  • Ferramenta de edição de caminhos avançada, com grandes capacidade de edição no local
  • Ferramenta avançada para edição de caminhos, com ótima capacidade de edição no local
  • Pokročilý nástroj na úpravu ciest s výbornými schopnosťami editácie na plátne
  • Avancerat konturredigeringsverktyg med utmärkta redigeringsmöjligheter direkt på duken
  • Інструмент редагування контурів з чудовими можливостями редагування безпосередньо на полотні.
  • xxAdvanced path editing tool with great on-canvas editing capabilitiesxx
  • 高级路径编辑工具,以及强大的画布编辑能力
  • Various drawing tools for creating path shapes including a draw path tool, a pencil tool as well as a calligraphy drawing tool
  • Razni alati za crtanje za kreiranje put oblike uključujući i put alat nacrtati ,olovka alat , kao i kaligrafija alat za crtanje
  • Diverses eines de dibuix per crear formes de camí incloent una eina de dibuix de camins, una eina de llapis i també una eina de dibuix de cal·ligrafia.
  • Diverses eines de dibuix per crear formes de camí incloent una eina de dibuix de camins, una eina de llapis i també una eina de dibuix de cal·ligrafia.
  • Various drawing tools for creating path shapes including a draw path tool, a pencil tool as well as a calligraphy drawing tool
  • Diversas herramientas de dibujo para crear figuras con rutas, entre las que se incluyen una herramienta de dibujo de rutas, una herramienta de pincel y una herramienta de dibujo caligráfico.
  • Mitmesugused kompleksjoontest kujundite loomise vahendid, sealhulgas kompleksjoone joonistamise tööriist, pliiatsitööriist ning kalligraafia loomise tööriist
  • Eri piirrostyökalut polkumuotojen luontiin: polunpiirto-, kynä- ja kalligrafinen piirrostyökalu
  • Outils de dessin variés incluant un outil de création de chemin, un crayon ainsi qu'un outil pour la calligraphie
  • Varias ferramentas de debuxo para crear formas de camiños, incluíndo unha ferramenta de debuxo de camiños, unha ferramenta de lapis, e unha ferramenta de debuxo caligráfico.
  • Vari strumenti di disegno per creare percorsi e forme che include uno strumento di tracciati, un matita e uno strumento calligrafico
  • Verschillende tekengereedschappen voor het maken van padvormen inclusief een tekenhulpmiddel voor paden, een potloodhulpmiddel evenals een tekengereedschap voor kalligrafie
  • Różne narzędzia rysownicze do tworzenia kształtów ścieżek uwzględniając w tym: narzędzie rysowania ścieżki, narzędzie ołówka, a także kaligraficzne narzędzia rysownicze
  • Diversas ferramentas de desenho para criar formas de caminhos, incluindo uma ferramenta de desenho de caminhos, uma ferramenta de lápis e uma ferramenta de desenho caligráfico
  • Diversas ferramentas de desenho para criar formas de caminhos, incluindo uma ferramenta de desenho de caminhos, uma ferramenta de lápis e uma ferramenta de desenho caligráfico
  • Rôzne kresliace nástroje na vytváranie tvarov ciest vrátane nástroja na kreslenie cesty, nástroja ceruzky ako aj kaligrafického kresliaceho nástroja
  • Diverse ritverktyg för att skapa konturformer som inkluderar ett konturritverktyg, ett pennverktyg samt ett kalligrafiskt ritverktyg
  • Різноманітні інструменти малювання для створення контурів, зокрема інструмент малювання контуру, олівець та інструмент каліграфічного малювання.
  • xxVarious drawing tools for creating path shapes including a draw path tool, a pencil tool as well as a calligraphy drawing toolxx
  • 多种绘图工具用于创作路径形状,包括路径绘制工具,铅笔工具和书法绘制工具
  • Gradient and pattern tools for easy on-canvas editing of gradient and pattern styles
  • Gradijent i uzorak alata za jednostavno na - platnu uređivanje gradijent i uzorak stilova
  • Eines de degradats i patrons per a una edició senzilla sobre el llenç d'estils de degradats i patrons
  • Eines de degradats i patrons per a una edició senzilla sobre el llenç d'estils de degradats i patrons
  • Gradient and pattern tools for easy on-canvas editing of gradient and pattern styles
  • Herramientas de degradados y patrones para una sencilla edición de estilos de degradados y patrones en el lienzo
  • Ülemineku- ja mustritööriistad üleminekute ja mustrite stiilide hõlpsaks redigeerimiseks otse lõuendil
  • Työkalut liukuvärien ja täyttökuvioiden helppoon muokkaukseen
  • Outils de gradient et de tramage pour l'édition facile de styles de gradients et de trames
  • Ferramentas de degradados e padróns para editar degradados e estilos de padróns facilmente sobre o lenzo.
  • Strumenti di gradienti e motivi per una facile modifica sul posto di stili di gradienti e motivi
  • Hulpmiddelen voor gradiënten en patronen voor gemakkelijk werken op het werkblad van gradiënten en patroonstijlen
  • Narzędzia gradientu i wzorca dla łatwiejszego ich edytowania na płótnie
  • Ferramentas de gradientes e padrões para uma edição simples no local dos estilos dos gradientes e dos padrões
  • Ferramentas de gradientes e padrões para fácil edição no local dos estilos dos gradientes e dos padrões
  • Nástroje na prechody a vzory pre ľahké editovanie na plátne štýlov prechodov a vzorov
  • Tonings- och mönsterverktyg för enkelt redigering av tonings- och mönsterstilar direkt på duken
  • Інструменти градієнта і візерунка для полегшення редагування на полотні областей з заповненням градієнтом і візерунком.
  • xxGradient and pattern tools for easy on-canvas editing of gradient and pattern stylesxx
  • 渐变和图案工具可以轻松地在画布上编辑渐变和图案样式
  • Top notch snapping facilities for guided drawing and editing (e.g. snapping to grid, guide lines, path nodes, bounding boxes, orthogonal positions, intersections of path shapes or extensions of lines and paths)
  • Vrhunska snapping objekti za vođene crtanje i uređivanje ( npr snapping na mrežu , vodič linije , staze čvorova , granični kutije , okomita pozicija , preplitanja put oblikuje ili proširenja linija i staze )
  • Capacitats òptimes d'ajust per dibuix guiat i edició (p.e. ajust a graella, línies de guia, nodes de camins, caixes contenidores, posicions ortogonals, interseccions de formes de camí o extensions de línies i camins)
  • Capacitats òptimes d'ajust per dibuix guiat i edició (p.e. ajust a graella, línies de guia, nodes de camins, caixes contenidores, posicions ortogonals, interseccions de formes de camí o extensions de línies i camins)
  • Top notch snapping facilities for guided drawing and editing (e.g. snapping to grid, guide lines, path nodes, bounding boxes, orthogonal positions, intersections of path shapes or extensions of lines and paths)
  • Capacidades de ajuste óptimas para el dibujo y la edición guiados (por ejemplo, ajustar a la rejilla, líneas guías, nodos de ruta, cuadros delimitadores, posiciones ortogonales, intersecciones de formas de rutas o extensiones de líneas y rutas)
  • Äärmiselt tulusad haardetööriistad juhtjoonte järgi joonistamiseks ja redigeerimiseks (nt tõmme alusvõrgule, juhtjooned, kompleksjoone sõlmed, piirdekastid, kompleksjoontest kujundite lõikekohad jms)
  • Huippulaatuinen kiinnitysominaisuus ohjattuun piirtämiseen ja muokkaukseen (esim. kiinnitys ruudukkoon, apuviivoihin, rajauslaatikkoon, kohtisuoraan asemaan, leikkaukseen, polkumuotoon tai viivojen ja polkujen jatkeisiin)
  • Fonctionnalités d'alignement top-niveau pour guider le dessin et l'édition (p.ex. alignement sur la grille, chemin nodal, boîtes, positions orthogonales, intersections de formes ou extensions de lignes ou de formes)
  • Funcionalidades de axuste de última xeración para editar e debuxar de maneira guiada (por exemplo, axustar á grade, liñas de guía, nodos de camiños, caixas de límites, posicións ortogonais, cruce de formas de camiños ou extensións de liñas e camiños).
  • Capacità di alto livello di regolazione per disegno e modifica guidata (ad es. aggancio alla griglia, linee guida, nodi di tracciato, riquadri collegati, posizioni ortgonali, intersezioni di forme di tracciato o estensioni di linee e tracciati)
  • Excellente vastklik faciliteiten voor geleid tekenen en bewerken (bijv. vastklikken aan raster, gidslijnen, padpunten, begrenzingsvakken, orthogonale posities, kruispunten van padvormen of extensies van lijnen en paden)
  • Możliwości przyciągania z najwyższej półki rysowania i edytowania ze wspomaganiem (np. przyciąganie do siatki, -prowadnic, węzłów ścieżki, pól ograniczających, prostopadłych położeń, przecięć kształtów ścieżek lub wydłużeń linii i ścieżek)
  • Capacidades de ajuste óptimas para o desenho e edição guiado (p.ex., ajustes à grelha, linhas-guias, nós de caminhos, áreas envolventes, posições ortogonais, intersecções de formas de caminhos ou extensões de linhas e caminhos)
  • Capacidades de melhor ajuste para desenho e edição guiado (p.ex., ajustes à grade, linhas-guias, nós de caminhos, caixas delimitadoras, posições ortogonais, intersecções de formas de caminhos ou extensões de linhas e caminhos)
  • Schopnosti prichytenia na vrch pre sprevádzané kreslenie a editovanie (napr. prichytenie k mriežke, pomocné čiary, uzly cesty, hraničné boxy, ortogonálne polohy, priesečníky tvarov cesty alebo rozšírenia čiar a ciest)
  • Väldigt bra låsfunktioner för att rita och redigera med styrning (t.ex. låsa till rutnät, styrlinjer, konturnoder, omgivande rutor, ortogonala positioner, korsningar av konturformer eller förlängningar av linjer och konturer)
  • Можливості використання прилипання та малювання за напрямними (прилипання до сітки, напрямні, вузли контурів, обмежувальні рамки, ортогональне розташування, перетини та розширення ліній і контурів).
  • xxTop notch snapping facilities for guided drawing and editing (e.g. snapping to grid, guide lines, path nodes, bounding boxes, orthogonal positions, intersections of path shapes or extensions of lines and paths)xx
  • 顶端对齐工具可以引导绘制和编辑(比如吸附到网格,参考线,路径节点,边界框,正交位置,路径焦点或线的延长线)
  • Includes many predefined shapes including basic shapes like stars, circle/ellipse, rectangle, image
  • Uključuje mnoge predefinisane oblike uključujući i osnovne oblike poput zvijezda , krugova / elipsa, pravougaonika, slika
  • Inclou moltes formes predefinides bàsiques com estels, cercles/el·lipses, rectangles, imatges
  • Inclou moltes formes predefinides bàsiques com estels, cercles/el·lipses, rectangles, imatges
  • Enthält viele vordefinierte Objekte einschließlich einfacher Objekte wie Sterne, Kreise, Ellipsen, Rechtecke und Bilder
  • Includes many predefined shapes including basic shapes like stars, circle/ellipse, rectangle, image
  • Incluye muchas formas predefinidas, entre otras figuras básicas como estrellas, círculos/elipses, rectángulos, imágenes
  • Hulk valmiskujundeid, sealhulgas tähed, ringid ja ellipsid, ristkülikud, pildid
  • Lukuisia esimääritettyjä muotoja (perusmuodot kuten tähdet, ympyrä ja soikio, nelikulmio, kuva)
  • Inclut de nombreuses formes prédéfinies, telles que les étoiles, cercles/ellipses, rectangles, images
  • Inclúe moitas formas predefinidas, entre elas formas básicas como estrelas, círculos, elipses, rectángulos e imaxes.
  • Include molte forme predefinite tra cui forme base come stelle, cerchi/ellissi, rettangoli e immagini
  • Omvat vele voorgedefinieerde vormen inclusief basisvormen als ster, cirkel/ellips, rechthoek, afbeelding
  • Zawiera wiele uprzednio stworzonych kształtów, do których można zaliczyć gwiazdy, okręgi, elipsy, prostokąty, obrazy
  • Inclui diversas formas predefinidas, incluindo formas básicas como as estrelas, círculos/elipses, rectângulos, imagens
  • Inclui diversas formas predefinidas, incluindo formas básicas como estrelas, círculos/elipses, retângulos e imagens
  • Obsahuje mnoho preddefinovaných tvarov vrátane základných tvarov ako hviezdy, kružnica/elipsa, obdĺžnik, obrázok
  • Innehåller många fördefinierade former inklusive grundläggande former som stjärnor, cirkel/ellips, rektangel, bild
  • Передбачено багато готових форм, зокрема базові форми зірки, кола або еліпса, прямокутника та зображення.
  • xxIncludes many predefined shapes including basic shapes like stars, circle/ellipse, rectangle, imagexx
  • 包含许多预定义形状,如星形,圆形/椭圆,矩形,图像
  • Artistic text shape with support for following path outlines (i.e. text on path)
  • Umjetnički oblik tekst s podrškom za sljedeći put naglašava ( tj tekst na putu )
  • Formes de text artístic que permeten camins de contorns de seguiment (p.e. text en camins)
  • Formes de text artístic que permeten camins de contorns de seguiment (p.e. text en camins)
  • Artistic text shape with support for following path outlines (i.e. text on path)
  • Forma de texto artístico con soporte para contornos de seguimiento de rutas (es decir, texto sobre una ruta)
  • Kunstiline tekstikujund, mis toetab kompleksjooni, st võimaldab asetada teksti kompleksjoonele
  • Taiteelliset tekstimuodot polunseurantatuella
  • Formes de texte artistiques prenant par exemple en charge la création de texte le long d'une courbe
  • Forma de texto artístico que permite seguir o contorno de camiños (é dicir, texto sobre camiños).
  • Forme di testo artistico con supporto per le bordature che seguono il tracciato (ad es. testo su tracciato)
  • Artistieke tekstvormen met ondersteuning voor het volden van paden (dwz. tekst op een pad)
  • Kształty artystycznego tekstu z obsługą dla następujących zarysów ścieżek (tj. tekst na ścieżce)
  • Forma de texto artístico com suporte para caminhos de seguimento (i.e., texto ao longo de um caminho)
  • Forma de texto artístico com suporte para caminhos de seguimento (isto é, texto no caminho)
  • Umelecký textový tvar s podporou pre nasledovacie obrysy cesty (napr. text na ceste)
  • Artistisk textform med stöd för att följa konturlinjer (t.ex. text på en kontur)
  • Форма художнього тексту для розташування тексту вздовж контуру.
  • xxArtistic text shape with support for following path outlines (i.e. text on path)xx
  • 艺术字形状,支持跟随路径轮廓(即路径上的文本)
  • Complex path operations and effects like boolean set operations, path flattening, rounding and refining as well as whirl/pinch effects
  • Kompleksne staze poslovanja i učinci poput Booleova postavljanje poslovanja , put ravnanje , zaokruživanje i rafiniranje kao vrtlog / za hvatanje učinci
  • Operacions complexes de camins i efectes com operacions de conjunts booleans, aplanament de camins, arrodoniment i afinació i també efectes de gir i pessic
  • Operacions complexes de camins i efectes com operacions de conjunts booleans, aplanament de camins, arrodoniment i afinació i també efectes de gir i pessic
  • Complex path operations and effects like boolean set operations, path flattening, rounding and refining as well as whirl/pinch effects
  • Operaciones de ruta complejas y efectos como operaciones booleanas con conjuntos, aplanamiento de rutas, redondeo y refinación así como también efectos de arremolinar/apretar
  • Ka keerukamad kompleksjoone operatsioonid ja efektid, näiteks tõeväärtustega operatsioonid, kompleksjoonte lamendamine, ümardamine ja täpsustamine, keerise efekt jms
  • Monimutkaiset polku-operaatiot ja -tehosteet kuten totuusarvo-operaatiot, polun tasoitus, pyöristys ja hienosäätö sekä pyörre- ja nipistystehosteet
  • Opérations de chemins complexes et effets tels que les opérateurs booléens, mise à plat et ajustements, de même que les effets de spirale et de pincement
  • Operacións de camiños complexas e efectos como operacións de grupos de valores booleanos; aplanamento, arredondamento e refinamento de camiños; así como efectos de xiro e picada.
  • Operazioni complesse su tracciati ed effetti del tipo operazioni su insiemi booleani, appiattimento di tracciati, arrotondamento e affinamento così come effetti di vortice/pizzico
  • Complexe bewerkingen van paden en effecten zoals booleaanse set bewerkingen, afvlakken van paden, afronding en verfijning evenals wervel/put-effecten
  • Złożone działania i efekty na ścieżce takie jak działania boolean, spłaszczanie ścieżki, zaokrąglanie, a także efekty polepszające wraz z efektami wiru/rozdmuchania
  • Opções complexas com caminhos e efeitos como as operações booleanas com conjuntos, o alisamento de caminhos, o arredondamento e a afinação, para além da ondulação/embate
  • Operações complexas com caminhos e efeitos, como operações booleanas com conjuntos, o nivelamento de caminhos, o arredondamento e refinamento, assim como efeitos de ondulação/aperto
  • Komplexné cestné operácie a efekty ako logické operácie, splošťovanie cesty, zaobľovanie a zjemňovanie ako aj efekty vírov.
  • Komplexa konturoperationer och effekter som Booleska mängdoperationer, konturutplattning, rundning och förfining samt virvla- och klämeffekter
  • Складні дії над контурами та ефекти, зокрема набір булівських операцій, спрощення контуру, заокруглення та покращення, ефекти вихору та втягування
  • xxComplex path operations and effects like boolean set operations, path flattening, rounding and refining as well as whirl/pinch effectsxx
  • 复杂路径操作和效果,例如布尔运算,路径扁平化,简化和优化,以及旋转和揉捏特效
  • Extensible by writing plugins for new tools, shapes and dockers
  • Extensible pisanjem dodataka za novim alatima , oblika i modnog brenda Dockers
  • Ampliable mitjançant l'escriptura de connectors per eines noves, formes i acobladors
  • Ampliable mitjançant l'escriptura de connectors per eines noves, formes i acobladors
  • Erweiterbar durch das Schreiben von Modulen für neue Werkzeuge, Objekte und andockbaren Dialogen
  • Extensible by writing plugins for new tools, shapes and dockers
  • Extensible mediante la escritura de complementos para nuevas herramientas, formas y paneles
  • Laiendamisvõimalus pluginate kirjutamise abil uute tööriistade, kujundite ja dokkide tarbeks
  • Laajennettavissa kirjoittamalla liitännäisiä uusille työkaluille, muodoille ja telakoille
  • Extensible par l'écriture de modules pour de nouveaux outils, formes et conteneurs
  • Permite ampliar as súas funcionalidades mediante complementos de ferramentas novas, de formas e de docas.
  • Espandibile tramite la scrittura di estensioni per nuovi strumenti, forme e aree di aggancio
  • Uit te breiden dor het schrijven van plug-ins voor nieuwe hulpmiddelen, vormen en verankering
  • Rozszerzalne wtyczki zapisu dla nowych narzędzi, kształtów i dokowań
  • Modular, através da criação de 'plugins' para novas ferramentas, formas e áreas acopláveis
  • Expansível através da criação de plugins para novas ferramentas, formas e áreas acopláveis
  • Rozšíriteľný pomocou písania pluginov pre nové nástroje, tvary a dockery
  • Utökningsbart genom att skriva insticksprogram för nya verktyg, former och paneler
  • Можливість розширення додатками нових інструментів, форм та панелей.
  • xxExtensible by writing plugins for new tools, shapes and dockersxx
  • 可通过工具,形状和停靠栏插件扩展功能
http://www.calligra.org/karbon/ https://bugs.kde.org/enter_bug.cgi?format=guided&product=karbon - http://kde.org/images/screenshots/karbon14.png + https://cdn.kde.org/screenshots/karbon/karbon.png KDE calligrakarbon
diff --git a/karbon/data/org.kde.karbon.desktop b/karbon/data/org.kde.karbon.desktop index ad42809c0cd..f73a84e4c84 100644 --- a/karbon/data/org.kde.karbon.desktop +++ b/karbon/data/org.kde.karbon.desktop @@ -1,156 +1,156 @@ [Desktop Entry] Type=Application Name=Karbon Name[bs]=Karbon Name[ca]=Karbon Name[ca@valencia]=Karbon Name[cs]=Karbon Name[da]=Karbon Name[de]=Karbon Name[el]=Karbon Name[en_GB]=Karbon Name[es]=Karbon Name[et]=Karbon Name[eu]=Karbon Name[fi]=Karbon Name[fr]=Karbon Name[gl]=Karbon Name[hu]=Karbon Name[ia]=Karbon Name[it]=Karbon Name[ja]=Karbon Name[kk]=Karbon Name[ko]=Karbon Name[lt]=Karbon Name[mr]=कार्बन Name[nb]=Karbon Name[nl]=Karbon Name[pl]=Karbon Name[pt]=Karbon Name[pt_BR]=Karbon Name[ru]=Karbon Name[sk]=Karbon Name[sl]=Karbon Name[sv]=Karbon Name[tr]=Karbon Name[ug]=Karbon Name[uk]=Karbon Name[x-test]=xxKarbonxx Exec=karbon %U GenericName=Scalable Graphics GenericName[af]=Skaal veranderbare Grafieka GenericName[bg]=Векторна графика GenericName[bs]=Grafika koja se može skalirati GenericName[ca]=Gràfics escalables GenericName[ca@valencia]=Gràfics escalables GenericName[cs]=Škálovatelná grafika GenericName[cy]=Graffeg Graddadwy GenericName[da]=Skalérbar grafik GenericName[de]=Vektorgrafik GenericName[el]=Διανυσματικά γραφικά GenericName[en_GB]=Scalable Graphics GenericName[eo]=Skaleblaj vektorgrafikoj GenericName[es]=Gráficos escalables GenericName[et]=Vektorgraafika GenericName[eu]=Grafiko eskalagarriak GenericName[fi]=Skaalautuva grafiikka GenericName[fr]=Graphiques vectoriels GenericName[fy]=GenericFektorôfbyldings GenericName[ga]=Grafaic Inscálaithe GenericName[gl]=Gráficos de tamaño variábel GenericName[he]=גרפיקה מדורגת GenericName[hi]=स्केलेबल ग्राफिक्स GenericName[hne]=स्केलेबल ग्राफिक्स GenericName[hr]=Skalabilna grafika GenericName[hu]=Vektoros rajzoló GenericName[ia]=Graphicos scalabile GenericName[is]=Scalable Graphics GenericName[it]=Grafica vettoriale GenericName[ja]=スケーラブルグラフィックス GenericName[kk]=Масштабтауға келетін суреттер GenericName[ko]=크기 조정이 가능한 그래픽 GenericName[lt]=Kintamo dydžio grafika GenericName[lv]=Mērogojama Grafika GenericName[mr]=स्केलेबल ग्राफिक्स GenericName[ms]=Grafik Boleh-skala GenericName[nb]=Skalerbar grafikk GenericName[nds]=Vektorgrafik GenericName[ne]=मापनयोग्य ग्राफिक्स GenericName[nl]=Vectorafbeeldingen GenericName[pl]=Skalowalna grafika GenericName[pt]=Imagens Escaláveis GenericName[pt_BR]=Imagens vetoriais GenericName[ro]=Grafică scalabilă GenericName[ru]=Векторные рисунки GenericName[se]=Skálehahtti grafihkka GenericName[sk]=Škálovateľná grafika GenericName[sl]=Raztegljiva grafika GenericName[sv]=Skalbar grafik GenericName[ta]=அளக்கக்கூடிய வண்ணங்கள் GenericName[tg]=Тасвирҳои вектор GenericName[tr]=Oranlanabilir Grafikler GenericName[uk]=Масштабовна графіка GenericName[uz]=Vektor grafikasi GenericName[uz@cyrillic]=Вектор графикаси GenericName[wa]=Imådjes metåves al schåle GenericName[xh]=Imizobo Ekalishekayo GenericName[x-test]=xxScalable Graphicsxx GenericName[zh_CN]=可缩放图像 GenericName[zh_TW]=可縮放向量圖 MimeType=application/vnd.oasis.opendocument.graphics;application/x-karbon;image/x-eps;application/postscript;image/svg+xml;image/svg+xml-compressed;image/x-wmf;application/pdf;image/x-xfig; # kghostview has 6 for application/postscript, we need to be under that... InitialPreference=5 X-KDE-ServiceTypes=Calligra/Application Icon=calligrakarbon X-KDE-NativeMimeType=application/vnd.oasis.opendocument.graphics X-Calligra-DefaultMimeTypes=application/vnd.oasis.opendocument.graphics,application/x-karbon,image/x-eps,application/postscript,image/svg+xml,image/svg+xml-compressed,image/x-wmf,application/pdf, -Categories=Qt;KDE;Graphics;Office; +Categories=Qt;KDE;Graphics;Office;VectorGraphics; Comment=Create scalable vector drawings Comment[bg]=Създаване на векторни графики Comment[bs]=Kreiraj vektorski crtež koji se može skalirati Comment[ca]=Crea dibuixos de vectors escalables Comment[ca@valencia]=Crea dibuixos de vectors escalables Comment[cs]=Vytvářejte vektorové kresby Comment[da]=Tegn skalerbare vektortegninger Comment[de]=Skalierbare Vektorzeichungen erstellen Comment[el]=Δημιουργία διανυσματικών γραφικών Comment[en_GB]=Create scalable vector drawings Comment[es]=Crear dibujos vectoriales escalables Comment[et]=Skaleeritavate vektrojoonistuste loomine Comment[eu]=Sortu bektore-marrazki eskalagarriak Comment[fi]=Luo vektorigrafiikkapiirroksia Comment[fr]=Créer des tracés vectoriels Comment[fy]=Te skalen fektor ôfbyldings oanmeitsje Comment[ga]=Cruthaigh líníochtaí veicteoireacha inscálaithe Comment[gl]=Cree debuxos vectoriais de tamaño variábel. Comment[he]=יצירת גרפיקה מדורגת עם וקטורים Comment[hi]=स्केलेबल वेक्टर ड्राइंग बनाएँ Comment[hne]=स्केलेबल वेक्टर ड्राइंग बनाव Comment[hu]=Átméretezhető vektorgrafikák létrehozása Comment[it]=Crea grafica vettoriale riscalabile Comment[ja]=スケーラブルベクター描画を作成 Comment[kk]=Масштабтауға болатын векторлы суретті салу Comment[ko]=크기 조정이 가능한 벡터 그래픽 만들기 Comment[lv]=Veido mērogojamus vektoru zīmējumus Comment[mr]=स्केलेबल व्हेक्टर चित्रे निर्माण करा Comment[nb]=Lag skalerbare vektortegninger Comment[nds]=Skaleerbor Vektorgrafiken opstellen Comment[ne]=मापनयोग्य भेक्टर रेखाचित्र सिर्जना गर्नुहोस् Comment[nl]=Schaalbare vectorafbeeldingen aanmaken Comment[pl]=Twórz skalowalne rysunki wektorowe Comment[pt]=Criar desenhos vectoriais Comment[pt_BR]=Criar desenhos vetoriais Comment[ru]=Создать векторный рисунок Comment[sk]=Vytvoriť scalable vector drawings Comment[sl]=Ustvarite raztegljive vektorske risbe Comment[sv]=Skapa skalbar vektorgrafik Comment[tg]=Эҷоди нақшаи тасвирҳои вектор Comment[tr]=Boyutlandırılabilir vektör çizimler oluştur Comment[uk]=Створити зображення з масштабовних векторів Comment[wa]=Ahiver des dessinaedjes pa royes metåves al schåle Comment[x-test]=xxCreate scalable vector drawingsxx Comment[zh_CN]=创建可缩放矢量绘图 Comment[zh_TW]=建立可調整向量圖 X-DocPath=http://userbase.kde.org/Special:MyLanguage/Karbon X-DBUS-StartupType=Multi X-DBUS-ServiceName=org.kde.karbon diff --git a/karbon/stencils/Flags/cambodia.desktop b/karbon/stencils/Flags/cambodia.desktop index d2af05b715c..8943953ed5a 100644 --- a/karbon/stencils/Flags/cambodia.desktop +++ b/karbon/stencils/Flags/cambodia.desktop @@ -1,43 +1,43 @@ [Desktop Entry] Name=Cambodia Name[bg]=Камбоджа Name[bs]=Kambodža Name[ca]=Cambodja Name[ca@valencia]=Cambodja Name[cs]=Kambodža Name[da]=Cambodia Name[de]=Kambodscha Name[el]=Καμπότζη Name[en_GB]=Cambodia Name[eo]=Kamboĝo Name[es]=Camboya Name[et]=Kambodža Name[eu]=Kanbodia Name[fi]=Kambodža Name[fr]=Cambodge Name[ga]=An Chambóid -Name[gl]=Camboxa +Name[gl]=Cambodja Name[hu]=Kambodzsa Name[ia]=Cambodia Name[it]=Cambogia Name[ja]=カンボジア Name[kk]=Камбоджа Name[ko]=캄보디아 Name[mr]=कंबोडिया Name[nb]=Kambodsja Name[nds]=Kambodscha Name[nl]=Cambodja Name[pl]=Kambodża Name[pt]=Cambodja Name[pt_BR]=Camboja Name[ru]=Камбоджа Name[se]=Kamboža Name[sk]=Kambodža Name[sl]=Kambodža Name[sv]=Kambodja Name[tr]=Kamboçya Name[ug]=كامبودژا Name[uk]=Камбоджа Name[x-test]=xxCambodiaxx Name[zh_CN]=柬埔寨 Name[zh_TW]=柬埔寨 diff --git a/karbon/stencils/Flags/fiji.desktop b/karbon/stencils/Flags/fiji.desktop index 3397f4abff3..65627a8a22d 100644 --- a/karbon/stencils/Flags/fiji.desktop +++ b/karbon/stencils/Flags/fiji.desktop @@ -1,42 +1,42 @@ [Desktop Entry] Name=Fiji Name[bg]=Фиджи Name[bs]=Fiđi Name[ca]=Fiji Name[ca@valencia]=Fiji Name[cs]=Fidži Name[da]=Fiji Name[de]=Fidschi Name[el]=Φίτζι Name[en_GB]=Fiji Name[es]=Fiyi Name[et]=Fidži Name[eu]=Fiji Name[fi]=Fidži Name[fr]=Fidji Name[ga]=Fidsí -Name[gl]=Fixi +Name[gl]=Fidxi Name[hu]=Fidzsi Name[ia]=Fiji Name[it]=Fiji Name[ja]=フィジー Name[kk]=Фиджи Name[ko]=피지 Name[mr]=फीजी Name[nb]=Fiji Name[nds]=Fidschi Name[nl]=Fiji Name[pl]=Fidżi Name[pt]=Fiji Name[pt_BR]=Fiji Name[ru]=Острова Фиджи Name[se]=Fiji Name[sk]=Fidži Name[sl]=Fidži Name[sv]=Fiji Name[tr]=Fiji Name[ug]=فىجى Name[uk]=Фіджі Name[x-test]=xxFijixx Name[zh_CN]=斐济 Name[zh_TW]=菲濟 diff --git a/karbon/stencils/Flags/kyrgyzstan.desktop b/karbon/stencils/Flags/kyrgyzstan.desktop index d9417f05f8a..1a39f56fed1 100644 --- a/karbon/stencils/Flags/kyrgyzstan.desktop +++ b/karbon/stencils/Flags/kyrgyzstan.desktop @@ -1,43 +1,43 @@ [Desktop Entry] Name=Kyrgyzstan Name[bg]=Киргизстан Name[bs]=Kirgistan Name[ca]=Kirguizistan Name[ca@valencia]=Kirguizistan Name[cs]=Kyrgyzstán Name[da]=Kirgizistan Name[de]=Kirgisistan Name[el]=Κιργιζία Name[en_GB]=Kyrgyzstan Name[eo]=Kirgizio Name[es]=Kirguistán Name[et]=Kõrgõzstan Name[eu]=Kirgizistan Name[fi]=Kirgisia Name[fr]=Khirgizistan Name[ga]=An Chirgeastáin -Name[gl]=Quirguicistán +Name[gl]=Kirguizistán Name[hu]=Kirgizisztán Name[ia]=Kirghizistan Name[it]=Kirghizistan Name[ja]=キルギス Name[kk]=Қырғызстан Name[ko]=키르기스스탄 Name[mr]=कीर्गीझस्तान Name[nb]=Kirgisistan Name[nds]=Kirgisien Name[nl]=Kirgizië Name[pl]=Kirgistan Name[pt]=Quirguistão Name[pt_BR]=Quirguistão Name[ru]=Киргизия Name[se]=Kirgisistan Name[sk]=Kirgizsko Name[sl]=Kirgizistan Name[sv]=Kirgizistan Name[tr]=Kırgızistan Name[ug]=قىرغىزىستان Name[uk]=Киргизстан Name[x-test]=xxKyrgyzstanxx Name[zh_CN]=吉尔吉斯斯坦 Name[zh_TW]=吉爾吉斯 diff --git a/karbon/stencils/Flags/turkmenistan.desktop b/karbon/stencils/Flags/turkmenistan.desktop index 2031053184c..cfc4c9acd27 100644 --- a/karbon/stencils/Flags/turkmenistan.desktop +++ b/karbon/stencils/Flags/turkmenistan.desktop @@ -1,43 +1,43 @@ [Desktop Entry] Name=Turkmenistan Name[bg]=Туркменистан Name[bs]=Turkmenistan Name[ca]=Turkmenistan Name[ca@valencia]=Turkmenistan Name[cs]=Turkmenistán Name[da]=Turkmenistan Name[de]=Turkmenistan Name[el]=Τουρκμενιστάν Name[en_GB]=Turkmenistan Name[eo]=Turkmenio Name[es]=Turkmenistán Name[et]=Türkmenistan Name[eu]=Turkmenistan Name[fi]=Turkmenistan Name[fr]=Turkmenistan Name[ga]=An Tuircméanastáin -Name[gl]=Turkmenistán +Name[gl]=Turcomenistán Name[hu]=Türkmenisztán Name[ia]=Turkmenistan Name[it]=Turkmenistan Name[ja]=トルクメニスタン Name[kk]=Түрікменстан Name[ko]=투르크메니스탄 Name[mr]=तुर्कमेनीस्तान Name[nb]=Turkmenistan Name[nds]=Turkmenistan Name[nl]=Turkmenistan Name[pl]=Turkmenistan Name[pt]=Turquemenistão Name[pt_BR]=Turcomenistão Name[ru]=Туркменистан Name[se]=Turkmenistan Name[sk]=Turkménsko Name[sl]=Turkmenistan Name[sv]=Turkmenistan Name[tr]=Türkmenistan Name[ug]=تۈركمەنىستان Name[uk]=Туркменістан Name[x-test]=xxTurkmenistanxx Name[zh_CN]=土库曼斯坦 Name[zh_TW]=土庫曼 diff --git a/karbon/stencils/Optics/scope.desktop b/karbon/stencils/Optics/scope.desktop index db4eb3ef2aa..3ab6c7172cb 100644 --- a/karbon/stencils/Optics/scope.desktop +++ b/karbon/stencils/Optics/scope.desktop @@ -1,35 +1,36 @@ [Desktop Entry] Name=Oscilloscope Name[bg]=Осцилоскоп Name[bs]=Osciloskop Name[ca]=Oscil·loscopi Name[ca@valencia]=Oscil·loscopi Name[cs]=Osciloskop Name[da]=Oscilloskop Name[de]=Oszilloskop Name[el]=Παλμογράφος Name[en_GB]=Oscilloscope Name[es]=Osciloscopio Name[et]=Ostsilloskoop Name[eu]=Osziloskopioa Name[fr]=Oscilloscope Name[gl]=Osciloscopio Name[hu]=Oszcilloszkóp Name[it]=Oscilloscopio Name[ja]=オシロスコープ Name[kk]=Осциллограф Name[lt]=Osciloskopas Name[nb]=Oscilloskop Name[nl]=oscilloscoop Name[pl]=Oscyloskop Name[pt]=Osciloscópio Name[pt_BR]=Osciloscópio Name[ru]=Осциллограф Name[sk]=Osciloskop Name[sl]=Osciloskop Name[sv]=Oscilloskop Name[tr]=Osiloskop Name[ug]=Oscilloscope Name[uk]=Осцилоскоп Name[x-test]=xxOscilloscopexx +Name[zh_CN]=示波器 Name[zh_TW]=示波器 diff --git a/libs/flake/CMakeLists.txt b/libs/flake/CMakeLists.txt index f1d617b6490..5100d45fbf5 100644 --- a/libs/flake/CMakeLists.txt +++ b/libs/flake/CMakeLists.txt @@ -1,356 +1,358 @@ project(flake) include_directories(${FLAKE_INCLUDES} ) add_subdirectory(styles) -add_subdirectory(tests) +if(BUILD_TESTING) + add_subdirectory(tests) +endif() add_subdirectory(pics) set(flake_SRCS KoGradientHelper.cpp KoFlake.cpp KoCanvasBase.cpp KoResourceManager_p.cpp KoCanvasResourceManager.cpp KoDocumentResourceManager.cpp KoCanvasObserverBase.cpp KoCanvasSupervisor.cpp KoCutController.cpp KoCopyController.cpp KoDockFactoryBase.cpp KoDockRegistry.cpp KoDataCenterBase.cpp KoInsets.cpp KoPasteController.cpp KoGuidesData.cpp KoGridData.cpp KoPathShape.cpp KoPathPoint.cpp KoPathSegment.cpp KoSelection.cpp KoShape.cpp KoShapeAnchor.cpp KoShapeBasedDocumentBase.cpp KoShapeApplicationData.cpp KoShapeContainer.cpp KoShapeContainerModel.cpp KoShapeContainerDefaultModel.cpp KoShapeGroup.cpp KoShapeManagerPaintingStrategy.cpp KoShapeManager.cpp KoShapePaintingContext.cpp KoShapePainter.cpp KoFrameShape.cpp KoUnavailShape.cpp KoMarkerData.cpp KoMarker.cpp KoMarkerCollection.cpp KoMarkerSharedLoadingData.cpp #KoUnavailData.cpp #KoUnavailCollection.cpp KoToolBase.cpp KoCanvasController.cpp KoCanvasControllerWidget.cpp KoCanvasControllerWidgetViewport_p.cpp KoShapeRegistry.cpp KoDeferredShapeFactoryBase.cpp KoToolFactoryBase.cpp KoPathShapeFactory.cpp KoShapeFactoryBase.cpp KoShapeUserData.cpp KoParameterShape.cpp KoPointerEvent.cpp KoShapeController.cpp KoToolSelection.cpp KoShapeLayer.cpp KoPostscriptPaintDevice.cpp KoInputDevice.cpp KoToolManager_p.cpp KoToolManager.cpp KoToolRegistry.cpp KoToolProxy.cpp KoShapeSavingContext.cpp KoShapeLoadingContext.cpp KoLoadingShapeUpdater.cpp KoPathShapeLoader.cpp KoShapeStrokeModel.cpp KoShapeStroke.cpp KoShapeBackground.cpp KoColorBackground.cpp KoGradientBackground.cpp KoOdfGradientBackground.cpp KoHatchBackground.cpp KoPatternBackground.cpp KoShapeConfigWidgetBase.cpp KoShapeConfigFactoryBase.cpp KoDrag.cpp KoDragOdfSaveHelper.cpp KoShapeOdfSaveHelper.cpp KoShapePaste.cpp KoConnectionPoint.cpp KoConnectionShape.cpp KoConnectionShapeLoadingUpdater.cpp KoConnectionShapeFactory.cpp KoConnectionShapeConfigWidget.cpp KoSnapGuide.cpp KoSnapProxy.cpp KoSnapStrategy.cpp KoSnapData.cpp SnapGuideConfigWidget.cpp KoShapeShadow.cpp KoSharedLoadingData.cpp KoSharedSavingData.cpp KoViewConverter.cpp KoInputDeviceHandler.cpp KoInputDeviceHandlerEvent.cpp KoInputDeviceHandlerRegistry.cpp KoEventAction.cpp KoEventActionFactoryBase.cpp KoEventActionRegistry.cpp KoImageData.cpp KoImageData_p.cpp KoImageCollection.cpp KoOdfWorkaround.cpp KoFilterEffect.cpp KoFilterEffectStack.cpp KoFilterEffectFactoryBase.cpp KoFilterEffectRegistry.cpp KoFilterEffectConfigWidgetBase.cpp KoFilterEffectRenderContext.cpp KoFilterEffectLoadingContext.cpp KoTextShapeDataBase.cpp KoTosContainer.cpp KoTosContainerModel.cpp KoClipPath.cpp KoCurveFit.cpp KoAnnotationLayoutManager.cpp commands/KoShapeGroupCommand.cpp commands/KoShapeAlignCommand.cpp commands/KoShapeBackgroundCommand.cpp commands/KoShapeCreateCommand.cpp commands/KoShapeDeleteCommand.cpp commands/KoShapeDistributeCommand.cpp commands/KoShapeLockCommand.cpp commands/KoShapeMoveCommand.cpp commands/KoShapeShearCommand.cpp commands/KoShapeSizeCommand.cpp commands/KoShapeStrokeCommand.cpp commands/KoShapeUngroupCommand.cpp commands/KoShapeReorderCommand.cpp commands/KoShapeKeepAspectRatioCommand.cpp commands/KoPathBaseCommand.cpp commands/KoPathPointMoveCommand.cpp commands/KoPathControlPointMoveCommand.cpp commands/KoPathPointTypeCommand.cpp commands/KoPathPointRemoveCommand.cpp commands/KoPathPointInsertCommand.cpp commands/KoPathSegmentBreakCommand.cpp commands/KoPathBreakAtPointCommand.cpp commands/KoPathSegmentTypeCommand.cpp commands/KoPathCombineCommand.cpp commands/KoSubpathRemoveCommand.cpp commands/KoSubpathJoinCommand.cpp commands/KoParameterHandleMoveCommand.cpp commands/KoParameterToPathCommand.cpp commands/KoShapeTransformCommand.cpp commands/KoPathFillRuleCommand.cpp commands/KoConnectionShapeTypeCommand.cpp commands/KoShapeShadowCommand.cpp commands/KoPathReverseCommand.cpp commands/KoEventActionAddCommand.cpp commands/KoEventActionRemoveCommand.cpp commands/KoShapeRenameCommand.cpp commands/KoShapeRunAroundCommand.cpp commands/KoPathPointMergeCommand.cpp commands/KoShapeTransparencyCommand.cpp commands/KoShapeClipCommand.cpp commands/KoShapeUnclipCommand.cpp commands/KoPathShapeMarkerCommand.cpp commands/KoShapeConnectionChangeCommand.cpp tools/KoCreateShapeStrategy.cpp tools/KoPathToolFactory.cpp tools/KoPathTool.cpp tools/KoPathToolSelection.cpp tools/KoPathToolHandle.cpp tools/PathToolOptionWidget.cpp tools/KoPathPointRubberSelectStrategy.cpp tools/KoPathPointMoveStrategy.cpp tools/KoPathConnectionPointStrategy.cpp tools/KoPathControlPointMoveStrategy.cpp tools/KoParameterChangeStrategy.cpp tools/KoZoomTool.cpp tools/KoZoomToolFactory.cpp tools/KoZoomToolWidget.cpp tools/KoZoomStrategy.cpp tools/KoPanTool.cpp tools/KoPanToolFactory.cpp tools/KoInteractionTool.cpp tools/KoInteractionStrategy.cpp tools/KoCreateShapesTool.cpp tools/KoCreateShapesToolFactory.cpp tools/KoShapeRubberSelectStrategy.cpp tools/KoPathSegmentChangeStrategy.cpp svg/SvgUtil.cpp svg/SvgGraphicContext.cpp svg/SvgSavingContext.cpp svg/SvgWriter.cpp svg/SvgStyleWriter.cpp svg/SvgShape.cpp svg/SvgParser.cpp svg/SvgStyleParser.cpp svg/SvgGradientHelper.cpp svg/SvgPatternHelper.cpp svg/SvgFilterHelper.cpp svg/SvgCssHelper.cpp svg/SvgClipPathHelper.cpp svg/SvgLoadingContext.cpp svg/SvgShapeFactory.cpp FlakeDebug.cpp ) ki18n_wrap_ui(flake_SRCS tools/PathToolOptionWidgetBase.ui KoConnectionShapeConfigWidget.ui SnapGuideConfigWidget.ui tools/KoZoomToolWidget.ui ) add_library(flake SHARED ${flake_SRCS}) generate_export_header(flake BASE_NAME flake) target_link_libraries(flake PUBLIC pigmentcms kowidgetutils koodf kundo2 PRIVATE koplugin KF5::IconThemes Qt5::Svg ) set_target_properties(flake PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) ########### install files ############### install(TARGETS flake ${INSTALL_TARGETS_DEFAULT_ARGS}) # TODO: with the new embedded JSON data for plugins there is no schema ATM to define extended properties # flake.desktop # flaketool.desktop # flakeshape.desktop # flakedevice.desktop # presentationeventaction.desktop # scripteventaction.desktop # filtereffect.desktop if (SHOULD_BUILD_DEVEL_HEADERS) install( FILES KoCanvasBase.h KoCanvasController.h KoCanvasControllerWidget.h KoCanvasObserverBase.h KoCanvasSupervisor.h KoCanvasResourceManager.h KoDocumentResourceManager.h KoConnectionPoint.h KoCopyController.h KoCutController.h KoDataCenterBase.h KoDockFactoryBase.h KoDockRegistry.h KoDeferredShapeFactoryBase.h KoDrag.h KoDragOdfSaveHelper.h KoFlake.h KoFrameShape.h KoGuidesData.h KoGridData.h KoImageCollection.h KoImageData.h KoInputDevice.h KoInsets.h KoPathSegment.h KoPointerEvent.h KoRTree.h KoSelection.h KoShape.h KoShapeAnchor.h KoShapeStrokeModel.h KoToolSelection.h KoShapeContainer.h KoShapeContainerModel.h KoShapeContainerDefaultModel.h KoShapeController.h KoShapeBasedDocumentBase.h KoShapeFactoryBase.h KoShapeGroup.h KoShapeLayer.h KoShapePainter.h KoShapeLoadingContext.h KoShapeOdfSaveHelper.h KoLoadingShapeUpdater.h KoShapeConfigWidgetBase.h KoShapeManager.h KoShapeManagerPaintingStrategy.h KoShapeRegistry.h KoShapeSavingContext.h KoShapeUserData.h KoShapeApplicationData.h KoSharedSavingData.h KoTextShapeDataBase.h KoToolBase.h KoToolManager.h KoToolFactoryBase.h KoToolProxy.h KoToolRegistry.h KoFilterEffect.h KoFilterEffectStack.h KoFilterEffectFactoryBase.h KoFilterEffectRegistry.h KoFilterEffectConfigWidgetBase.h KoFilterEffectRenderContext.h KoFilterEffectLoadingContext.h commands/KoEventActionAddCommand.h commands/KoEventActionRemoveCommand.h commands/KoParameterToPathCommand.h commands/KoPathCombineCommand.h commands/KoPathControlPointMoveCommand.h commands/KoPathFillRuleCommand.h commands/KoPathPointInsertCommand.h commands/KoPathPointMergeCommand.h commands/KoPathPointMoveCommand.h commands/KoPathPointRemoveCommand.h commands/KoPathPointTypeCommand.h commands/KoPathReverseCommand.h commands/KoPathSegmentTypeCommand.h commands/KoShapeAlignCommand.h commands/KoShapeBackgroundCommand.h commands/KoShapeStrokeCommand.h commands/KoShapeCreateCommand.h commands/KoShapeDeleteCommand.h commands/KoShapeDistributeCommand.h commands/KoShapeGroupCommand.h commands/KoShapeMoveCommand.h commands/KoShapeRenameCommand.h commands/KoShapeReorderCommand.h commands/KoShapeShadowCommand.h commands/KoShapeShearCommand.h commands/KoShapeSizeCommand.h commands/KoShapeTransformCommand.h commands/KoShapeUngroupCommand.h commands/KoSubpathRemoveCommand.h commands/KoPathShapeMarkerCommand.h tools/KoInteractionTool.h tools/KoPanTool.h KoViewConverter.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligra COMPONENT Devel ) endif() diff --git a/libs/flake/svg/SvgParser.cpp b/libs/flake/svg/SvgParser.cpp index 30065c91c60..193529d4b86 100644 --- a/libs/flake/svg/SvgParser.cpp +++ b/libs/flake/svg/SvgParser.cpp @@ -1,1337 +1,1337 @@ /* This file is part of the KDE project * Copyright (C) 2002-2005,2007 Rob Buis * Copyright (C) 2002-2004 Nicolas Goutte * Copyright (C) 2005-2006 Tim Beaulen * Copyright (C) 2005-2009 Jan Hambrecht * Copyright (C) 2005,2007 Thomas Zander * Copyright (C) 2006-2007 Inge Wallin * Copyright (C) 2007-2008,2010 Thorsten Zachmann * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "SvgParser.h" #include "SvgUtil.h" #include "SvgShape.h" #include "SvgGraphicContext.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "KoFilterEffectStack.h" #include "KoFilterEffectLoadingContext.h" #include #include #include #include SvgParser::SvgParser(KoDocumentResourceManager *documentResourceManager) : m_context(documentResourceManager) , m_documentResourceManager(documentResourceManager) { } SvgParser::~SvgParser() { } void SvgParser::setXmlBaseDir(const QString &baseDir) { m_context.setInitialXmlBaseDir(baseDir); } QList SvgParser::shapes() const { return m_shapes; } // Helper functions // --------------------------------------------------------------------------------------- SvgGradientHelper* SvgParser::findGradient(const QString &id, const QString &href) { // check if gradient was already parsed, and return it if (m_gradients.contains(id)) return &m_gradients[ id ]; // check if gradient was stored for later parsing if (!m_context.hasDefinition(id)) return 0; const KoXmlElement &e = m_context.definition(id); if (!e.tagName().contains("Gradient")) return 0; if (e.childNodesCount() == 0) { QString mhref = e.attribute("xlink:href").mid(1); if (m_context.hasDefinition(mhref)) return findGradient(mhref, id); else return 0; } else { // ok parse gradient now if (! parseGradient(m_context.definition(id), m_context.definition(href))) return 0; } // return successfully parsed gradient or NULL QString n; if (href.isEmpty()) n = id; else n = href; if (m_gradients.contains(n)) return &m_gradients[ n ]; else return 0; } SvgPatternHelper* SvgParser::findPattern(const QString &id) { // check if pattern was already parsed, and return it if (m_patterns.contains(id)) return &m_patterns[ id ]; // check if pattern was stored for later parsing if (!m_context.hasDefinition(id)) return 0; SvgPatternHelper pattern; const KoXmlElement &e = m_context.definition(id); if (e.tagName() != "pattern") return 0; // are we referencing another pattern ? if (e.hasAttribute("xlink:href")) { QString mhref = e.attribute("xlink:href").mid(1); SvgPatternHelper *refPattern = findPattern(mhref); // inherit attributes of referenced pattern if (refPattern) pattern = *refPattern; } // ok parse pattern now parsePattern(pattern, m_context.definition(id)); // add to parsed pattern list m_patterns.insert(id, pattern); return &m_patterns[ id ]; } SvgFilterHelper* SvgParser::findFilter(const QString &id, const QString &href) { // check if filter was already parsed, and return it if (m_filters.contains(id)) return &m_filters[ id ]; // check if filter was stored for later parsing if (!m_context.hasDefinition(id)) return 0; const KoXmlElement &e = m_context.definition(id); if (e.childNodesCount() == 0) { QString mhref = e.attribute("xlink:href").mid(1); if (m_context.hasDefinition(mhref)) return findFilter(mhref, id); else return 0; } else { // ok parse filter now if (! parseFilter(m_context.definition(id), m_context.definition(href))) return 0; } // return successfully parsed filter or NULL QString n; if (href.isEmpty()) n = id; else n = href; if (m_filters.contains(n)) return &m_filters[ n ]; else return 0; } SvgClipPathHelper* SvgParser::findClipPath(const QString &id, const QString &href) { // check if clip path was already parsed, and return it if (m_clipPaths.contains(id)) return &m_clipPaths[ id ]; // check if clip path was stored for later parsing if (!m_context.hasDefinition(id)) return 0; const KoXmlElement &e = m_context.definition(id); if (e.childNodesCount() == 0) { QString mhref = e.attribute("xlink:href").mid(1); if (m_context.hasDefinition(mhref)) return findClipPath(mhref, id); else return 0; } else { // ok clip path filter now if (! parseClipPath(m_context.definition(id), m_context.definition(href))) return 0; } // return successfully parsed clip path or NULL const QString n = href.isEmpty() ? id : href; if (m_clipPaths.contains(n)) return &m_clipPaths[ n ]; else return 0; } // Parsing functions // --------------------------------------------------------------------------------------- qreal SvgParser::parseUnit(const QString &unit, bool horiz, bool vert, const QRectF &bbox) { return SvgUtil::parseUnit(m_context.currentGC(), unit, horiz, vert, bbox); } qreal SvgParser::parseUnitX(const QString &unit) { return SvgUtil::parseUnitX(m_context.currentGC(), unit); } qreal SvgParser::parseUnitY(const QString &unit) { return SvgUtil::parseUnitY(m_context.currentGC(), unit); } qreal SvgParser::parseUnitXY(const QString &unit) { return SvgUtil::parseUnitXY(m_context.currentGC(), unit); } bool SvgParser::parseGradient(const KoXmlElement &e, const KoXmlElement &referencedBy) { // IMPROVEMENTS: // - Store the parsed colorstops in some sort of a cache so they don't need to be parsed again. // - A gradient inherits attributes it does not have from the referencing gradient. // - Gradients with no color stops have no fill or stroke. // - Gradients with one color stop have a solid color. SvgGraphicsContext *gc = m_context.currentGC(); if (!gc) return false; SvgGradientHelper gradhelper; if (e.hasAttribute("xlink:href")) { QString href = e.attribute("xlink:href").mid(1); if (! href.isEmpty()) { // copy the referenced gradient if found SvgGradientHelper *pGrad = findGradient(href); if (pGrad) gradhelper = *pGrad; } else { //gc->fillType = SvgGraphicsContext::None; // <--- TODO Fill OR Stroke are none return false; } } // Use the gradient that is referencing, or if there isn't one, the original gradient. KoXmlElement b; if (!referencedBy.isNull()) b = referencedBy; else b = e; QString gradientId = b.attribute("id"); if (! gradientId.isEmpty()) { // check if we have this gradient already parsed // copy existing gradient if it exists if (m_gradients.find(gradientId) != m_gradients.end()) gradhelper.copyGradient(m_gradients[ gradientId ].gradient()); } if (b.attribute("gradientUnits") == "userSpaceOnUse") gradhelper.setGradientUnits(SvgGradientHelper::UserSpaceOnUse); // parse color prop QColor c = gc->currentColor; if (!b.attribute("color").isEmpty()) { m_context.styleParser().parseColor(c, b.attribute("color")); } else { // try style attr QString style = b.attribute("style").simplified(); const QStringList substyles = style.split(';', QString::SkipEmptyParts); for (QStringList::ConstIterator it = substyles.begin(); it != substyles.end(); ++it) { QStringList substyle = it->split(':'); QString command = substyle[0].trimmed(); QString params = substyle[1].trimmed(); if (command == "color") m_context.styleParser().parseColor(c, params); } } gc->currentColor = c; if (b.tagName() == "linearGradient") { QLinearGradient *g = new QLinearGradient(); if (gradhelper.gradientUnits() == SvgGradientHelper::ObjectBoundingBox) { g->setCoordinateMode(QGradient::ObjectBoundingMode); g->setStart(QPointF(SvgUtil::fromPercentage(b.attribute("x1", "0%")), SvgUtil::fromPercentage(b.attribute("y1", "0%")))); g->setFinalStop(QPointF(SvgUtil::fromPercentage(b.attribute("x2", "100%")), SvgUtil::fromPercentage(b.attribute("y2", "0%")))); } else { g->setStart(QPointF(SvgUtil::fromUserSpace(b.attribute("x1").toDouble()), SvgUtil::fromUserSpace(b.attribute("y1").toDouble()))); g->setFinalStop(QPointF(SvgUtil::fromUserSpace(b.attribute("x2").toDouble()), SvgUtil::fromUserSpace(b.attribute("y2").toDouble()))); } // preserve color stops if (gradhelper.gradient()) g->setStops(gradhelper.gradient()->stops()); gradhelper.setGradient(g); } else if (b.tagName() == "radialGradient") { QRadialGradient *g = new QRadialGradient(); if (gradhelper.gradientUnits() == SvgGradientHelper::ObjectBoundingBox) { g->setCoordinateMode(QGradient::ObjectBoundingMode); g->setCenter(QPointF(SvgUtil::fromPercentage(b.attribute("cx", "50%")), SvgUtil::fromPercentage(b.attribute("cy", "50%")))); g->setRadius(SvgUtil::fromPercentage(b.attribute("r", "50%"))); g->setFocalPoint(QPointF(SvgUtil::fromPercentage(b.attribute("fx", "50%")), SvgUtil::fromPercentage(b.attribute("fy", "50%")))); } else { g->setCenter(QPointF(SvgUtil::fromUserSpace(b.attribute("cx").toDouble()), SvgUtil::fromUserSpace(b.attribute("cy").toDouble()))); g->setFocalPoint(QPointF(SvgUtil::fromUserSpace(b.attribute("fx").toDouble()), SvgUtil::fromUserSpace(b.attribute("fy").toDouble()))); g->setRadius(SvgUtil::fromUserSpace(b.attribute("r").toDouble())); } // preserve color stops if (gradhelper.gradient()) g->setStops(gradhelper.gradient()->stops()); gradhelper.setGradient(g); } else if (b.tagName() == "conicalGradient") { QConicalGradient *g = new QConicalGradient(); if (gradhelper.gradientUnits() == SvgGradientHelper::ObjectBoundingBox) { g->setCoordinateMode(QGradient::ObjectBoundingMode); g->setCenter(QPointF(SvgUtil::fromPercentage(b.attribute("cx", "50%")), SvgUtil::fromPercentage(b.attribute("cy", "50%")))); g->setAngle(SvgUtil::fromPercentage(b.attribute("a", "50%"))); } else { g->setCenter(QPointF(SvgUtil::fromUserSpace(b.attribute("cx").toDouble()), SvgUtil::fromUserSpace(b.attribute("cy").toDouble()))); g->setAngle(SvgUtil::fromUserSpace(b.attribute("a").toDouble())); } // preserve color stops if (gradhelper.gradient()) g->setStops(gradhelper.gradient()->stops()); gradhelper.setGradient(g); } else { return false; } // handle spread method QString spreadMethod = b.attribute("spreadMethod"); if (!spreadMethod.isEmpty()) { if (spreadMethod == "reflect") gradhelper.gradient()->setSpread(QGradient::ReflectSpread); else if (spreadMethod == "repeat") gradhelper.gradient()->setSpread(QGradient::RepeatSpread); else gradhelper.gradient()->setSpread(QGradient::PadSpread); } else gradhelper.gradient()->setSpread(QGradient::PadSpread); // Parse the color stops. The referencing gradient does not have colorstops, // so use the stops from the gradient it references to (e in this case and not b) m_context.styleParser().parseColorStops(gradhelper.gradient(), e); gradhelper.setTransform(SvgUtil::parseTransform(b.attribute("gradientTransform"))); m_gradients.insert(gradientId, gradhelper); return true; } void SvgParser::parsePattern(SvgPatternHelper &pattern, const KoXmlElement &e) { if (e.attribute("patternUnits") == "userSpaceOnUse") { pattern.setPatternUnits(SvgPatternHelper::UserSpaceOnUse); } if (e.attribute("patternContentUnits") == "objectBoundingBox") { pattern.setPatternContentUnits(SvgPatternHelper::ObjectBoundingBox); } const QString viewBox = e.attribute("viewBox"); if (!viewBox.isEmpty()) { pattern.setPatternContentViewbox(SvgUtil::parseViewBox(viewBox)); } const QString transform = e.attribute("patternTransform"); if (!transform.isEmpty()) { pattern.setTransform(SvgUtil::parseTransform(transform)); } const QString x = e.attribute("x"); const QString y = e.attribute("y"); const QString w = e.attribute("width"); const QString h = e.attribute("height"); // parse tile reference rectangle if (pattern.patternUnits() == SvgPatternHelper::UserSpaceOnUse) { if (!x.isEmpty() && !y.isEmpty()) { pattern.setPosition(QPointF(parseUnitX(x), parseUnitY(y))); } if (!w.isEmpty() && !h.isEmpty()) { pattern.setSize(QSizeF(parseUnitX(w), parseUnitY(h))); } } else { // x, y, width, height are in percentages of the object referencing the pattern // so we just parse the percentages if (!x.isEmpty() && !y.isEmpty()) { pattern.setPosition(QPointF(SvgUtil::fromPercentage(x), SvgUtil::fromPercentage(y))); } if (!w.isEmpty() && !h.isEmpty()) { pattern.setSize(QSizeF(SvgUtil::fromPercentage(w), SvgUtil::fromPercentage(h))); } } if (e.hasChildNodes()) { pattern.setContent(e); } } bool SvgParser::parseFilter(const KoXmlElement &e, const KoXmlElement &referencedBy) { SvgFilterHelper filter; // Use the filter that is referencing, or if there isn't one, the original filter KoXmlElement b; if (!referencedBy.isNull()) b = referencedBy; else b = e; // check if we are referencing another filter if (e.hasAttribute("xlink:href")) { QString href = e.attribute("xlink:href").mid(1); if (! href.isEmpty()) { // copy the referenced filter if found SvgFilterHelper *refFilter = findFilter(href); if (refFilter) filter = *refFilter; } } else { filter.setContent(b); } if (b.attribute("filterUnits") == "userSpaceOnUse") filter.setFilterUnits(SvgFilterHelper::UserSpaceOnUse); if (b.attribute("primitiveUnits") == "objectBoundingBox") filter.setPrimitiveUnits(SvgFilterHelper::ObjectBoundingBox); // parse filter region rectangle if (filter.filterUnits() == SvgFilterHelper::UserSpaceOnUse) { filter.setPosition(QPointF(parseUnitX(b.attribute("x")), parseUnitY(b.attribute("y")))); filter.setSize(QSizeF(parseUnitX(b.attribute("width")), parseUnitY(b.attribute("height")))); } else { // x, y, width, height are in percentages of the object referencing the filter // so we just parse the percentages filter.setPosition(QPointF(SvgUtil::fromPercentage(b.attribute("x", "-0.1")), SvgUtil::fromPercentage(b.attribute("y", "-0.1")))); filter.setSize(QSizeF(SvgUtil::fromPercentage(b.attribute("width", "1.2")), SvgUtil::fromPercentage(b.attribute("height", "1.2")))); } m_filters.insert(b.attribute("id"), filter); return true; } bool SvgParser::parseClipPath(const KoXmlElement &e, const KoXmlElement &referencedBy) { SvgClipPathHelper clipPath; // Use the filter that is referencing, or if there isn't one, the original filter KoXmlElement b; if (!referencedBy.isNull()) b = referencedBy; else b = e; // check if we are referencing another clip path if (e.hasAttribute("xlink:href")) { QString href = e.attribute("xlink:href").mid(1); if (! href.isEmpty()) { // copy the referenced clip path if found SvgClipPathHelper *refClipPath = findClipPath(href); if (refClipPath) clipPath = *refClipPath; } } else { clipPath.setContent(b); } if (b.attribute("clipPathUnits") == "objectBoundingBox") clipPath.setClipPathUnits(SvgClipPathHelper::ObjectBoundingBox); m_clipPaths.insert(b.attribute("id"), clipPath); return true; } void SvgParser::applyStyle(KoShape *obj, const KoXmlElement &e) { applyStyle(obj, m_context.styleParser().collectStyles(e)); } void SvgParser::applyStyle(KoShape *obj, const SvgStyles &styles) { SvgGraphicsContext *gc = m_context.currentGC(); if (!gc) return; m_context.styleParser().parseStyle(styles); if (!obj) return; if (!dynamic_cast(obj)) { applyFillStyle(obj); applyStrokeStyle(obj); } applyFilter(obj); applyClipping(obj); if (! gc->display) obj->setVisible(false); obj->setTransparency(1.0 - gc->opacity); } void SvgParser::applyFillStyle(KoShape *shape) { SvgGraphicsContext *gc = m_context.currentGC(); if (! gc) return; if (gc->fillType == SvgGraphicsContext::None) { shape->setBackground(QSharedPointer(0)); } else if (gc->fillType == SvgGraphicsContext::Solid) { shape->setBackground(QSharedPointer(new KoColorBackground(gc->fillColor))); } else if (gc->fillType == SvgGraphicsContext::Complex) { // try to find referenced gradient SvgGradientHelper *gradient = findGradient(gc->fillId); if (gradient) { // great, we have a gradient fill QSharedPointer bg; if (gradient->gradientUnits() == SvgGradientHelper::ObjectBoundingBox) { bg = QSharedPointer(new KoGradientBackground(*gradient->gradient())); bg->setTransform(gradient->transform()); } else { QGradient *convertedGradient = SvgGradientHelper::convertGradient(gradient->gradient(), shape->size()); bg = QSharedPointer(new KoGradientBackground(convertedGradient)); QTransform invShapematrix = shape->transformation().inverted(); bg->setTransform(gradient->transform() * gc->matrix * invShapematrix); } shape->setBackground(bg); } else { // try to find referenced pattern SvgPatternHelper *pattern = findPattern(gc->fillId); KoImageCollection *imageCollection = m_documentResourceManager->imageCollection(); if (pattern && imageCollection) { // great we have a pattern fill QRectF objectBound = QRectF(QPoint(), shape->size()); QRectF currentBoundbox = gc->currentBoundbox; // properties from the object are not inherited // so we are creating a new context without copying SvgGraphicsContext *gc = m_context.pushGraphicsContext(pattern->content(), false); // the pattern establishes a new coordinate system with its // origin at the patterns x and y attributes gc->matrix = QTransform(); // object bounding box units are relative to the object the pattern is applied if (pattern->patternContentUnits() == SvgPatternHelper::ObjectBoundingBox) { gc->currentBoundbox = objectBound; gc->forcePercentage = true; } else { // inherit the current bounding box gc->currentBoundbox = currentBoundbox; } applyStyle(0, pattern->content()); // parse the pattern content elements QList patternContent = parseContainer(pattern->content()); // generate the pattern image from the shapes and the object bounding rect QImage image = pattern->generateImage(objectBound, patternContent); m_context.popGraphicsContext(); // delete the shapes created from the pattern content qDeleteAll(patternContent); if (!image.isNull()) { QSharedPointer bg(new KoPatternBackground(imageCollection)); bg->setPattern(image); QPointF refPoint = shape->documentToShape(pattern->position(objectBound)); QSizeF tileSize = pattern->size(objectBound); bg->setPatternDisplaySize(tileSize); if (pattern->patternUnits() == SvgPatternHelper::ObjectBoundingBox) { if (tileSize == objectBound.size()) bg->setRepeat(KoPatternBackground::Stretched); } // calculate pattern reference point offset in percent of tileSize // and relative to the topleft corner of the shape qreal fx = refPoint.x() / tileSize.width(); qreal fy = refPoint.y() / tileSize.height(); if (fx < 0.0) fx = floor(fx); else if (fx > 1.0) fx = ceil(fx); else fx = 0.0; if (fy < 0.0) fy = floor(fy); else if (fx > 1.0) fy = ceil(fy); else fy = 0.0; qreal offsetX = 100.0 * (refPoint.x() - fx * tileSize.width()) / tileSize.width(); qreal offsetY = 100.0 * (refPoint.y() - fy * tileSize.height()) / tileSize.height(); bg->setReferencePointOffset(QPointF(offsetX, offsetY)); shape->setBackground(bg); } } else { // no referenced fill found, use fallback color shape->setBackground(QSharedPointer(new KoColorBackground(gc->fillColor))); } } } KoPathShape *path = dynamic_cast(shape); if (path) path->setFillRule(gc->fillRule); } void SvgParser::applyStrokeStyle(KoShape *shape) { SvgGraphicsContext *gc = m_context.currentGC(); if (! gc) return; if (gc->strokeType == SvgGraphicsContext::None) { shape->setStroke(0); } else if (gc->strokeType == SvgGraphicsContext::Solid) { double lineWidth = gc->stroke.lineWidth(); QVector dashes = gc->stroke.lineDashes(); KoShapeStroke *stroke = new KoShapeStroke(gc->stroke); // apply line width to dashes and dash offset if (dashes.count() && lineWidth > 0.0) { QVector dashes = stroke->lineDashes(); for (int i = 0; i < dashes.count(); ++i) dashes[i] /= lineWidth; double dashOffset = stroke->dashOffset(); stroke->setLineStyle(Qt::CustomDashLine, dashes); stroke->setDashOffset(dashOffset / lineWidth); } else { stroke->setLineStyle(Qt::SolidLine, QVector()); } shape->setStroke(stroke); } else if (gc->strokeType == SvgGraphicsContext::Complex) { // try to find referenced gradient SvgGradientHelper *gradient = findGradient(gc->strokeId); if (gradient) { // great, we have a gradient stroke QBrush brush; if (gradient->gradientUnits() == SvgGradientHelper::ObjectBoundingBox) { brush = *gradient->gradient(); brush.setTransform(gradient->transform()); } else { QGradient *convertedGradient(SvgGradientHelper::convertGradient(gradient->gradient(), shape->size())); brush = *convertedGradient; delete convertedGradient; brush.setTransform(gradient->transform() * gc->matrix * shape->transformation().inverted()); } KoShapeStroke *stroke = new KoShapeStroke(gc->stroke); stroke->setLineBrush(brush); stroke->setLineStyle(Qt::SolidLine, QVector()); shape->setStroke(stroke); } else { // no referenced stroke found, use fallback color KoShapeStroke *stroke = new KoShapeStroke(gc->stroke); stroke->setLineStyle(Qt::SolidLine, QVector()); shape->setStroke(stroke); } } } void SvgParser::applyFilter(KoShape *shape) { SvgGraphicsContext *gc = m_context.currentGC(); if (! gc) return; if (gc->filterId.isEmpty()) return; SvgFilterHelper *filter = findFilter(gc->filterId); if (! filter) return; KoXmlElement content = filter->content(); // parse filter region QRectF bound(shape->position(), shape->size()); - // work on bounding box without viewbox tranformation applied + // work on bounding box without viewbox transformation applied // so user space coordinates of bounding box and filter region match up bound = gc->viewboxTransform.inverted().mapRect(bound); QRectF filterRegion(filter->position(bound), filter->size(bound)); // convert filter region to boundingbox units QRectF objectFilterRegion; objectFilterRegion.setTopLeft(SvgUtil::userSpaceToObject(filterRegion.topLeft(), bound)); objectFilterRegion.setSize(SvgUtil::userSpaceToObject(filterRegion.size(), bound)); KoFilterEffectLoadingContext context(m_context.xmlBaseDir()); context.setShapeBoundingBox(bound); // enable units conversion context.enableFilterUnitsConversion(filter->filterUnits() == SvgFilterHelper::UserSpaceOnUse); context.enableFilterPrimitiveUnitsConversion(filter->primitiveUnits() == SvgFilterHelper::UserSpaceOnUse); KoFilterEffectRegistry *registry = KoFilterEffectRegistry::instance(); KoFilterEffectStack *filterStack = 0; QSet stdInputs; stdInputs << "SourceGraphic" << "SourceAlpha"; stdInputs << "BackgroundImage" << "BackgroundAlpha"; stdInputs << "FillPaint" << "StrokePaint"; QMap inputs; // create the filter effects and add them to the shape for (KoXmlNode n = content.firstChild(); !n.isNull(); n = n.nextSibling()) { KoXmlElement primitive = n.toElement(); KoFilterEffect *filterEffect = registry->createFilterEffectFromXml(primitive, context); if (!filterEffect) { debugFlake << "filter effect" << primitive.tagName() << "is not implemented yet"; continue; } const QString input = primitive.attribute("in"); if (!input.isEmpty()) { filterEffect->setInput(0, input); } const QString output = primitive.attribute("result"); if (!output.isEmpty()) { filterEffect->setOutput(output); } QRectF subRegion; // parse subregion if (filter->primitiveUnits() == SvgFilterHelper::UserSpaceOnUse) { const QString xa = primitive.attribute("x"); const QString ya = primitive.attribute("y"); const QString wa = primitive.attribute("width"); const QString ha = primitive.attribute("height"); if (xa.isEmpty() || ya.isEmpty() || wa.isEmpty() || ha.isEmpty()) { bool hasStdInput = false; bool isFirstEffect = filterStack == 0; // check if one of the inputs is a standard input foreach(const QString &input, filterEffect->inputs()) { if ((isFirstEffect && input.isEmpty()) || stdInputs.contains(input)) { hasStdInput = true; break; } } if (hasStdInput || primitive.tagName() == "feImage") { // default to 0%, 0%, 100%, 100% subRegion.setTopLeft(QPointF(0, 0)); subRegion.setSize(QSizeF(1, 1)); } else { // defaults to bounding rect of all referenced nodes foreach(const QString &input, filterEffect->inputs()) { if (!inputs.contains(input)) continue; KoFilterEffect *inputFilter = inputs[input]; if (inputFilter) subRegion |= inputFilter->filterRect(); } } } else { const qreal x = parseUnitX(xa); const qreal y = parseUnitY(ya); const qreal w = parseUnitX(wa); const qreal h = parseUnitY(ha); subRegion.setTopLeft(SvgUtil::userSpaceToObject(QPointF(x, y), bound)); subRegion.setSize(SvgUtil::userSpaceToObject(QSizeF(w, h), bound)); } } else { // x, y, width, height are in percentages of the object referencing the filter // so we just parse the percentages const qreal x = SvgUtil::fromPercentage(primitive.attribute("x", "0")); const qreal y = SvgUtil::fromPercentage(primitive.attribute("y", "0")); const qreal w = SvgUtil::fromPercentage(primitive.attribute("width", "1")); const qreal h = SvgUtil::fromPercentage(primitive.attribute("height", "1")); subRegion = QRectF(QPointF(x, y), QSizeF(w, h)); } filterEffect->setFilterRect(subRegion); if (!filterStack) filterStack = new KoFilterEffectStack(); filterStack->appendFilterEffect(filterEffect); inputs[filterEffect->output()] = filterEffect; } if (filterStack) { filterStack->setClipRect(objectFilterRegion); shape->setFilterEffectStack(filterStack); } } void SvgParser::applyClipping(KoShape *shape) { SvgGraphicsContext *gc = m_context.currentGC(); if (! gc) return; if (gc->clipPathId.isEmpty()) return; SvgClipPathHelper *clipPath = findClipPath(gc->clipPathId); if (! clipPath) return; debugFlake << "applying clip path" << gc->clipPathId << "clip rule" << gc->clipRule; const bool boundingBoxUnits = clipPath->clipPathUnits() == SvgClipPathHelper::ObjectBoundingBox; debugFlake << "using" << (boundingBoxUnits ? "boundingBoxUnits" : "userSpaceOnUse"); QTransform shapeMatrix = shape->absoluteTransformation(0); // TODO: // clip path element can have a clip-path property // -> clip-path = intersection of children with referenced clip-path // any of its children can have a clip-path property // -> child element is clipped and the ORed with other children m_context.pushGraphicsContext(); if (boundingBoxUnits) { SvgGraphicsContext *gc = m_context.currentGC(); gc->matrix.reset(); gc->viewboxTransform.reset(); gc->currentBoundbox = shape->boundingRect(); gc->forcePercentage = true; } QList clipShapes = parseContainer(clipPath->content()); QList pathShapes; while (!clipShapes.isEmpty()) { KoShape *clipShape = clipShapes.first(); clipShapes.removeFirst(); // remove clip shape from list of all parsed shapes m_shapes.removeOne(clipShape); // check if we have a path shape KoPathShape *path = dynamic_cast(clipShape); if (!path) { // if shape is a group, ungroup and add children to lits of clip shapes KoShapeGroup *group = dynamic_cast(clipShape); if (group) { QList groupedShapes = group->shapes(); KoShapeUngroupCommand cmd(group, groupedShapes); cmd.redo(); clipShapes.append(groupedShapes); } else { // shape is not a group shape, use its outline as clip path QPainterPath outline = clipShape->absoluteTransformation(0).map(clipShape->outline()); path = KoPathShape::createShapeFromPainterPath(outline); } delete clipShape; } if (path) { debugFlake << "using shape" << path->name() << "as clip path"; pathShapes.append(path); if (boundingBoxUnits) path->applyAbsoluteTransformation(shapeMatrix); } } m_context.popGraphicsContext(); if (pathShapes.count()) { QTransform transformToShape; if (!boundingBoxUnits) transformToShape = shape->absoluteTransformation(0).inverted(); KoClipData *clipData = new KoClipData(pathShapes); KoClipPath *clipPath = new KoClipPath(shape, clipData); clipPath->setClipRule(gc->clipRule); shape->setClipPath(clipPath); } } QList SvgParser::parseUse(const KoXmlElement &e) { QList shapes; QString id = e.attribute("xlink:href"); // if (!id.isEmpty()) { SvgGraphicsContext *gc = m_context.pushGraphicsContext(e); // TODO: use width and height attributes too gc->matrix.translate(parseUnitX(e.attribute("x", "0")), parseUnitY(e.attribute("y", "0"))); QString key = id.mid(1); if (m_context.hasDefinition(key)) { const KoXmlElement &a = m_context.definition(key); SvgStyles styles = m_context.styleParser().mergeStyles(e, a); if (a.tagName() == "g" || a.tagName() == "a" || a.tagName() == "symbol") { m_context.pushGraphicsContext(a); KoShapeGroup *group = new KoShapeGroup(); group->setZIndex(m_context.nextZIndex()); applyStyle(0, styles); m_context.styleParser().parseFont(styles); QList childShapes = parseContainer(a); // handle id applyId(a.attribute("id"), group); addToGroup(childShapes, group); applyStyle(group, styles); // apply style to group after size is set shapes.append(group); m_context.popGraphicsContext(); } else { // Create the object with the merged styles. // The object inherits all style attributes from the use tag, but keeps it's own attributes. // So, not just use the style attributes of the use tag, but merge them first. KoShape *shape = createObject(a, styles); if (shape) shapes.append(shape); } } else { // TODO: any named object can be referenced too } m_context.popGraphicsContext(); } return shapes; } void SvgParser::addToGroup(QList shapes, KoShapeGroup *group) { m_shapes += shapes; if (! group) return; KoShapeGroupCommand cmd(group, shapes); cmd.redo(); } QList SvgParser::parseSvg(const KoXmlElement &e, QSizeF *fragmentSize) { // check if we are the root svg element const bool isRootSvg = !m_context.currentGC(); SvgGraphicsContext *gc = m_context.pushGraphicsContext(); applyStyle(0, e); QRectF viewBox; const QString viewBoxStr = e.attribute("viewBox"); if (!viewBoxStr.isEmpty()) { viewBox = SvgUtil::parseViewBox(viewBoxStr); } const QString w = e.attribute("width"); const QString h = e.attribute("height"); const qreal width = w.isEmpty() ? 550.0 : parseUnit(w, true, false, viewBox); const qreal height = h.isEmpty() ? 841.0 : parseUnit(h, false, true, viewBox); QSizeF svgFragmentSize(QSizeF(width, height)); if (fragmentSize) *fragmentSize = svgFragmentSize; gc->currentBoundbox = QRectF(QPointF(0, 0), svgFragmentSize); if (! isRootSvg) { QTransform move; // x and y attribute has no meaning for outermost svg elements const qreal x = parseUnit(e.attribute("x", "0")); const qreal y = parseUnit(e.attribute("y", "0")); move.translate(x, y); gc->matrix = move * gc->matrix; gc->viewboxTransform = move *gc->viewboxTransform; } if (!viewBoxStr.isEmpty()) { QTransform viewTransform; viewTransform.translate(viewBox.x(), viewBox.y()); viewTransform.scale(width / viewBox.width() , height / viewBox.height()); gc->matrix = viewTransform * gc->matrix; gc->viewboxTransform = viewTransform *gc->viewboxTransform; gc->currentBoundbox.setWidth(gc->currentBoundbox.width() * (viewBox.width() / width)); gc->currentBoundbox.setHeight(gc->currentBoundbox.height() * (viewBox.height() / height)); } QList shapes = parseContainer(e); m_context.popGraphicsContext(); return shapes; } QList SvgParser::parseContainer(const KoXmlElement &e) { QList shapes; // are we parsing a switch container bool isSwitch = e.tagName() == "switch"; for (KoXmlNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { KoXmlElement b = n.toElement(); if (b.isNull()) continue; if (isSwitch) { // if we are parsing a switch check the requiredFeatures, requiredExtensions // and systemLanguage attributes // TODO: evaluate feature list if (b.hasAttribute("requiredFeatures")) { continue; } if (b.hasAttribute("requiredExtensions")) { // we do not support any extensions continue; } if (b.hasAttribute("systemLanguage")) { // not implemeted yet } } if (b.tagName() == "svg") { shapes += parseSvg(b); } else if (b.tagName() == "g" || b.tagName() == "a" || b.tagName() == "symbol") { // treat svg link as group so we don't miss its child elements m_context.pushGraphicsContext(b); KoShapeGroup *group = new KoShapeGroup(); group->setZIndex(m_context.nextZIndex()); SvgStyles styles = m_context.styleParser().collectStyles(b); m_context.styleParser().parseFont(styles); applyStyle(0, styles); // parse style for inheritance QList childShapes = parseContainer(b); // handle id applyId(b.attribute("id"), group); addToGroup(childShapes, group); const QString viewBoxStr = b.attribute("viewBox"); if (!viewBoxStr.isEmpty()) { QRectF viewBox = SvgUtil::parseViewBox(viewBoxStr); QTransform viewTransform; viewTransform.translate(viewBox.x(), viewBox.y()); viewTransform.scale(group->size().width() / viewBox.width() , group->size().height() / viewBox.height()); group->applyAbsoluteTransformation(viewTransform); } applyStyle(group, styles); // apply style to this group after size is set shapes.append(group); m_context.popGraphicsContext(); } else if (b.tagName() == "switch") { m_context.pushGraphicsContext(b); shapes += parseContainer(b); m_context.popGraphicsContext(); } else if (b.tagName() == "defs") { parseDefs(b); } else if (b.tagName() == "linearGradient" || b.tagName() == "radialGradient") { parseGradient(b); } else if (b.tagName() == "pattern") { m_context.addDefinition(b); } else if (b.tagName() == "filter") { parseFilter(b); } else if (b.tagName() == "clipPath") { parseClipPath(b); } else if (b.tagName() == "style") { m_context.addStyleSheet(b); } else if (b.tagName() == "rect" || b.tagName() == "ellipse" || b.tagName() == "circle" || b.tagName() == "line" || b.tagName() == "polyline" || b.tagName() == "polygon" || b.tagName() == "path" || b.tagName() == "image" || b.tagName() == "text") { KoShape *shape = createObject(b); if (shape) shapes.append(shape); } else if (b.tagName() == "use") { shapes += parseUse(b); } else { // this is an unknown element, so try to load it anyway // there might be a shape that handles that element KoShape *shape = createObject(b); if (shape) { shapes.append(shape); } else { continue; } } // if we are parsing a switch, stop after the first supported element if (isSwitch) break; } return shapes; } void SvgParser::parseDefs(const KoXmlElement &e) { for (KoXmlNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) { KoXmlElement b = n.toElement(); if (b.isNull()) continue; if (b.tagName() == "style") { m_context.addStyleSheet(b); } else if (b.tagName() == "defs") { parseDefs(b); } else { m_context.addDefinition(b); } } } // Creating functions // --------------------------------------------------------------------------------------- KoShape * SvgParser::createPath(const KoXmlElement &element) { KoShape *obj = 0; if (element.tagName() == "line") { KoPathShape *path = static_cast(createShape(KoPathShapeId)); if (path) { double x1 = element.attribute("x1").isEmpty() ? 0.0 : parseUnitX(element.attribute("x1")); double y1 = element.attribute("y1").isEmpty() ? 0.0 : parseUnitY(element.attribute("y1")); double x2 = element.attribute("x2").isEmpty() ? 0.0 : parseUnitX(element.attribute("x2")); double y2 = element.attribute("y2").isEmpty() ? 0.0 : parseUnitY(element.attribute("y2")); path->clear(); path->moveTo(QPointF(x1, y1)); path->lineTo(QPointF(x2, y2)); path->normalize(); obj = path; } } else if (element.tagName() == "polyline" || element.tagName() == "polygon") { KoPathShape *path = static_cast(createShape(KoPathShapeId)); if (path) { path->clear(); bool bFirst = true; QString points = element.attribute("points").simplified(); points.replace(',', ' '); points.remove('\r'); points.remove('\n'); const QStringList pointList = points.split(' ', QString::SkipEmptyParts); for (QStringList::ConstIterator it = pointList.begin(); it != pointList.end(); ++it) { QPointF point; point.setX(SvgUtil::fromUserSpace((*it).toDouble())); ++it; if (it == pointList.end()) break; point.setY(SvgUtil::fromUserSpace((*it).toDouble())); if (bFirst) { path->moveTo(point); bFirst = false; } else path->lineTo(point); } if (element.tagName() == "polygon") path->close(); path->setPosition(path->normalize()); obj = path; } } else if (element.tagName() == "path") { KoPathShape *path = static_cast(createShape(KoPathShapeId)); if (path) { path->clear(); KoPathShapeLoader loader(path); loader.parseSvg(element.attribute("d"), true); path->setPosition(path->normalize()); QPointF newPosition = QPointF(SvgUtil::fromUserSpace(path->position().x()), SvgUtil::fromUserSpace(path->position().y())); QSizeF newSize = QSizeF(SvgUtil::fromUserSpace(path->size().width()), SvgUtil::fromUserSpace(path->size().height())); path->setSize(newSize); path->setPosition(newPosition); obj = path; } } return obj; } KoShape * SvgParser::createObject(const KoXmlElement &b, const SvgStyles &style) { m_context.pushGraphicsContext(b); KoShape *obj = createShapeFromElement(b, m_context); if (obj) { obj->applyAbsoluteTransformation(m_context.currentGC()->matrix); SvgStyles objStyle = style.isEmpty() ? m_context.styleParser().collectStyles(b) : style; m_context.styleParser().parseFont(objStyle); applyStyle(obj, objStyle); // handle id applyId(b.attribute("id"), obj); obj->setZIndex(m_context.nextZIndex()); } m_context.popGraphicsContext(); return obj; } KoShape * SvgParser::createShapeFromElement(const KoXmlElement &element, SvgLoadingContext &context) { KoShape *object = 0; QList factories = KoShapeRegistry::instance()->factoriesForElement(KoXmlNS::svg, element.tagName()); foreach (KoShapeFactoryBase *f, factories) { KoShape *shape = f->createDefaultShape(m_documentResourceManager); if (!shape) continue; SvgShape *svgShape = dynamic_cast(shape); if (!svgShape) { delete shape; continue; } // reset transformation that might come from the default shape shape->setTransformation(QTransform()); // reset border KoShapeStrokeModel *oldStroke = shape->stroke(); shape->setStroke(0); delete oldStroke; // reset fill shape->setBackground(QSharedPointer(0)); if (!svgShape->loadSvg(element, context)) { delete shape; continue; } object = shape; break; } if (!object) { object = createPath(element); } return object; } KoShape * SvgParser::createShape(const QString &shapeID) { KoShapeFactoryBase *factory = KoShapeRegistry::instance()->get(shapeID); if (!factory) { debugFlake << "Could not find factory for shape id" << shapeID; return 0; } KoShape *shape = factory->createDefaultShape(m_documentResourceManager); if (!shape) { debugFlake << "Could not create Default shape for shape id" << shapeID; return 0; } if (shape->shapeId().isEmpty()) shape->setShapeId(factory->id()); - // reset tranformation that might come from the default shape + // reset transformation that might come from the default shape shape->setTransformation(QTransform()); // reset border KoShapeStrokeModel *oldStroke = shape->stroke(); shape->setStroke(0); delete oldStroke; // reset fill shape->setBackground(QSharedPointer(0)); return shape; } void SvgParser::applyId(const QString &id, KoShape *shape) { if (id.isEmpty()) return; shape->setName(id); m_context.registerShape(id, shape); } diff --git a/libs/odf/CMakeLists.txt b/libs/odf/CMakeLists.txt index 24d2250567e..7803bf89099 100644 --- a/libs/odf/CMakeLists.txt +++ b/libs/odf/CMakeLists.txt @@ -1,102 +1,104 @@ -add_subdirectory( tests ) +if(BUILD_TESTING) + add_subdirectory( tests ) +endif() add_subdirectory( writeodf ) include_directories( ${KOODF_INCLUDES} ) ########### libkoodf ############### set(koodf_LIB_SRCS KoOdf.cpp KoOdfManifestEntry.cpp KoDocumentInfo.cpp KoGenStyle.cpp KoGenStyles.cpp KoFontFace.cpp KoOdfLoadingContext.cpp KoOasisSettings.cpp KoOdfStylesReader.cpp KoOdfNumberStyles.cpp KoOdfPaste.cpp KoOdfReadStore.cpp KoOdfWriteStore.cpp KoStyleStack.cpp KoOdfGraphicStyles.cpp KoGenChange.cpp KoGenChanges.cpp KoDocumentBase.cpp KoEmbeddedDocumentSaver.cpp KoBorder.cpp Ko3dScene.cpp KoShadowStyle.cpp KoPageLayout.cpp KoPageFormat.cpp KoColumns.cpp KoUnit.cpp KoOdfNotesConfiguration.cpp KoOdfBibliographyConfiguration.cpp KoOdfNumberDefinition.cpp KoOdfLineNumberingConfiguration.cpp KoElementReference.cpp writeodf/helpers.cpp OdfDebug.cpp ) add_library(koodf SHARED ${koodf_LIB_SRCS}) add_dependencies(koodf writeodf.h-target) generate_export_header(koodf BASE_NAME koodf) target_link_libraries(koodf PUBLIC kostore Qt5::PrintSupport Qt5::Xml PRIVATE koversion KF5::I18n ) set_target_properties(koodf PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) install(TARGETS koodf ${INSTALL_TARGETS_DEFAULT_ARGS} ) if (SHOULD_BUILD_DEVEL_HEADERS) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/koodf_export.h KoOdf.h KoOdfManifestEntry.h KoGenStyle.h KoGenStyles.h KoFontFace.h KoOdfLoadingContext.h KoOasisSettings.h KoOdfStylesReader.h KoOdfNotesConfiguration.h KoOdfBibliographyConfiguration.h KoOdfNumberStyles.h KoOdfNumberDefinition.h KoOdfLineNumberingConfiguration.h KoOdfPaste.h KoOdfReadStore.h KoOdfWriteStore.h KoStyleStack.h KoOdfGraphicStyles.h KoDocumentBase.h KoGenChange.h KoGenChanges.h KoEmbeddedDocumentSaver.h KoBorder.h Ko3dScene.h KoShadowStyle.h KoPageLayout.h KoPageFormat.h KoColumns.h KoUnit.h KoElementReference.h KoTableTemplate.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligra COMPONENT Devel) endif() diff --git a/libs/pageapp/CMakeLists.txt b/libs/pageapp/CMakeLists.txt index 7fc88af89cf..78ffccb243b 100644 --- a/libs/pageapp/CMakeLists.txt +++ b/libs/pageapp/CMakeLists.txt @@ -1,97 +1,99 @@ include_directories( ${KOPAGEAPP_INCLUDES} ) -add_subdirectory(tests) +if(BUILD_TESTING) + add_subdirectory(tests) +endif() add_subdirectory(tools) ### kopageapp ### set( kopageapp_LIB_SRCS PageAppDebug.cpp KoPADocument.cpp KoShapeTraversal.cpp KoPAView.cpp KoPAViewBase.cpp KoPACanvas.cpp KoPACanvasItem.cpp KoPACanvasBase.cpp KoPASavingContext.cpp KoPAPixmapCache.cpp KoPAPageBase.cpp KoPAMasterPage.cpp KoPAPage.cpp KoPAPageContainerModel.cpp KoPAViewMode.cpp KoPAViewModeNormal.cpp KoPALoadingContext.cpp KoPAOdfPageSaveHelper.cpp KoPAPastePage.cpp KoPADocumentModel.cpp KoPAPageThumbnailModel.cpp KoPADocumentStructureDocker.cpp KoPAUtil.cpp KoPAPrintJob.cpp KoPATextPage.cpp KoPAPageProvider.cpp commands/KoPAPageInsertCommand.cpp commands/KoPAPageDeleteCommand.cpp commands/KoPAPageMoveCommand.cpp commands/KoPAChangeMasterPageCommand.cpp commands/KoPAChangePageLayoutCommand.cpp commands/KoPADisplayMasterShapesCommand.cpp commands/KoPADisplayMasterBackgroundCommand.cpp dialogs/KoPAMasterPageDialog.cpp dialogs/KoPAPageLayoutDialog.cpp dialogs/KoPAConfigureDialog.cpp widgets/KoPageNavigator.cpp widgets/KoPageNavigatorButton.cpp widgets/KoPABackgroundFillWidget.cpp tools/backgroundTool/KoPABackgroundTool.cpp tools/backgroundTool/KoPABackgroundToolWidget.cpp ) ki18n_wrap_ui(kopageapp_LIB_SRCS tools/backgroundTool/BackgroundToolWidget.ui ) add_library(kopageapp SHARED ${kopageapp_LIB_SRCS}) generate_export_header(kopageapp EXPORT_FILE_NAME kopageapp_generated_export.h) target_link_libraries(kopageapp PUBLIC komain PRIVATE kotextlayout ) set_target_properties(kopageapp PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) install(TARGETS kopageapp ${INSTALL_TARGETS_DEFAULT_ARGS}) if (SHOULD_BUILD_DEVEL_HEADERS) install(FILES kopageapp_export.h KoPACanvas.h KoPACanvasItem.h KoPACanvasBase.h KoPADocument.h KoPageApp.h KoPALoadingContext.h KoPAPage.h KoPAMasterPage.h KoPAPageBase.h KoPAPageContainerModel.h KoPASavingContext.h KoPAUtil.h KoPAView.h KoPAViewBase.h KoPAViewMode.h KoPAViewModeNormal.h tools/backgroundTool/KoPABackgroundTool.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligra COMPONENT Devel ) endif() diff --git a/libs/pigment/CMakeLists.txt b/libs/pigment/CMakeLists.txt index eb8e0c5f126..a039251e2ad 100644 --- a/libs/pigment/CMakeLists.txt +++ b/libs/pigment/CMakeLists.txt @@ -1,167 +1,169 @@ project(pigmentcms) # we have to repeat platform specifics from top-level if (WIN32) include_directories(${CMAKE_SOURCE_DIR}/winquirks) add_definitions(-D_USE_MATH_DEFINES) add_definitions(-DNOMINMAX) set(WIN32_PLATFORM_NET_LIBS ws2_32.lib netapi32.lib) endif () include_directories( ${PIGMENT_INCLUDES} ${Boost_INCLUDE_DIR}) set(FILE_OPENEXR_SOURCES) set(LINK_OPENEXR_LIB) if(OPENEXR_FOUND) include_directories(${OPENEXR_INCLUDE_DIR}) set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES}) add_definitions(${OPENEXR_DEFINITIONS}) endif() set(LINK_VC_LIB) if(HAVE_VC) include_directories(${Vc_INCLUDE_DIR}) set(LINK_VC_LIB ${Vc_LIBRARIES}) kde_enable_exceptions() ko_compile_for_all_implementations_no_scalar(__per_arch_factory_objs compositeops/KoOptimizedCompositeOpFactoryPerArch.cpp) message("Following objects are generated from the per-arch lib") message(${__per_arch_factory_objs}) # silence warnings for using older Vc API for now, where "the tuple" should be built "with Vc::tie" if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_GNUC) add_definitions(-Wno-deprecated-declarations) endif () endif() -add_subdirectory(tests) -add_subdirectory(benchmarks) +if(BUILD_TESTING) + add_subdirectory(tests) + add_subdirectory(benchmarks) +endif() set(pigmentcms_SRCS DebugPigment.cpp KoBasicHistogramProducers.cpp KoColor.cpp KoColorDisplayRendererInterface.cpp KoColorConversionAlphaTransformation.cpp KoColorConversionCache.cpp KoColorConversions.cpp KoColorConversionSystem.cpp KoColorConversionTransformation.cpp KoColorConversionTransformationFactory.cpp KoColorModelStandardIds.cpp KoColorProfile.cpp KoColorSpace.cpp KoColorSpaceEngine.cpp KoColorSpaceFactory.cpp KoColorSpaceMaths.cpp KoColorSpaceRegistry.cpp KoColorTransformation.cpp KoColorTransformationFactory.cpp KoColorTransformationFactoryRegistry.cpp KoCompositeColorTransformation.cpp KoCompositeOp.cpp KoCompositeOpRegistry.cpp KoCopyColorConversionTransformation.cpp KoFallBackColorTransformation.cpp KoHistogramProducer.cpp KoMultipleColorConversionTransformation.cpp KoUniqueNumberForIdServer.cpp colorspaces/KoAlphaColorSpace.cpp colorspaces/KoLabColorSpace.cpp colorspaces/KoRgbU16ColorSpace.cpp colorspaces/KoRgbU8ColorSpace.cpp colorspaces/KoSimpleColorSpaceEngine.cpp compositeops/KoOptimizedCompositeOpFactory.cpp compositeops/KoOptimizedCompositeOpFactoryPerArch_Scalar.cpp ${__per_arch_factory_objs} colorprofiles/KoDummyColorProfile.cpp resources/KoAbstractGradient.cpp resources/KoColorSet.cpp resources/KoPattern.cpp resources/KoResource.cpp resources/KoMD5Generator.cpp resources/KoHashGeneratorProvider.cpp resources/KoStopGradient.cpp resources/KoSegmentGradient.cpp ) set(PIGMENT_INSTALL_FILES ${CMAKE_CURRENT_BINARY_DIR}/pigment_export.h DebugPigment.h KoBasicHistogramProducers.h KoChannelInfo.h KoColor.h KoColorConversionTransformation.h KoColorConversionTransformationAbstractFactory.h KoColorConversionTransformationFactory.h KoColorModelStandardIds.h KoColorProfile.h KoColorSpace.h KoColorSpaceEngine.h KoColorSpaceFactory.h KoColorSpaceAbstract.h KoColorSpaceConstants.h KoColorSpaceMaths.h KoColorSpaceRegistry.h KoCmykColorSpaceTraits.h KoColorSpaceTraits.h KoGrayColorSpaceTraits.h KoLabColorSpaceTraits.h KoRgbColorSpaceTraits.h KoXyzColorSpaceTraits.h KoYcbcrColorSpaceTraits.h KoColorTransformation.h KoColorTransformationFactory.h KoColorTransformationFactoryRegistry.h KoCompositeOp.h KoConvolutionOp.h KoFallBackColorTransformation.h KoIntegerMaths.h KoLabDarkenColorTransformation.h KoMixColorsOp.h KoMixColorsOpImpl.h KoHistogramProducer.h ) set (EXTRA_LIBRARIES ${LINK_OPENEXR_LIB} ${LINK_VC_LIB}) if(MSVC OR (WIN32 AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")) # avoid "cannot open file 'LIBC.lib'" error set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /NODEFAULTLIB:LIBC.LIB") endif() add_library(pigmentcms SHARED ${pigmentcms_SRCS}) generate_export_header(pigmentcms BASE_NAME pigment EXPORT_MACRO_NAME PIGMENTCMS_EXPORT ) target_link_libraries(pigmentcms PUBLIC KF5::I18n Qt5::Xml Qt5::Gui ${EXTRA_LIBRARIES} PRIVATE koplugin ${WIN32_PLATFORM_NET_LIBS} ) if (HAVE_VC AND NOT PACKAGERS_BUILD) set_property(TARGET pigmentcms APPEND PROPERTY COMPILE_OPTIONS "${Vc_ARCHITECTURE_FLAGS}") endif() set_target_properties(pigmentcms PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) install(TARGETS pigmentcms ${INSTALL_TARGETS_DEFAULT_ARGS}) # TODO: with the new embedded JSON data for plugins there is no schema ATM to define extended properties # pigment.desktop pigmentextension.desktop if (SHOULD_BUILD_DEVEL_HEADERS) install( FILES ${PIGMENT_INSTALL_FILES} DESTINATION ${INCLUDE_INSTALL_DIR}/calligra COMPONENT Devel) endif() diff --git a/libs/pigment/KoColorConversionTransformationFactory.h b/libs/pigment/KoColorConversionTransformationFactory.h index 25e63877199..bbcb893a99a 100644 --- a/libs/pigment/KoColorConversionTransformationFactory.h +++ b/libs/pigment/KoColorConversionTransformationFactory.h @@ -1,103 +1,103 @@ /* * Copyright (c) 2007 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef _KO_COLOR_CONVERSION_TRANSFORMATION_FACTORY_H_ #define _KO_COLOR_CONVERSION_TRANSFORMATION_FACTORY_H_ class KoColorSpace; class KoColorConversionTransformation; #include #include /** * Factory to create a color transformation between two colorsapces. */ class PIGMENTCMS_EXPORT KoColorConversionTransformationFactory : public KoColorConversionTransformationAbstractFactory { public: /** - * Create a color conversion tranformation factory, that can create + * Create a color conversion transformation factory, that can create * KoColorConversionTrasnformation object between a source colorspace * and a destination colorspace. * * @param _srcModelId id for the source color model * @param _srcDepthId id for the source depth * @param _dstModelId id for the destination color model * @param _dstDepthId id for the destination depth * @param _srcProfile name of the source profile, or empty if any profile * @param _dstProfile name of the destination profile, or empty if any profile */ KoColorConversionTransformationFactory(const QString &_srcModelId, const QString &_srcDepthId, const QString &_srcProfile, const QString &_dstModelId, const QString &_dstDepthId, const QString &_dstProfile); virtual ~KoColorConversionTransformationFactory(); /** * @return true if this factory creates a color conversion transformation which * conserve color information (typical color transformation that lose that information * is anything to grayscale). */ virtual bool conserveColorInformation() const = 0; /** * @return true if this factory creates a color conversion transformation which * conserve the dynamic range of the color. */ virtual bool conserveDynamicRange() const = 0; public: /** * @return the id of the source color model */ QString srcColorModelId() const; /** * @return the id of the source color depth */ QString srcColorDepthId() const; /** * @return the name of the source profile (note that an empty name * means all profiles can be used) */ QString srcProfile() const; /** * @return the id of the destination color model */ QString dstColorModelId() const; /** * @return the id of the destination color depth */ QString dstColorDepthId() const; /** * @return the name of the destination profile (note that an empty name * means all profiles can be used) */ QString dstProfile() const; protected: /** * @param srcCS source color space * @return true if the color space given as argument can be used as a source colorspace */ bool canBeSource(const KoColorSpace* srcCS) const; /** * @param dstCS destination color space * @return true if the color space given as argument can be used as a destination colorspace */ bool canBeDestination(const KoColorSpace* dstCS) const; private: struct Private; Private* const d; }; #endif diff --git a/libs/pigment/KoColorSpaceFactory.cpp b/libs/pigment/KoColorSpaceFactory.cpp index efb5b0c8b51..7d36b189ba9 100644 --- a/libs/pigment/KoColorSpaceFactory.cpp +++ b/libs/pigment/KoColorSpaceFactory.cpp @@ -1,107 +1,107 @@ /* * Copyright (c) 2010 Cyrille Berger * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoColorSpaceFactory.h" #include "DebugPigment.h" #include #include "KoColorProfile.h" #include "KoColorSpace.h" #include "KoColorSpaceRegistry.h" struct Q_DECL_HIDDEN KoColorSpaceFactory::Private { QList colorprofiles; QList colorspaces; QHash availableColorspaces; QMutex mutex; #ifndef NDEBUG QHash stackInformation; #endif }; KoColorSpaceFactory::KoColorSpaceFactory() : d(new Private) { } KoColorSpaceFactory::~KoColorSpaceFactory() { #ifndef NDEBUG // Check that all color spaces have been released int count = 0; count += d->availableColorspaces.size(); for(QHash::const_iterator it = d->stackInformation.constBegin(); it != d->stackInformation.constEnd(); ++it) { errorPigment << "*******************************************"; - errorPigment << it.key()->id() << " still in used, and grabed in: "; + errorPigment << it.key()->id() << " still in used, and grabbed in: "; errorPigment << it.value(); } if( count != d->colorspaces.size()) { errorPigment << (d->colorspaces.size() - count) << " colorspaces are still used"; } Q_ASSERT(count == d->colorspaces.size()); #endif foreach(KoColorSpace* cs, d->colorspaces) { delete cs; } foreach(KoColorProfile* profile, d->colorprofiles) { KoColorSpaceRegistry::instance()->removeProfile(profile); delete profile; } delete d; } const KoColorProfile* KoColorSpaceFactory::colorProfile(const QByteArray& rawData) const { KoColorProfile* colorProfile = createColorProfile(rawData); if (colorProfile && colorProfile->valid()) { if (const KoColorProfile* existingProfile = KoColorSpaceRegistry::instance()->profileByName(colorProfile->name())) { delete colorProfile; return existingProfile; } KoColorSpaceRegistry::instance()->addProfile(colorProfile); d->colorprofiles.append(colorProfile); } return colorProfile; } const KoColorSpace *KoColorSpaceFactory::grabColorSpace(const KoColorProfile * profile) { QMutexLocker l(&d->mutex); Q_ASSERT(profile); auto it = d->availableColorspaces.find(profile->name()); KoColorSpace* cs; if (it == d->availableColorspaces.end()) { cs = createColorSpace(profile); if (cs) { d->availableColorspaces[profile->name()] = cs; } } else { cs = it.value(); } return cs; } diff --git a/libs/rdf/CMakeLists.txt b/libs/rdf/CMakeLists.txt index 1cbf46ff049..cb875cc9737 100644 --- a/libs/rdf/CMakeLists.txt +++ b/libs/rdf/CMakeLists.txt @@ -1,76 +1,78 @@ -add_subdirectory( tests ) +if(BUILD_TESTING) + add_subdirectory( tests ) +endif() kde_enable_exceptions() include_directories( ${KORDF_INCLUDES}) ########### next target ############### set(kordf_LIB_SRCS KoDocumentRdf.cpp KoDocumentRdfEditWidget.cpp KoSopranoTableModelDelegate.cpp KoSopranoTableModel.cpp KoRdfPrefixMapping.cpp KoSemanticStylesheet.cpp KoRdfBasicSemanticItem.cpp KoRdfSemanticItem.cpp KoRdfSemanticItemFactoryBase.cpp KoRdfSemanticItemRegistry.cpp KoRdfSemanticItemViewSite.cpp KoRdfSemanticTree.cpp KoRdfSemanticTreeWidgetItem.cpp KoSemanticStylesheetsEditor.cpp KoChangeTrackerDisabledRAII.cpp RdfSemanticTreeWidgetAction.cpp RdfSemanticTreeWidgetSelectAction.cpp InsertSemanticObjectActionBase.cpp InsertSemanticObjectCreateAction.cpp InsertSemanticObjectReferenceAction.cpp ) ki18n_wrap_ui( kordf_LIB_SRCS KoDocumentRdfEditWidget.ui KoSemanticStylesheetsEditor.ui ) add_library(kordf SHARED ${kordf_LIB_SRCS}) target_link_libraries(kordf PUBLIC komain ${SOPRANO_LIBRARIES} PRIVATE koplugin ) set_target_properties(kordf PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) install(TARGETS kordf ${INSTALL_TARGETS_DEFAULT_ARGS} ) # TODO: with the new embedded JSON data for plugins there is no schema ATM to define extended properties # calligra_semanticitem.desktop if (SHOULD_BUILD_DEVEL_HEADERS) install( FILES kordf_export.h RdfForward.h KoDocumentRdf.h KoDocumentRdfEditWidget.h KoRdfPrefixMapping.h KoRdfBasicSemanticItem.h KoRdfSemanticItem.h KoRdfSemanticItemFactoryBase.h KoRdfSemanticItemRegistry.h KoRdfSemanticItemViewSite.h KoRdfSemanticTree.h KoRdfSemanticTreeWidgetItem.h KoSemanticStylesheet.h KoSemanticStylesheetsEditor.h RdfSemanticTreeWidgetAction.h RdfSemanticTreeWidgetSelectAction.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligra COMPONENT Devel) endif() diff --git a/libs/store/CMakeLists.txt b/libs/store/CMakeLists.txt index 58b98878c53..358fec2a853 100644 --- a/libs/store/CMakeLists.txt +++ b/libs/store/CMakeLists.txt @@ -1,56 +1,58 @@ -add_subdirectory(tests) +if(BUILD_TESTING) + add_subdirectory(tests) +endif() ########### libkostore ############### if( Qca-qt5_FOUND ) add_definitions( -DQCA2 ) endif() set(kostore_LIB_SRCS KoDirectoryStore.cpp KoEncryptedStore.cpp KoEncryptionChecker.cpp KoLZF.cpp KoStore.cpp KoStoreDevice.cpp KoTarStore.cpp KoXmlNS.cpp KoXmlReader.cpp KoXmlWriter.cpp KoZipStore.cpp StoreDebug.cpp KoNetAccess.cpp # temporary while porting ) add_library(kostore SHARED ${kostore_LIB_SRCS}) generate_export_header(kostore BASE_NAME kostore) target_link_libraries(kostore PUBLIC Qt5::Xml Qt5::Core KF5::KIOCore PRIVATE Qt5::Gui KF5::Archive KF5::Wallet KF5::KIOWidgets KF5::I18n ) if( Qca-qt5_FOUND ) target_link_libraries(kostore PRIVATE qca-qt5) endif() set_target_properties(kostore PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) install(TARGETS kostore ${INSTALL_TARGETS_DEFAULT_ARGS} ) if (SHOULD_BUILD_DEVEL_HEADERS) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/kostore_export.h KoStore.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligra COMPONENT Devel) endif() diff --git a/libs/text/CMakeLists.txt b/libs/text/CMakeLists.txt index 67c8c14ec89..16d7e066fa2 100644 --- a/libs/text/CMakeLists.txt +++ b/libs/text/CMakeLists.txt @@ -1,244 +1,246 @@ include_directories(${KOTEXT_INCLUDES} ${FONTCONFIG_INCLUDE_DIR} ${FREETYPE_INCLUDE_DIRS}) -add_subdirectory( tests ) -add_subdirectory( styles/tests ) +if(BUILD_TESTING) + add_subdirectory( tests ) + add_subdirectory( styles/tests ) +endif() ########### next target ############### set(kotext_LIB_SRCS KoDocumentRdfBase.cpp KoText.cpp KoTextBlockData.cpp KoTextBlockBorderData.cpp KoTextBlockPaintStrategyBase.cpp KoTextOdfSaveHelper.cpp KoTextPaste.cpp KoTextDocument.cpp KoTextEditor.cpp KoTextEditor_undo.cpp KoTextEditor_format.cpp KoList.cpp KoTextEditingRegistry.cpp KoTextEditingFactory.cpp KoTextEditingPlugin.cpp KoTextRangeManager.cpp KoInlineTextObjectManager.cpp KoInlineObjectFactoryBase.cpp KoInlineObjectRegistry.cpp InsertInlineObjectActionBase_p.cpp InsertVariableAction.cpp InsertNamedVariableAction.cpp InsertTextReferenceAction.cpp InsertTextLocator.cpp KoInlineObject.cpp KoTextRange.cpp KoVariable.cpp KoVariableManager.cpp KoNamedVariable.cpp KoSection.cpp KoSectionEnd.cpp KoSectionUtils.cpp KoSectionModel.cpp KoTextLocator.cpp KoTextReference.cpp KoAnchorInlineObject.cpp KoAnchorTextRange.cpp KoTextShapeSavingContext.cpp KoAnnotation.cpp KoAnnotationManager.cpp KoBookmark.cpp KoBookmarkManager.cpp KoInlineNote.cpp KoInlineCite.cpp KoTextSoftPageBreak.cpp FindDirection_p.cpp KoFindStrategy.cpp KoReplaceStrategy.cpp KoFind_p.cpp KoFind.cpp KoTextDebug.cpp KoTextPage.cpp KoPageProvider.cpp KoTableColumnAndRowStyleManager.cpp KoTextInlineRdf.cpp KoTextMeta.cpp KoTextTableTemplate.cpp OdfTextTrackStyles.cpp ToCBibGeneratorInfo.cpp KoTableOfContentsGeneratorInfo.cpp KoBibliographyInfo.cpp BibliographyGenerator.cpp styles/Styles_p.cpp styles/KoCharacterStyle.cpp styles/KoParagraphStyle.cpp styles/KoStyleManager.cpp styles/KoListStyle.cpp styles/KoListLevelProperties.cpp styles/KoTableStyle.cpp styles/KoTableColumnStyle.cpp styles/KoTableRowStyle.cpp styles/KoTableCellStyle.cpp styles/KoSectionStyle.cpp opendocument/KoTextSharedLoadingData.cpp opendocument/KoTextSharedSavingData.cpp opendocument/KoTextLoader.cpp opendocument/KoTextWriter_p.cpp opendocument/KoTextWriter.cpp changetracker/KoChangeTracker.cpp changetracker/KoChangeTrackerElement.cpp changetracker/KoFormatChangeInformation.cpp changetracker/KoDeletedRowColumnDataStore.cpp changetracker/KoDeletedRowData.cpp changetracker/KoDeletedColumnData.cpp changetracker/KoDeletedCellData.cpp commands/ChangeAnchorPropertiesCommand.cpp commands/ChangeListCommand.cpp commands/ChangeStylesCommand.cpp commands/ChangeStylesMacroCommand.cpp commands/DeleteAnchorsCommand.cpp commands/DeleteAnnotationsCommand.cpp commands/DeleteCommand.cpp commands/DeleteTableColumnCommand.cpp commands/DeleteTableRowCommand.cpp commands/InsertNoteCommand.cpp commands/InsertTableColumnCommand.cpp commands/InsertTableRowCommand.cpp commands/ResizeTableCommand.cpp commands/InsertInlineObjectCommand.cpp commands/ListItemNumberingCommand.cpp commands/TextPasteCommand.cpp commands/AddTextRangeCommand.cpp commands/AddAnnotationCommand.cpp commands/ParagraphFormattingCommand.cpp commands/RenameSectionCommand.cpp commands/NewSectionCommand.cpp commands/SplitSectionsCommand.cpp KoTextDrag.cpp KoTextCommandBase.cpp TextDebug.cpp ) if( SHOULD_BUILD_FEATURE_RDF ) set(kotext_LIB_SRCS ${kotext_LIB_SRCS} KoTextRdfCore.cpp ) endif() add_library(kotext SHARED ${kotext_LIB_SRCS}) generate_export_header(kotext BASE_NAME kotext) target_link_libraries(kotext PUBLIC flake KF5::TextWidgets PRIVATE koplugin KF5::WindowSystem # KoFind to activate the window with content found ) if( SHOULD_BUILD_FEATURE_RDF ) target_link_libraries(kotext PRIVATE ${SOPRANO_LIBRARIES}) endif() if( FONTCONFIG_FOUND ) target_link_libraries(kotext PRIVATE ${FONTCONFIG_LIBRARIES}) endif() if( FREETYPE_FOUND ) target_link_libraries(kotext PRIVATE ${FREETYPE_LIBRARIES}) endif() set_target_properties(kotext PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) install(TARGETS kotext ${INSTALL_TARGETS_DEFAULT_ARGS}) ########### install files ############### # TODO: with the new embedded JSON data for plugins there is no schema ATM to define extended properties # texteditingplugin.desktop inlinetextobject.desktop if (SHOULD_BUILD_DEVEL_HEADERS) install( FILES KoDocumentRdfBase.h KoInlineObject.h KoTextEditor.h KoTextEditingFactory.h KoTextEditingPlugin.h KoTextEditingRegistry.h KoInlineObjectRegistry.h KoInlineObjectFactoryBase.h KoBookmark.h KoBookmarkManager.h KoAnnotationManager.h KoInlineTextObjectManager.h KoAnchorInlineObject.h KoAnchorTextRange.h KoTextBlockBorderData.h KoTextBlockData.h KoTextDocument.h KoText.h KoTextRange.h KoTextRangeManager.h KoList.h KoTextLocator.h KoTextPage.h KoTextPaste.h KoVariable.h KoVariableManager.h KoTextRdfCore.h KoTextInlineRdf.h KoTextMeta.h KoTextSoftPageBreak.cpp KoSection.h KoSectionEnd.h KoSectionUtils.h KoSectionModel.h KoTextCommandBase.h KoTextTableTemplate.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligra COMPONENT Devel ) install( FILES styles/KoCharacterStyle.h styles/KoListLevelProperties.h styles/KoListStyle.h styles/KoParagraphStyle.h styles/KoTableColumnStyle.h styles/KoTableRowStyle.h styles/KoTableCellStyle.h styles/KoSectionStyle.h styles/KoStyleManager.h styles/KoTableStyle.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligra/styles COMPONENT Devel ) install( FILES changetracker/KoChangeTracker.h changetracker/KoChangeTrackerElement.h changetracker/KoDeletedRowColumnDataStore.h changetracker/KoDeletedRowData.cpp changetracker/KoDeletedColumnData.cpp changetracker/KoDeletedCellData.cpp DESTINATION ${INCLUDE_INSTALL_DIR}/calligra/changetracker COMPONENT Devel ) endif() diff --git a/libs/text/KoTextBlockData.cpp b/libs/text/KoTextBlockData.cpp index 29abbf6dfc1..dc7fd0545a5 100644 --- a/libs/text/KoTextBlockData.cpp +++ b/libs/text/KoTextBlockData.cpp @@ -1,302 +1,312 @@ /* This file is part of the KDE project * Copyright (C) 2006 Thomas Zander * Copyright (C) 2010 C. Boemann * Copyright (C) 2011 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoTextBlockData.h" #include "KoTextBlockBorderData.h" #include "KoTextBlockPaintStrategyBase.h" class Q_DECL_HIDDEN KoTextBlockData::Private : public QTextBlockUserData { public: Private() : counterWidth(-1.0) , counterSpacing(0) , counterIsImage(false) , counterIndex(1) , border(0) , paintStrategy(0) { layoutedMarkupRanges[KoTextBlockData::Misspell] = false; layoutedMarkupRanges[KoTextBlockData::Grammar] = false; } ~Private() { if (border && !border->deref()) delete border; delete paintStrategy; } qreal counterWidth; qreal counterSpacing; QString counterPrefix; QString counterPlainText; QString counterSuffix; QString partialCounterText; bool counterIsImage; int counterIndex; QPointF counterPos; QTextCharFormat labelFormat; KoTextBlockBorderData *border; KoTextBlockPaintStrategyBase *paintStrategy; QMap > markupRangesMap; QMap layoutedMarkupRanges; }; KoTextBlockData::KoTextBlockData(QTextBlock &block) : d(block.userData() ? dynamic_cast(block.userData()) : new Private()) { block.setUserData(d); } KoTextBlockData::KoTextBlockData(QTextBlockUserData *userData) : d(dynamic_cast(userData)) { Q_ASSERT(d); } KoTextBlockData::~KoTextBlockData() { // explicitly do not delete the d-pointer here } void KoTextBlockData::appendMarkup(MarkupType type, int firstChar, int lastChar) { - Q_ASSERT(d->markupRangesMap[type].isEmpty() || d->markupRangesMap[type].last().lastChar < firstChar); + Q_ASSERT(d->markupRangesMap[type].isEmpty() || firstChar > d->markupRangesMap[type].last().lastChar + d->markupRangesMap[type].last().lastRebased); MarkupRange range; range.firstChar = firstChar; range.lastChar = lastChar; + range.firstRebased = 0; + range.lastRebased = 0; + if (!d->markupRangesMap[type].isEmpty()) { + // The document may have been changed (and thus markup has moved) while + // the plugin has done its job in the background + range.firstChar += d->markupRangesMap[type].last().firstRebased; + range.lastChar += d->markupRangesMap[type].last().lastRebased; + } d->layoutedMarkupRanges[type] = false; d->markupRangesMap[type].append(range); } void KoTextBlockData::clearMarkups(MarkupType type) { d->markupRangesMap[type].clear(); d->layoutedMarkupRanges[type] = false; } KoTextBlockData::MarkupRange KoTextBlockData::findMarkup(MarkupType type, int positionWithin) const { foreach (const MarkupRange &range, d->markupRangesMap[type]) { - if (positionWithin <= range.lastChar) { + if (positionWithin <= range.lastChar + range.lastRebased) { // possible hit - if (positionWithin >= range.firstChar) { + if (positionWithin >= range.firstChar + range.firstRebased) { return range; } else { return MarkupRange(); // we have passed it without finding } } } return MarkupRange(); // either no ranges or not in last either } void KoTextBlockData::rebaseMarkups(MarkupType type, int fromPosition, int delta) { QVector::Iterator markIt = markupsBegin(type); QVector::Iterator markEnd = markupsEnd(type); while (markIt != markEnd) { if (fromPosition <= markIt->lastChar) { // we need to modify the end of this markIt->lastChar += delta; + markIt->lastRebased += delta; } if (fromPosition < markIt->firstChar) { // we need to modify the end of this markIt->firstChar += delta; + markIt->firstRebased += delta; } ++markIt; } } void KoTextBlockData::setMarkupsLayoutValidity(MarkupType type, bool valid) { d->layoutedMarkupRanges[type] = valid; } bool KoTextBlockData::isMarkupsLayoutValid(MarkupType type) const { return d->layoutedMarkupRanges[type]; } QVector::Iterator KoTextBlockData::markupsBegin(MarkupType type) { return d->markupRangesMap[type].begin(); } QVector::Iterator KoTextBlockData::markupsEnd(MarkupType type) { return d->markupRangesMap[type].end(); } bool KoTextBlockData::hasCounterData() const { return d->counterWidth >= 0 && (!d->counterPlainText.isNull() || d->counterIsImage); } qreal KoTextBlockData::counterWidth() const { return qMax(qreal(0), d->counterWidth); } void KoTextBlockData::setBorder(KoTextBlockBorderData *border) { if (d->border && !d->border->deref()) delete d->border; d->border = border; if (d->border) d->border->ref(); } void KoTextBlockData::setCounterWidth(qreal width) { d->counterWidth = width; } qreal KoTextBlockData::counterSpacing() const { return d->counterSpacing; } void KoTextBlockData::setCounterSpacing(qreal spacing) { d->counterSpacing = spacing; } QString KoTextBlockData::counterText() const { return d->counterPrefix + d->counterPlainText + d->counterSuffix; } void KoTextBlockData::clearCounter() { d->partialCounterText.clear(); d->counterPlainText.clear(); d->counterPrefix.clear(); d->counterSuffix.clear(); d->counterSpacing = 0.0; d->counterWidth = 0.0; d->counterIsImage = false; } void KoTextBlockData::setPartialCounterText(const QString &text) { d->partialCounterText = text; } QString KoTextBlockData::partialCounterText() const { return d->partialCounterText; } void KoTextBlockData::setCounterPlainText(const QString &text) { d->counterPlainText = text; } QString KoTextBlockData::counterPlainText() const { return d->counterPlainText; } void KoTextBlockData::setCounterPrefix(const QString &text) { d->counterPrefix = text; } QString KoTextBlockData::counterPrefix() const { return d->counterPrefix; } void KoTextBlockData::setCounterSuffix(const QString &text) { d->counterSuffix = text; } QString KoTextBlockData::counterSuffix() const { return d->counterSuffix; } void KoTextBlockData::setCounterIsImage(bool isImage) { d->counterIsImage = isImage; } bool KoTextBlockData::counterIsImage() const { return d->counterIsImage; } void KoTextBlockData::setCounterIndex(int index) { d->counterIndex = index; } int KoTextBlockData::counterIndex() const { return d->counterIndex; } void KoTextBlockData::setCounterPosition(const QPointF &position) { d->counterPos = position; } QPointF KoTextBlockData::counterPosition() const { return d->counterPos; } void KoTextBlockData::setLabelFormat(const QTextCharFormat &format) { d->labelFormat = format; } QTextCharFormat KoTextBlockData::labelFormat() const { return d->labelFormat; } KoTextBlockBorderData *KoTextBlockData::border() const { return d->border; } void KoTextBlockData::setPaintStrategy(KoTextBlockPaintStrategyBase *paintStrategy) { delete d->paintStrategy; d->paintStrategy = paintStrategy; } KoTextBlockPaintStrategyBase *KoTextBlockData::paintStrategy() const { return d->paintStrategy; } bool KoTextBlockData::saveXmlID() const { // as suggested by boemann, http://lists.kde.org/?l=calligra-devel&m=132396354701553&w=2 return d->paintStrategy != 0; } diff --git a/libs/text/KoTextBlockData.h b/libs/text/KoTextBlockData.h index 7abb932a9ce..251fa042dd6 100644 --- a/libs/text/KoTextBlockData.h +++ b/libs/text/KoTextBlockData.h @@ -1,225 +1,227 @@ /* This file is part of the KDE project * Copyright (C) 2006 Thomas Zander * Copyright (C) 2011 Boudewijn Rempt * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KOTEXTBLOCKDATA_H #define KOTEXTBLOCKDATA_H #include #include "kotext_export.h" class KoTextBlockBorderData; class KoTextBlockPaintStrategyBase; /** * This class is used to store properties for KoText layouting inside Qt QTextBlock * instances. */ class KOTEXT_EXPORT KoTextBlockData { public: /** * Supplemental data to allow advanced tabs to be used for layout and painting. * Qt-Scribe knows only left-tabs and it also only knows tab-positions per paragraph * which is not enough for our needs. * Using the tabs list we calculated in the layout step, we can emulate * all tabs by setting these as left-tabs on scribe prior to the re-layout of the text in the * line which is redone at painting time. * The tabLength list holds a length for each tab in the line and thus corresponds to the tab * positions in the tabs list. We can then calculate the tab to have started the position * minus the length and use that to paint special tab attributes. */ struct TabLineData { /// the tab positions as set on the QTextOption.setTabArray() QList tabs; /// the length of each tab so we know which area to paint when we want to decorate it. QList tabLength; }; /** * Datastructure to define a range of characters assigned a temporary meaning. * Common use cases are spellchecking and grammar. */ struct MarkupRange { int firstChar; int lastChar; qreal startX; qreal endX; + int firstRebased; + int lastRebased; }; /** * The different types of markups. */ enum MarkupType { Misspell, Grammar }; explicit KoTextBlockData(QTextBlock &block); explicit KoTextBlockData(QTextBlockUserData *userData); virtual ~KoTextBlockData(); /** * Add a range to the _end_ of the list of markups. It's important that firstChar is after * lastChar of the previous range for that type of markup. */ void appendMarkup(MarkupType type, int firstChar, int lastChar); /** * Clear all ranges for a specific type of markup. */ void clearMarkups(MarkupType type); /** * Move all ranges following fromPosition delta number of characters to the right. * Applies to a specific type of markup. */ void rebaseMarkups(MarkupType type, int fromPosition, int delta); /** * Find a range that contains positionWithin. * If none is found a default Markuprange firstChar = lastChar = 0 is returned */ MarkupRange findMarkup(MarkupType type, int positionWithin) const; void setMarkupsLayoutValidity(MarkupType type, bool valid); bool isMarkupsLayoutValid(MarkupType type) const; QVector::Iterator markupsBegin(MarkupType type); QVector::Iterator markupsEnd(MarkupType type); /** * Clear the counter and set everything to default values. */ void clearCounter(); /// return if this block has up-to-date counter data bool hasCounterData() const; /// return the width (in pt) of the counter. qreal counterWidth() const; /// set the width of the counter in pt. void setCounterWidth(qreal width); /// return the spacing (in pt) between the counter and the text qreal counterSpacing() const; /// set the spacing (in pt) between the counter and the text void setCounterSpacing(qreal spacing); /** sets the index that is used at this level. * If this represents a paragraph with counter 3.1, then the text is the 1. * If this represents a paragraph with counter IV.V, then the index is 5. */ void setCounterIndex(int index); /// returns the index for the counter at this level int counterIndex() const; /// return the exact text that will be painted as the counter QString counterText() const; /** * set the text that is used for the counter at this level. the text is formatted * depending on the language/style. * If this represents a parag with counter 3.1 then the text is the '1'.. * If this represents a paragraph with counter IV.V, then the text is V. * since the rest is not dependent on this parag, but only its location in the text * */ void setPartialCounterText(const QString &text); /// return the partial text for this paragraphs counter QString partialCounterText() const; /// set the plain counter text which equals the counterText minus prefix and sufix void setCounterPlainText(const QString &text); /// return the plain counter text which equals the counterText minus prefix and sufix QString counterPlainText() const; void setCounterPrefix(const QString &text); QString counterPrefix() const; void setCounterSuffix(const QString &text); QString counterSuffix() const; /// Set if the counter is a image or not void setCounterIsImage(bool isImage); /// return if the counter is a image or not bool counterIsImage() const; /** * The actual position of the counter can be set, in actual (text) document coordinates. * @param position the location of the top/left of the counter text line. */ void setCounterPosition(const QPointF &position); /** * Return the counter position. * @see setCounterPosition */ QPointF counterPosition() const; /** * Sets a textformat to be used for the counter/bullet * @param font the format */ void setLabelFormat(const QTextCharFormat &format); /** * Return the format to be used for the counter/bullet */ QTextCharFormat labelFormat() const; /** * When a paragraph has a border, it will have a KoTextBlockBorderData instance. * Adding the border will increase the refcount. * @param border the border used for this paragraph, or 0 if no border is needed (anymore). */ void setBorder(KoTextBlockBorderData *border); /** * Return the border associated with this paragraph, or 0 if there is no border set. */ KoTextBlockBorderData *border() const; /** * sets a paintStrategy of this paragraph * @param paintStrategy the paintStrategy to be used for this paragraph */ void setPaintStrategy(KoTextBlockPaintStrategyBase *paintStrategy); /** * Return the paintStrategy of this paragraph */ KoTextBlockPaintStrategyBase *paintStrategy() const; /** * @brief saveXmlID can be used to determine whether we need to save the xml:id * for this text block data object. This is true if the text block data describes * animations. * @return true of we need to save the xml id, false if not. */ bool saveXmlID() const; private: class Private; Private * const d; }; Q_DECLARE_TYPEINFO(KoTextBlockData::MarkupRange, Q_MOVABLE_TYPE); Q_DECLARE_METATYPE(QTextBlockUserData*) #endif diff --git a/libs/text/KoTextEditor_p.h b/libs/text/KoTextEditor_p.h index a686e19517c..28636663959 100644 --- a/libs/text/KoTextEditor_p.h +++ b/libs/text/KoTextEditor_p.h @@ -1,264 +1,264 @@ /* This file is part of the KDE project * Copyright (C) 2009 Pierre Stirnweiss * Copyright (C) 2009 Thomas Zander * Copyright (C) 2015 Soma Schliszka * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KOTEXTEDITOR_P_H #define KOTEXTEDITOR_P_H #include "KoTextEditor.h" #include "KoTextDocument.h" #include "styles/KoParagraphStyle.h" #include "styles/KoStyleManager.h" #include "changetracker/KoChangeTracker.h" #include #include #include #include #include #include #include class KUndo2Command; class Q_DECL_HIDDEN KoTextEditor::Private { public: enum State { NoOp, KeyPress, Delete, Format, Custom }; explicit Private(KoTextEditor *qq, QTextDocument *document); ~Private() {} void documentCommandAdded(); void updateState(State newState, const KUndo2MagicString &title = KUndo2MagicString()); void newLine(KUndo2Command *parent); void clearCharFormatProperty(int propertyId); void emitTextFormatChanged(); KoTextEditor *q; QTextCursor caret; QTextDocument *document; QStack commandStack; bool addNewCommand; bool dummyMacroAdded; int customCommandCount; KUndo2MagicString commandTitle; State editorState; bool editProtected; bool editProtectionCached; }; class KoTextVisitor { public: /// The ObjectVisitingMode enum marks how was the visited object selected. enum ObjectVisitingMode { Partly, /// The visited object (table, cell, ...) is just @b partly selected. (Eg. just one cell is selected in the visited table) Entirely, /// The visited object (table, cell, ...) is @b entirely selected. }; explicit KoTextVisitor(KoTextEditor *editor) : m_abortVisiting(false) , m_editor(editor) { } virtual ~KoTextVisitor() {} // called whenever a visit was prevented by editprotection virtual void nonVisit() {} virtual void visitFragmentSelection(QTextCursor &) { } /** * This method allows to perform custom operation when the visitor reaches a QTextTable * @param visitedTable pointer to the currenlty visited table object * @param visitingMode flag, marks if the table is just partly visited or entirely */ virtual void visitTable(QTextTable *visitedTable, ObjectVisitingMode visitingMode) { Q_UNUSED(visitedTable); Q_UNUSED(visitingMode); } /** * This method allows to perform custom operation when the visitor reaches a QTextTableCell * @param visitedTable pointer to the currenlty visited cell object * @param visitingMode flag, marks if the cell is just partly visited or entirely */ virtual void visitTableCell(QTextTableCell *visitedCell, ObjectVisitingMode visitingMode) { Q_UNUSED(visitedCell); Q_UNUSED(visitingMode); } // The default implementation calls visitFragmentSelection on each fragment.intersect.selection virtual void visitBlock(QTextBlock &block, const QTextCursor &caret) { for (QTextBlock::iterator it = block.begin(); it != block.end(); ++it) { QTextCursor fragmentSelection(caret); fragmentSelection.setPosition(qMax(caret.selectionStart(), it.fragment().position())); fragmentSelection.setPosition(qMin(caret.selectionEnd(), it.fragment().position() + it.fragment().length()), QTextCursor::KeepAnchor); if (fragmentSelection.anchor() >= fragmentSelection.position()) { continue; } visitFragmentSelection(fragmentSelection); } } bool abortVisiting() { return m_abortVisiting;} void setAbortVisiting(bool abort) {m_abortVisiting = abort;} KoTextEditor * editor() const {return m_editor;} private: bool m_abortVisiting; KoTextEditor *m_editor; }; class BlockFormatVisitor { public: BlockFormatVisitor() {} virtual ~BlockFormatVisitor() {} virtual void visit(QTextBlock &block) const = 0; static void visitSelection(KoTextEditor *editor, const BlockFormatVisitor &visitor, const KUndo2MagicString &title = kundo2_i18n("Format"), bool resetProperties = false, bool registerChange = true) { int start = qMin(editor->position(), editor->anchor()); int end = qMax(editor->position(), editor->anchor()); QTextBlock block = editor->block(); if (block.position() > start) block = block.document()->findBlock(start); // now loop over all blocks that the selection contains and alter the text fragments where applicable. while (block.isValid() && block.position() <= end) { QTextBlockFormat prevFormat = block.blockFormat(); if (resetProperties) { if (KoTextDocument(editor->document()).styleManager()) { KoParagraphStyle *old = KoTextDocument(editor->document()).styleManager()->paragraphStyle(block.blockFormat().intProperty(KoParagraphStyle::StyleId)); if (old) old->unapplyStyle(block); } } visitor.visit(block); QTextCursor cursor(block); QTextBlockFormat format = cursor.blockFormat(); if (registerChange) editor->registerTrackedChange(cursor, KoGenChange::FormatChange, title, format, prevFormat, true); block = block.next(); } } }; class CharFormatVisitor { public: CharFormatVisitor() {} virtual ~CharFormatVisitor() {} virtual void visit(QTextCharFormat &format) const = 0; static void visitSelection(KoTextEditor *editor, const CharFormatVisitor &visitor, const KUndo2MagicString &title = kundo2_i18n("Format"), bool registerChange = true) { int start = qMin(editor->position(), editor->anchor()); int end = qMax(editor->position(), editor->anchor()); if (start == end) { // just set a new one. QTextCharFormat format = editor->charFormat(); visitor.visit(format); if (registerChange && KoTextDocument(editor->document()).changeTracker() && KoTextDocument(editor->document()).changeTracker()->recordChanges()) { QTextCharFormat prevFormat(editor->charFormat()); int changeId = KoTextDocument(editor->document()).changeTracker()->getFormatChangeId(title, format, prevFormat, editor->charFormat().property( KoCharacterStyle::ChangeTrackerId ).toInt()); format.setProperty(KoCharacterStyle::ChangeTrackerId, changeId); } editor->cursor()->setCharFormat(format); return; } QTextBlock block = editor->block(); if (block.position() > start) block = block.document()->findBlock(start); QList cursors; QVector formats; // now loop over all blocks that the selection contains and alter the text fragments where applicable. while (block.isValid() && block.position() < end) { QTextBlock::iterator iter = block.begin(); while (! iter.atEnd()) { QTextFragment fragment = iter.fragment(); if (fragment.position() > end) break; if (fragment.position() + fragment.length() <= start) { ++iter; continue; } QTextCursor cursor(block); cursor.setPosition(fragment.position() + 1); - QTextCharFormat format = cursor.charFormat(); // this gets the format one char after the postion. + QTextCharFormat format = cursor.charFormat(); // this gets the format one char after the position. visitor.visit(format); if (registerChange && KoTextDocument(editor->document()).changeTracker() && KoTextDocument(editor->document()).changeTracker()->recordChanges()) { QTextCharFormat prevFormat(cursor.charFormat()); int changeId = KoTextDocument(editor->document()).changeTracker()->getFormatChangeId(title, format, prevFormat, cursor.charFormat().property( KoCharacterStyle::ChangeTrackerId ).toInt()); format.setProperty(KoCharacterStyle::ChangeTrackerId, changeId); } cursor.setPosition(qMax(start, fragment.position())); int to = qMin(end, fragment.position() + fragment.length()); cursor.setPosition(to, QTextCursor::KeepAnchor); cursors.append(cursor); formats.append(format); QTextCharFormat prevFormat(cursor.charFormat()); if (registerChange) editor->registerTrackedChange(cursor,KoGenChange::FormatChange,title, format, prevFormat, false); //this will lead to every fragment having a different change untill the change merging in registerTrackedChange checks also for formatChange or not? ++iter; } block = block.next(); } QVector::Iterator iter = formats.begin(); foreach(QTextCursor cursor, cursors) { cursor.setCharFormat(*iter); ++iter; } } }; #endif //KOTEXTEDITOR_P_H diff --git a/libs/textlayout/CMakeLists.txt b/libs/textlayout/CMakeLists.txt index 6e020417cc3..e1a776462d2 100644 --- a/libs/textlayout/CMakeLists.txt +++ b/libs/textlayout/CMakeLists.txt @@ -1,62 +1,64 @@ include_directories(${TEXTLAYOUT_INCLUDES}) -add_subdirectory( tests ) +if(BUILD_TESTING) + add_subdirectory( tests ) +endif() ########### next target ############### set(textlayout_LIB_SRCS KoTextLayoutCellHelper.cpp KoTextLayoutArea.cpp KoTextLayoutArea_paint.cpp KoTextLayoutEndNotesArea.cpp KoTextLayoutTableArea.cpp KoTextLayoutNoteArea.cpp KoTextLayoutRootArea.cpp KoTextLayoutRootAreaProvider.cpp KoTextDocumentLayout.cpp ListItemsHelper.cpp KoTextShapeContainerModel.cpp RunAroundHelper.cpp KoTextLayoutObstruction.cpp FrameIterator.cpp TableIterator.cpp KoPointedAt.cpp KoTextShapeData.cpp FloatingAnchorStrategy.cpp InlineAnchorStrategy.cpp AnchorStrategy.cpp ToCGenerator.cpp DummyDocumentLayout.cpp IndexGeneratorManager.cpp KoStyleThumbnailer.cpp TextLayoutDebug.cpp ) add_library(kotextlayout SHARED ${textlayout_LIB_SRCS}) generate_export_header(kotextlayout BASE_NAME kotextlayout) target_link_libraries(kotextlayout PUBLIC kotext ) set_target_properties(kotextlayout PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) install(TARGETS kotextlayout ${INSTALL_TARGETS_DEFAULT_ARGS}) if (SHOULD_BUILD_DEVEL_HEADERS) install( FILES KoTextDocumentLayout.h KoTextLayoutArea.h KoTextLayoutRootArea.h KoTextShapeData.h KoPointedAt.h KoStyleThumbnailer.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligra COMPONENT Devel ) endif() diff --git a/libs/textlayout/KoTextLayoutArea.cpp b/libs/textlayout/KoTextLayoutArea.cpp index 5af10ec9799..016eda8db59 100644 --- a/libs/textlayout/KoTextLayoutArea.cpp +++ b/libs/textlayout/KoTextLayoutArea.cpp @@ -1,2188 +1,2182 @@ /* This file is part of the KDE project * Copyright (C) 2006-2010 Thomas Zander * Copyright (C) 2008,2011 Thorsten Zachmann * Copyright (C) 2008 Girish Ramakrishnan * Copyright (C) 2008 Roopesh Chander * Copyright (C) 2007-2008 Pierre Ducroquet * Copyright (C) 2009-2011 KO GmbH * Copyright (C) 2009-2011 C. Boemann * Copyright (C) 2010 Nandita Suri * Copyright (C) 2010 Ajay Pundhir * Copyright (C) 2011 Lukáš Tvrdý * Copyright (C) 2011 Gopalakrishna Bhat A * Copyright (C) 2011 Stuart Dickson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoTextLayoutArea.h" #include "KoTextLayoutArea_p.h" #include "TableIterator.h" #include "ListItemsHelper.h" #include "RunAroundHelper.h" #include "KoTextDocumentLayout.h" #include "FrameIterator.h" #include "KoPointedAt.h" #include "KoCharAreaInfo.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern int qt_defaultDpiY(); Q_DECLARE_METATYPE(QTextDocument *) #define DropCapsAdditionalFormattingId 25602902 #define PresenterFontStretch 1.2 KoTextLayoutArea::KoTextLayoutArea(KoTextLayoutArea *p, KoTextDocumentLayout *documentLayout) : d (new Private) { d->parent = p; d->documentLayout = documentLayout; } KoTextLayoutArea::~KoTextLayoutArea() { qDeleteAll(d->tableAreas); qDeleteAll(d->footNoteAreas); qDeleteAll(d->preregisteredFootNoteAreas); delete d->startOfArea; delete d->endOfArea; delete d; } KoPointedAt KoTextLayoutArea::hitTest(const QPointF &p, Qt::HitTestAccuracy accuracy) const { QPointF point = p - QPointF(0, d->verticalAlignOffset); if (d->startOfArea == 0) // We have not been layouted yet return KoPointedAt(); KoPointedAt pointedAt; bool basicallyFound = false; QTextFrame::iterator it = d->startOfArea->it; QTextFrame::iterator stop = d->endOfArea->it; if (!stop.atEnd()) { if(!stop.currentBlock().isValid() || d->endOfArea->lineTextStart >= 0) { // Last thing we contain is a frame (table) or first part of a paragraph split in two // The stop point should be the object after that // However if stop is already atEnd we shouldn't increment further ++stop; } } int tableAreaIndex = 0; int tocIndex = 0; int footNoteIndex = 0; for (; it != stop && !it.atEnd(); ++it) { QTextBlock block = it.currentBlock(); QTextTable *table = qobject_cast(it.currentFrame()); QTextFrame *subFrame = it.currentFrame(); if (table) { if (tableAreaIndex >= d->tableAreas.size()) { continue; } if (point.y() > d->tableAreas[tableAreaIndex]->top() && point.y() < d->tableAreas[tableAreaIndex]->bottom()) { return d->tableAreas[tableAreaIndex]->hitTest(point, accuracy); } ++tableAreaIndex; continue; } else if (subFrame) { if (it.currentFrame()->format().intProperty(KoText::SubFrameType) == KoText::AuxillaryFrameType) { if (point.y() > d->endNotesArea->top() && point.y() < d->endNotesArea->bottom()) { pointedAt = d->endNotesArea->hitTest(point, accuracy); return pointedAt; } } break; } else { if (!block.isValid()) continue; } if (block.blockFormat().hasProperty(KoParagraphStyle::GeneratedDocument)) { // check if p is over table of content if (point.y() > d->generatedDocAreas[tocIndex]->top() && point.y() < d->generatedDocAreas[tocIndex]->bottom()) { pointedAt = d->generatedDocAreas[tocIndex]->hitTest(point, accuracy); pointedAt.position = block.position(); return pointedAt; } ++tocIndex; continue; } if (basicallyFound) // a subsequent table or lines have now had their chance return pointedAt; QTextLayout *layout = block.layout(); QTextFrame::iterator next = it; ++next; if (next != stop && next.currentFrame() == 0 && point.y() > layout->boundingRect().bottom()) { // just skip this block. continue; } for (int i = 0; i < layout->lineCount(); i++) { QTextLine line = layout->lineAt(i); if (block == d->startOfArea->it.currentBlock() && line.textStart() < d->startOfArea->lineTextStart) { continue; // this line is part of a previous layoutArea } if (point.y() > line.y() + line.height()) { pointedAt.position = block.position() + line.textStart() + line.textLength(); if (block == d->endOfArea->it.currentBlock() && line.textStart() + line.textLength() >= d->endOfArea->lineTextStart) { pointedAt.position = block.position() + line.xToCursor(point.x()); break; // this and following lines are part of a next layoutArea } continue; } if (accuracy == Qt::ExactHit && point.y() < line.y()) { // between lines return KoPointedAt(); } const QRectF lineRect = line.naturalTextRect(); if (accuracy == Qt::ExactHit && // left or right of line (point.x() < lineRect.left() || point.x() > lineRect.right())) { return KoPointedAt(); } if (point.x() > lineRect.x() + lineRect.width() && layout->textOption().textDirection() == Qt::RightToLeft) { // totally right of RTL text means the position is the start of the text. //TODO how about the other side? pointedAt.position = block.position() + line.textStart(); return pointedAt; } if (basicallyFound && point.y() < lineRect.y()) { // This was not same baseline so basicallyFound was correct return pointedAt; } if (point.x() > lineRect.x() + lineRect.width()) { // right of line basicallyFound = true; pointedAt.position = block.position() + line.textStart() + line.textLength(); continue; // don't break as next line may be on same baseline } pointedAt.position = block.position() + line.xToCursor(point.x()); QTextCursor tmpCursor(block); tmpCursor.setPosition(block.position() + line.xToCursor(point.x(), QTextLine::CursorOnCharacter) + 1); pointedAt.fillInLinks(tmpCursor, d->documentLayout->inlineTextObjectManager(), d->documentLayout->textRangeManager()); return pointedAt; } } //and finally test the footnotes point -= QPointF(0, bottom() - d->footNotesHeight); while (footNoteIndex < d->footNoteAreas.length()) { // check if p is over foot notes area if (point.y() > 0 && point.y() < d->footNoteAreas[footNoteIndex]->bottom() - d->footNoteAreas[footNoteIndex]->top()) { pointedAt = d->footNoteAreas[footNoteIndex]->hitTest(point, accuracy); return pointedAt; } point -= QPointF(0, d->footNoteAreas[footNoteIndex]->bottom() - d->footNoteAreas[footNoteIndex]->top()); ++footNoteIndex; } return pointedAt; } QVector KoTextLayoutArea::generateCharAreaInfos() const { QVector result; if (d->startOfArea == 0 || d->endOfArea == 0) { // We have not been completely layouted yet debugTextLayout << "called when not completely layouted yet"; return result; } QTextFrame::iterator it = d->startOfArea->it; QTextFrame::iterator stop = d->endOfArea->it; int tableAreaIndex = 0; int tocIndex = 0; for (; it != stop && !it.atEnd(); ++it) { QTextTable *table = qobject_cast(it.currentFrame()); if (table) { if (tableAreaIndex >= d->tableAreas.size()) { continue; } result.append(d->tableAreas[tableAreaIndex]->generateCharAreaInfos()); ++tableAreaIndex; continue; } QTextFrame *subFrame = it.currentFrame(); if (subFrame) { if (subFrame->format().intProperty(KoText::SubFrameType) == KoText::AuxillaryFrameType) { result.append(d->endNotesArea->generateCharAreaInfos()); } continue; } QTextBlock block = it.currentBlock(); if (!block.isValid()) { continue; } if (block.blockFormat().hasProperty(KoParagraphStyle::GeneratedDocument)) { result.append(d->generatedDocAreas[tocIndex]->generateCharAreaInfos()); ++tocIndex; continue; } // TODO: also include header/paragraph numbering/bullet points QTextLayout *layout = block.layout(); for (int i = 0; i < layout->lineCount(); ++i) { QTextLine line = layout->lineAt(i); if (block == d->startOfArea->it.currentBlock() && line.textStart() < d->startOfArea->lineTextStart) { continue; // this line is part of a previous layoutArea } if (block == d->endOfArea->it.currentBlock() && line.textStart() + line.textLength() >= d->endOfArea->lineTextStart) { break; // this and following lines are part of a next layoutArea } qreal xLeading; qreal xTrailing; for (int j = line.textStart(); j < line.textStart() + line.textLength(); ++j) { // TODO: support RTL xLeading = line.cursorToX(j, QTextLine::Leading); xTrailing = line.cursorToX(j, QTextLine::Trailing); QRectF rect(xLeading, line.y(), xTrailing-xLeading, line.height()); // TODO: at least height needs more work result.append(KoCharAreaInfo(rect, block.text().at(j))); } // TODO: perhaps only at end of paragraph (last qtextline) add linebreak, for in-paragraph linebreak // use real whitespace(s) found in original text (or see if forced linebreak) QRectF rect(xTrailing, line.y(), 1, line.height()); // TODO: better dummy width needed, with reasoning result.append(KoCharAreaInfo(rect, QLatin1Char('\n'))); } } qreal footNoteYOffset = bottom() - d->footNotesHeight; foreach(KoTextLayoutNoteArea *footerArea, d->footNoteAreas) { QVector footNoteCharAreaInfos = footerArea->generateCharAreaInfos(); QMutableVectorIterator it(footNoteCharAreaInfos); while (it.hasNext()) { KoCharAreaInfo &info = it.next(); info.rect.translate(0, footNoteYOffset); } result.append(footNoteCharAreaInfos); footNoteYOffset += footerArea->bottom() - footerArea->top(); } return result; } QRectF KoTextLayoutArea::selectionBoundingBox(QTextCursor &cursor) const { QRectF retval(-5E6, top(), 105E6, 0); if (d->startOfArea == 0) // We have not been layouted yet return QRectF(); if (d->endOfArea == 0) // no end area yet return QRectF(); QTextFrame::iterator it = d->startOfArea->it; QTextFrame::iterator stop = d->endOfArea->it; if (!stop.atEnd()) { if(!stop.currentBlock().isValid() || d->endOfArea->lineTextStart >= 0) { // Last thing we show is a frame (table) or first part of a paragraph split in two // The stop point should be the object after that // However if stop is already atEnd we shouldn't increment further ++stop; } } QTextFrame *subFrame; int footNoteIndex = 0; qreal offset = bottom() - d->footNotesHeight; while (footNoteIndex < d->footNoteAreas.length()) { subFrame = d->footNoteFrames[footNoteIndex]; if (cursor.selectionStart() >= subFrame->firstPosition() && cursor.selectionEnd() <= subFrame->lastPosition()) { return d->footNoteAreas[footNoteIndex]->selectionBoundingBox(cursor).translated(0, offset) ; } offset += d->footNoteAreas[footNoteIndex]->bottom() - d->footNoteAreas[footNoteIndex]->top(); ++footNoteIndex; } int tableAreaIndex = 0; int tocIndex = 0; for (; it != stop && !it.atEnd(); ++it) { QTextBlock block = it.currentBlock(); QTextTable *table = qobject_cast(it.currentFrame()); QTextFrame *subFrame = it.currentFrame(); if (table) { if (tableAreaIndex >= d->tableAreas.size()) { continue; } if (cursor.selectionEnd() < table->firstPosition()) { return retval.translated(0, d->verticalAlignOffset); } if (cursor.selectionStart() > table->lastPosition()) { ++tableAreaIndex; continue; } if (cursor.selectionStart() >= table->firstPosition() && cursor.selectionEnd() <= table->lastPosition()) { return d->tableAreas[tableAreaIndex]->selectionBoundingBox(cursor).translated(0, d->verticalAlignOffset); } if (cursor.selectionStart() >= table->firstPosition()) { retval = d->tableAreas[tableAreaIndex]->boundingRect(); } else { retval |= d->tableAreas[tableAreaIndex]->boundingRect(); } ++tableAreaIndex; continue; } else if (subFrame) { if (it.currentFrame()->format().intProperty(KoText::SubFrameType) == KoText::AuxillaryFrameType) { if (cursor.selectionEnd() < subFrame->firstPosition()) { return retval.translated(0, d->verticalAlignOffset); } if (cursor.selectionStart() > subFrame->lastPosition()) { break; } if (cursor.selectionStart() >= subFrame->firstPosition() && cursor.selectionEnd() <= subFrame->lastPosition()) { return d->endNotesArea->selectionBoundingBox(cursor).translated(0, d->verticalAlignOffset); } break; } } else { if (!block.isValid()) continue; } if (block.blockFormat().hasProperty(KoParagraphStyle::GeneratedDocument)) { if (cursor.selectionStart() <= block.position() && cursor.selectionEnd() >= block.position()) { retval |= d->generatedDocAreas[tocIndex]->boundingRect(); } ++tocIndex; continue; } if(cursor.selectionEnd() < block.position()) { return retval.translated(0, d->verticalAlignOffset); } if(cursor.selectionStart() >= block.position() && cursor.selectionStart() < block.position() + block.length()) { QTextLine line = block.layout()->lineForTextPosition(cursor.selectionStart() - block.position()); if (line.isValid()) { retval.setTop(line.y()); retval.setBottom(line.y()); } } if(cursor.selectionEnd() >= block.position() && cursor.selectionEnd() < block.position() + block.length()) { QTextLine line = block.layout()->lineForTextPosition(cursor.selectionEnd() - block.position()); if (line.isValid()) { retval.setBottom(line.y() + line.height()); if (line.ascent()==0) { // Block is empty from any visible content and has as such no height // but in that case the block font defines line height retval.setBottom(line.y() + 24); } if (cursor.selectionStart() == cursor.selectionEnd()) { // We only have a caret so let's set the rect a bit more narrow retval.setX(line.cursorToX(cursor.position() - block.position())); retval.setWidth(1); } } } // if the full paragraph is selected to add it to the rect. This makes sure we get a rect for the case // where the end of the selection lies is a different area. if (cursor.selectionEnd() >= block.position() + block.length() && cursor.selectionStart() <= block.position()) { QTextLine line = block.layout()->lineForTextPosition(block.length()-1); if (line.isValid()) { retval.setBottom(line.y() + line.height()); if (line.ascent()==0) { // Block is empty from any visible content and has as such no height // but in that case the block font defines line height retval.setBottom(line.y() + 24); } } } } return retval.translated(0, d->verticalAlignOffset); } bool KoTextLayoutArea::isStartingAt(FrameIterator *cursor) const { if (d->startOfArea) { return *d->startOfArea == *cursor; } return false; } QTextFrame::iterator KoTextLayoutArea::startTextFrameIterator() const { return d->startOfArea->it; } QTextFrame::iterator KoTextLayoutArea::endTextFrameIterator() const { return d->endOfArea->it; } void KoTextLayoutArea::backtrackKeepWithNext(FrameIterator *cursor) { QTextFrame::iterator it = cursor->it; while (!(it == d->startOfArea->it)) { --it; QTextBlock block = it.currentBlock(); QTextTable *table = qobject_cast(it.currentFrame()); QTextFrame *subFrame = it.currentFrame(); bool keepWithNext = false; if (table) { keepWithNext = table->format().boolProperty(KoTableStyle::KeepWithNext); //setBottom(tableArea->bottom() + d->footNotesHeight); } else if (subFrame) { Q_ASSERT(false); // there should never be an aux frame before normal layouted stuff } else if (block.isValid()) { keepWithNext = block.blockFormat().boolProperty(KoParagraphStyle::KeepWithNext); //setBottom(d->blockRects.last()->bottom() + d->footNotesHeight); } if (!keepWithNext) { cursor->it = ++it; break; } } } bool KoTextLayoutArea::layout(FrameIterator *cursor) { qDeleteAll(d->tableAreas); d->tableAreas.clear(); qDeleteAll(d->footNoteAreas); d->footNoteAreas.clear(); qDeleteAll(d->preregisteredFootNoteAreas); d->preregisteredFootNoteAreas.clear(); d->footNoteFrames.clear(); d->preregisteredFootNoteFrames.clear(); qDeleteAll(d->generatedDocAreas); d->generatedDocAreas.clear(); d->blockRects.clear(); delete d->endNotesArea; d->endNotesArea=0; if (d->copyEndOfArea && !d->copyEndOfArea->isValid()) { delete d->copyEndOfArea; d->copyEndOfArea = 0; } if (d->endOfArea && d->endOfArea->isValid()) { delete d->copyEndOfArea; d->copyEndOfArea = new FrameIterator(d->endOfArea); } delete d->startOfArea; delete d->endOfArea; d->dropCapsWidth = 0; d->dropCapsDistance = 0; d->startOfArea = new FrameIterator(cursor); d->endOfArea = 0; d->y = top(); d->neededWidth = 0; setBottom(top()); d->bottomSpacing = 0; d->footNoteAutoCount = 0; d->footNotesHeight = 0; d->preregisteredFootNotesHeight = 0; d->prevBorder = 0; d->prevBorderPadding = 0; if (d->footNoteCursorFromPrevious) { KoTextLayoutNoteArea *footNoteArea = new KoTextLayoutNoteArea(d->continuedNoteFromPrevious, this, d->documentLayout); d->footNoteFrames.append(d->continuedNoteFromPrevious->textFrame()); footNoteArea->setReferenceRect(left(), right(), 0, maximumAllowedBottom()); footNoteArea->setAsContinuedArea(true); footNoteArea->layout(d->footNoteCursorFromPrevious); d->footNotesHeight += footNoteArea->bottom() - footNoteArea->top(); d->footNoteAreas.append(footNoteArea); } while (!cursor->it.atEnd()) { QTextBlock block = cursor->it.currentBlock(); QTextTable *table = qobject_cast(cursor->it.currentFrame()); QTextFrame *subFrame = cursor->it.currentFrame(); if (table) { QString masterPageName = table->frameFormat().property(KoTableStyle::MasterPageName).toString(); bool masterPageNameChanged = !masterPageName.isEmpty(); if (masterPageNameChanged) { cursor->masterPageName = masterPageName; } if (!virginPage()) { int breaktype = table->frameFormat().intProperty(KoTableStyle::BreakBefore); if ((acceptsPageBreak() && (masterPageNameChanged || (breaktype == KoText::PageBreak))) || (acceptsColumnBreak() && (breaktype == KoText::ColumnBreak))) { d->endOfArea = new FrameIterator(cursor); setBottom(d->y + d->footNotesHeight); if (!d->blockRects.isEmpty()) { d->blockRects.last().setBottom(d->y); } return false; } } // Let's create KoTextLayoutTableArea and let that handle the table KoTextLayoutTableArea *tableArea = new KoTextLayoutTableArea(table, this, d->documentLayout); d->tableAreas.append(tableArea); d->y += d->bottomSpacing; if (!d->blockRects.isEmpty()) { d->blockRects.last().setBottom(d->y); } tableArea->setVirginPage(virginPage()); tableArea->setReferenceRect(left(), right(), d->y, maximumAllowedBottom()); if (tableArea->layoutTable(cursor->tableIterator(table)) == false) { d->endOfArea = new FrameIterator(cursor); d->y = tableArea->bottom(); setBottom(d->y + d->footNotesHeight); // Expand bounding rect so if we have content outside we show it expandBoundingLeft(tableArea->boundingRect().left()); expandBoundingRight(tableArea->boundingRect().right()); return false; } setVirginPage(false); // Expand bounding rect so if we have content outside we show it expandBoundingLeft(tableArea->boundingRect().left()); expandBoundingRight(tableArea->boundingRect().right()); d->bottomSpacing = 0; d->y = tableArea->bottom(); delete cursor->currentTableIterator; cursor->currentTableIterator = 0; } else if (subFrame) { if (subFrame->format().intProperty(KoText::SubFrameType) == KoText::AuxillaryFrameType) { Q_ASSERT(d->endNotesArea == 0); d->endNotesArea = new KoTextLayoutEndNotesArea(this, d->documentLayout); d->y += d->bottomSpacing; if (!d->blockRects.isEmpty()) { d->blockRects.last().setBottom(d->y); } d->endNotesArea->setVirginPage(virginPage()); d->endNotesArea->setReferenceRect(left(), right(), d->y, maximumAllowedBottom()); if (d->endNotesArea->layout(cursor->subFrameIterator(subFrame)) == false) { d->endOfArea = new FrameIterator(cursor); d->y = d->endNotesArea->bottom(); setBottom(d->y + d->footNotesHeight); // Expand bounding rect so if we have content outside we show it expandBoundingLeft(d->endNotesArea->boundingRect().left()); expandBoundingRight(d->endNotesArea->boundingRect().right()); return false; } setVirginPage(false); // Expand bounding rect so if we have content outside we show it expandBoundingLeft(d->endNotesArea->boundingRect().left()); expandBoundingRight(d->endNotesArea->boundingRect().right()); d->bottomSpacing = 0; d->y = d->endNotesArea->bottom(); delete cursor->currentSubFrameIterator; cursor->currentSubFrameIterator = 0; // we have layouted till the end of the document except for a blank block // which we should ignore ++(cursor->it); ++(cursor->it); break; } } else if (block.isValid()) { if (block.blockFormat().hasProperty(KoParagraphStyle::GeneratedDocument)) { QVariant data = block.blockFormat().property(KoParagraphStyle::GeneratedDocument); QTextDocument *generatedDocument = data.value(); // Let's create KoTextLayoutArea and let it handle the generated document KoTextLayoutArea *area = new KoTextLayoutArea(this, documentLayout()); d->generatedDocAreas.append(area); d->y += d->bottomSpacing; if (!d->blockRects.isEmpty()) { d->blockRects.last().setBottom(d->y); } area->setVirginPage(virginPage()); area->setAcceptsPageBreak(acceptsPageBreak()); area->setAcceptsColumnBreak(acceptsColumnBreak()); area->setReferenceRect(left(), right(), d->y, maximumAllowedBottom()); QTextLayout *blayout = block.layout(); blayout->beginLayout(); QTextLine line = blayout->createLine(); line.setNumColumns(0); line.setPosition(QPointF(left(), d->y)); blayout->endLayout(); if (area->layout(cursor->subFrameIterator(generatedDocument->rootFrame())) == false) { cursor->lineTextStart = 1; // fake we are not done d->endOfArea = new FrameIterator(cursor); d->y = area->bottom(); setBottom(d->y + d->footNotesHeight); // Expand bounding rect so if we have content outside we show it expandBoundingLeft(area->boundingRect().left()); expandBoundingRight(area->boundingRect().right()); return false; } setVirginPage(false); // Expand bounding rect so if we have content outside we show it expandBoundingLeft(area->boundingRect().left()); expandBoundingRight(area->boundingRect().right()); d->bottomSpacing = 0; d->y = area->bottom(); delete cursor->currentSubFrameIterator; cursor->lineTextStart = -1; // fake we are done cursor->currentSubFrameIterator = 0; } else { // FIXME this doesn't work for cells inside tables. We probably should make it more // generic to handle such cases too. QString masterPageName = block.blockFormat().property(KoParagraphStyle::MasterPageName).toString(); bool masterPageNameChanged = !masterPageName.isEmpty(); if (masterPageNameChanged) { cursor->masterPageName = masterPageName; } if (!virginPage()) { int breaktype = block.blockFormat().intProperty(KoParagraphStyle::BreakBefore); if ((acceptsPageBreak() && (masterPageNameChanged || (breaktype == KoText::PageBreak))) ||(acceptsColumnBreak() && (breaktype == KoText::ColumnBreak))) { d->endOfArea = new FrameIterator(cursor); setBottom(d->y + d->footNotesHeight); if (!d->blockRects.isEmpty()) { d->blockRects.last().setBottom(d->y); } return false; } } if (layoutBlock(cursor) == false) { if (cursor->lineTextStart == -1) { //Nothing was added so lets backtrack keep-with-next backtrackKeepWithNext(cursor); } d->endOfArea = new FrameIterator(cursor); setBottom(d->y + d->footNotesHeight); d->blockRects.last().setBottom(d->y); return false; } d->extraTextIndent = 0; int breaktype = block.blockFormat().intProperty(KoParagraphStyle::BreakAfter); if ((acceptsPageBreak() && (breaktype & KoText::PageBreak)) || (acceptsColumnBreak() && (breaktype & KoText::ColumnBreak))) { Q_ASSERT(!cursor->it.atEnd()); QTextFrame::iterator nextIt = cursor->it; ++nextIt; bool wasIncremented = !nextIt.currentFrame(); if (wasIncremented) cursor->it = nextIt; d->endOfArea = new FrameIterator(cursor); if (!wasIncremented) ++(cursor->it); setBottom(d->y + d->footNotesHeight); d->blockRects.last().setBottom(d->y); return false; } } } bool atEnd = cursor->it.atEnd(); if (!atEnd) { ++(cursor->it); } } d->endOfArea = new FrameIterator(cursor); d->y = qMin(maximumAllowedBottom(), d->y + d->bottomSpacing); setBottom(d->y + d->footNotesHeight); if (!d->blockRects.isEmpty()) { d->blockRects.last().setBottom(d->y); } if (d->maximumAllowedWidth>0) { d->right += d->neededWidth - d->width; d->maximumAllowedWidth = 0; setVirginPage(true); KoTextLayoutArea::layout(new FrameIterator(d->startOfArea)); } return true; // we have layouted till the end of the frame } QTextLine KoTextLayoutArea::Private::restartLayout(QTextBlock &block, int lineTextStartOfLastKeep) { QTextLayout *layout = block.layout(); KoTextBlockData blockData(block); QPointF stashedCounterPosition = blockData.counterPosition(); QVector stashedLines; QTextLine line; for(int i = 0; i < layout->lineCount(); i++) { QTextLine l = layout->lineAt(i); if (l.textStart() >= lineTextStartOfLastKeep) { break; } LineKeeper lk; lk.lineWidth = l.width(); lk.columns = l.textLength(); lk.position = l.position(); stashedLines.append(lk); } layout->clearLayout(); layout->beginLayout(); line = layout->createLine(); return recreatePartialLayout(block, stashedLines, stashedCounterPosition, line); } void KoTextLayoutArea::Private::stashRemainingLayout(QTextBlock &block, int lineTextStartOfFirstKeep, QVector &stashedLines, QPointF &stashedCounterPosition) { QTextLayout *layout = block.layout(); KoTextBlockData blockData(block); stashedCounterPosition = blockData.counterPosition(); QTextLine line; for(int i = 0; i < layout->lineCount(); i++) { QTextLine l = layout->lineAt(i); if (l.textStart() < lineTextStartOfFirstKeep) { continue; } LineKeeper lk; lk.lineWidth = l.width(); lk.columns = l.textLength(); lk.position = l.position(); stashedLines.append(lk); } } QTextLine KoTextLayoutArea::Private::recreatePartialLayout(QTextBlock &block, const QVector &stashedLines, QPointF &stashedCounterPosition, QTextLine &line) { QTextLayout *layout = block.layout(); KoTextBlockData blockData(block); documentLayout->allowPositionInlineObject(false); if (layout->lineCount() == 1) { blockData.setCounterPosition(stashedCounterPosition); } foreach(const LineKeeper &lk, stashedLines) { line.setLineWidth(lk.lineWidth); if (lk.columns != line.textLength()) { // As setNumColumns might break differently we only use it if setLineWidth doesn't give // the same textLength as we had before line.setNumColumns(lk.columns, lk.lineWidth); } line.setPosition(lk.position); line = layout->createLine(); if (!line.isValid()) break; } documentLayout->allowPositionInlineObject(true); return line; } static bool compareTab(const QTextOption::Tab &tab1, const QTextOption::Tab &tab2) { return tab1.position < tab2.position; } // layoutBlock() method is structured like this: // // 1) Setup various helper values // a) related to or influenced by lists // b) related to or influenced by dropcaps // c) related to or influenced by margins // d) related to or influenced by tabs // e) related to or influenced by borders // f) related to or influenced by list counters // 2)layout each line (possibly restarting where we stopped earlier) // a) fit line into sub lines with as needed for text runaround // b) break if we encounter softbreak // c) make sure we keep above maximumAllowedBottom // d) calls addLine() // e) update dropcaps related variables bool KoTextLayoutArea::layoutBlock(FrameIterator *cursor) { QTextBlock block(cursor->it.currentBlock()); KoTextBlockData blockData(block); KoParagraphStyle pStyle(block.blockFormat(), block.charFormat()); - qInfo()<<"layoutBlock:"<copyEndOfArea && d->copyEndOfArea->it.currentBlock() == block); KoText::Direction dir = pStyle.textProgressionDirection(); if (dir == KoText::InheritDirection) dir = parentTextDirection(); if (dir == KoText::AutoDirection) d->isRtl = block.text().isRightToLeft(); else d->isRtl = dir == KoText::RightLeftTopBottom; // initialize list item stuff for this parag. QTextList *textList = block.textList(); QTextListFormat listFormat; QTextCharFormat labelFormat; if (textList) { listFormat = textList->format(); if (block.text().size() == 0 || d->documentLayout->wordprocessingMode()) { labelFormat = block.charFormat(); } else { labelFormat = block.begin().fragment().charFormat(); } if (d->documentLayout->styleManager()) { const int id = listFormat.intProperty(KoListStyle::CharacterStyleId); KoCharacterStyle *cs = d->documentLayout->styleManager()->characterStyle(id); if (cs) { cs->applyStyle(labelFormat); cs->ensureMinimalProperties(labelFormat); } } // fetch the text-properties of the label if (listFormat.hasProperty(KoListStyle::CharacterProperties)) { QVariant v = listFormat.property(KoListStyle::CharacterProperties); QSharedPointer textPropertiesCharStyle = v.value< QSharedPointer >(); if (!textPropertiesCharStyle.isNull()) { textPropertiesCharStyle->applyStyle(labelFormat); textPropertiesCharStyle->ensureMinimalProperties(labelFormat); } } // Calculate the correct font point size taking into account the current // block format and the relative font size percent if the size is not absolute if (listFormat.hasProperty(KoListStyle::RelativeBulletSize)) { qreal percent = listFormat.property(KoListStyle::RelativeBulletSize).toDouble(); labelFormat.setFontPointSize((percent*labelFormat.fontPointSize())/100.00); } QFont font(labelFormat.font(), d->documentLayout->paintDevice()); if (!blockData.hasCounterData()) { ListItemsHelper lih(textList, font); lih.recalculateBlock(block); } blockData.setLabelFormat(labelFormat); } else { // make sure it is empty blockData.clearCounter(); } QTextLayout *layout = block.layout(); QTextOption option = layout->textOption(); option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); option.setAlignment(QStyle::visualAlignment(d->isRtl ? Qt::RightToLeft : Qt::LeftToRight, pStyle.alignment())); if (d->isRtl) { option.setTextDirection(Qt::RightToLeft); // For right-to-left we need to make sure that trailing spaces are included into the QTextLine naturalTextWidth // and naturalTextRect calculation so they are proper handled in the RunAroundHelper. For left-to-right we do // not like to include trailing spaces in the calculations cause else justified text would not look proper // justified. Seems for right-to-left we have to accept that justified text will not look proper justified then. // only set it for justified text as otherwise we will cut of text at the beginning of the line if (pStyle.alignment() == Qt::AlignJustify) { option.setFlags(QTextOption::IncludeTrailingSpaces); } } else { option.setFlags(0); option.setTextDirection(Qt::LeftToRight); } option.setUseDesignMetrics(true); //========== // Drop caps //========== d->dropCapsNChars = 0; if (cursor->lineTextStart == -1) { // first remove any drop-caps related formatting that's already there in the layout. // we'll do it all afresh now. QList formatRanges = layout->additionalFormats(); for (QList< QTextLayout::FormatRange >::Iterator iter = formatRanges.begin(); iter != formatRanges.end(); ) { if (iter->format.boolProperty(DropCapsAdditionalFormattingId)) { iter = formatRanges.erase(iter); } else { ++iter; } } if (formatRanges.count() != layout->additionalFormats().count()) layout->setAdditionalFormats(formatRanges); bool dropCaps = pStyle.dropCaps(); int dropCapsLength = pStyle.dropCapsLength(); int dropCapsLines = pStyle.dropCapsLines(); if (dropCaps && dropCapsLines > 1 && block.length() > 1) { QString blockText = block.text(); d->dropCapsDistance = pStyle.dropCapsDistance(); if (dropCapsLength == 0) { // means whole word is to be dropped int firstNonSpace = blockText.indexOf(QRegExp("[^ ]")); dropCapsLength = blockText.indexOf(QRegExp("\\W"), firstNonSpace); } else { // LibreOffice skips softbreaks but not spaces. We will do the same QTextCursor c1(block); c1.setPosition(block.position()); c1.setPosition(c1.position() + 1, QTextCursor::KeepAnchor); KoTextSoftPageBreak *softPageBreak = dynamic_cast(d->documentLayout->inlineTextObjectManager()->inlineTextObject(c1)); if (softPageBreak) { dropCapsLength++; } } dropCapsLength = qMin(dropCapsLength, blockText.length() - 1); if (dropCapsLength > 0) { // increase the size of the dropped chars QTextCursor blockStart(block); QTextLayout::FormatRange dropCapsFormatRange; dropCapsFormatRange.format = blockStart.charFormat(); // find out lineHeight for this block. QTextBlock::iterator it = block.begin(); QTextFragment lineRepresentative = it.fragment(); qreal lineHeight = pStyle.lineHeightAbsolute(); qreal dropCapsHeight = 0; if (lineHeight == 0) { lineHeight = lineRepresentative.charFormat().fontPointSize(); qreal linespacing = pStyle.lineSpacing(); if (linespacing == 0) { // unset qreal percent = pStyle.lineHeightPercent(); if (percent != 0) linespacing = lineHeight * ((percent - 100) / 100.0); else if (linespacing == 0) linespacing = lineHeight * 0.2; // default } dropCapsHeight = linespacing * (dropCapsLines-1); } const qreal minimum = pStyle.minimumLineHeight(); if (minimum > 0.0) { lineHeight = qMax(lineHeight, minimum); } dropCapsHeight += lineHeight * dropCapsLines; int dropCapsStyleId = pStyle.dropCapsTextStyleId(); KoCharacterStyle *dropCapsCharStyle = 0; if (dropCapsStyleId > 0 && d->documentLayout->styleManager()) { dropCapsCharStyle = d->documentLayout->styleManager()->characterStyle(dropCapsStyleId); dropCapsCharStyle->applyStyle(dropCapsFormatRange.format); } QFont f(dropCapsFormatRange.format.font(), d->documentLayout->paintDevice()); QString dropCapsText(block.text().left(dropCapsLength)); f.setPointSizeF(dropCapsHeight); for (int i=0; i < 5; ++i) { QTextLayout tmplayout(dropCapsText, f); tmplayout.setTextOption(option); tmplayout.beginLayout(); QTextLine tmpline = tmplayout.createLine(); tmplayout.endLayout(); d->dropCapsWidth = tmpline.naturalTextWidth(); QFontMetricsF fm(f, documentLayout()->paintDevice()); QRectF rect = fm.tightBoundingRect(dropCapsText); const qreal diff = dropCapsHeight - rect.height(); dropCapsPositionAdjust = rect.top() + fm.ascent(); if (qAbs(diff) < 0.5) // good enough break; const qreal adjustment = diff * (f.pointSizeF() / rect.height()); // warnTextLayout << "adjusting with" << adjustment; f.setPointSizeF(f.pointSizeF() + adjustment); } dropCapsFormatRange.format.setFontPointSize(f.pointSizeF()); dropCapsFormatRange.format.setProperty(DropCapsAdditionalFormattingId, (QVariant) true); dropCapsFormatRange.start = 0; dropCapsFormatRange.length = dropCapsLength; formatRanges.append(dropCapsFormatRange); layout->setAdditionalFormats(formatRanges); d->dropCapsNChars = dropCapsLength; dropCapsAffectsNMoreLines = (d->dropCapsNChars > 0) ? dropCapsLines : 0; } } } //======== // Margins //======== qreal startMargin = block.blockFormat().leftMargin(); qreal endMargin = block.blockFormat().rightMargin(); if (d->isRtl) { qSwap(startMargin, endMargin); } d->indent = textIndent(block, textList, pStyle) + d->extraTextIndent; qreal labelBoxWidth = 0; qreal labelBoxIndent = 0; if (textList) { if (listFormat.boolProperty(KoListStyle::AlignmentMode)) { // according to odf 1.2 17.20 list margin should be used when paragraph margin is // not specified by the auto style (additionally LO/OO uses 0 as condition so we do too) int id = pStyle.styleId(); bool set = false; if (id && d->documentLayout->styleManager()) { KoParagraphStyle *originalParagraphStyle = d->documentLayout->styleManager()->paragraphStyle(id); if (originalParagraphStyle->leftMargin() != startMargin) { set = (startMargin != 0); } } else { set = (startMargin != 0); } if (! set) { startMargin = listFormat.doubleProperty(KoListStyle::Margin); } labelBoxWidth = blockData.counterWidth(); Qt::Alignment align = static_cast(listFormat.intProperty(KoListStyle::Alignment)); if (align == 0) { align = Qt::AlignLeft; } if (align & Qt::AlignLeft) { d->indent += labelBoxWidth; } else if (align & Qt::AlignHCenter) { d->indent += labelBoxWidth/2; } labelBoxIndent = d->indent - labelBoxWidth; } else { labelBoxWidth = blockData.counterSpacing() + blockData.counterWidth(); } } d->width = right() - left(); d->width -= startMargin + endMargin; d->x = left() + (d->isRtl ? 0.0 : startMargin); d->documentLayout->clearInlineObjectRegistry(block); //======== // Tabs //======== const QVector tabs = pStyle.tabPositions(); // Handle tabs relative to startMargin qreal tabOffset = -d->indent; if (!d->documentLayout->relativeTabs(block)) { tabOffset -= startMargin; } // Make a list of tabs that Qt can use QList qTabs; // Note: Converting to Qt tabs is needed as long as we use Qt for layout, but we // loose the possibility to do leader chars. foreach (const KoText::Tab &kTab, tabs) { qreal value = kTab.position; if (value == MaximumTabPos) { // MaximumTabPos is used in index generators // note: we subtract right margin as this is where the tab should be // note: we subtract indent so tab is not relative to it // note: we subtract left margin so tab is not relative to it // if rtl the above left/right reasons swap but formula stays the same // -tabOfset is just to cancel that we add it next // -2 is to avoid wrap at right edge to the next line value = right() - left() - startMargin - endMargin - d->indent - tabOffset - 2; } // conversion here is required because Qt thinks in device units and we don't value *= qt_defaultDpiY() / 72.0; value += tabOffset * qt_defaultDpiY() / 72.0; QTextOption::Tab tab; tab.position = value; tab.type = kTab.type; tab.delimiter = kTab.delimiter; qTabs.append(tab); } qreal presentationListTabValue(0.0); // for use in presentationListTabWorkaround // For some lists we need to add a special list tab according to odf 1.2 19.830 if (textList && listFormat.intProperty(KoListStyle::LabelFollowedBy) == KoListStyle::ListTab) { qreal listTab = 0; if (listFormat.hasProperty(KoListStyle::TabStopPosition)) { listTab = listFormat.doubleProperty(KoListStyle::TabStopPosition); if (!d->documentLayout->relativeTabs(block)) { // How list tab is defined if fixed tabs: // listTab //|>-------------------------| // d->indent // |---------<| // LABEL TEXT STARTS HERE AND GOES ON // TO THE NEXT LINE //|>------------------| // startMargin listTab -= startMargin; } else { // How list tab is defined if relative tabs: // It's relative to startMargin - list.startMargin // listTab // |>-------------------| // d->indent // |---------<| // LABEL TEXT STARTS HERE AND GOES ON // TO THE NEXT LINE //|>--------------------| // startMargin | // |>-------------| // list.margin listTab -= listFormat.doubleProperty(KoListStyle::Margin); } } // How list tab is defined now: // listTab // |>-----| // d->indent // |---------<| // LABEL TEXT STARTS HERE AND GOES ON // TO THE NEXT LINE //|>------------------| // startMargin presentationListTabValue = listTab; listTab -= d->indent; // And now listTab is like this: // x() // | listTab // |>---------------| // d->indent // |---------<| // LABEL TEXT STARTS HERE AND GOES ON // TO THE NEXT LINE //|>------------------| // startMargin // conversion here is required because Qt thinks in device units and we don't listTab *= qt_defaultDpiY() / 72.0; QTextOption::Tab tab; tab.position = listTab; tab.type = d->isRtl ? QTextOption::RightTab : QTextOption::LeftTab; qTabs.append(tab); } // We need to sort as the MaximumTabPos may be converted to a value that really // should be in the middle, and listtab needs to be sorted in too qSort(qTabs.begin(), qTabs.end(), compareTab); // Regular interval tabs. Since Qt doesn't handle regular interval tabs offset // by a fixed number we need to create the regular tabs ourselves. qreal tabStopDistance = pStyle.tabStopDistance() * qt_defaultDpiY() / 72.0; if (tabStopDistance <= 0) { tabStopDistance = d->documentLayout->defaultTabSpacing() * qt_defaultDpiY() / 72.0; } qreal regularSpacedTabPos = -d->indent * qt_defaultDpiY() / 72.0 -0.1; // first possible position if (!qTabs.isEmpty()) { regularSpacedTabPos = qTabs.last().position; } regularSpacedTabPos -= tabOffset * qt_defaultDpiY() / 72.0; if (regularSpacedTabPos < 0) { regularSpacedTabPos = -int(-regularSpacedTabPos / tabStopDistance) * tabStopDistance; } else { regularSpacedTabPos = (int(regularSpacedTabPos / tabStopDistance) + 1) * tabStopDistance; } regularSpacedTabPos += tabOffset * qt_defaultDpiY() / 72.0; while (regularSpacedTabPos < MaximumTabPos) { QTextOption::Tab tab; tab.position = regularSpacedTabPos; qTabs.append(tab); regularSpacedTabPos += tabStopDistance; } option.setTabs(qTabs); // conversion here is required because Qt thinks in device units and we don't option.setTabStop(tabStopDistance * qt_defaultDpiY() / 72.); layout->setTextOption(option); // ============== // Possibly store the old layout of lines in case we end up splitting the paragraph at the same position // ============== QVector stashedLines; QPointF stashedCounterPosition; if (lastOfPreviousRun) { // we have been layouted before, and the block ended on the following page so better // stash the layout for later d->stashRemainingLayout(block, d->copyEndOfArea->lineTextStart, stashedLines, stashedCounterPosition); } // ============== // Setup line and possibly restart paragraph continuing from previous other area // ============== QTextLine line; if (cursor->lineTextStart == -1) { layout->beginLayout(); line = layout->createLine(); cursor->fragmentIterator = block.begin(); } else { line = d->restartLayout(block, cursor->lineTextStart); d->indent = d->extraTextIndent; } if (block.blockFormat().boolProperty(KoParagraphStyle::UnnumberedListItem)) { // Unnumbered list items act like "following lines" in a numbered block d->indent = 0; } // ============== // List label/counter positioning // ============== if (textList && block.layout()->lineCount() == 1 && ! block.blockFormat().boolProperty(KoParagraphStyle::UnnumberedListItem)) { // If first line in a list then set the counterposition. Following lines in the same // list-item have nothing to do with the counter. if (listFormat.boolProperty(KoListStyle::AlignmentMode) == false) { qreal minLabelWidth = listFormat.doubleProperty(KoListStyle::MinimumWidth); if (!d->isRtl) { d->x += listFormat.doubleProperty(KoListStyle::Indent) + minLabelWidth; } d->width -= listFormat.doubleProperty(KoListStyle::Indent) + minLabelWidth; d->indent += labelBoxWidth - minLabelWidth; blockData.setCounterPosition(QPointF(d->x + d->indent - labelBoxWidth, d->y)); } else if (labelBoxWidth > 0.0 || blockData.counterText().length() > 0) { // Alignmentmode and there is a label (double check needed to account for both // picture bullets and non width chars) blockData.setCounterPosition(QPointF(d->x + labelBoxIndent, d->y)); if (listFormat.intProperty(KoListStyle::LabelFollowedBy) == KoListStyle::ListTab && !presentationListTabWorkaround(textIndent(block, textList, pStyle), labelBoxWidth, presentationListTabValue)) { foreach(QTextOption::Tab tab, qTabs) { qreal position = tab.position * 72. / qt_defaultDpiY(); if (position > 0.0) { d->indent += position; break; } } //And finally it's like this: // x() // d->indent // |>-----| // LABEL TEXT STARTS HERE AND GOES ON // TO THE NEXT LINE //|>------------------| // startMargin } else if (listFormat.intProperty(KoListStyle::LabelFollowedBy) == KoListStyle::Space) { QFontMetrics fm(labelFormat.font(), d->documentLayout->paintDevice()); d->indent += fm.width(' '); } // default needs to be no space so presentationListTabWorkaround above makes us go here } } // Whenever we relayout the markup layout becomes invalid blockData.setMarkupsLayoutValidity(KoTextBlockData::Misspell, false); blockData.setMarkupsLayoutValidity(KoTextBlockData::Grammar, false); // ============== // Now once we know the physical context we can work on the borders of the paragraph // ============== if (block.blockFormat().hasProperty(KoParagraphStyle::HiddenByTable)) { if (!d->blockRects.isEmpty()) { d->blockRects.last().setBottom(d->y); } d->y += d->bottomSpacing; d->bottomSpacing = 0; d->blockRects.append(QRectF(d->x, d->y, d->width, 10.0)); } else { handleBordersAndSpacing(blockData, &block); } // Expand bounding rect so if we have content outside we show it expandBoundingLeft(d->blockRects.last().x()); expandBoundingRight(d->blockRects.last().right()); // ============== // Create the lines of this paragraph // ============== RunAroundHelper runAroundHelper; runAroundHelper.setObstructions(documentLayout()->currentObstructions()); qreal maxLineHeight = 0; qreal y_justBelowDropCaps = 0; bool anyLineAdded = false; int numBaselineShifts = 0; while (line.isValid()) { runAroundHelper.setLine(this, line); runAroundHelper.setObstructions(documentLayout()->currentObstructions()); QRectF anchoringRect = d->blockRects.last(); anchoringRect.setTop(d->anchoringParagraphContentTop); documentLayout()->setAnchoringParagraphContentRect(anchoringRect); anchoringRect.setLeft(left()); anchoringRect.setWidth(right() - left()); anchoringRect.setTop(d->anchoringParagraphTop); documentLayout()->setAnchoringParagraphRect(anchoringRect); documentLayout()->setAnchoringLayoutEnvironmentRect(layoutEnvironmentRect()); runAroundHelper.fit( /* resetHorizontalPosition */ false, /* rightToLeft */ d->isRtl, QPointF(x(), d->y)); documentLayout()->positionAnchorTextRanges(block.position()+line.textStart(), line.textLength(), block.document()); qreal bottomOfText = line.y() + line.height(); bool softBreak = false; bool moreInMiddle = d->y > maximumAllowedBottom() - 150; if (acceptsPageBreak() && !pStyle.nonBreakableLines() && moreInMiddle) { int softBreakPos = -1; QString text = block.text(); int pos = text.indexOf(QChar::ObjectReplacementCharacter, line.textStart()); while (pos >= 0 && pos <= line.textStart() + line.textLength()) { QTextCursor c1(block); c1.setPosition(block.position() + pos); c1.setPosition(c1.position() + 1, QTextCursor::KeepAnchor); KoTextSoftPageBreak *softPageBreak = dynamic_cast(d->documentLayout->inlineTextObjectManager()->inlineTextObject(c1)); if (softPageBreak) { softBreakPos = pos; break; } pos = text.indexOf(QChar::ObjectReplacementCharacter, pos + 1); } if (softBreakPos >= 0 && softBreakPos < line.textStart() + line.textLength()) { line.setNumColumns(softBreakPos - line.textStart() + 1, line.width()); softBreak = true; // if the softBreakPos is at the start of the line stop here so // we don't add a line here. That fixes the problem that e.g. the counter is before // the page break and the text is after the page break if (!virginPage() && softBreakPos == 0) { d->recreatePartialLayout(block, stashedLines, stashedCounterPosition, line); layout->endLayout(); - qInfo()<<1<<"layoutBlock: softbreak at start of line"; return false; } } } if (documentLayout()->anchoringSoftBreak() <= block.position() + line.textStart() + line.textLength()) { //don't add an anchor that has been moved away line.setNumColumns(documentLayout()->anchoringSoftBreak() - block.position() - line.textStart(), line.width()); softBreak = true; // if the softBreakPos is at the start of the block stop here so // we don't add a line here. That fixes the problem that e.g. the counter is before // the page break and the text is after the page break if (!virginPage() && documentLayout()->anchoringSoftBreak() == block.position()) { d->recreatePartialLayout(block, stashedLines, stashedCounterPosition, line); layout->endLayout(); - qInfo()<<2<<"layoutBlock: softbreak at start of line"; return false; } } findFootNotes(block, line, bottomOfText); if (bottomOfText > maximumAllowedBottom()) { // We can not fit line within our allowed space // in case we resume layout on next page the line is reused later // but if not then we need to make sure the line becomes invisible // we use d->maximalAllowedBottom because we want to be below // footnotes too. if (!virginPage() && pStyle.nonBreakableLines()) { line.setPosition(QPointF(x(), d->maximalAllowedBottom)); cursor->lineTextStart = -1; d->recreatePartialLayout(block, stashedLines, stashedCounterPosition, line); layout->endLayout(); clearPreregisteredFootNotes(); - qInfo()<<1<<"layoutBlock: line does not fit in allowed space"; return false; //to indicate block was not done! } if (!virginPage() && pStyle.orphanThreshold() != 0 && pStyle.orphanThreshold() > numBaselineShifts) { line.setPosition(QPointF(x(), d->maximalAllowedBottom)); cursor->lineTextStart = -1; d->recreatePartialLayout(block, stashedLines, stashedCounterPosition, line); layout->endLayout(); clearPreregisteredFootNotes(); - qInfo()<<2<<"layoutBlock: line does not fit in allowed space"; return false; //to indicate block was not done! } if (!virginPage() || anyLineAdded) { line.setPosition(QPointF(x(), d->maximalAllowedBottom)); d->recreatePartialLayout(block, stashedLines, stashedCounterPosition, line); layout->endLayout(); clearPreregisteredFootNotes(); - qInfo()<<3<<"layoutBlock: line does not fit in allowed space"; return false; //to indicate block was not done! } } confirmFootNotes(); anyLineAdded = true; maxLineHeight = qMax(maxLineHeight, addLine(line, cursor, blockData)); d->neededWidth = qMax(d->neededWidth, line.naturalTextWidth() + d->indent); if (!runAroundHelper.stayOnBaseline() && !(block.blockFormat().hasProperty(KoParagraphStyle::HiddenByTable) && block.length() <= 1)) { d->y += maxLineHeight; maxLineHeight = 0; d->indent = 0; d->extraTextIndent = 0; ++numBaselineShifts; } // drop caps if (d->dropCapsNChars > 0) { // we just laid out the dropped chars y_justBelowDropCaps = d->y; // save the y position just below the dropped characters d->y = line.y(); // keep the same y for the next line line.setPosition(line.position() - QPointF(0, dropCapsPositionAdjust)); d->dropCapsNChars -= line.textLength(); } else if (dropCapsAffectsNMoreLines > 0) { // we just laid out a drop-cap-affected line dropCapsAffectsNMoreLines--; if (dropCapsAffectsNMoreLines == 0) { // no more drop-cap-affected lines if (d->y < y_justBelowDropCaps) d->y = y_justBelowDropCaps; // make sure d->y is below the dropped characters y_justBelowDropCaps = 0; d->dropCapsWidth = 0; d->dropCapsDistance = 0; } } documentLayout()->positionAnchoredObstructions(); // line fitted so try and do the next one line = layout->createLine(); if (!line.isValid()) { break; // no more line means our job is done } cursor->lineTextStart = line.textStart(); if (softBreak) { d->recreatePartialLayout(block, stashedLines, stashedCounterPosition, line); layout->endLayout(); return false; // page-break means we need to start again on the next page } } d->bottomSpacing = pStyle.bottomMargin(); layout->endLayout(); setVirginPage(false); cursor->lineTextStart = -1; //set lineTextStart to -1 and returning true indicate new block block.setLineCount(layout->lineCount()); return true; } bool KoTextLayoutArea::presentationListTabWorkaround(qreal indent, qreal labelBoxWidth, qreal presentationListTabValue) { if (!d->documentLayout->wordprocessingMode() && indent < 0.0) { // Impress / Powerpoint expects the label to be before the text if (indent + labelBoxWidth >= presentationListTabValue) { // but here is an unforseen overlap with normal text return true; } } return false; } qreal KoTextLayoutArea::textIndent(const QTextBlock &block, QTextList *textList, const KoParagraphStyle &pStyle) const { if (pStyle.autoTextIndent()) { // if auto-text-indent is set, // return an indent approximately 3-characters wide as per current font QTextCursor blockCursor(block); qreal guessGlyphWidth = QFontMetricsF(blockCursor.charFormat().font()).width('x'); return guessGlyphWidth * 3; } qreal blockTextIndent = block.blockFormat().textIndent(); if (textList && textList->format().boolProperty(KoListStyle::AlignmentMode)) { // according to odf 1.2 17.20 list text indent should be used when paragraph text indent is // not specified (additionally LO/OO uses 0 as condition so we do too) int id = pStyle.styleId(); bool set = false; if (id && d->documentLayout->styleManager()) { KoParagraphStyle *originalParagraphStyle = d->documentLayout->styleManager()->paragraphStyle(id); if (originalParagraphStyle->textIndent() != blockTextIndent) { set = (blockTextIndent != 0); } } else { set = (blockTextIndent != 0); } if (! set) { return textList->format().doubleProperty(KoListStyle::TextIndent); } } return blockTextIndent; } void KoTextLayoutArea::setExtraTextIndent(qreal extraTextIndent) { d->extraTextIndent = extraTextIndent; } qreal KoTextLayoutArea::x() const { if (d->isRtl) { return d->x; } else { if (d->dropCapsNChars > 0 || d->dropCapsWidth == 0) return d->x + d->indent ; else return d->x + d->indent + d->dropCapsWidth + d->dropCapsDistance; } } qreal KoTextLayoutArea::width() const { if (d->dropCapsNChars > 0) { return d->dropCapsWidth; } qreal width = d->width; if (d->maximumAllowedWidth > 0) { // lets use that instead but remember all the indent stuff we have calculated width = d->width - (d->right - d->left) + d->maximumAllowedWidth; } return width - d->indent - d->dropCapsWidth - d->dropCapsDistance; } void KoTextLayoutArea::setAcceptsPageBreak(bool accept) { d->acceptsPageBreak = accept; } bool KoTextLayoutArea::acceptsPageBreak() const { return d->acceptsPageBreak; } void KoTextLayoutArea::setAcceptsColumnBreak(bool accept) { d->acceptsColumnBreak = accept; } bool KoTextLayoutArea::acceptsColumnBreak() const { return d->acceptsColumnBreak; } void KoTextLayoutArea::setVirginPage(bool virgin) { d->virginPage = virgin; } bool KoTextLayoutArea::virginPage() const { return d->virginPage; } void KoTextLayoutArea::setVerticalAlignOffset(qreal offset) { d->boundingRect.setTop(d->top + qMin(qreal(0.0), offset)); d->boundingRect.setBottom(d->bottom + qMax(qreal(0.0), offset)); Q_ASSERT_X(d->boundingRect.top() <= d->boundingRect.bottom(), __FUNCTION__, "Bounding-rect is not normalized"); d->verticalAlignOffset = offset; } qreal KoTextLayoutArea::verticalAlignOffset() const { return d->verticalAlignOffset; } qreal KoTextLayoutArea::addLine(QTextLine &line, FrameIterator *cursor, KoTextBlockData &blockData) { QTextBlock block = cursor->it.currentBlock(); QTextBlockFormat format = block.blockFormat(); KoParagraphStyle style(format, block.charFormat()); if (block.textList() && block.layout()->lineCount() == 1) { Qt::Alignment alignment = format.alignment(); if (d->isRtl && (alignment & Qt::AlignAbsolute) == 0) { if (alignment & Qt::AlignLeft) { alignment = Qt::AlignRight; } else if (alignment & Qt::AlignRight) { alignment = Qt::AlignLeft; } } alignment &= Qt::AlignRight | Qt::AlignLeft | Qt::AlignHCenter; // First line, lets check where the line ended up and adjust the positioning of the counter. qreal newX; if (alignment & Qt::AlignHCenter) { const qreal padding = (line.width() - line.naturalTextWidth()) / 2; newX = blockData.counterPosition().x() + (d->isRtl ? -padding : padding); } else if (alignment & Qt::AlignRight) { const qreal padding = line.width() - line.naturalTextWidth(); newX = blockData.counterPosition().x() + (d->isRtl ? -padding : padding); } else { newX = blockData.counterPosition().x(); } if (d->isRtl) { newX = line.x() + line.naturalTextWidth() + line.x() + d->indent - newX; } blockData.setCounterPosition(QPointF(newX, blockData.counterPosition().y())); } qreal height = 0; qreal breakHeight = 0.0; qreal ascent = 0.0; qreal descent = 0.0; const bool useFontProperties = format.boolProperty(KoParagraphStyle::LineSpacingFromFont); if (cursor->fragmentIterator.atEnd()) {// no text in parag. qreal fontStretch = 1; QTextCharFormat charFormat = block.charFormat(); if (block.blockFormat().hasProperty(KoParagraphStyle::EndCharStyle)) { QVariant v = block.blockFormat().property(KoParagraphStyle::EndCharStyle); QSharedPointer endCharStyle = v.value< QSharedPointer >(); if (!endCharStyle.isNull()) { endCharStyle->applyStyle(charFormat); endCharStyle->ensureMinimalProperties(charFormat); } } if (useFontProperties) { //stretch line height to powerpoint size fontStretch = PresenterFontStretch; } else if (block.charFormat().hasProperty(KoCharacterStyle::FontYStretch)) { // stretch line height to ms-word size fontStretch = charFormat.property(KoCharacterStyle::FontYStretch).toDouble(); } height = charFormat.fontPointSize() * fontStretch; } else { qreal fontStretch = 1; QTextFragment fragment = cursor->fragmentIterator.fragment(); if (useFontProperties) { //stretch line height to powerpoint size fontStretch = PresenterFontStretch; } else if (fragment.charFormat().hasProperty(KoCharacterStyle::FontYStretch)) { // stretch line height to ms-word size fontStretch = fragment.charFormat().property(KoCharacterStyle::FontYStretch).toDouble(); } // read max font height height = qMax(height, fragment.charFormat().fontPointSize() * fontStretch); KoInlineObjectExtent pos = d->documentLayout->inlineObjectExtent(fragment); ascent = qMax(ascent, pos.m_ascent); descent = qMax(descent, pos.m_descent); bool lineBreak = false; int lastCharPos = block.position() + line.textStart() + line.textLength() - 1; int blockLastCharWithoutPreedit = line.textStart() + line.textLength() - 1; if (block.layout()->preeditAreaPosition() >= block.position() + line.textStart() && block.layout()->preeditAreaPosition() <= lastCharPos) { blockLastCharWithoutPreedit -= block.layout()->preeditAreaText().length(); } if (block.text().at(blockLastCharWithoutPreedit) == QChar(0x2028)) { // Was a line with line-break if (line.textLength() != 1) { //unless empty line we should ignore the format of it --lastCharPos; } lineBreak = true; } while (!(fragment.contains(lastCharPos))) { cursor->fragmentIterator++; if (cursor->fragmentIterator.atEnd()) { break; } fragment = cursor->fragmentIterator.fragment(); if (!d->documentLayout->changeTracker() || !d->documentLayout->changeTracker()->displayChanges() || !d->documentLayout->changeTracker()->containsInlineChanges(fragment.charFormat()) || !d->documentLayout->changeTracker()->elementById(fragment.charFormat().property(KoCharacterStyle::ChangeTrackerId).toInt()) || !d->documentLayout->changeTracker()->elementById(fragment.charFormat().property(KoCharacterStyle::ChangeTrackerId).toInt())->isEnabled() || (d->documentLayout->changeTracker()->elementById(fragment.charFormat().property(KoCharacterStyle::ChangeTrackerId).toInt())->getChangeType() != KoGenChange::DeleteChange) || d->documentLayout->changeTracker()->displayChanges()) { qreal fontStretch = 1; if (useFontProperties) { //stretch line height to powerpoint size fontStretch = PresenterFontStretch; } else if (fragment.charFormat().hasProperty(KoCharacterStyle::FontYStretch)) { // stretch line height to ms-word size fontStretch = fragment.charFormat().property(KoCharacterStyle::FontYStretch).toDouble(); } // read max font height height = qMax(height, fragment.charFormat().fontPointSize() * fontStretch); KoInlineObjectExtent pos = d->documentLayout->inlineObjectExtent(fragment); ascent = qMax(ascent, pos.m_ascent); descent = qMax(descent, pos.m_descent); } } if (lineBreak) { // Was a line with line-break - the format of the line-break should not be // considered for the next line either. So we may have to advance the fragmentIterator. while (!cursor->fragmentIterator.atEnd() && lastCharPos > fragment.position() + fragment.length()-1) { cursor->fragmentIterator++; fragment = cursor->fragmentIterator.fragment(); } qreal breakAscent = ascent; qreal breakDescent = descent; breakHeight = height; int firstPos = block.position() + line.textStart() + line.textLength(); // Was a line with line-break - the format of the line-break should not be // considered for the next line either. So we may have to advance the fragmentIterator. while (!cursor->fragmentIterator.atEnd() && firstPos > fragment.position() + fragment.length()-1) { cursor->fragmentIterator++; if (!cursor->fragmentIterator.atEnd()) { fragment = cursor->fragmentIterator.fragment(); // read max font height breakHeight = qMax(breakHeight, fragment.charFormat().fontPointSize() * fontStretch); KoInlineObjectExtent pos = d->documentLayout->inlineObjectExtent(fragment); breakAscent = qMax(breakAscent, pos.m_ascent); breakDescent = qMax(breakDescent, pos.m_descent); } } breakHeight = qMax(breakHeight, breakAscent + breakDescent); } } height = qMax(height, ascent + descent); if (height < 0.01) { height = 12; // default size for uninitialized styles. } // Calculate adjustment to the height due to line height calculated by qt which shouldn't be // there in reality. We will just move the line qreal lineAdjust = 0.0; if (breakHeight > height) { lineAdjust = height - breakHeight; } // Adjust the line-height according to a probably defined fixed line height, // a proportional (percent) line-height and/or the line-spacing. Together // with the line-height we maybe also need to adjust the position of the // line. This is for example needed if the line needs to shrink in height // so the line-text stays on the baseline. If the line grows in height then // we don't need to do anything. if (d->dropCapsNChars <= 0) { // linespacing rules doesn't apply to drop caps qreal fixedLineHeight = format.doubleProperty(KoParagraphStyle::FixedLineHeight); if (fixedLineHeight != 0.0) { qreal prevHeight = height; height = fixedLineHeight; lineAdjust += height - prevHeight; } else { qreal lineSpacing = format.doubleProperty(KoParagraphStyle::LineSpacing); if (lineSpacing == 0.0) { // unset qreal percent = format.doubleProperty(KoParagraphStyle::PercentLineHeight); if (percent != 0) { height *= percent / 100.0; } else { height *= 1.2; // default } } height += lineSpacing; } qreal minimum = style.minimumLineHeight(); if (minimum > 0.0) { height = qMax(height, minimum); } } else { // for drop caps we just work with a basic linespacing for the dropped characters height *= 1.2; } //rounding problems due to Qt-scribe internally using ints. //also used when line was moved down because of intersections with other shapes if (qAbs(d->y - line.y()) >= 0.126) { d->y = line.y(); } if (lineAdjust) { // Adjust the position of the line itself. line.setPosition(QPointF(line.x(), line.y() + lineAdjust)); // Adjust the position of the block-rect for this line which is used later // to proper clip the line while drawing. If we would not adjust it here // then we could end with text-lines being partly cutoff. if (lineAdjust < 0.0) { d->blockRects.last().moveTop(d->blockRects.last().top() + lineAdjust); } if (block.textList() && block.layout()->lineCount() == 1) { // If this is the first line in a list (aka the first line after the list- // item) then we also need to adjust the counter to match to the line again. blockData.setCounterPosition(QPointF(blockData.counterPosition().x(), blockData.counterPosition().y() + lineAdjust)); } } return height; } void KoTextLayoutArea::setLayoutEnvironmentResctictions(bool isLayoutEnvironment, bool actsHorizontally) { d->isLayoutEnvironment = isLayoutEnvironment; d->actsHorizontally = actsHorizontally; } QRectF KoTextLayoutArea::layoutEnvironmentRect() const { QRectF rect(-5e10, -5e10, 10e10, 10e20); // large values that never really restrict anything if (d->parent) { rect = d->parent->layoutEnvironmentRect(); } if (d->isLayoutEnvironment) { if (d->actsHorizontally) { rect.setLeft(left()); rect.setRight(right()); } rect.setTop(top()); rect.setBottom(maximumAllowedBottom()); } return rect; } QRectF KoTextLayoutArea::boundingRect() const { return d->boundingRect; } qreal KoTextLayoutArea::maximumAllowedBottom() const { return d->maximalAllowedBottom - d->footNotesHeight - d->preregisteredFootNotesHeight; } FrameIterator *KoTextLayoutArea::footNoteCursorToNext() const { return d->footNoteCursorToNext; } KoInlineNote *KoTextLayoutArea::continuedNoteToNext() const { return d->continuedNoteToNext; } int KoTextLayoutArea::footNoteAutoCount() const { return d->footNoteAutoCount; } void KoTextLayoutArea::setFootNoteCountInDoc(int count) { d->footNoteCountInDoc = count; } void KoTextLayoutArea::setFootNoteFromPrevious(FrameIterator *footNoteCursor, KoInlineNote *note) { d->footNoteCursorFromPrevious = footNoteCursor; d->continuedNoteFromPrevious = note; } void KoTextLayoutArea::setNoWrap(qreal maximumAllowedWidth) { d->maximumAllowedWidth = maximumAllowedWidth; } KoText::Direction KoTextLayoutArea::parentTextDirection() const { Q_ASSERT(d->parent); //Root areas should overload this method return d->parent->parentTextDirection(); } KoTextLayoutArea *KoTextLayoutArea::parent() const { return d->parent; } KoTextDocumentLayout *KoTextLayoutArea::documentLayout() const { return d->documentLayout; } void KoTextLayoutArea::setReferenceRect(qreal left, qreal right, qreal top, qreal maximumAllowedBottom) { d->left = left; d->right = right; d->top = top; d->boundingRect = QRectF(left, top, right - left, 0.0); Q_ASSERT_X(d->boundingRect.top() <= d->boundingRect.bottom() && d->boundingRect.left() <= d->boundingRect.right(), __FUNCTION__, "Bounding-rect is not normalized"); d->maximalAllowedBottom = maximumAllowedBottom; } QRectF KoTextLayoutArea::referenceRect() const { return QRectF(d->left, d->top, d->right - d->left, d->bottom - d->top); } qreal KoTextLayoutArea::left() const { return d->left; } qreal KoTextLayoutArea::right() const { return d->right; } qreal KoTextLayoutArea::top() const { return d->top; } qreal KoTextLayoutArea::bottom() const { return d->bottom; } void KoTextLayoutArea::setBottom(qreal bottom) { d->boundingRect.setBottom(bottom + qMax(qreal(0.0), d->verticalAlignOffset)); Q_ASSERT_X(d->boundingRect.top() <= d->boundingRect.bottom(), __FUNCTION__, "Bounding-rect is not normalized"); d->bottom = bottom; } void KoTextLayoutArea::findFootNotes(const QTextBlock &block, const QTextLine &line, qreal bottomOfText) { if (d->documentLayout->inlineTextObjectManager() == 0) { return; } QString text = block.text(); int pos = text.indexOf(QChar::ObjectReplacementCharacter, line.textStart()); while (pos >= 0 && pos <= line.textStart() + line.textLength()) { QTextCursor c1(block); c1.setPosition(block.position() + pos); c1.setPosition(c1.position() + 1, QTextCursor::KeepAnchor); KoInlineNote *note = dynamic_cast(d->documentLayout->inlineTextObjectManager()->inlineTextObject(c1)); if (note && note->type() == KoInlineNote::Footnote) { preregisterFootNote(note, bottomOfText); } pos = text.indexOf(QChar::ObjectReplacementCharacter, pos + 1); } } qreal KoTextLayoutArea::preregisterFootNote(KoInlineNote *note, qreal bottomOfText) { if (d->parent == 0) { // TODO to support footnotes at end of document this is // where we need to add some extra condition if (note->autoNumbering()) { KoOdfNotesConfiguration *notesConfig = d->documentLayout->styleManager()->notesConfiguration(KoOdfNotesConfiguration::Footnote); if (notesConfig->numberingScheme() == KoOdfNotesConfiguration::BeginAtDocument) { note->setAutoNumber(d->footNoteCountInDoc + (d->footNoteAutoCount++)); } else if (notesConfig->numberingScheme() == KoOdfNotesConfiguration::BeginAtPage) { note->setAutoNumber(d->footNoteAutoCount++); } } if (maximumAllowedBottom() - bottomOfText > 0) { QTextFrame *subFrame = note->textFrame(); d->footNoteCursorToNext = new FrameIterator(subFrame); KoTextLayoutNoteArea *footNoteArea = new KoTextLayoutNoteArea(note, this, d->documentLayout); d->preregisteredFootNoteFrames.append(subFrame); footNoteArea->setReferenceRect(left(), right(), 0, maximumAllowedBottom() - bottomOfText); bool contNotNeeded = footNoteArea->layout(d->footNoteCursorToNext); if (contNotNeeded) { delete d->footNoteCursorToNext; d->footNoteCursorToNext = 0; d->continuedNoteToNext = 0; } else { d->continuedNoteToNext = note; //layout again now it has set up a continuationObstruction delete d->footNoteCursorToNext; d->footNoteCursorToNext = new FrameIterator(subFrame); footNoteArea->setReferenceRect(left(), right(), 0, maximumAllowedBottom() - bottomOfText); footNoteArea->layout(d->footNoteCursorToNext); documentLayout()->setContinuationObstruction(0); // remove it again } d->preregisteredFootNotesHeight += footNoteArea->bottom() - footNoteArea->top(); d->preregisteredFootNoteAreas.append(footNoteArea); return footNoteArea->bottom() - footNoteArea->top(); } return 0.0; } qreal h = d->parent->preregisterFootNote(note, bottomOfText); d->preregisteredFootNotesHeight += h; return h; } void KoTextLayoutArea::confirmFootNotes() { d->footNotesHeight += d->preregisteredFootNotesHeight; d->footNoteAreas.append(d->preregisteredFootNoteAreas); d->footNoteFrames.append(d->preregisteredFootNoteFrames); d->preregisteredFootNotesHeight = 0; d->preregisteredFootNoteAreas.clear(); d->preregisteredFootNoteFrames.clear(); if (d->parent) { d->parent->confirmFootNotes(); } } void KoTextLayoutArea::expandBoundingLeft(qreal x) { d->boundingRect.setLeft(qMin(x, d->boundingRect.x())); } void KoTextLayoutArea::expandBoundingRight(qreal x) { d->boundingRect.setRight(qMax(x, d->boundingRect.right())); } void KoTextLayoutArea::clearPreregisteredFootNotes() { d->preregisteredFootNotesHeight = 0; d->preregisteredFootNoteAreas.clear(); d->preregisteredFootNoteFrames.clear(); if (d->parent) { d->parent->clearPreregisteredFootNotes(); } } void KoTextLayoutArea::handleBordersAndSpacing(KoTextBlockData &blockData, QTextBlock *block) { QTextBlockFormat format = block->blockFormat(); KoParagraphStyle formatStyle(format, block->charFormat()); // The AddParaTableSpacingAtStart config-item is used to be able to optionally prevent that // defined fo:margin-top are applied to the first paragraph. If true then the fo:margin-top // is applied to all except the first paragraph. If false fo:margin-top is applied to all // paragraphs. bool paraTableSpacingAtStart = KoTextDocument(d->documentLayout->document()).paraTableSpacingAtStart(); bool paddingExpandsBorders = false;//KoTextDocument(d->documentLayout->document()).paddingExpandsBorders(); qreal topMargin = 0; if (paraTableSpacingAtStart || block->previous().isValid()) { topMargin = formatStyle.topMargin(); } qreal spacing = qMax(d->bottomSpacing, topMargin); qreal dx = 0.0; qreal x = d->x; qreal width = d->width; if (d->indent < 0) { x += d->indent; width -= d->indent; } if (blockData.hasCounterData() && blockData.counterPosition().x() < x) { width += x - blockData.counterPosition().x(); x = blockData.counterPosition().x(); } KoTextBlockBorderData border(QRectF(x, d->y, width, 1)); border.setEdge(border.Left, format, KoParagraphStyle::LeftBorderStyle, KoParagraphStyle::LeftBorderWidth, KoParagraphStyle::LeftBorderColor, KoParagraphStyle::LeftBorderSpacing, KoParagraphStyle::LeftInnerBorderWidth); border.setEdge(border.Right, format, KoParagraphStyle::RightBorderStyle, KoParagraphStyle::RightBorderWidth, KoParagraphStyle::RightBorderColor, KoParagraphStyle::RightBorderSpacing, KoParagraphStyle::RightInnerBorderWidth); border.setEdge(border.Top, format, KoParagraphStyle::TopBorderStyle, KoParagraphStyle::TopBorderWidth, KoParagraphStyle::TopBorderColor, KoParagraphStyle::TopBorderSpacing, KoParagraphStyle::TopInnerBorderWidth); border.setEdge(border.Bottom, format, KoParagraphStyle::BottomBorderStyle, KoParagraphStyle::BottomBorderWidth, KoParagraphStyle::BottomBorderColor, KoParagraphStyle::BottomBorderSpacing, KoParagraphStyle::BottomInnerBorderWidth); border.setMergeWithNext(formatStyle.joinBorder()); if (border.hasBorders()) { // check if we can merge with the previous parags border. if (d->prevBorder && d->prevBorder->equals(border)) { blockData.setBorder(d->prevBorder); // Merged mean we don't have inserts inbetween the blocks d->anchoringParagraphTop = d->y; if (d->bottomSpacing + topMargin) { d->anchoringParagraphTop += spacing * d->bottomSpacing / (d->bottomSpacing + topMargin); } if (!d->blockRects.isEmpty()) { d->blockRects.last().setBottom(d->anchoringParagraphTop); } d->anchoringParagraphTop = d->y; d->y += spacing; d->blockRects.append(QRectF(x, d->anchoringParagraphTop, width, 1.0)); } else { // can't merge; then these are our new borders. KoTextBlockBorderData *newBorder = new KoTextBlockBorderData(border); blockData.setBorder(newBorder); if (d->prevBorder) { d->y += d->prevBorderPadding; d->y += d->prevBorder->inset(KoTextBlockBorderData::Bottom); } if (!d->blockRects.isEmpty()) { d->blockRects.last().setBottom(d->y); } d->anchoringParagraphTop = d->y; if (d->bottomSpacing + topMargin) { d->anchoringParagraphTop += spacing * d->bottomSpacing / (d->bottomSpacing + topMargin); } d->y += spacing; if (paddingExpandsBorders) { d->blockRects.append(QRectF(x - format.doubleProperty(KoParagraphStyle::LeftPadding), d->y, width + format.doubleProperty(KoParagraphStyle::LeftPadding) + format.doubleProperty(KoParagraphStyle::RightPadding), 1.0)); } else { d->blockRects.append(QRectF(x, d->y, width, 1.0)); } d->y += newBorder->inset(KoTextBlockBorderData::Top); d->y += format.doubleProperty(KoParagraphStyle::TopPadding); } // finally, horizontal components of the borders dx = border.inset(KoTextBlockBorderData::Left); d->x += dx; d->width -= border.inset(KoTextBlockBorderData::Left); d->width -= border.inset(KoTextBlockBorderData::Right); } else { // this parag has no border. if (d->prevBorder) { d->y += d->prevBorderPadding; d->y += d->prevBorder->inset(KoTextBlockBorderData::Bottom); } blockData.setBorder(0); // remove an old one, if there was one. if (!d->blockRects.isEmpty()) { d->blockRects.last().setBottom(d->y); } d->anchoringParagraphTop = d->y; if (d->bottomSpacing + topMargin) { d->anchoringParagraphTop += spacing * d->bottomSpacing / (d->bottomSpacing + topMargin); } d->y += spacing; d->blockRects.append(QRectF(x, d->y, width, 1.0)); } if (!paddingExpandsBorders) { // add padding inside the border dx += format.doubleProperty(KoParagraphStyle::LeftPadding); d->x += format.doubleProperty(KoParagraphStyle::LeftPadding); d->width -= format.doubleProperty(KoParagraphStyle::LeftPadding); d->width -= format.doubleProperty(KoParagraphStyle::RightPadding); } if (block->layout()->lineCount() == 1 && blockData.hasCounterData()) { blockData.setCounterPosition(QPointF(blockData.counterPosition().x() + dx, d->y)); } d->prevBorder = blockData.border(); d->prevBorderPadding = format.doubleProperty(KoParagraphStyle::BottomPadding); d->anchoringParagraphContentTop = d->y; } diff --git a/libs/textlayout/RunAroundHelper.cpp b/libs/textlayout/RunAroundHelper.cpp index bc941145fe6..381511e0909 100644 --- a/libs/textlayout/RunAroundHelper.cpp +++ b/libs/textlayout/RunAroundHelper.cpp @@ -1,315 +1,314 @@ /* This file is part of the KDE project * Copyright (C) 2006-2007, 2010 Thomas Zander * Copyright (C) 2010-2011 KO Gmbh * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "RunAroundHelper.h" #include "KoTextLayoutObstruction.h" #include "KoTextLayoutArea.h" #include const qreal RIDICULOUSLY_LARGE_NEGATIVE_INDENT = -5E6; #define MIN_WIDTH 0.01f RunAroundHelper::RunAroundHelper() { m_lineRect = QRectF(); m_updateValidObstructions = false; m_horizontalPosition = RIDICULOUSLY_LARGE_NEGATIVE_INDENT; m_stayOnBaseline = false; } void RunAroundHelper::setLine(KoTextLayoutArea *area, const QTextLine &l) { m_area = area; line = l; } void RunAroundHelper::setObstructions(const QList &obstructions) { m_obstructions = obstructions; } bool RunAroundHelper::stayOnBaseline() const { return m_stayOnBaseline; } void RunAroundHelper::updateObstruction(KoTextLayoutObstruction *obstruction) { QRectF obstructionLineRect = obstruction->cropToLine(m_lineRect); if (obstructionLineRect.isValid()) { m_updateValidObstructions = true; } } bool RunAroundHelper::fit(const bool resetHorizontalPosition, bool isRightToLeft, const QPointF &position) { Q_ASSERT(line.isValid()); if (resetHorizontalPosition) { m_horizontalPosition = RIDICULOUSLY_LARGE_NEGATIVE_INDENT; m_stayOnBaseline = false; } const qreal maxLineWidth = m_area->width(); // Make sure at least some text is fitted if the basic width (page, table cell, column) // is too small if (maxLineWidth <= 0.) { // we need to make sure that something like "line.setLineWidth(0.0);" is called here to prevent // the QTextLine from being removed again and leading at a later point to crashes. It seems // following if-condition including the setNumColumns call was added to do exactly that. But // it's not clear for what the if-condition was added. In any case that condition is wrong or // incompleted cause things can still crash with m_state->layout->text().length() == 0 (see the // document attached to bug 244411). //if (m_state->layout->lineCount() > 1 || m_state->layout->text().length() > 0) line.setNumColumns(1); line.setPosition(position); return false; } // Too little width because of wrapping is handled in the remainder of this method line.setLineWidth(maxLineWidth); - qInfo()<<"line:"< m_textWidth) { // This can happen if spaces are added at the end of a line. Those spaces will not result in a // line-break. On left-to-right everything is fine and the spaces at the end are just not visible // but on right-to-left we need to adust the position cause spaces at the end are displayed at // the beginning and we need to make sure that doesn't result in us cutting of text at the right side. qreal diff = line.naturalTextWidth() - m_textWidth; lineRectPart.setX(lineRectPart.x() - diff); } line.setLineWidth(m_textWidth); line.setPosition(QPointF(lineRectPart.x(), lineRectPart.y())); checkEndOfLine(lineRectPart, maxNaturalTextWidth); return true; } void RunAroundHelper::validateObstructions() { m_validObstructions.clear(); foreach (KoTextLayoutObstruction *obstruction, m_obstructions) { validateObstruction(obstruction); } } void RunAroundHelper::validateObstruction(KoTextLayoutObstruction *obstruction) { QRectF obstructionLineRect = obstruction->cropToLine(m_lineRect); if (obstructionLineRect.isValid()) { m_validObstructions.append(obstruction); } } void RunAroundHelper::createLineParts() { m_lineParts.clear(); if (m_validObstructions.isEmpty()) { // Add whole line rect m_lineParts.append(m_lineRect); } else { QVector lineParts; QRectF rightLineRect = m_lineRect; bool lastRightRectValid = false; qSort(m_validObstructions.begin(), m_validObstructions.end(), KoTextLayoutObstruction::compareRectLeft); // Divide rect to parts, part can be invalid when obstructions are not disjunct. foreach (KoTextLayoutObstruction *validObstruction, m_validObstructions) { QRectF leftLineRect = validObstruction->getLeftLinePart(rightLineRect); lineParts.append(leftLineRect); QRectF lineRect = validObstruction->getRightLinePart(rightLineRect); if (lineRect.isValid()) { rightLineRect = lineRect; lastRightRectValid = true; } else { lastRightRectValid = false; } } if (lastRightRectValid) { lineParts.append(rightLineRect); } else { lineParts.append(QRect()); } Q_ASSERT(m_validObstructions.size() + 1 == lineParts.size()); // Select invalid parts because of wrap. for (int i = 0; i < m_validObstructions.size(); i++) { KoTextLayoutObstruction *obstruction = m_validObstructions.at(i); if (obstruction->noTextAround()) { lineParts.replace(i, QRectF()); lineParts.replace(i + 1, QRect()); } else if (obstruction->textOnLeft()) { lineParts.replace(i + 1, QRect()); } else if (obstruction->textOnRight()) { lineParts.replace(i, QRectF()); } else if (obstruction->textOnEnoughSides()) { QRectF leftRect = obstruction->getLeftLinePart(m_lineRect); QRectF rightRect = obstruction->getRightLinePart(m_lineRect); if (leftRect.width() < obstruction->runAroundThreshold()) { lineParts.replace(i, QRectF()); } if (rightRect.width() < obstruction->runAroundThreshold()) { lineParts.replace(i + 1, QRectF()); } } else if (obstruction->textOnBiggerSide()) { QRectF leftRect = obstruction->getLeftLinePart(m_lineRect); QRectF rightRect = obstruction->getRightLinePart(m_lineRect); if (leftRect.width() < rightRect.width()) { lineParts.replace(i, QRectF()); } else { lineParts.replace(i + 1, QRectF()); } } } // Filter invalid parts. foreach (const QRectF &rect, lineParts) { if (rect.isValid()) { m_lineParts.append(rect); } } } } QRectF RunAroundHelper::minimizeHeightToLeastNeeded(const QRectF &lineRect) { Q_ASSERT(line.isValid()); QRectF lineRectBase = lineRect; // Get width of one char or shape (as-char). m_textWidth = line.cursorToX(line.textStart() + 1) - line.cursorToX(line.textStart()); // Make sure width is not wider than the area allows. if (m_textWidth > m_area->width()) { m_textWidth = m_area->width(); } line.setLineWidth(m_textWidth); // Base linerect height on the width calculated above. lineRectBase.setHeight(line.height()); return lineRectBase; } void RunAroundHelper::updateLineParts(const QRectF &lineRect) { if (m_lineRect != lineRect || m_updateValidObstructions) { m_lineRect = lineRect; m_updateValidObstructions = false; validateObstructions(); createLineParts(); } } QRectF RunAroundHelper::getLineRectPart() { QRectF retVal; foreach (const QRectF &lineRectPart, m_lineParts) { if (m_horizontalPosition <= lineRectPart.left() && m_textWidth <= lineRectPart.width()) { retVal = lineRectPart; break; } } return retVal; } void RunAroundHelper::setMaxTextWidth(const QRectF &minLineRectPart, const qreal leftIndent, const qreal maxNaturalTextWidth) { Q_ASSERT(line.isValid()); qreal width = m_textWidth; qreal maxWidth = minLineRectPart.width() - leftIndent; qreal height; qreal maxHeight = minLineRectPart.height(); qreal widthDiff = maxWidth - width; widthDiff /= 2; while (width <= maxWidth && width <= maxNaturalTextWidth && widthDiff > MIN_WIDTH) { qreal linewidth = width + widthDiff; line.setLineWidth(linewidth); height = line.height(); if (height <= maxHeight) { width = linewidth; m_textWidth = width; } widthDiff /= 2; } } QRectF RunAroundHelper::getLineRect(const QRectF &lineRect, const qreal maxNaturalTextWidth) { Q_ASSERT(line.isValid()); const qreal leftIndent = lineRect.left(); QRectF minLineRect = minimizeHeightToLeastNeeded(lineRect); updateLineParts(minLineRect); // Get appropriate line rect part, to fit line, // using horizontal position, minimal height and width of line. QRectF lineRectPart = getLineRectPart(); if (lineRectPart.isValid()) { qreal x = lineRectPart.x(); qreal width = lineRectPart.width(); // Limit moved the left edge, keep the indent. if (leftIndent < x) { x += leftIndent; width -= leftIndent; } line.setLineWidth(width); // Check if line rect is big enough to fit line. // Otherwise find shorter width, what means also shorter height of line. // Condition is reverted. if (line.height() > lineRectPart.height()) { setMaxTextWidth(lineRectPart, leftIndent, maxNaturalTextWidth); } else { m_textWidth = width; } } return lineRectPart; } void RunAroundHelper::checkEndOfLine(const QRectF &lineRectPart, const qreal maxNaturalTextWidth) { if (lineRectPart == m_lineParts.last() || maxNaturalTextWidth <= lineRectPart.width()) { m_horizontalPosition = RIDICULOUSLY_LARGE_NEGATIVE_INDENT; m_stayOnBaseline = false; } else { m_horizontalPosition = lineRectPart.right(); m_stayOnBaseline = true; } } diff --git a/libs/textlayout/tests/TestBlockLayout.cpp b/libs/textlayout/tests/TestBlockLayout.cpp index e51e581492a..f4077f02b15 100644 --- a/libs/textlayout/tests/TestBlockLayout.cpp +++ b/libs/textlayout/tests/TestBlockLayout.cpp @@ -1,1078 +1,1227 @@ /* * This file is part of Calligra tests * * Copyright (C) 2006-2010 Thomas Zander * Copyright (C) 2011 C. Boemann * * 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 "TestBlockLayout.h" #include "MockRootAreaProvider.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #define FRAME_SPACING 10.0 void TestBlockLayout::initTestCase() { m_doc = 0; m_layout = 0; m_loremIpsum = QString("Lorem ipsum dolor sit amet, XgXgectetuer adiXiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi."); } void TestBlockLayout::setupTest(const QString &initText) { m_doc = new QTextDocument; Q_ASSERT(m_doc); MockRootAreaProvider *provider = new MockRootAreaProvider(); Q_ASSERT(provider); KoTextDocument(m_doc).setInlineTextObjectManager(new KoInlineTextObjectManager); m_doc->setDefaultFont(QFont("Sans Serif", 12, QFont::Normal, false)); //do it manually since we do not load the appDefaultStyle m_styleManager = new KoStyleManager(0); KoTextDocument(m_doc).setStyleManager(m_styleManager); m_layout = new KoTextDocumentLayout(m_doc, provider); Q_ASSERT(m_layout); m_doc->setDocumentLayout(m_layout); //m_area = provider->provide(m_layout); m_block = m_doc->begin(); if (initText.length() > 0) { QTextCursor cursor(m_doc); cursor.insertText(initText); KoParagraphStyle style; style.setFontPointSize(12.0); style.setStyleId(101); // needed to do manually since we don't use the stylemanager QTextBlock b2 = m_doc->begin(); while (b2.isValid()) { style.applyStyle(b2); b2 = b2.next(); } } } void TestBlockLayout::testLineBreaking() { setupTest(m_loremIpsum); m_layout->layout(); QTextLayout *blockLayout = m_block.layout(); //QCOMPARE(blockLayout->lineCount(), 16); QCOMPARE(blockLayout->lineForTextPosition(1).width(), 200.0); } void TestBlockLayout::testBasicLineSpacing() { /// Tests incrementing Y pos based on the font size setupTest(m_loremIpsum); QTextCursor cursor(m_doc); cursor.setPosition(0); cursor.setPosition(m_loremIpsum.length() - 1, QTextCursor::KeepAnchor); QTextCharFormat charFormat = cursor.charFormat(); charFormat.setFontPointSize(12); cursor.mergeCharFormat(charFormat); m_layout->layout(); QTextLayout *blockLayout = m_block.layout(); const qreal fontHeight12 = 12; qreal lineSpacing12 = fontHeight12 * 1.2; // 120% is the normal lineSpacing. const qreal fontHeight18 = 18; qreal lineSpacing18 = fontHeight18 * 1.2; // 120% is the normal lineSpacing. // QCOMPARE(blockLayout->lineCount(), 15); QTextLine line; for (int i = 0; i < 15; i++) { line = blockLayout->lineAt(i); QVERIFY(line.isValid()); // The reason for this weird check is that the values are stored internally // as 26.6 fixed point integers. The entire internal text layout is // actually done using fixed point arithmetic. This is due to embedded // considerations, and offers general performance benefits across all // platforms. //qDebug() << i << qAbs(line.y() - i * lineSpacing12); QVERIFY(qAbs(line.y() - (i * lineSpacing12 + 100.0)) < ROUNDING); } // make first word smaller, should have zero effect on lineSpacing. cursor.setPosition(0); cursor.setPosition(11, QTextCursor::KeepAnchor); charFormat.setFontPointSize(10); cursor.mergeCharFormat(charFormat); m_layout->layout(); for (int i = 0; i < 15; i++) { line = blockLayout->lineAt(i); QVERIFY(line.isValid()); //qDebug() << i << qAbs(line.y() - i * lineSpacing12); QVERIFY(qAbs(line.y() - (i * lineSpacing12 + 100.0)) < ROUNDING); } // make first word on second line word bigger, should move that line down a little. int pos = blockLayout->lineAt(1).textStart(); cursor.setPosition(pos); cursor.setPosition(pos + 12, QTextCursor::KeepAnchor); charFormat.setFontPointSize(18); cursor.mergeCharFormat(charFormat); m_layout->layout(); line = blockLayout->lineAt(0); QCOMPARE(line.y(), 0.0 + 100.0); line = blockLayout->lineAt(1); QVERIFY(qAbs(line.y() - (lineSpacing12 + 100.0)) < ROUNDING); for (int i = 2; i < 15; i++) { line = blockLayout->lineAt(i); //qDebug() << "i: " << i << " gives: " << line.y() << (lineSpacing12 + lineSpacing18 + (i - 2) * lineSpacing12); QVERIFY(qAbs(line.y() - (lineSpacing12 + lineSpacing18 + (i - 2) * lineSpacing12 + 100.0)) < ROUNDING); } } void TestBlockLayout::testBasicLineSpacing2() { setupTest(m_loremIpsum); QTextCursor cursor(m_doc); cursor.insertText("foo\n\n"); // insert empty parag; m_layout->layout(); QTextBlock block = m_doc->begin().next(); QTextLayout *blockLayout = block.layout(); QVERIFY(block.isValid()); blockLayout = block.layout(); QCOMPARE(blockLayout->lineCount(), 1); block = block.next(); QVERIFY(block.isValid()); blockLayout = block.layout(); //qDebug() << blockLayout->lineAt(0).y(); QVERIFY(qAbs(blockLayout->lineAt(0).y() - (28.8 + 100.0)) < ROUNDING); } void TestBlockLayout::testFixedLineSpacing() { setupTest(QString("Line1")+QChar(0x2028)+"Line2"+QChar(0x2028)+"Line3"); KoParagraphStyle style; style.setFontPointSize(12.0); style.setLineHeightAbsolute(28.0); QTextBlock block = m_doc->begin(); style.applyStyle(block); QCOMPARE(block.blockFormat().doubleProperty(KoParagraphStyle::FixedLineHeight), 28.0); m_layout->layout(); QTextLayout *blockLayout = block.layout(); // lines with fontsize less than the fixed height are bottom aligned, resulting in // positive y for first line QCOMPARE(blockLayout->lineAt(0).y(), 28.0-12.0 + 100.0); QCOMPARE(blockLayout->lineAt(1).y(), 28.0 + 28.0-12.0 + 100.0); QCOMPARE(blockLayout->lineAt(2).y(), 56.0 + 28.0-12.0 + 100.0); style.setLineHeightAbsolute(8.0); style.applyStyle(block); QCOMPARE(block.blockFormat().doubleProperty(KoParagraphStyle::FixedLineHeight), 8.0); m_layout->layout(); blockLayout = block.layout(); // lines with fontsize more than the fixed height are bottom aligned, resulting in //negative y for first line QCOMPARE(blockLayout->lineAt(0).y(), 8.0-12.0 + 100.0); QCOMPARE(blockLayout->lineAt(1).y(), 8.0-12.0 + 8.0 + 100.0); QCOMPARE(blockLayout->lineAt(2).y(), 8.0-12.0 + 8.0 + 8.0 + 100.0); } void TestBlockLayout::testPercentageLineSpacing() { setupTest(QString("Line1")+QChar(0x2028)+"Line2"+QChar(0x2028)+"Line3"); KoParagraphStyle style; style.setFontPointSize(12.0); style.setLineHeightPercent(150); QTextBlock block = m_doc->begin(); style.applyStyle(block); QCOMPARE(block.blockFormat().doubleProperty(KoParagraphStyle::PercentLineHeight), 150.0); m_layout->layout(); QTextLayout *blockLayout = block.layout(); QCOMPARE(blockLayout->lineAt(0).y(), 0.0 + 100.0); QCOMPARE(blockLayout->lineAt(1).y(), 0.0 + 18.0 + 100.0); QCOMPARE(blockLayout->lineAt(2).y(), 0.0 + 18.0 + 18.0 + 100.0); style.setLineHeightPercent(50); style.applyStyle(block); QCOMPARE(block.blockFormat().doubleProperty(KoParagraphStyle::PercentLineHeight), 50.0); m_layout->layout(); blockLayout = block.layout(); QCOMPARE(blockLayout->lineAt(0).y(), 0.0 + 100.0); QCOMPARE(blockLayout->lineAt(1).y(), 0.0 + 6.0 + 100.0); QCOMPARE(blockLayout->lineAt(2).y(), 0.0 + 6.0 + 6.0 + 100.0); } void TestBlockLayout::testAdvancedLineSpacing() { setupTest("Line1\nLine2\nLine3\nLine4\nLine5\nLine6\nLine7"); KoParagraphStyle style; style.setFontPointSize(12.0); style.setLineHeightPercent(80); QTextBlock block = m_doc->begin(); style.applyStyle(block); // check if styles do their work ;) QCOMPARE(block.blockFormat().doubleProperty(KoParagraphStyle::PercentLineHeight), 80.0); block = block.next(); QVERIFY(block.isValid()); //line2 style.setLineHeightAbsolute(28.0); // removes the percentage style.applyStyle(block); QCOMPARE(block.blockFormat().doubleProperty(KoParagraphStyle::PercentLineHeight), 0.0); QCOMPARE(block.blockFormat().doubleProperty(KoParagraphStyle::FixedLineHeight), 28.0); block = block.next(); QVERIFY(block.isValid()); // line3 style.setMinimumLineHeight(QTextLength(QTextLength::FixedLength, 40.0)); style.setLineHeightPercent(120); style.applyStyle(block); block = block.next(); QVERIFY(block.isValid()); // line4 style.remove(KoParagraphStyle::FixedLineHeight); style.setMinimumLineHeight(QTextLength(QTextLength::FixedLength, 5.0)); style.applyStyle(block); block = block.next(); QVERIFY(block.isValid()); // line5 style.setMinimumLineHeight(QTextLength(QTextLength::FixedLength, 0.0)); style.setLineSpacing(8.0); style.remove(KoParagraphStyle::PercentLineHeight); style.applyStyle(block); block = block.next(); QVERIFY(block.isValid()); // line6 style.setLineSpacingFromFont(true); style.setLineHeightPercent(100); style.remove(KoParagraphStyle::LineSpacing); style.applyStyle(block); block = m_block; // line1 m_layout->layout(); QTextLayout *blockLayout = block.layout(); QVERIFY(qAbs(blockLayout->lineAt(0).y() - (0.0 + 100.0)) < ROUNDING); block = block.next(); // line2 with fixed we are bottom aligned so offset by 28.0-12.0 QVERIFY(block.isValid()); blockLayout = block.layout(); //qDebug() << blockLayout->lineAt(0).y(); QVERIFY(qAbs(blockLayout->lineAt(0).y() - (0.8*12 + 28.0-12.0 + 100.0)) < ROUNDING); block = block.next(); // line3 QVERIFY(block.isValid()); blockLayout = block.layout(); //qDebug() << blockLayout->lineAt(0).y(); QVERIFY(qAbs(blockLayout->lineAt(0).y() - (0.8*12 + 28.0 + 100.0)) < ROUNDING); block = block.next(); // line4 QVERIFY(block.isValid()); blockLayout = block.layout(); //qDebug() << blockLayout->lineAt(0).y(); // percentage overrides minimum so percentage value is the right to test against //QVERIFY(qAbs(blockLayout->lineAt(0).y() - (0.8*12 + 28.0 + 40.0 + 100.0)) < ROUNDING); QVERIFY(qAbs(blockLayout->lineAt(0).y() - (0.8*12 + 28.0 + 1.2*12 + 100.0)) < ROUNDING); block = block.next(); // line5 QVERIFY(block.isValid()); blockLayout = block.layout(); //qDebug() << blockLayout->lineAt(0).y(); // minimum of 5 is irelevant and percentage of 1.2 was still there QVERIFY(qAbs(blockLayout->lineAt(0).y() - (0.8*12 + 28.0 + 1.2*12 + 1.2*12 + 100.0)) < ROUNDING); block = block.next(); // line6 QVERIFY(block.isValid()); blockLayout = block.layout(); //qDebug() << blockLayout->lineAt(0).y(); QVERIFY(qAbs(blockLayout->lineAt(0).y() - (0.8*12 + 28.0 + 1.2*12 + 1.2*12 + 12+8 + 100.0)) < ROUNDING); block = block.next(); // line 7 QVERIFY(block.isValid()); blockLayout = block.layout(); //qDebug() << blockLayout->lineAt(0).y(); QVERIFY(qAbs(blockLayout->lineAt(0).y() - (0.8*12 + 28.0 + 1.2*12 + 1.2*12 + 12+8 + 1.2*12 + 100.0)) < ROUNDING); } void TestBlockLayout::testEmptyLineHeights() { // 1) a blank line is affected by the line break after // 1b) a line with contents is not affected by the linebreak // 2) a final line if blank can have it's height specified by a special textstyle // If the special style is empty the par style is used for the line setupTest(QString("")+QChar(0x2028)+QChar(0x2028)+"\nNextBlock"); QTextCursor cursor(m_doc); QTextCharFormat bigCharFormat; bigCharFormat.setFontPointSize(20.0); QTextCharFormat smallCharFormat; smallCharFormat.setFontPointSize(8.0); KoParagraphStyle style; style.setFontPointSize(12.0); style.setLineHeightPercent(100); QTextBlock block = m_doc->begin(); style.applyStyle(block); // apply formats cursor.setPosition(0); cursor.setPosition(1, QTextCursor::KeepAnchor); cursor.mergeCharFormat(bigCharFormat); cursor.setPosition(1); cursor.setPosition(2, QTextCursor::KeepAnchor); cursor.mergeCharFormat(smallCharFormat); m_layout->layout(); QTextLayout *blockLayout = block.layout(); QCOMPARE(blockLayout->lineAt(0).y(), 0.0 + 100.0); QCOMPARE(blockLayout->lineAt(1).y(), 20.0 + 100.0); QCOMPARE(blockLayout->lineAt(2).y(), 20.0 + 8.0 + 100.0); block = block.next(); QVERIFY(block.isValid()); blockLayout = block.layout(); QCOMPARE(blockLayout->lineAt(0).y(), 20.0 + 8.0 + 12.0 + 100.0); // Now do the test again but with last line having bigger font block = m_doc->begin(); QTextBlockFormat blockFormat = block.blockFormat(); KoCharacterStyle charStyle; charStyle.setFontPointSize(20.0); blockFormat.setProperty(KoParagraphStyle::EndCharStyle, QVariant::fromValue< QSharedPointer >(QSharedPointer(&charStyle))); cursor.setBlockFormat(blockFormat); m_layout->layout(); blockLayout = block.layout(); QCOMPARE(blockLayout->lineAt(0).y(), 0.0 + 100.0); QCOMPARE(blockLayout->lineAt(1).y(), 20.0 + 100.0); QCOMPARE(blockLayout->lineAt(2).y(), 20.0 + 8.0 + 100.0); block = block.next(); QVERIFY(block.isValid()); blockLayout = block.layout(); QCOMPARE(blockLayout->lineAt(0).y(), 20.0 + 8.0 + 20.0 + 100.0); // Now do the test again but with last line having a small font block = m_doc->begin(); KoCharacterStyle charStyle2; charStyle2.setFontPointSize(6.0); blockFormat.setProperty(KoParagraphStyle::EndCharStyle, QVariant::fromValue< QSharedPointer >(QSharedPointer(&charStyle2))); cursor.setBlockFormat(blockFormat); m_layout->layout(); blockLayout = block.layout(); QCOMPARE(blockLayout->lineAt(0).y(), 0.0 + 100.0); QCOMPARE(blockLayout->lineAt(1).y(), 20.0 + 100.0); QCOMPARE(blockLayout->lineAt(2).y(), 20.0 + 8.0 + 100.0); block = block.next(); QVERIFY(block.isValid()); blockLayout = block.layout(); QCOMPARE(blockLayout->lineAt(0).y(), 20.0 + 8.0 + 6.0 + 100.0); } // Test that spacing between blocks are the max of bottomMargin and topMargin // of the top and bottom block respectively // If the block doesn't connect to another block (top and bottom of pages or // table cells, oif blocks are intersperced with say a table. Then it's // just the plain margin // For completeness sake we test with 3 blocks just to make sure it works void TestBlockLayout::testBlockSpacing() { setupTest(m_loremIpsum); QTextCursor cursor(m_doc); QTextCursor cursor1(m_doc); // create second parag cursor.setPosition(m_loremIpsum.length()); cursor.insertText("\n"); cursor.insertText(m_loremIpsum); // create third parag cursor.insertText("\n"); cursor.insertText(m_loremIpsum); m_layout->layout(); QTextBlock block2 = m_doc->begin().next(); QTextBlock block3 = m_doc->begin().next().next(); QTextCursor cursor2(block2); QTextCursor cursor3(block3); // and test spacing between blocks QTextBlockFormat bf1 = cursor1.blockFormat(); QTextLayout *block1Layout = m_block.layout(); QTextBlockFormat bf2 = cursor2.blockFormat(); QTextLayout *block2Layout = block2.layout(); QTextBlockFormat bf3 = cursor3.blockFormat(); QTextLayout *block3Layout = block3.layout(); int lastLineNum = block1Layout->lineCount() - 1; const qreal lineSpacing = 12.0 * 1.2; KoTextDocument(m_doc).setParaTableSpacingAtStart(false); bool paraTableSpacingAtStart = KoTextDocument(m_doc).paraTableSpacingAtStart(); qreal spaces[3] = {0.0, 3.0, 6.0}; for (int t1 = 0; t1 < 3; ++t1) { for (int t2 = 0; t2 < 3; ++t2) { for (int t3 = 0; t3 < 3; ++t3) { for (int b1 = 0; b1 < 3; ++b1) { bf1.setTopMargin(spaces[t1]); bf1.setBottomMargin(spaces[b1]); cursor1.setBlockFormat(bf1); for (int b2 = 0; b2 < 3; ++b2) { bf2.setTopMargin(spaces[t2]); bf2.setBottomMargin(spaces[b2]); cursor2.setBlockFormat(bf2); for (int b3 = 0; b3 < 3; ++b3) { bf3.setTopMargin(spaces[t3]); bf3.setBottomMargin(spaces[b3]); cursor3.setBlockFormat(bf3); m_layout->layout(); // Now lets do the actual testing //Above first block is just plain if (paraTableSpacingAtStart) { QVERIFY(qAbs(block1Layout->lineAt(0).y() - spaces[t1]) < ROUNDING); } else { QVERIFY(qAbs(block1Layout->lineAt(0).y() - (0.0 + 100.0)) < ROUNDING); } // Between 1st and 2nd block is max of spaces QVERIFY(qAbs((block2Layout->lineAt(0).y() - block1Layout->lineAt(lastLineNum).y() - lineSpacing) - qMax(spaces[b1], spaces[t2])) < ROUNDING); // Between 2nd and 3rd block is max of spaces QVERIFY(qAbs((block3Layout->lineAt(0).y() - block2Layout->lineAt(lastLineNum).y() - lineSpacing) - qMax(spaces[b2], spaces[t3])) < ROUNDING); //Below 3rd block is just plain //QVERIFY(qAbs(bottom()-block3Layout->lineAt(lastLineNum).y() - lineSpacing - spaces[t1]) < ROUNDING); } } } } } } KoTextDocument(m_doc).setParaTableSpacingAtStart(true); paraTableSpacingAtStart = KoTextDocument(m_doc).paraTableSpacingAtStart(); for (int t1 = 0; t1 < 3; ++t1) { for (int t2 = 0; t2 < 3; ++t2) { for (int t3 = 0; t3 < 3; ++t3) { for (int b1 = 0; b1 < 3; ++b1) { bf1.setTopMargin(spaces[t1]); bf1.setBottomMargin(spaces[b1]); cursor1.setBlockFormat(bf1); for (int b2 = 0; b2 < 3; ++b2) { bf2.setTopMargin(spaces[t2]); bf2.setBottomMargin(spaces[b2]); cursor2.setBlockFormat(bf2); for (int b3 = 0; b3 < 3; ++b3) { bf3.setTopMargin(spaces[t3]); bf3.setBottomMargin(spaces[b3]); cursor3.setBlockFormat(bf3); m_layout->layout(); // Now lets do the actual testing //Above first block is just plain if (paraTableSpacingAtStart) { QVERIFY(qAbs(block1Layout->lineAt(0).y() - (spaces[t1] + 100.0)) < ROUNDING); } else { QVERIFY(qAbs(block1Layout->lineAt(0).y() - (0.0 + 100.0)) < ROUNDING); } // Between 1st and 2nd block is max of spaces QVERIFY(qAbs((block2Layout->lineAt(0).y() - block1Layout->lineAt(lastLineNum).y() - lineSpacing) - qMax(spaces[b1], spaces[t2])) < ROUNDING); // Between 2nd and 3rd block is max of spaces QVERIFY(qAbs((block3Layout->lineAt(0).y() - block2Layout->lineAt(lastLineNum).y() - lineSpacing) - qMax(spaces[b2], spaces[t3])) < ROUNDING); //Below 3rd block is just plain //QVERIFY(qAbs(bottom()-block3Layout->lineAt(lastLineNum).y() - lineSpacing - spaces[t1]) < ROUNDING); } } } } } } } void TestBlockLayout::testLeftRightMargins() { setupTest(m_loremIpsum); QTextCursor cursor(m_doc); QTextBlockFormat bf = cursor.blockFormat(); bf.setLeftMargin(10.0); cursor.setBlockFormat(bf); m_layout->layout(); QTextLayout *blockLayout = m_block.layout(); QCOMPARE(blockLayout->lineAt(0).x(), 10.0 + 100.0); QCOMPARE(blockLayout->lineAt(0).width(), 190.0); bf.setRightMargin(15.0); cursor.setBlockFormat(bf); m_layout->layout(); QCOMPARE(blockLayout->lineAt(0).x(), 10.0 + 100.0); QCOMPARE(blockLayout->lineAt(0).width(), 175.0); bf.setLeftMargin(0.0); cursor.setBlockFormat(bf); m_layout->layout(); QCOMPARE(blockLayout->lineAt(0).x(), 0.0 + 100.0); QCOMPARE(blockLayout->lineAt(0).width(), 185.0); // still uses the right margin of 15 // create second parag cursor.setPosition(m_loremIpsum.length()); cursor.insertText("\n"); bf.setTopMargin(12); cursor.setBlockFormat(bf); cursor.insertText(m_loremIpsum); m_layout->layout(); QCOMPARE(blockLayout->lineAt(0).x(), 0.0 + 100.0); // parag 1 QCOMPARE(blockLayout->lineAt(0).width(), 185.0); // and test parag 2 QTextBlock block2 = m_doc->begin().next(); QTextLayout *block2Layout = block2.layout(); QCOMPARE(block2Layout->lineAt(0).x(), 0.0 + 100.0); QCOMPARE(block2Layout->lineAt(0).width(), 185.0); } void TestBlockLayout::testTextIndent() { setupTest(m_loremIpsum); QTextCursor cursor(m_doc); QTextBlockFormat bf = cursor.blockFormat(); bf.setTextIndent(20); cursor.setBlockFormat(bf); m_layout->layout(); QTextLayout *blockLayout = m_block.layout(); QCOMPARE(blockLayout->lineAt(0).x(), 20.0 + 100.0); QCOMPARE(blockLayout->lineAt(0).width(), 180.0); QCOMPARE(blockLayout->lineAt(1).x(), 0.0 + 100.0); QCOMPARE(blockLayout->lineAt(1).width(), 200.0); // Add som left margin to check for no correlation bf.setLeftMargin(15.0); cursor.setBlockFormat(bf); m_layout->layout(); QCOMPARE(blockLayout->lineAt(0).x(), 35.0 + 100.0); QCOMPARE(blockLayout->lineAt(0).width(), 165.0); QCOMPARE(blockLayout->lineAt(1).x(), 15.0 + 100.0); QCOMPARE(blockLayout->lineAt(1).width(), 185.0); // create second parag and see it works too cursor.setPosition(m_loremIpsum.length()); cursor.insertText("\n"); bf.setTopMargin(12); cursor.setBlockFormat(bf); cursor.insertText(m_loremIpsum); m_layout->layout(); QTextBlock block2 = m_doc->begin().next(); QTextLayout *block2Layout = block2.layout(); QCOMPARE(block2Layout->lineAt(0).x(), 35.0 + 100.0); QCOMPARE(block2Layout->lineAt(0).width(), 165.0); QCOMPARE(block2Layout->lineAt(1).x(), 15.0 + 100.0); QCOMPARE(block2Layout->lineAt(1).width(), 185.0); } void TestBlockLayout::testTabs_data() { static const struct TestCaseData { bool relativeTabs; qreal leftMargin; qreal textIndent; qreal rightMargin; qreal expected; // expected value of pos=2 of each line } testcaseDataList[] = { { true, 0, 0, 0, 50}, { true, 0, 0, 5, 50}, { true, 0, 10, 0, 50}, { true, 0, 10, 5, 50}, { true, 0, -10, 0, 0}, { true, 0, -10, 5, 0}, { true, 20, 0, 0, 70}, { true, 20, 0, 5, 70}, { true, 20, 10, 0, 70}, { true, 20, 10, 5, 70}, { true, 20, -10, 0, 20}, { true, 20, -10, 5, 20}, { true, -20, 0, 0+20, 30}, //+20 to avoid extra tab fitting in { true, -20, 0, 5+20, 30}, //+20 to avoid extra tab fitting in { true, -20, 10, 0+20, 30}, //+20 to avoid extra tab fitting in { true, -20, 10, 5+20, 30}, //+20 to avoid extra tab fitting in { true, -20, -10, 0+20, -20}, //+20 to avoid extra tab fitting in { true, -20, -10, 5+20, -20}, //+20 to avoid extra tab fitting in { false, 0, 0, 0, 50}, { false, 0, 0, 5, 50}, { false, 0, 10, 0, 50}, { false, 0, 10, 5, 50}, { false, 0, -10, 0, 0}, { false, 0, -10, 5, 0}, { false, 20, 0, 0, 50}, { false, 20, 0, 5, 50}, { false, 20, 10, 0, 50}, { false, 20, 10, 5, 50}, { false, 20, -10, 0, 50}, { false, 20, -10, 5, 50}, { false, -20, 0, 0+70, 0}, //+70 to avoid extra tab fitting in { false, -20, 0, 5+70, 0}, //+70 to avoid extra tab fitting in { false, -20, 10, 0+70, 0}, //+70 to avoid extra tab fitting in { false, -20, 10, 5+70, 0}, //+70 to avoid extra tab fitting in { false, -20, -10, 0+70, 0}, //+70 to avoid extra tab fitting in { false, -20, -10, 5+70, 0}, //+70 to avoid extra tab fitting in }; static const int testcasesCount = sizeof(testcaseDataList)/sizeof(testcaseDataList[0]); QTest::addColumn("relativeTabs"); QTest::addColumn("leftMargin"); QTest::addColumn("textIndent"); QTest::addColumn("rightMargin"); QTest::addColumn("expected"); for (int i = 0; i < testcasesCount; ++i) { const TestCaseData &testcaseData = testcaseDataList[i]; QTest::newRow(QString::number(i).toLatin1()) << testcaseData.relativeTabs << testcaseData.leftMargin << testcaseData.textIndent << testcaseData.rightMargin << testcaseData.expected; } } void TestBlockLayout::testTabs() { QFETCH(bool, relativeTabs); QFETCH(qreal, leftMargin); QFETCH(qreal, textIndent); QFETCH(qreal, rightMargin); QFETCH(qreal, expected); // expected value of pos=2 of each line setupTest("x\tx\tx\tx\tx\tx\tx\tx\tx\tx\tx\tx\tx\tx\tx\te"); QTextCursor cursor(m_doc); QTextBlockFormat bf = cursor.blockFormat(); cursor.setBlockFormat(bf); m_layout->layout(); QTextLayout *blockLayout = m_block.layout(); const qreal tabSpacing = 50.0; // in pt m_layout->setTabSpacing(tabSpacing); KoTextDocument(m_doc).setRelativeTabs(relativeTabs); bf.setLeftMargin(leftMargin); bf.setTextIndent(textIndent); bf.setRightMargin(rightMargin); cursor.setBlockFormat(bf); m_layout->layout(); for (int pos=0; pos<4; pos++) { if (pos==0) QCOMPARE(blockLayout->lineAt(0).cursorToX(pos*2), leftMargin + textIndent); else { warnTextLayout << blockLayout->lineAt(0).cursorToX(pos*2) << expected+(pos-1)*tabSpacing; QVERIFY(qAbs(blockLayout->lineAt(0).cursorToX(pos*2) - (expected+(pos-1)*tabSpacing)) < 1.0); } } if (textIndent == 0.0) { // excluding known fails for (int pos=0; pos<4; pos++) { // pos==0 is known to fail see https://bugs.kde.org/show_bug.cgi?id=239819 if (pos!=0) QVERIFY(qAbs(blockLayout->lineAt(1).cursorToX(pos*2+8)- (expected+(pos-1)*tabSpacing)) < 1.0); } for (int pos=0; pos<4; pos++) { // pos==0 is known to fail see https://bugs.kde.org/show_bug.cgi?id=239819 if (pos!=0) QVERIFY(qAbs(blockLayout->lineAt(2).cursorToX(pos*2+16)- (expected+(pos-1)*tabSpacing)) < 1.0); } } } void TestBlockLayout::testBasicTextAlignments() { setupTest("Left\nCenter\nRight"); QTextCursor cursor(m_doc); QTextBlockFormat format = cursor.blockFormat(); format.setAlignment(Qt::AlignLeft); cursor.setBlockFormat(format); cursor.setPosition(6); format.setAlignment(Qt::AlignHCenter); cursor.setBlockFormat(format); cursor.setPosition(13); format.setAlignment(Qt::AlignRight); cursor.setBlockFormat(format); m_layout->layout(); QTextLayout *blockLayout = m_block.layout(); QCOMPARE(blockLayout->lineAt(0).x(), 100.0); QTextBlock block = m_doc->begin().next(); QVERIFY(block.isValid()); blockLayout = block.layout(); QRectF rect = blockLayout->lineAt(0).naturalTextRect(); QVERIFY(rect.x() > 60); QCOMPARE(rect.x() + rect.width() + (200 - rect.right()), 200.0); block = block.next(); QVERIFY(block.isValid()); blockLayout = block.layout(); rect = blockLayout->lineAt(0).naturalTextRect(); QVERIFY(rect.x() > 150); QVERIFY(rect.right() >= 200.0 + 100.0); } void TestBlockLayout::testTextAlignments() { // TODO justified & justified, last line setupTest("Left\nRight\nﺵﻻﺆﻴﺜﺒ\nﺵﻻﺆﻴﺜﺒ\nLast Line."); KoParagraphStyle start; start.setFontPointSize(12.0); start.setAlignment(Qt::AlignLeading); KoParagraphStyle end; end.setFontPointSize(12.0); end.setAlignment(Qt::AlignTrailing); KoParagraphStyle startRTL; startRTL.setFontPointSize(12.0); startRTL.setAlignment(Qt::AlignLeading); startRTL.setTextProgressionDirection(KoText::RightLeftTopBottom); KoParagraphStyle endRTL; endRTL.setAlignment(Qt::AlignTrailing); endRTL.setTextProgressionDirection(KoText::RightLeftTopBottom); endRTL.setFontPointSize(12.0); QTextBlock block = m_doc->begin(); start.applyStyle(block); block = block.next(); end.applyStyle(block); block = block.next(); startRTL.applyStyle(block); block = block.next(); endRTL.applyStyle(block); block = block.next(); endRTL.applyStyle(block); m_layout->layout(); QTextLayout *blockLayout = m_block.layout(); // line 'Left' QRectF rect = blockLayout->lineAt(0).naturalTextRect(); QCOMPARE(rect.x(), 100.0); // line 'Right' block = m_doc->begin().next(); rect = block.layout()->lineAt(0).naturalTextRect(); QVERIFY(rect.right() - 200 <= (1 + 100.0)); QVERIFY(rect.left() > 100.0); // line with align Leading and RTL progression block = block.next(); rect = block.layout()->lineAt(0).naturalTextRect(); QVERIFY(rect.right() - 200 <= (1 + 100.0)); QVERIFY(rect.left() > 100.0); // expect right alignment // line with align tailing and RTL progression block = block.next(); rect = block.layout()->lineAt(0).naturalTextRect(); QCOMPARE(rect.x(), 100.0); // expect left alignment // non RTL _text_ but RTL progression as well as align trailing block = block.next(); rect = block.layout()->lineAt(0).naturalTextRect(); QCOMPARE(rect.x(), 100.0); // expect left alignment // TODO can we check if the dot is the left most painted char? } void TestBlockLayout::testParagraphBorders() { setupTest("Paragraph with Borders\nAnother parag\n"); QTextCursor cursor(m_doc->begin()); QTextBlockFormat bf = cursor.blockFormat(); bf.setProperty(KoParagraphStyle::LeftBorderStyle, KoBorder::BorderSolid); bf.setProperty(KoParagraphStyle::TopBorderStyle, KoBorder::BorderSolid); bf.setProperty(KoParagraphStyle::BottomBorderStyle, KoBorder::BorderSolid); bf.setProperty(KoParagraphStyle::RightBorderStyle, KoBorder::BorderSolid); bf.setProperty(KoParagraphStyle::LeftBorderWidth, 8.0); bf.setProperty(KoParagraphStyle::TopBorderWidth, 9.0); bf.setProperty(KoParagraphStyle::BottomBorderWidth, 10.0); bf.setProperty(KoParagraphStyle::RightBorderWidth, 11.0); cursor.setBlockFormat(bf); m_layout->layout(); QTextBlock block = m_doc->begin(); QTextLayout *blockLayout = block.layout(); QCOMPARE(blockLayout->lineAt(0).x(), 8.0 + 100.0); QCOMPARE(blockLayout->lineAt(0).y(), 9.0 + 100.0); QCOMPARE(blockLayout->lineAt(0).width(), 200.0 - 8.0 - 11.0); block = block.next(); blockLayout = block.layout(); //warnTextLayout << "blockLayout->lineAt(0).y() "<lineAt(0).y(); QVERIFY(qAbs(blockLayout->lineAt(0).y() - (9.0 + 14.4 + 10.0 + 100.0)) < ROUNDING); // 14.4 is 12 pt font + 20% linespacing // borders + padding create the total inset. bf.setProperty(KoParagraphStyle::LeftPadding, 5.0); bf.setProperty(KoParagraphStyle::RightPadding, 5.0); bf.setProperty(KoParagraphStyle::TopPadding, 5.0); bf.setProperty(KoParagraphStyle::BottomPadding, 5.0); cursor.setBlockFormat(bf); m_layout->layout(); block = m_doc->begin(); blockLayout = block.layout(); QCOMPARE(blockLayout->lineAt(0).x(), 13.0 + 100.0); QCOMPARE(blockLayout->lineAt(0).y(), 14.0 + 100.0); QCOMPARE(blockLayout->lineAt(0).width(), 200.0 - 8.0 - 11.0 - 5.0 * 2); block = block.next(); blockLayout = block.layout(); //qDebug() << blockLayout->lineAt(0).y() << (9.0 + 14.4 + 10 + 5.0 * 2); QVERIFY(qAbs(blockLayout->lineAt(0).y() - (9.0 + 14.4 + 10 + 5.0 * 2 + 100.0)) < ROUNDING); // borders are positioned outside the padding, lets check that to be the case. block = m_doc->begin(); KoTextBlockData data(block); KoTextBlockBorderData *border = data.border(); QVERIFY(border); QCOMPARE(border->hasBorders(), true); /* QRectF borderOutline = border->rect(); QCOMPARE(borderOutline.top(), 0.); QCOMPARE(borderOutline.left(), 0.); QCOMPARE(borderOutline.right(), 200.); */ // qreal borders. Specify an additional width for each side. bf.setProperty(KoParagraphStyle::LeftBorderStyle, KoBorder::BorderDouble); bf.setProperty(KoParagraphStyle::TopBorderStyle, KoBorder::BorderDouble); bf.setProperty(KoParagraphStyle::BottomBorderStyle, KoBorder::BorderDouble); bf.setProperty(KoParagraphStyle::RightBorderStyle, KoBorder::BorderDouble); bf.setProperty(KoParagraphStyle::LeftInnerBorderWidth, 2.0); bf.setProperty(KoParagraphStyle::RightInnerBorderWidth, 2.0); bf.setProperty(KoParagraphStyle::BottomInnerBorderWidth, 2.0); bf.setProperty(KoParagraphStyle::TopInnerBorderWidth, 2.0); cursor.setBlockFormat(bf); m_layout->layout(); block = m_doc->begin(); blockLayout = block.layout(); QCOMPARE(blockLayout->lineAt(0).x(), 15.0 + 100.0); QCOMPARE(blockLayout->lineAt(0).y(), 16.0 + 100.0); QCOMPARE(blockLayout->lineAt(0).width(), 200.0 - 8.0 - 11.0 - (5.0 + 2.0) * 2); block = block.next(); blockLayout = block.layout(); //qDebug() << blockLayout->lineAt(0).y(); QVERIFY(qAbs(blockLayout->lineAt(0).y() - (9.0 + 14.4 + 10 + (5.0 + 2.0) * 2 + 100.0)) < ROUNDING); // and last, make the 2 qreal border have a blank space in the middle. bf.setProperty(KoParagraphStyle::LeftBorderSpacing, 3.0); bf.setProperty(KoParagraphStyle::RightBorderSpacing, 3.0); bf.setProperty(KoParagraphStyle::BottomBorderSpacing, 3.0); bf.setProperty(KoParagraphStyle::TopBorderSpacing, 3.0); cursor.setBlockFormat(bf); m_layout->layout(); block = m_doc->begin(); blockLayout = block.layout(); QCOMPARE(blockLayout->lineAt(0).x(), 18.0 + 100.0); QCOMPARE(blockLayout->lineAt(0).y(), 19.0 + 100.0); QCOMPARE(blockLayout->lineAt(0).width(), 200.0 - 8.0 - 11.0 - (5.0 + 2.0 + 3.0) * 2); block = block.next(); blockLayout = block.layout(); //qDebug() << blockLayout->lineAt(0).y(); QVERIFY(qAbs(blockLayout->lineAt(0).y() - (9.0 + 14.4 + 10 + (5.0 + 2.0 + 3.0) * 2 + 100.0)) < ROUNDING); } void TestBlockLayout::testParagraphMargins() { setupTest("Emtpy\nParagraph\nAnother parag\n"); KoParagraphStyle style; style.setFontPointSize(12.0); m_styleManager->add(&style); style.setTopMargin(QTextLength(QTextLength::FixedLength, 10)); KoListStyle listStyle; KoListLevelProperties llp = listStyle.levelProperties(1); llp.setLabelType(KoListStyle::NumberLabelType); llp.setNumberFormat(KoOdfNumberDefinition::Numeric); listStyle.setLevelProperties(llp); style.setListStyle(&listStyle); style.setLeftBorderWidth(3); QTextBlock block = m_doc->begin().next(); style.applyStyle(block); block = block.next(); style.applyStyle(block); m_layout->layout(); block = m_doc->begin().next(); KoTextBlockData data(block); KoTextBlockBorderData *border = data.border(); QVERIFY(border); QCOMPARE(data.counterPosition(), QPointF(3 + 100.0, 24.4 + 100.0)); block = block.next(); KoTextBlockData data2(block); QCOMPARE(data2.counterPosition(), QPointF(3 + 100.0, 48.8 + 100.0)); style.setBottomMargin(QTextLength(QTextLength::FixedLength, 5)); //bottom spacing // manually reapply and relayout to force immediate reaction. block = m_doc->begin().next(); style.applyStyle(block); block = block.next(); style.applyStyle(block); m_layout->layout(); block = m_doc->begin().next(); border = data2.border(); QVERIFY(border); KoTextBlockData data3(block); QCOMPARE(data3.counterPosition(), QPointF(3 + 100.0, 24.4 + 100.0)); block = block.next(); KoTextBlockData data4(block); QCOMPARE(data4.counterPosition(), QPointF(3 + 100.0, 48.8 + 100.0)); // same y as before as we take max spacing } void TestBlockLayout::testEmptyParag() { setupTest("Foo\n\nBar\n"); m_layout->layout(); QTextBlock block = m_doc->begin(); QTextLayout *lay = block.layout(); QVERIFY(lay); QCOMPARE(lay->lineCount(), 1); const qreal y = lay->lineAt(0).position().y(); block = block.next(); lay = block.layout(); QVERIFY(lay); QCOMPARE(lay->lineCount(), 1); QVERIFY(lay->lineAt(0).position().y() > y); QVERIFY(qAbs(lay->lineAt(0).position().y() - (14.4 + 100.0)) < ROUNDING); } -void TestBlockLayout::testDropCaps() +void TestBlockLayout::testDropCapsLongText() { - setupTest(QString("Lorem ipsum dolor sit amet, XgXgectetuer adiXiscing elit, sed diam\nsome more text")); // some not too long text so the dropcap will be bigger than the block + // This text should be so long that it is split into more lines + // than the number of dropcap lines. + // This is the normal use case, with a shorter line dropcaps doesn't work well + setupTest(m_loremIpsum); + + KoParagraphStyle style; + style.setFontPointSize(12.0); + style.setDropCaps(false); + style.setDropCapsLength(1); + style.setDropCapsLines(4); + style.setDropCapsDistance(9.0); + QTextBlock block = m_doc->begin(); + style.applyStyle(block); + + qInfo()<<"Font:"<layout(); + + // dummy version, caps is still false. + QTextLayout *blockLayout = block.layout(); + QVERIFY(blockLayout->lineCount() > 4); + QTextLine line = blockLayout->lineAt(0); + QVERIFY(line.textLength() > 1); + + qInfo()<<"Dropcaps on"; + style.setDropCaps(true); + style.applyStyle(block); + m_layout->layout(); + + // test that the first text line is the dropcaps and the positions are right. + QVERIFY(blockLayout->lineCount() > 4); + line = blockLayout->lineAt(0); + QCOMPARE(line.textLength(), 1); + + QCOMPARE(line.x(), 100.0); + QVERIFY(line.y() <= 100.0); // can't get a tight-boundingrect here. + + qInfo()<<"dropcaps:"<lineAt(1); + QVERIFY(line.textLength() > 1); + qreal heightNormalLine = line.height(); + QCOMPARE(line.y(), 100.0); // aligned top + QCOMPARE(line.x(), dropCapsRight + style.dropCapsDistance()); + + line = blockLayout->lineAt(1); + QVERIFY(line.textLength() > 2); + QCOMPARE(line.y(), 100.0); // aligned top + QCOMPARE(line.x(), dropCapsRight + style.dropCapsDistance()); + + line = blockLayout->lineAt(3); + QVERIFY(line.textLength() > 0); + QCOMPARE(line.x(), dropCapsRight + style.dropCapsDistance()); + + line = blockLayout->lineAt(4); + QVERIFY(line.textLength() > 0); + QCOMPARE(line.x(), dropCapsRight + style.dropCapsDistance()); + + // This should be below the dropcap and thus not indented + line = blockLayout->lineAt(5); + QVERIFY(line.textLength() > 0); + QCOMPARE(line.x(), 100.0); // aligned left + qInfo()<<"dropCapsBottom"<= dropCapsBottom); + + qInfo()<<"Dropcaps off"; + style.setDropCaps(false); // remove it + style.applyStyle(block); + m_layout->layout(); + blockLayout = block.layout(); + + // test that the first text line is no longer dropcaps + QVERIFY(blockLayout->lineCount() > 1); + line = blockLayout->lineAt(0); + QVERIFY(line.textLength() > 1); + QCOMPARE(line.height(), heightNormalLine); +} + +void TestBlockLayout::testDropCapsShortText() +{ + setupTest(QString("Lorem ipsum")); // short enough to only get one line KoParagraphStyle style; style.setFontPointSize(12.0); style.setDropCaps(false); style.setDropCapsLength(1); style.setDropCapsLines(4); style.setDropCapsDistance(9.0); QTextBlock block = m_doc->begin(); - QTextBlock secondblock = block.next(); style.applyStyle(block); qInfo()<<"Font:"<layout(); // dummy version, caps is still false. - QTextLayout *blockLayout =block.layout(); - QVERIFY(blockLayout->lineCount() > 2); + QTextLayout *blockLayout = block.layout(); + int lineCount = blockLayout->lineCount(); + + QVERIFY(lineCount > 0); + QTextLine line = blockLayout->lineAt(0); + QVERIFY(line.textLength() > 1); + + qInfo()<<"Dropcaps on"; + style.setDropCaps(true); + style.applyStyle(block); + m_layout->layout(); + + // test that the first text line is the dropcaps and the positions are right. + QCOMPARE(blockLayout->lineCount(), lineCount + 1); + line = blockLayout->lineAt(0); + QCOMPARE(line.textLength(), 1); + + QCOMPARE(line.x(), 100.0); + QVERIFY(line.y() <= 100.0); // can't get a tight-boundingrect here. + + qreal dropCapsRight = line.rect().right(); + + line = blockLayout->lineAt(1); + QVERIFY(line.textLength() > 1); + qreal heightNormalLine = line.height(); + QCOMPARE(line.y(), 100.0); // aligned top + QCOMPARE(line.x(), dropCapsRight + style.dropCapsDistance()); + + qInfo()<<"Dropcaps off"; + style.setDropCaps(false); // remove it + style.applyStyle(block); + m_layout->layout(); + blockLayout = block.layout(); + + // test that the first text line is no longer dropcaps + QCOMPARE(blockLayout->lineCount(), lineCount); + line = blockLayout->lineAt(0); + QVERIFY(line.textLength() > 1); + QCOMPARE(line.height(), heightNormalLine); +} + +void TestBlockLayout::testDropCapsWithNewline() +{ + // Some not too long text so the dropcap will be bigger than the block. + // The text after newline will be in a separate block, but + // shall also be indeneted by the dropcap. + // Note that we cannot be certain of the number of lines we will get + // as it depends on the actual available font. + setupTest(QString("Lorem ipsum dolor sit amet, XgXgectetuer adiXiscing elit, sed diam\nsome more text")); + + KoParagraphStyle style; + style.setFontPointSize(12.0); + style.setDropCaps(false); + style.setDropCapsLength(1); + style.setDropCapsLines(4); + style.setDropCapsDistance(9.0); + QTextBlock block = m_doc->begin(); + QTextBlock secondblock = block.next(); // "some more text" + style.applyStyle(block); + + qInfo()<<"Font:"<layout(); + + // dummy version, caps is still false. + QTextLayout *blockLayout = block.layout(); + int lineCount = blockLayout->lineCount(); + // lineCount must be >= 1 and <= 3 for this test to work + if (lineCount == 0 || lineCount >= 3) { + qWarning()<<"The text was not layouted in the required number of lines, so this test will fail"; + } + QVERIFY(blockLayout->lineCount() >= 1); + QVERIFY(blockLayout->lineCount() <= 3); QTextLine line = blockLayout->lineAt(0); - QVERIFY(line.textLength() > 3); + QVERIFY(line.textLength() > 1); qInfo()<<"Dropcaps on"; style.setDropCaps(true); style.applyStyle(block); m_layout->layout(); // test that the first text line is the dropcaps and the positions are right. - QVERIFY(blockLayout->lineCount() > 2); + QVERIFY(blockLayout->lineCount() > 1); line = blockLayout->lineAt(0); QCOMPARE(line.textLength(), 1); - QCOMPARE(line.position().x(), 100.0); - QVERIFY(line.position().y() <= 100.0); // can't get a tight-boundingrect here. + QCOMPARE(line.x(), 100.0); + QVERIFY(line.y() <= 100.0); // can't get a tight-boundingrect here. + + qreal dropCapsRight = line.rect().right(); line = blockLayout->lineAt(1); QVERIFY(line.textLength() > 2); qreal heightNormalLine = line.height(); - qreal linexpos = line.position().x(); - QCOMPARE(line.position().y(), 100.0); // aligned top - //qDebug()< 149.0); // can't get a tight-boundingrect here. - QVERIFY(line.position().x() < 154.0); // can't get a tight-boundingrect here. - - // Now test that a following block is moved inward by the same about since - // it should still be influenced by the dropcap + QCOMPARE(line.y(), 100.0); // aligned top + QCOMPARE(line.x(), dropCapsRight + style.dropCapsDistance()); + + // Now test that a following block is indented by the same amount + // since it also should be influenced by the dropcap blockLayout = secondblock.layout(); - QVERIFY(blockLayout->lineCount() == 1); + QVERIFY(blockLayout->lineCount() >= 1); line = blockLayout->lineAt(0); - QVERIFY(line.textLength() > 3); - QCOMPARE(line.position().x(), linexpos); - QVERIFY(line.position().x() > 149.0); // can't get a tight-boundingrect here. - QVERIFY(line.position().x() < 154.0); // can't get a tight-boundingrect here. + QCOMPARE(line.x(), dropCapsRight + style.dropCapsDistance()); qInfo()<<"Dropcaps off"; style.setDropCaps(false); // remove it style.applyStyle(block); m_layout->layout(); blockLayout = block.layout(); // test that the first text line is no longer dropcaps - QVERIFY(blockLayout->lineCount() > 2); + QCOMPARE(blockLayout->lineCount(), lineCount); line = blockLayout->lineAt(0); QVERIFY(line.textLength() > 1); QCOMPARE(line.height(), heightNormalLine); } - QTEST_MAIN(TestBlockLayout) diff --git a/libs/textlayout/tests/TestBlockLayout.h b/libs/textlayout/tests/TestBlockLayout.h index 79505e1f8f5..2148ba0423b 100644 --- a/libs/textlayout/tests/TestBlockLayout.h +++ b/libs/textlayout/tests/TestBlockLayout.h @@ -1,101 +1,103 @@ /* * This file is part of Calligra tests * * Copyright (C) 2006-2010 Thomas Zander * * 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 TESTBLOCKLAYOUT_H #define TESTBLOCKLAYOUT_H #include #include #include class KoStyleManager; class QTextDocument; #define ROUNDING 0.126 class TestBlockLayout : public QObject { Q_OBJECT public: TestBlockLayout() {} /// FIXME: fix these broken tests /// Test tabs. void testTabs_data(); void testTabs(); private Q_SLOTS: void initTestCase(); - void testDropCaps(); - /// make sure empty paragraphs are initialized properly void testEmptyParag(); /// Test breaking lines based on the width of the reference rect. void testLineBreaking(); /// Tests incrementing Y pos based on the font size void testBasicLineSpacing(); /// Tests incrementing Y pos based on the font size void testBasicLineSpacing2(); /// Tests fixed linespacing. void testFixedLineSpacing(); /// Tests percentage linespacing. void testPercentageLineSpacing(); /// Tests advanced linespacing options provided in our style. void testAdvancedLineSpacing(); /// Tests that empty lines are given the correct height like in LibreOffice void testEmptyLineHeights(); /// Test distance above and below paragraphs. void testBlockSpacing(); /// Test left and right margins of paragraphs. void testLeftRightMargins(); /// Test first line indent of paragraphs. void testTextIndent(); void testBasicTextAlignments(); void testTextAlignments(); // relativeBulletSize //etc //void testParagOffset(); void testParagraphBorders(); void testParagraphMargins(); + void testDropCapsLongText(); + void testDropCapsShortText(); + void testDropCapsWithNewline(); + private: void setupTest(const QString &initText = QString()); private: QTextDocument *m_doc; KoTextDocumentLayout *m_layout; QTextBlock m_block; QString m_loremIpsum; KoStyleManager *m_styleManager; KoTextLayoutRootArea *m_area; }; #endif diff --git a/libs/widgets/CMakeLists.txt b/libs/widgets/CMakeLists.txt index e7734477f2b..7ae974b63ac 100644 --- a/libs/widgets/CMakeLists.txt +++ b/libs/widgets/CMakeLists.txt @@ -1,189 +1,191 @@ -add_subdirectory( tests ) +if(BUILD_TESTING) + add_subdirectory( tests ) +endif() add_subdirectory( pics ) include_directories(${KOTEXT_INCLUDES} ${KOODF_INCLUDES} ${PIGMENT_INCLUDES}) include_directories(${CMAKE_SOURCE_DIR}/libs/widgetutils) include_directories(${CMAKE_CURRENT_BINARY_DIR}) if (LIBATTICA_FOUND) include_directories(${LIBATTICA_INCLUDE_DIR}) endif () set(kowidgets_LIB_SRCS KoGradientEditWidget.cpp KoResourcePaths.cpp KoVBox.cpp KoDialog.cpp KoGlobal.cpp KoZoomWidget.cpp KoTagToolButton.cpp KoTagChooserWidget.cpp KoTagFilterWidget.cpp KoResourceTaggingManager.cpp KoResourceItemChooserContextMenu.cpp KoAspectButton.cpp KoCsvImportDialog.cpp KoPageLayoutDialog.cpp KoPageLayoutWidget.cpp KoPagePreviewWidget.cpp KoPositionSelector.cpp KoSliderCombo.cpp KoColorPopupButton.cpp KoConfigAuthorPage.cpp KoUnitDoubleSpinBox.cpp KoZoomAction.cpp KoZoomController.cpp KoZoomInput.cpp KoZoomHandler.cpp KoZoomMode.cpp KoDpi.cpp KoGlobal.cpp KoColorPatch.cpp KoColorPopupAction.cpp KoColorSetWidget.cpp KoColorSlider.cpp KoDualColorButton.cpp KoEditColorSetDialog.cpp KoTriangleColorSelector.cpp KoResourcePopupAction.cpp KoStrokeConfigWidget.cpp KoFillConfigWidget.cpp KoShadowConfigWidget.cpp KoIconToolTip.cpp KoResourceItemChooser.cpp KoResourceItemChooserSync.cpp KoResourceSelector.cpp KoResourceModel.cpp KoResourceItemDelegate.cpp KoResourceItemView.cpp KoResourceTagStore.cpp KoRuler.cpp KoRulerController.cpp KoItemToolTip.cpp KoCheckerBoardPainter.cpp KoResourceServerAdapter.cpp KoResourceServerProvider.cpp KoLineStyleSelector.cpp KoLineStyleItemDelegate.cpp KoLineStyleModel.cpp KoMarkerModel.cpp KoMarkerItemDelegate.cpp KoMarkerSelector.cpp KoDockWidgetTitleBar.cpp KoDockWidgetTitleBarButton.cpp KoViewItemContextBar.cpp KoContextBarButton.cpp KoResourceFiltering.cpp KoResourceModelBase.cpp KoToolBoxButton.cpp KoToolBox.cpp KoToolBoxDocker.cpp KoToolBoxFactory.cpp KoToolBoxLayout_p.cpp KoToolDocker.cpp KoModeBox.cpp KoModeBoxDocker.cpp KoModeBoxFactory.cpp KoDocumentInfoDlg.cpp KoDocumentInfoPropsPage.cpp KoGlobal.cpp KoTableView.cpp WidgetsDebug.cpp ) ki18n_wrap_ui( kowidgets_LIB_SRCS KoConfigAuthorPage.ui KoCsvImportDialog.ui koDocumentInfoAboutWidget.ui koDocumentInfoAuthorWidget.ui KoEditColorSet.ui KoPageLayoutWidget.ui KoShadowConfigWidget.ui ) add_library(kowidgets SHARED ${kowidgets_LIB_SRCS}) generate_export_header(kowidgets BASE_NAME kowidgets) target_link_libraries(kowidgets PUBLIC kotext pigmentcms kowidgetutils KF5::KIOWidgets PRIVATE KF5::GuiAddons KF5::WidgetsAddons KF5::ConfigCore KF5::Codecs KF5::Completion KF5::IconThemes ) if(GHNS) target_link_libraries(kowidgets PRIVATE KF5::NewStuff) endif () if(X11_FOUND) target_link_libraries(kowidgets PRIVATE Qt5::X11Extras ${X11_LIBRARIES}) endif() set_target_properties(kowidgets PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) install(TARGETS kowidgets ${INSTALL_TARGETS_DEFAULT_ARGS}) if (SHOULD_BUILD_DEVEL_HEADERS) install( FILES KoGlobal.h KoResourceItemChooserContextMenu.h KoGenericRegistryModel.h KoPageLayoutDialog.h KoPageLayoutWidget.h KoPagePreviewWidget.h KoPositionSelector.h ${CMAKE_CURRENT_BINARY_DIR}/kowidgets_export.h KoZoomAction.h KoZoomController.h KoZoomInput.h KoDpi.h KoZoomHandler.h KoZoomMode.h KoGlobal.h KoColorPatch.h KoStrokeConfigWidget.h KoFillConfigWidget.h KoShadowConfigWidget.h KoColorPopupAction.h KoColorSetWidget.h KoColorSlider.h KoDualColorButton.h KoEditColorSetDialog.h KoTriangleColorSelector.h KoResourceItemChooser.h KoResourceSelector.h KoResourceServer.h KoResourceServerAdapter.h KoResourceServerObserver.h KoResourceServerProvider.h KoResourceTagStore.h KoLineStyleSelector.h KoDockWidgetTitleBar.h KoDockWidgetTitleBarButton.h KoResourceModelBase.h KoGlobal.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligra COMPONENT Devel) endif() diff --git a/libs/widgetutils/CMakeLists.txt b/libs/widgetutils/CMakeLists.txt index 146cff004d4..7edcad67692 100644 --- a/libs/widgetutils/CMakeLists.txt +++ b/libs/widgetutils/CMakeLists.txt @@ -1,49 +1,51 @@ -add_subdirectory(tests) +if(BUILD_TESTING) + add_subdirectory(tests) +endif() set(kowidgetutils_LIB_SRCS KoGroupButton.cpp KoProgressBar.cpp KoProgressUpdater.cpp KoUpdater.cpp KoUpdaterPrivate_p.cpp KoProperties.cpp KoFileDialog.cpp ) add_library(kowidgetutils SHARED ${kowidgetutils_LIB_SRCS}) generate_export_header(kowidgetutils BASE_NAME kowidgetutils) target_link_libraries(kowidgetutils PUBLIC Qt5::Widgets Qt5::Gui Qt5::Xml Qt5::Core PRIVATE KF5::I18n KF5::ConfigCore ) set_target_properties(kowidgetutils PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) install(TARGETS kowidgetutils ${INSTALL_TARGETS_DEFAULT_ARGS}) if (SHOULD_BUILD_DEVEL_HEADERS) install(FILES KoGroupButton.h KoProgressBar.h KoProgressProxy.h KoProgressUpdater.h KoUpdater.h KoProperties.h KoFileDialog.h ${CMAKE_CURRENT_BINARY_DIR}/kowidgetutils_export.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligra COMPONENT Devel ) endif() diff --git a/plan/CMakeLists.txt b/plan/CMakeLists.txt index 64d3d239bce..3138a8c90df 100644 --- a/plan/CMakeLists.txt +++ b/plan/CMakeLists.txt @@ -1,189 +1,191 @@ project(kplato) # set kplato debug area add_definitions( -DKDE_DEFAULT_DEBUG_AREA=42000 ) if(KF5Holidays_FOUND) add_definitions(-DHAVE_KHOLIDAYS) endif() if (KF5AkonadiContact_FOUND) # disable for now: there is a bug # it only works if you use kde contacts (of course) but many use other stuff, so gets dissapointed # add_definitions(-DPLAN_KDEPIMLIBS_FOUND) message(WARNING "AkonadiContacs available, but funtion is disabled due to Bug 311940") endif () #### Disable kreport, reconsidder when stable #### # if (KReport_FOUND AND KReport_VERSION_MAJOR EQUAL 3 AND KReport_VERSION_MINOR EQUAL 0 AND KReport_VERSION_PATCH LESS 80) # message(STATUS "Acceptable KReport version: ${KReport_VERSION}") # add_definitions(-DPLAN_USE_KREPORT) # set(PLAN_BUILD_REPORTS true) # else() # message(WARNING "Unacceptable KReport version: ${KReport_VERSION}") # endif() if (PLANCHARTDEBUG) add_definitions(-DPLAN_CHART_DEBUG) endif () set( KPLATO_INCLUDES ${CMAKE_SOURCE_DIR}/plan/libs/kernel ${CMAKE_BINARY_DIR}/plan/libs/kernel ${CMAKE_SOURCE_DIR}/plan/libs/models ${CMAKE_BINARY_DIR}/plan/libs/models ${CMAKE_SOURCE_DIR}/plan/libs/ui ${CMAKE_BINARY_DIR}/plan/libs/ui ${CMAKE_BINARY_DIR}/plan ${KOMAIN_INCLUDES} ) add_subdirectory( libs ) add_subdirectory( templates ) add_subdirectory( pics ) add_subdirectory( toolbar ) add_subdirectory( plugins ) -add_subdirectory( tests ) +if(BUILD_TESTING) + add_subdirectory( tests ) +endif() add_subdirectory( workpackage ) include_directories(${KPLATO_INCLUDES}) add_definitions(-DTRANSLATION_DOMAIN=\"calligraplan\") ########### KPlato private library ############### set(planprivate_LIB_SRCS kptviewlistdocker.cpp kptviewlist.cpp kptviewlistdialog.cpp kptschedulesdocker.cpp kptconfig.cpp ConfigWorkVacationPanel.cpp ConfigProjectPanel.cpp kpttaskdefaultpanel.cpp kptworkpackageconfigpanel.cpp kptcolorsconfigpanel.cpp kptcontext.cpp kptfactory.cpp kptpart.cpp kptmaindocument.cpp kptview.cpp # KPtViewAdaptor.cpp kptprintingcontrolprivate.cpp kptschedulerpluginloader.cpp kptbuiltinschedulerplugin.cpp kptconfigskeleton.cpp kptinsertfiledlg.cpp about/aboutpage.cpp KPlatoXmlLoader.cpp ) ki18n_wrap_ui(planprivate_LIB_SRCS kptviewlistaddview.ui kptviewlisteditview.ui kptviewlisteditcategory.ui ConfigWorkVacationPanel.ui ConfigProjectPanel.ui kptconfigtaskpanelbase.ui kptworkpackageconfigpanel.ui kptcolorsconfigpanel.ui kptinsertfilepanel.ui ) kconfig_add_kcfg_files(plansettings_SRCS calligraplansettings.kcfgc) add_library(planprivate SHARED ${planprivate_LIB_SRCS} ${plansettings_SRCS} ) generate_export_header(planprivate BASE_NAME kplato) target_link_libraries(planprivate PUBLIC kplatokernel kplatomodels kplatoui komain PRIVATE koplugin KF5::IconThemes #KF5::KHtml ) if(KF5AkonadiContact_FOUND) target_link_libraries(planprivate PRIVATE KF5::AkonadiContact) endif() set_target_properties(planprivate PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) install(TARGETS planprivate ${INSTALL_TARGETS_DEFAULT_ARGS}) ########### KPlato part ############### set(planpart_PART_SRCS kptfactoryinit.cpp ) add_library(calligraplanpart MODULE ${planpart_PART_SRCS}) calligra_part_desktop_to_json(calligraplanpart planpart.desktop) target_link_libraries(calligraplanpart KF5::Parts planprivate) install(TARGETS calligraplanpart DESTINATION ${PLUGIN_INSTALL_DIR}/calligra/parts) ########### KPlato executable ############### if(NOT RELEASE_BUILD) add_definitions(-DMAINTANER_WANTED_SPLASH) endif() set(calligraplan_KDEINIT_SRCS main.cpp ) file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/pics/*-apps-calligraplan.png") ecm_add_app_icon(calligraplan_KDEINIT_SRCS ICONS ${ICONS_SRCS}) kf5_add_kdeinit_executable( calligraplan ${calligraplan_KDEINIT_SRCS}) if (APPLE) set_target_properties(calligraplan PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.template) set_target_properties(calligraplan PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.calligra.plan") set_target_properties(calligraplan PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Plan") install( FILES ${CMAKE_CURRENT_BINARY_DIR}/calligraplan_KDEINIT_SRCS.icns DESTINATION ${BUNDLE_INSTALL_DIR}/calligraplan.app/Contents/Resources) endif () target_link_libraries(kdeinit_calligraplan komain) install(TARGETS kdeinit_calligraplan ${INSTALL_TARGETS_DEFAULT_ARGS}) target_link_libraries(calligraplan kdeinit_calligraplan komain) install(TARGETS calligraplan ${INSTALL_TARGETS_DEFAULT_ARGS}) ########### install files ############### install( FILES calligraplan.rc calligraplan_readonly.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/calligraplan) install( PROGRAMS org.kde.calligraplan.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) install( FILES calligraplanrc DESTINATION ${CONFIG_INSTALL_DIR}) install(FILES calligraplansettings.kcfg DESTINATION ${KCFG_INSTALL_DIR}) install(FILES org.kde.calligraplan.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) # TODO: with the new embedded JSON data for plugins there is no schema ATM to define extended properties # plan_viewplugin.desktop install(FILES about/top-left-plan.png about/main.html about/intro.html about/tips.html about/tutorial.html about/plan.css DESTINATION ${DATA_INSTALL_DIR}/calligraplan/about ) diff --git a/plan/ConfigWorkVacationPanel.ui b/plan/ConfigWorkVacationPanel.ui index 9f1e8ff3ebd..f73a509e764 100644 --- a/plan/ConfigWorkVacationPanel.ui +++ b/plan/ConfigWorkVacationPanel.ui @@ -1,426 +1,438 @@ Dag Andersen KPlato::ConfigWorkVacationPanel 0 0 - 590 + 624 362 - - These values are used if your estimate is not in hours. -Let us say Hours per day is 8. If you estimate that a task needs 3 days effort to complete, this is converted to 24 hours when the task is scheduled. The actual time it will take to complete is of course dependent on the availability of the person (or persons) that is assigned to the task. - 0 - + - Estimate Conversions + Working Hours - + - + + + <html><head/><body><p>Values used for generating default calendars for working weeks and holidays.</p><p>The <span style=" font-style:italic;">Week</span> calendar uses the values defined in <span style=" font-style:italic;">Working week</span>.</p><p>The <span style=" font-style:italic;">Week</span> calendar can be <span style=" font-style:italic;">Always</span> generated or only generated <span style=" font-style:italic;">If none exists</span>. The latter choice is the most common as you often have calendars defined as part of templates or shared resources.</p><p><span style=" font-style:italic;">Holidays</span> can be generated for your region (default) or any region you choose. The holidays can be added into the week calendar, as sub-calendar to the week calendar or as a separate calendar.</p><p>If a week calendar is not generated, the holidays are put into a separate calendar.</p></body></html> + - + Calendar generation - + - + - Ho&urs per year: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - false - - - kcfg_HoursPrYear + Week - - - 1.000000000000000 - - - 8784.000000000000000 - - - 1760.000000000000000 - - - - - - - Hours per &month: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - false - - - kcfg_HoursPrMonth - - - - - - - 1.000000000000000 - - - 744.000000000000000 - - - 1.000000000000000 - - - 176.000000000000000 - + + + + If none exists + + + + + Always + + - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Hours per wee&k: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - false - - - kcfg_HoursPrWeek - + + + + + 0 + + + 0 + + + 0 + + + + + + + + Holidays + + + + + + + + In week calendar + + + + + As sub-calendar + + + + + As separate calendar + + + + + + + + Holida&y region: + + + region + + + + + + + - - - 1.000000000000000 - - - 168.000000000000000 - - - 40.000000000000000 - - - - - - - These values are used when you estimate the effort needed to complete a task. - - - Hours per da&y: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - false - - - kcfg_HoursPrDay - - - - - - - 1.000000000000000 - - - 24.000000000000000 - - - 8.000000000000000 - - - - - + Qt::Vertical - 20 - 40 + 119 + 106 - - - - - Working Hours - - - - - - Week - - - - - - - - Always - - - - - If none exists - - - - - + Working week Qt::Horizontal 40 20 Tick if working day Tuesday: Tick if working day Wednesday: Tick if working day Thursday: Tick if working day Friday: Tick if working day Saturday: Tick if working day Sunday: Tick if working day Monday: - - - - - 0 - - - 0 - - - 0 - + + + + + Estimate Conversions + + + + + + These values are used if your estimate is not in hours. +Let us say Hours per day is 8. If you estimate that a task needs 3 days effort to complete, this is converted to 24 hours when the task is scheduled. The actual time it will take to complete is of course dependent on the availability of the person (or persons) that is assigned to the task. + + + + + - + - Holidays + Hours &per year: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + kcfg_HoursPrYear - - - - In week calendar - - - - - As sub-calendar - - - - - As separate calendar - - + + + 1.000000000000000 + + + 8784.000000000000000 + + + 1760.000000000000000 + - + - Holida&y region: + Hours per &month: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false - region + kcfg_HoursPrMonth - + + + 1.000000000000000 + + + 744.000000000000000 + + + 1.000000000000000 + + + 176.000000000000000 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Hours per wee&k: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + kcfg_HoursPrWeek + + - + + + 1.000000000000000 + + + 168.000000000000000 + + + 40.000000000000000 + + + + + + + These values are used when you estimate the effort needed to complete a task. + + + Hours per &day: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + false + + + kcfg_HoursPrDay + + + + + + + 1.000000000000000 + + + 24.000000000000000 + + + 8.000000000000000 + + + + + + + Qt::Vertical + + + + 20 + 40 + + + - - - - Qt::Vertical - - - - 20 - 148 - - - - diff --git a/plan/calligraplansettings.kcfg b/plan/calligraplansettings.kcfg index d670da8c271..fbd80442e4b 100644 --- a/plan/calligraplansettings.kcfg +++ b/plan/calligraplansettings.kcfg @@ -1,352 +1,352 @@ 1760 176 40 8 true - - - + + + NoneExists true InWeekCalendar Default true 08:00 16:00 true 08:00 16:00 true 08:00 16:00 true 08:00 16:00 true 08:00 16:00 false 08:00 16:00 false 08:00 16:00 false AsSoonAsPossible CurrentdateTime CurrentdateTime Effort Hour 8.0 -75 100 Linear false #0000ff #0000ff #0000ff #0000ff #0000ff #00ff00 #ff0000 #A0A0A0 #ffff00 #0000ff #ff0000 #A0A0A0 #ffff00 EnumUnit::Hour EnumUnit::Month diff --git a/plan/kptview.cpp b/plan/kptview.cpp index 00738617bac..0b4f92f1a39 100644 --- a/plan/kptview.cpp +++ b/plan/kptview.cpp @@ -1,3222 +1,3224 @@ /* This file is part of the KDE project Copyright (C) 1998, 1999, 2000 Torben Weis Copyright (C) 2002 - 2011 Dag Andersen Copyright (C) 2012 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kptview.h" #include #include #include "KoDocumentInfo.h" #include "KoMainWindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kptlocale.h" #include "kptviewbase.h" #include "kptaccountsview.h" #include "kptaccountseditor.h" #include "kptcalendareditor.h" #include "kptfactory.h" #include "kptmilestoneprogressdialog.h" #include "kpttaskdescriptiondialog.h" #include "kptnode.h" #include "kptmaindocument.h" #include "kptproject.h" #include "kptmainprojectdialog.h" #include "kpttask.h" #include "kptsummarytaskdialog.h" #include "kpttaskdialog.h" #include "kpttaskprogressdialog.h" #include "kptganttview.h" #include "kpttaskeditor.h" #include "kptdependencyeditor.h" #include "kptperteditor.h" #include "kptdatetime.h" #include "kptcommand.h" #include "kptrelation.h" #include "kptrelationdialog.h" #include "kptresourceappointmentsview.h" #include "kptresourceeditor.h" #include "kptscheduleeditor.h" #include "kptresourcedialog.h" #include "kptresource.h" #include "kptstandardworktimedialog.h" #include "kptwbsdefinitiondialog.h" #include "kptresourceassignmentview.h" #include "kpttaskstatusview.h" #include "kptsplitterview.h" #include "kptpertresult.h" #include "ConfigProjectPanel.h" #include "ConfigWorkVacationPanel.h" #include "kpttaskdefaultpanel.h" #include "kptworkpackageconfigpanel.h" #include "kptcolorsconfigpanel.h" #include "kptinsertfiledlg.h" #include "kpthtmlview.h" #include "about/aboutpage.h" #include "kptlocaleconfigmoneydialog.h" #include "kptflatproxymodel.h" #include "kpttaskstatusmodel.h" #include "reportsgenerator/ReportsGeneratorView.h" #ifdef PLAN_USE_KREPORT #include "reports/reportview.h" #include "reports/reportdata.h" #endif #include "kptviewlistdialog.h" #include "kptviewlistdocker.h" #include "kptviewlist.h" #include "kptschedulesdocker.h" #include "kptpart.h" #include "kptdebug.h" #include "calligraplansettings.h" #include "kptprintingcontrolprivate.h" // #include "KPtViewAdaptor.h" #include namespace KPlato { //------------------------------- ConfigDialog::ConfigDialog(QWidget *parent, const QString& name, KConfigSkeleton *config ) : KConfigDialog( parent, name, config ), m_config( config ) { KConfigDialogManager::changedMap()->insert("KRichTextWidget", SIGNAL(textChanged()) ); } bool ConfigDialog::hasChanged() { QRegExp kcfg( "kcfg_*" ); foreach ( KRichTextWidget *w, findChildren( kcfg ) ) { KConfigSkeletonItem *item = m_config->findItem( w->objectName().mid(5) ); if ( ! item->isEqual( w->toHtml() ) ) { return true; } } return false; } void ConfigDialog::updateSettings() { bool changed = false; QRegExp kcfg( "kcfg_*" ); foreach ( KRichTextWidget *w, findChildren( kcfg ) ) { KConfigSkeletonItem *item = m_config->findItem( w->objectName().mid(5) ); if ( ! item ) { warnPlan << "The setting '" << w->objectName().mid(5) << "' has disappeared!"; continue; } if ( ! item->isEqual( QVariant( w->toHtml() ) ) ) { item->setProperty( QVariant( w->toHtml() ) ); changed = true; } } if ( changed ) { m_config->save(); } } void ConfigDialog::updateWidgets() { QRegExp kcfg( "kcfg_*" ); foreach ( KRichTextWidget *w, findChildren( kcfg ) ) { KConfigSkeletonItem *item = m_config->findItem( w->objectName().mid(5) ); if ( ! item ) { warnPlan << "The setting '" << w->objectName().mid(5) << "' has disappeared!"; continue; } if ( ! item->isEqual( QVariant( w->toHtml() ) ) ) { w->setHtml( item->property().toString() ); } } } void ConfigDialog::updateWidgetsDefault() { bool usedefault = m_config->useDefaults( true ); updateWidgets(); m_config->useDefaults( usedefault ); } bool ConfigDialog::isDefault() { bool bUseDefaults = m_config->useDefaults(true); bool result = !hasChanged(); m_config->useDefaults(bUseDefaults); return result; } //------------------------------------ View::View(KoPart *part, MainDocument *doc, QWidget *parent) : KoView(part, doc, parent), m_currentEstimateType( Estimate::Use_Expected ), m_scheduleActionGroup( new QActionGroup( this ) ), m_trigged( false ), m_nextScheduleManager( 0 ), m_readWrite( false ), m_defaultView(1), m_partpart (part) { //debugPlan; doc->registerView( this ); setComponentName(Factory::global().componentName(), Factory::global().componentDisplayName()); if ( !doc->isReadWrite() ) setXMLFile( "calligraplan_readonly.rc" ); else setXMLFile( "calligraplan.rc" ); // new ViewAdaptor( this ); m_sp = new QSplitter( this ); QVBoxLayout *layout = new QVBoxLayout( this ); layout->setMargin(0); layout->addWidget( m_sp ); ViewListDocker *docker = 0; if ( mainWindow() == 0 ) { // Don't use docker if embedded m_viewlist = new ViewListWidget(doc, m_sp); m_viewlist->setProject( &( getProject() ) ); connect( m_viewlist, SIGNAL(selectionChanged(ScheduleManager*)), SLOT(slotSelectionChanged(ScheduleManager*)) ); connect( this, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)), m_viewlist, SLOT(setSelectedSchedule(ScheduleManager*)) ); connect( m_viewlist, SIGNAL(updateViewInfo(ViewListItem*)), SLOT(slotUpdateViewInfo(ViewListItem*)) ); } else { ViewListDockerFactory vl(this); docker = static_cast(mainWindow()->createDockWidget(&vl)); if (docker->view() != this) { docker->setView(this); } m_viewlist = docker->viewList(); #if 0 //SchedulesDocker SchedulesDockerFactory sdf; SchedulesDocker *sd = dynamic_cast( createDockWidget( &sdf ) ); Q_ASSERT( sd ); sd->setProject( &getProject() ); connect( sd, SIGNAL(selectionChanged(ScheduleManager*)), SLOT(slotSelectionChanged(ScheduleManager*)) ); connect( this, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)), sd, SLOT(setSelectedSchedule(ScheduleManager*)) ); #endif } m_tab = new QStackedWidget( m_sp ); //////////////////////////////////////////////////////////////////////////////////////////////////// // Add sub views createIntroductionView(); // The menu items // ------ File actionCreateTemplate = new QAction( i18n( "&Create Template From Document..." ), this ); actionCollection()->addAction("file_createtemplate", actionCreateTemplate ); connect( actionCreateTemplate, SIGNAL(triggered(bool)), SLOT(slotCreateTemplate()) ); actionCreateNewProject = new QAction( i18n( "&Create New Project..." ), this ); actionCollection()->addAction("file_createnewproject", actionCreateNewProject ); connect( actionCreateNewProject, SIGNAL(triggered(bool)), SLOT(slotCreateNewProject()) ); // ------ Edit actionCut = actionCollection()->addAction(KStandardAction::Cut, "edit_cut", this, SLOT(slotEditCut())); actionCopy = actionCollection()->addAction(KStandardAction::Copy, "edit_copy", this, SLOT(slotEditCopy())); actionPaste = actionCollection()->addAction(KStandardAction::Paste, "edit_paste", this, SLOT(slotEditPaste())); // ------ View actionCollection()->addAction( KStandardAction::Redisplay, "view_refresh" , this, SLOT(slotRefreshView()) ); actionViewSelector = new KToggleAction(i18n("Show Selector"), this); actionCollection()->addAction("view_show_selector", actionViewSelector ); connect( actionViewSelector, SIGNAL(triggered(bool)), SLOT(slotViewSelector(bool)) ); // ------ Insert // ------ Project actionEditMainProject = new QAction(koIcon("view-time-schedule-edit"), i18n("Edit Main Project..."), this); actionCollection()->addAction("project_edit", actionEditMainProject ); connect( actionEditMainProject, SIGNAL(triggered(bool)), SLOT(slotProjectEdit()) ); actionEditStandardWorktime = new QAction(koIcon("configure"), i18n("Define Estimate Conversions..."), this); actionCollection()->addAction("project_worktime", actionEditStandardWorktime ); connect( actionEditStandardWorktime, SIGNAL(triggered(bool)), SLOT(slotProjectWorktime()) ); // ------ Tools actionDefineWBS = new QAction(koIcon("configure"), i18n("Define WBS Pattern..."), this); actionCollection()->addAction("tools_define_wbs", actionDefineWBS ); connect( actionDefineWBS, SIGNAL(triggered(bool)), SLOT(slotDefineWBS()) ); actionInsertFile = new QAction(koIcon("document-import"), i18n("Insert Project File..."), this); actionCollection()->addAction("insert_file", actionInsertFile ); connect( actionInsertFile, SIGNAL(triggered(bool)), SLOT(slotInsertFile()) ); // ------ Settings actionConfigure = new QAction(koIcon("configure"), i18n("Configure Plan..."), this); actionCollection()->addAction("configure", actionConfigure ); connect( actionConfigure, SIGNAL(triggered(bool)), SLOT(slotConfigure()) ); actionCurrencyConfig = new QAction(koIcon("configure"), i18n("Define Currency..."), this); actionCollection()->addAction( "config_currency", actionCurrencyConfig ); connect( actionCurrencyConfig, SIGNAL(triggered(bool)), SLOT(slotCurrencyConfig()) ); +#ifdef PLAN_USE_KREPORT actionOpenReportFile = new QAction(koIcon("document-open"), i18n("Open Report Definition File..."), this); actionCollection()->addAction( "reportdesigner_open_file", actionOpenReportFile ); connect( actionOpenReportFile, SIGNAL(triggered(bool)), SLOT(slotOpenReportFile()) ); +#endif // ------ Help actionIntroduction = new QAction(koIcon("dialog-information"), i18n("Introduction to Plan"), this); actionCollection()->addAction("plan_introduction", actionIntroduction ); connect( actionIntroduction, SIGNAL(triggered(bool)), SLOT(slotIntroduction()) ); // ------ Popup actionOpenNode = new QAction(koIcon("document-edit"), i18n("Edit..."), this); actionCollection()->addAction("node_properties", actionOpenNode ); connect( actionOpenNode, SIGNAL(triggered(bool)), SLOT(slotOpenNode()) ); actionTaskProgress = new QAction(koIcon("document-edit"), i18n("Progress..."), this); actionCollection()->addAction("task_progress", actionTaskProgress ); connect( actionTaskProgress, SIGNAL(triggered(bool)), SLOT(slotTaskProgress()) ); actionDeleteTask = new QAction(koIcon("edit-delete"), i18n("Delete Task"), this); actionCollection()->addAction("delete_task", actionDeleteTask ); connect( actionDeleteTask, SIGNAL(triggered(bool)), SLOT(slotDeleteTask()) ); actionTaskDescription = new QAction(koIcon("document-edit"), i18n("Description..."), this); actionCollection()->addAction("task_description", actionTaskDescription ); connect( actionTaskDescription, SIGNAL(triggered(bool)), SLOT(slotTaskDescription()) ); actionIndentTask = new QAction(koIcon("format-indent-more"), i18n("Indent Task"), this); actionCollection()->addAction("indent_task", actionIndentTask ); connect( actionIndentTask, SIGNAL(triggered(bool)), SLOT(slotIndentTask()) ); actionUnindentTask= new QAction(koIcon("format-indent-less"), i18n("Unindent Task"), this); actionCollection()->addAction("unindent_task", actionUnindentTask ); connect( actionUnindentTask, SIGNAL(triggered(bool)), SLOT(slotUnindentTask()) ); actionMoveTaskUp = new QAction(koIcon("arrow-up"), i18n("Move Task Up"), this); actionCollection()->addAction("move_task_up", actionMoveTaskUp ); connect( actionMoveTaskUp, SIGNAL(triggered(bool)), SLOT(slotMoveTaskUp()) ); actionMoveTaskDown = new QAction(koIcon("arrow-down"), i18n("Move Task Down"), this); actionCollection()->addAction("move_task_down", actionMoveTaskDown ); connect( actionMoveTaskDown, SIGNAL(triggered(bool)), SLOT(slotMoveTaskDown()) ); actionEditResource = new QAction(koIcon("document-edit"), i18n("Edit Resource..."), this); actionCollection()->addAction("edit_resource", actionEditResource ); connect( actionEditResource, SIGNAL(triggered(bool)), SLOT(slotEditResource()) ); actionEditRelation = new QAction(koIcon("document-edit"), i18n("Edit Dependency..."), this); actionCollection()->addAction("edit_dependency", actionEditRelation ); connect( actionEditRelation, SIGNAL(triggered(bool)), SLOT(slotModifyRelation()) ); actionDeleteRelation = new QAction(koIcon("edit-delete"), i18n("Delete Dependency"), this); actionCollection()->addAction("delete_dependency", actionDeleteRelation ); connect( actionDeleteRelation, SIGNAL(triggered(bool)), SLOT(slotDeleteRelation()) ); // Viewlist popup connect( m_viewlist, SIGNAL(createView()), SLOT(slotCreateView()) ); m_estlabel = new QLabel( "", 0 ); if ( statusBar() ) { addStatusBarItem( m_estlabel, 0, true ); } connect( &getProject(), SIGNAL(scheduleChanged(MainSchedule*)), SLOT(slotScheduleChanged(MainSchedule*)) ); connect( &getProject(), SIGNAL(scheduleAdded(const MainSchedule*)), SLOT(slotScheduleAdded(const MainSchedule*)) ); connect( &getProject(), SIGNAL(scheduleRemoved(const MainSchedule*)), SLOT(slotScheduleRemoved(const MainSchedule*)) ); slotPlugScheduleActions(); connect( doc, SIGNAL(changed()), SLOT(slotUpdate()) ); connect( m_scheduleActionGroup, SIGNAL(triggered(QAction*)), SLOT(slotViewSchedule(QAction*)) ); connect( getPart(), SIGNAL(workPackageLoaded()), SLOT(slotWorkPackageLoaded()) ); // hide unused dockers QTimer::singleShot( 0, this, SLOT(hideToolDocker()) ); // create views after dockers hidden, views take time for large projects QTimer::singleShot( 100, this, SLOT(initiateViews()) ); const QList pluginFactories = KoPluginLoader::instantiatePluginFactories(QStringLiteral("calligraplan/extensions")); foreach (KPluginFactory* factory, pluginFactories) { QObject *object = factory->create(this, QVariantList()); KXMLGUIClient *clientPlugin = dynamic_cast(object); if (clientPlugin) { insertChildClient(clientPlugin); } else { // not our/valid plugin, so delete the created object object->deleteLater(); } } //debugPlan<<" end"; } View::~View() { ViewBase *view = currentView(); if (view) { // deactivate view to remove dockers etc slotGuiActivated(view, false); } /* removeStatusBarItem( m_estlabel ); delete m_estlabel;*/ } // hackish way to get rid of unused dockers, but as long as no official way exists... void View::hideToolDocker() { if ( mainWindow() ) { QStringList lst; lst << "KPlatoViewList" << "Scripting"; QStringList names; foreach ( QDockWidget *w, mainWindow()->dockWidgets() ) { if ( ! lst.contains( w->objectName() ) ) { names << w->windowTitle(); w->setFeatures( QDockWidget::DockWidgetClosable ); w->hide(); } } foreach(const KActionCollection *c, KActionCollection::allCollections()) { KActionMenu *a = qobject_cast(c->action("settings_dockers_menu")); if ( a ) { QList actions = a->menu()->actions(); foreach ( QAction *act, actions ) { if ( names.contains( act->text() ) ) { a->removeAction( act ); } } a->addSeparator(); break; } } } } void View::initiateViews() { QApplication::setOverrideCursor( Qt::WaitCursor ); createViews(); connect( m_viewlist, SIGNAL(activated(ViewListItem*,ViewListItem*)), SLOT(slotViewActivated(ViewListItem*,ViewListItem*)) ); // after createViews() !! connect( m_viewlist, SIGNAL(viewListItemRemoved(ViewListItem*)), SLOT(slotViewListItemRemoved(ViewListItem*)) ); // after createViews() !! connect( m_viewlist, SIGNAL(viewListItemInserted(ViewListItem*,ViewListItem*,int)), SLOT(slotViewListItemInserted(ViewListItem*,ViewListItem*,int)) ); QDockWidget *docker = qobject_cast( m_viewlist->parent() ); if ( docker ) { // after createViews() !! connect( m_viewlist, SIGNAL(modified()), docker, SLOT(slotModified())); connect( m_viewlist, SIGNAL(modified()), getPart(), SLOT(viewlistModified())); connect(getPart(), SIGNAL(viewlistModified(bool)), docker, SLOT(updateWindowTitle(bool))); } connect( m_tab, SIGNAL(currentChanged(int)), this, SLOT(slotCurrentChanged(int)) ); slotSelectDefaultView(); loadContext(); QApplication::restoreOverrideCursor(); } void View::slotCreateTemplate() { KoTemplateCreateDia::createTemplate(koDocument()->documentPart()->templatesResourcePath(), ".plant", getPart(), this); } void View::slotCreateNewProject() { debugPlan; if ( KMessageBox::Continue == KMessageBox::warningContinueCancel( this, xi18nc( "@info", "This action cannot be undone." "Create a new Project from the current project " "with new project- and task identities." "Resource- and calendar identities are not changed." "All scheduling information is removed." "Do you want to continue?" ) ) ) { getPart()->createNewProject(); slotOpenNode( &getProject() ); } } void View::createViews() { Context *ctx = getPart()->context(); if ( ctx && ctx->isLoaded() ) { debugPlan<<"isLoaded"; KoXmlNode n = ctx->context().namedItem( "categories" ); if ( n.isNull() ) { warnPlan<<"No categories"; } else { n = n.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement e = n.toElement(); if (e.tagName() != "category") { continue; } debugPlan<<"category: "<addCategory( ct, cn ); KoXmlNode n1 = e.firstChild(); for ( ; ! n1.isNull(); n1 = n1.nextSibling() ) { if ( ! n1.isElement() ) { continue; } KoXmlElement e1 = n1.toElement(); if (e1.tagName() != "view") { continue; } ViewBase *v = 0; QString type = e1.attribute( "viewtype" ); QString tag = e1.attribute( "tag" ); QString name = e1.attribute( "name" ); QString tip = e1.attribute( "tooltip" ); v = createView( cat, type, tag, name, tip ); //KoXmlNode settings = e1.namedItem( "settings " ); ???? KoXmlNode settings = e1.firstChild(); for ( ; ! settings.isNull(); settings = settings.nextSibling() ) { if ( settings.nodeName() == "settings" ) { break; } } if ( v && settings.isElement() ) { debugPlan<<" settings"; v->loadContext( settings.toElement() ); } } } } } else { debugPlan<<"Default"; ViewListItem *cat; QString ct = "Editors"; cat = m_viewlist->addCategory( ct, defaultCategoryInfo( ct ).name ); createCalendarEditor( cat, "CalendarEditor", QString(), TIP_USE_DEFAULT_TEXT ); createAccountsEditor( cat, "AccountsEditor", QString(), TIP_USE_DEFAULT_TEXT ); createResourceEditor( cat, "ResourceEditor", QString(), TIP_USE_DEFAULT_TEXT ); createTaskEditor( cat, "TaskEditor", QString(), TIP_USE_DEFAULT_TEXT ); createDependencyEditor( cat, "DependencyEditor", QString(), TIP_USE_DEFAULT_TEXT ); createPertEditor( cat, "PertEditor", QString(), TIP_USE_DEFAULT_TEXT ); createScheduleHandler( cat, "ScheduleHandlerView", QString(), TIP_USE_DEFAULT_TEXT ); ct = "Views"; cat = m_viewlist->addCategory( ct, defaultCategoryInfo( ct ).name ); createGanttView( cat, "GanttView", QString(), TIP_USE_DEFAULT_TEXT ); createMilestoneGanttView( cat, "MilestoneGanttView", QString(), TIP_USE_DEFAULT_TEXT ); createResourceAppointmentsView( cat, "ResourceAppointmentsView", QString(), TIP_USE_DEFAULT_TEXT ); createResourceAppointmentsGanttView( cat, "ResourceAppointmentsGanttView", QString(), TIP_USE_DEFAULT_TEXT ); createAccountsView( cat, "AccountsView", QString(), TIP_USE_DEFAULT_TEXT ); ct = "Execution"; cat = m_viewlist->addCategory( ct, defaultCategoryInfo( ct ).name ); createProjectStatusView( cat, "ProjectStatusView", QString(), TIP_USE_DEFAULT_TEXT ); createPerformanceStatusView( cat, "PerformanceStatusView", QString(), TIP_USE_DEFAULT_TEXT ); createTaskStatusView( cat, "TaskStatusView", QString(), TIP_USE_DEFAULT_TEXT ); createTaskView( cat, "TaskView", QString(), TIP_USE_DEFAULT_TEXT ); createTaskWorkPackageView( cat, "TaskWorkPackageView", QString(), TIP_USE_DEFAULT_TEXT ); ct = "Reports"; cat = m_viewlist->addCategory(ct, defaultCategoryInfo(ct).name); createReportsGeneratorView(cat, "ReportsGeneratorView", i18n("Generate reports"), TIP_USE_DEFAULT_TEXT); #ifdef PLAN_USE_KREPORT // A little hack to get the user started... ReportView *rv = qobject_cast( createReportView( cat, "ReportView", i18n( "Task Status Report" ), TIP_USE_DEFAULT_TEXT ) ); if ( rv ) { QDomDocument doc; doc.setContent( standardTaskStatusReport() ); rv->loadXML( doc ); } #endif } } ViewBase *View::createView( ViewListItem *cat, const QString &type, const QString &tag, const QString &name, const QString &tip, int index ) { ViewBase *v = 0; //NOTE: type is the same as classname (so if it is changed...) if ( type == "CalendarEditor" ) { v = createCalendarEditor( cat, tag, name, tip, index ); } else if ( type == "AccountsEditor" ) { v = createAccountsEditor( cat, tag, name, tip, index ); } else if ( type == "ResourceEditor" ) { v = createResourceEditor( cat, tag, name, tip, index ); } else if ( type == "TaskEditor" ) { v = createTaskEditor( cat, tag, name, tip, index ); } else if ( type == "DependencyEditor" ) { v = createDependencyEditor( cat, tag, name, tip, index ); } else if ( type == "PertEditor" ) { v = createPertEditor( cat, tag, name, tip, index ); } else if ( type == "ScheduleEditor" ) { v = createScheduleEditor( cat, tag, name, tip, index ); } else if ( type == "ScheduleHandlerView" ) { v = createScheduleHandler( cat, tag, name, tip, index ); } else if ( type == "ProjectStatusView" ) { v = createProjectStatusView( cat, tag, name, tip, index ); } else if ( type == "TaskStatusView" ) { v = createTaskStatusView( cat, tag, name, tip, index ); } else if ( type == "TaskView" ) { v = createTaskView( cat, tag, name, tip, index ); } else if ( type == "TaskWorkPackageView" ) { v = createTaskWorkPackageView( cat, tag, name, tip, index ); } else if ( type == "GanttView" ) { v = createGanttView( cat, tag, name, tip, index ); } else if ( type == "MilestoneGanttView" ) { v = createMilestoneGanttView( cat, tag, name, tip, index ); } else if ( type == "ResourceAppointmentsView" ) { v = createResourceAppointmentsView( cat, tag, name, tip, index ); } else if ( type == "ResourceAppointmentsGanttView" ) { v = createResourceAppointmentsGanttView( cat, tag, name, tip, index ); } else if ( type == "AccountsView" ) { v = createAccountsView( cat, tag, name, tip, index ); } else if ( type == "PerformanceStatusView" ) { v = createPerformanceStatusView( cat, tag, name, tip, index ); } else if ( type == "ReportsGeneratorView" ) { v = createReportsGeneratorView(cat, tag, name, tip, index); } else if ( type == "ReportView" ) { #ifdef PLAN_USE_KREPORT v = createReportView( cat, tag, name, tip, index ); #endif } else { warnPlan<<"Unknown viewtype: "<type() == ViewListItem::ItemType_SubView ) { itm->setViewInfo( defaultViewInfo( itm->viewType() ) ); } else if ( itm->type() == ViewListItem::ItemType_Category ) { ViewInfo vi = defaultCategoryInfo( itm->tag() ); itm->setViewInfo( vi ); } } ViewInfo View::defaultViewInfo( const QString &type ) const { ViewInfo vi; if ( type == "CalendarEditor" ) { vi.name = i18n( "Work & Vacation" ); vi.tip = xi18nc( "@info:tooltip", "Edit working- and vacation days for resources" ); } else if ( type == "AccountsEditor" ) { vi.name = i18n( "Cost Breakdown Structure" ); vi.tip = xi18nc( "@info:tooltip", "Edit cost breakdown structure." ); } else if ( type == "ResourceEditor" ) { vi.name = i18n( "Resources" ); vi.tip = xi18nc( "@info:tooltip", "Edit resource breakdown structure" ); } else if ( type == "TaskEditor" ) { vi.name = i18n( "Tasks" ); vi.tip = xi18nc( "@info:tooltip", "Edit work breakdown structure" ); } else if ( type == "DependencyEditor" ) { vi.name = i18n( "Dependencies (Graphic)" ); vi.tip = xi18nc( "@info:tooltip", "Edit task dependencies" ); } else if ( type == "PertEditor" ) { vi.name = i18n( "Dependencies (List)" ); vi.tip = xi18nc( "@info:tooltip", "Edit task dependencies" ); } else if ( type == "ScheduleEditor" ) { // This view is not used stand-alone atm vi.name = i18n( "Schedules" ); } else if ( type == "ScheduleHandlerView" ) { vi.name = i18n( "Schedules" ); vi.tip = xi18nc( "@info:tooltip", "Calculate and analyze project schedules" ); } else if ( type == "ProjectStatusView" ) { vi.name = i18n( "Project Performance Chart" ); vi.tip = xi18nc( "@info:tooltip", "View project status information" ); } else if ( type == "TaskStatusView" ) { vi.name = i18n( "Task Status" ); vi.tip = xi18nc( "@info:tooltip", "View task progress information" ); } else if ( type == "TaskView" ) { vi.name = i18n( "Task Execution" ); vi.tip = xi18nc( "@info:tooltip", "View task execution information" ); } else if ( type == "TaskWorkPackageView" ) { vi.name = i18n( "Work Package View" ); vi.tip = xi18nc( "@info:tooltip", "View task work package information" ); } else if ( type == "GanttView" ) { vi.name = i18n( "Gantt" ); vi.tip = xi18nc( "@info:tooltip", "View Gantt chart" ); } else if ( type == "MilestoneGanttView" ) { vi.name = i18n( "Milestone Gantt" ); vi.tip = xi18nc( "@info:tooltip", "View milestone Gantt chart" ); } else if ( type == "ResourceAppointmentsView" ) { vi.name = i18n( "Resource Assignments" ); vi.tip = xi18nc( "@info:tooltip", "View resource assignments in a table" ); } else if ( type == "ResourceAppointmentsGanttView" ) { vi.name = i18n( "Resource Assignments (Gantt)" ); vi.tip = xi18nc( "@info:tooltip", "View resource assignments in Gantt chart" ); } else if ( type == "AccountsView" ) { vi.name = i18n( "Cost Breakdown" ); vi.tip = xi18nc( "@info:tooltip", "View planned and actual cost" ); } else if ( type == "PerformanceStatusView" ) { vi.name = i18n( "Tasks Performance Chart" ); vi.tip = xi18nc( "@info:tooltip", "View tasks performance status information" ); } else if ( type == "ReportsGeneratorView" ) { vi.name = i18n( "Reports Generator" ); vi.tip = xi18nc( "@info:tooltip", "Generate reports" ); } else if ( type == "ReportView" ) { vi.name = i18n( "Report" ); vi.tip = xi18nc( "@info:tooltip", "View report" ); } else { warnPlan<<"Unknown viewtype: "<count()-1) : m_visitedViews.at(m_visitedViews.count() - 2); debugPlan<<"Prev:"<setCurrentIndex(view); return; } if ( url.url().startsWith( QLatin1String( "about:plan" ) ) ) { getPart()->aboutPage().generatePage( v->htmlPart(), url ); return; } } if ( url.scheme() == QLatin1String("help") ) { KHelpClient::invokeHelp( "", url.fileName() ); return; } // try to open the url debugPlan<htmlPart().setJScriptEnabled(false); v->htmlPart().setJavaEnabled(false); v->htmlPart().setMetaRefreshEnabled(false); v->htmlPart().setPluginsEnabled(false); slotOpenUrlRequest( v, QUrl( "about:plan/main" ) ); connect( v, SIGNAL(openUrlRequest(HtmlView*,QUrl)), SLOT(slotOpenUrlRequest(HtmlView*,QUrl)) ); m_tab->addWidget( v ); return v; } ViewBase *View::createResourceAppointmentsGanttView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { ResourceAppointmentsGanttView *v = new ResourceAppointmentsGanttView(getKoPart(), getPart(), m_tab ); m_tab->addWidget( v ); ViewListItem *i = m_viewlist->addView( cat, tag, name, v, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "ResourceAppointmentsGanttView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } connect( v, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); connect( this, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)), v, SLOT(setScheduleManager(ScheduleManager*)) ); connect( v, SIGNAL(requestPopupMenu(QString,QPoint)), this, SLOT(slotPopupMenu(QString,QPoint)) ); v->setProject( &( getProject() ) ); v->setScheduleManager( currentScheduleManager() ); v->updateReadWrite( m_readWrite ); return v; } ViewBase *View::createResourceAppointmentsView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { ResourceAppointmentsView *v = new ResourceAppointmentsView(getKoPart(), getPart(), m_tab ); m_tab->addWidget( v ); ViewListItem *i = m_viewlist->addView( cat, tag, name, v, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "ResourceAppointmentsView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } connect( v, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); connect( this, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)), v, SLOT(setScheduleManager(ScheduleManager*)) ); connect( v, SIGNAL(requestPopupMenu(QString,QPoint)), this, SLOT(slotPopupMenu(QString,QPoint)) ); v->setProject( &( getProject() ) ); v->setScheduleManager( currentScheduleManager() ); v->updateReadWrite( m_readWrite ); return v; } ViewBase *View::createResourceEditor( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { ResourceEditor *resourceeditor = new ResourceEditor(getKoPart(), getPart(), m_tab ); m_tab->addWidget( resourceeditor ); resourceeditor->setProject( &(getProject()) ); ViewListItem *i = m_viewlist->addView( cat, tag, name, resourceeditor, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "ResourceEditor" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } connect( resourceeditor, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); connect( resourceeditor, SIGNAL(deleteObjectList(QObjectList)), SLOT(slotDeleteResourceObjects(QObjectList)) ); connect( resourceeditor, SIGNAL(requestPopupMenu(QString,QPoint)), this, SLOT(slotPopupMenu(QString,QPoint)) ); resourceeditor->updateReadWrite( m_readWrite ); return resourceeditor; } ViewBase *View::createTaskEditor( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { TaskEditor *taskeditor = new TaskEditor(getKoPart(), getPart(), m_tab ); m_tab->addWidget( taskeditor ); m_defaultView = m_tab->count() - 1; ViewListItem *i = m_viewlist->addView( cat, tag, name, taskeditor, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "TaskEditor" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } taskeditor->setProject( &(getProject()) ); taskeditor->setScheduleManager( currentScheduleManager() ); connect( this, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)), taskeditor, SLOT(setScheduleManager(ScheduleManager*)) ); connect( taskeditor, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); connect( taskeditor, SIGNAL(addTask()), SLOT(slotAddTask()) ); connect( taskeditor, SIGNAL(addMilestone()), SLOT(slotAddMilestone()) ); connect( taskeditor, SIGNAL(addSubtask()), SLOT(slotAddSubTask()) ); connect( taskeditor, SIGNAL(addSubMilestone()), SLOT(slotAddSubMilestone()) ); connect( taskeditor, SIGNAL(deleteTaskList(QList)), SLOT(slotDeleteTask(QList)) ); connect( taskeditor, SIGNAL(moveTaskUp()), SLOT(slotMoveTaskUp()) ); connect( taskeditor, SIGNAL(moveTaskDown()), SLOT(slotMoveTaskDown()) ); connect( taskeditor, SIGNAL(indentTask()), SLOT(slotIndentTask()) ); connect( taskeditor, SIGNAL(unindentTask()), SLOT(slotUnindentTask()) ); connect(taskeditor, SIGNAL(saveTaskModule(QUrl,Project*)), SLOT(saveTaskModule(QUrl,Project*))); connect(taskeditor, SIGNAL(removeTaskModule(QUrl)), SLOT(removeTaskModule(QUrl))); connect( taskeditor, SIGNAL(requestPopupMenu(QString,QPoint)), this, SLOT(slotPopupMenu(QString,QPoint)) ); taskeditor->updateReadWrite( m_readWrite ); // last: QStringList modules = KoResourcePaths::findAllResources( "calligraplan_taskmodules", "*.plan", KoResourcePaths::NoDuplicates|KoResourcePaths::Recursive ); debugPlan<setTaskModules( modules ); return taskeditor; } ViewBase *View::createAccountsEditor( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { AccountsEditor *ae = new AccountsEditor(getKoPart(), getPart(), m_tab ); m_tab->addWidget( ae ); ViewListItem *i = m_viewlist->addView( cat, tag, name, ae, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "AccountsEditor" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } ae->draw( getProject() ); connect( ae, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); ae->updateReadWrite( m_readWrite ); return ae; } ViewBase *View::createCalendarEditor( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { CalendarEditor *calendareditor = new CalendarEditor(getKoPart(), getPart(), m_tab ); m_tab->addWidget( calendareditor ); ViewListItem *i = m_viewlist->addView( cat, tag, name, calendareditor, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "CalendarEditor" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } calendareditor->draw( getProject() ); connect( calendareditor, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); connect( calendareditor, SIGNAL(requestPopupMenu(QString,QPoint)), this, SLOT(slotPopupMenu(QString,QPoint)) ); calendareditor->updateReadWrite( m_readWrite ); return calendareditor; } ViewBase *View::createScheduleHandler( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { ScheduleHandlerView *handler = new ScheduleHandlerView(getKoPart(), getPart(), m_tab ); m_tab->addWidget( handler ); ViewListItem *i = m_viewlist->addView( cat, tag, name, handler, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "ScheduleHandlerView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } connect( handler->scheduleEditor(), SIGNAL(addScheduleManager(Project*)), SLOT(slotAddScheduleManager(Project*)) ); connect( handler->scheduleEditor(), SIGNAL(deleteScheduleManager(Project*,ScheduleManager*)), SLOT(slotDeleteScheduleManager(Project*,ScheduleManager*)) ); connect( handler->scheduleEditor(), SIGNAL(moveScheduleManager(ScheduleManager*,ScheduleManager*,int)), SLOT(slotMoveScheduleManager(ScheduleManager*,ScheduleManager*,int))); connect( handler->scheduleEditor(), SIGNAL(calculateSchedule(Project*,ScheduleManager*)), SLOT(slotCalculateSchedule(Project*,ScheduleManager*)) ); connect( handler->scheduleEditor(), SIGNAL(baselineSchedule(Project*,ScheduleManager*)), SLOT(slotBaselineSchedule(Project*,ScheduleManager*)) ); connect( handler, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); connect( this, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)), handler, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)) ); connect( handler, SIGNAL(requestPopupMenu(QString,QPoint)), this, SLOT(slotPopupMenu(QString,QPoint)) ); connect(handler, SIGNAL(editNode(Node*)), this, SLOT(slotOpenNode(Node*))); connect(handler, SIGNAL(editResource(Resource*)), this, SLOT(slotEditResource(Resource*))); handler->draw( getProject() ); handler->updateReadWrite( m_readWrite ); return handler; } ScheduleEditor *View::createScheduleEditor( QWidget *parent ) { ScheduleEditor *scheduleeditor = new ScheduleEditor(getKoPart(), getPart(), parent ); connect( scheduleeditor, SIGNAL(addScheduleManager(Project*)), SLOT(slotAddScheduleManager(Project*)) ); connect( scheduleeditor, SIGNAL(deleteScheduleManager(Project*,ScheduleManager*)), SLOT(slotDeleteScheduleManager(Project*,ScheduleManager*)) ); connect( scheduleeditor, SIGNAL(calculateSchedule(Project*,ScheduleManager*)), SLOT(slotCalculateSchedule(Project*,ScheduleManager*)) ); connect( scheduleeditor, SIGNAL(baselineSchedule(Project*,ScheduleManager*)), SLOT(slotBaselineSchedule(Project*,ScheduleManager*)) ); scheduleeditor->updateReadWrite( m_readWrite ); return scheduleeditor; } ViewBase *View::createScheduleEditor( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { ScheduleEditor *scheduleeditor = new ScheduleEditor(getKoPart(), getPart(), m_tab ); m_tab->addWidget( scheduleeditor ); ViewListItem *i = m_viewlist->addView( cat, tag, name, scheduleeditor, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "ScheduleEditor" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } scheduleeditor->setProject( &( getProject() ) ); connect( scheduleeditor, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); connect( scheduleeditor, SIGNAL(addScheduleManager(Project*)), SLOT(slotAddScheduleManager(Project*)) ); connect( scheduleeditor, SIGNAL(deleteScheduleManager(Project*,ScheduleManager*)), SLOT(slotDeleteScheduleManager(Project*,ScheduleManager*)) ); connect( scheduleeditor, SIGNAL(calculateSchedule(Project*,ScheduleManager*)), SLOT(slotCalculateSchedule(Project*,ScheduleManager*)) ); connect( scheduleeditor, SIGNAL(baselineSchedule(Project*,ScheduleManager*)), SLOT(slotBaselineSchedule(Project*,ScheduleManager*)) ); scheduleeditor->updateReadWrite( m_readWrite ); return scheduleeditor; } ViewBase *View::createDependencyEditor( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { DependencyEditor *editor = new DependencyEditor(getKoPart(), getPart(), m_tab ); m_tab->addWidget( editor ); ViewListItem *i = m_viewlist->addView( cat, tag, name, editor, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "DependencyEditor" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } editor->draw( getProject() ); connect( editor, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); connect( editor, SIGNAL(addRelation(Node*,Node*,int)), SLOT(slotAddRelation(Node*,Node*,int)) ); connect( editor, SIGNAL(modifyRelation(Relation*,int)), SLOT(slotModifyRelation(Relation*,int)) ); connect( editor, SIGNAL(modifyRelation(Relation*)), SLOT(slotModifyRelation(Relation*)) ); connect( editor, SIGNAL(editNode(Node*)), SLOT(slotOpenNode(Node*)) ); connect( editor, SIGNAL(addTask()), SLOT(slotAddTask()) ); connect( editor, SIGNAL(addMilestone()), SLOT(slotAddMilestone()) ); connect( editor, SIGNAL(addSubMilestone()), SLOT(slotAddSubMilestone()) ); connect( editor, SIGNAL(addSubtask()), SLOT(slotAddSubTask()) ); connect( editor, SIGNAL(deleteTaskList(QList)), SLOT(slotDeleteTask(QList)) ); connect( this, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)), editor, SLOT(setScheduleManager(ScheduleManager*)) ); connect( editor, SIGNAL(requestPopupMenu(QString,QPoint)), this, SLOT(slotPopupMenu(QString,QPoint)) ); editor->updateReadWrite( m_readWrite ); editor->setScheduleManager( currentScheduleManager() ); return editor; } ViewBase *View::createPertEditor( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { PertEditor *perteditor = new PertEditor(getKoPart(), getPart(), m_tab ); m_tab->addWidget( perteditor ); ViewListItem *i = m_viewlist->addView( cat, tag, name, perteditor, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "PertEditor" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } perteditor->draw( getProject() ); connect( perteditor, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); m_updatePertEditor = true; perteditor->updateReadWrite( m_readWrite ); return perteditor; } ViewBase *View::createProjectStatusView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { ProjectStatusView *v = new ProjectStatusView(getKoPart(), getPart(), m_tab ); m_tab->addWidget( v ); ViewListItem *i = m_viewlist->addView( cat, tag, name, v, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "ProjectStatusView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } connect( v, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); connect( this, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)), v, SLOT(setScheduleManager(ScheduleManager*)) ); v->updateReadWrite( m_readWrite ); v->setProject( &getProject() ); v->setScheduleManager( currentScheduleManager() ); return v; } ViewBase *View::createPerformanceStatusView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { PerformanceStatusView *v = new PerformanceStatusView(getKoPart(), getPart(), m_tab ); m_tab->addWidget( v ); ViewListItem *i = m_viewlist->addView( cat, tag, name, v, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "PerformanceStatusView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } connect( v, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); connect( this, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)), v, SLOT(setScheduleManager(ScheduleManager*)) ); connect( v, SIGNAL(requestPopupMenu(QString,QPoint)), this, SLOT(slotPopupMenu(QString,QPoint)) ); v->updateReadWrite( m_readWrite ); v->setProject( &getProject() ); v->setScheduleManager( currentScheduleManager() ); return v; } ViewBase *View::createTaskStatusView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { TaskStatusView *taskstatusview = new TaskStatusView(getKoPart(), getPart(), m_tab ); m_tab->addWidget( taskstatusview ); ViewListItem *i = m_viewlist->addView( cat, tag, name, taskstatusview, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "TaskStatusView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } connect( taskstatusview, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); connect( this, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)), taskstatusview, SLOT(setScheduleManager(ScheduleManager*)) ); connect( taskstatusview, SIGNAL(requestPopupMenu(QString,QPoint)), this, SLOT(slotPopupMenu(QString,QPoint)) ); taskstatusview->updateReadWrite( m_readWrite ); taskstatusview->draw( getProject() ); taskstatusview->setScheduleManager( currentScheduleManager() ); return taskstatusview; } ViewBase *View::createTaskView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { TaskView *v = new TaskView(getKoPart(), getPart(), m_tab ); m_tab->addWidget( v ); ViewListItem *i = m_viewlist->addView( cat, tag, name, v, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "TaskView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } v->draw( getProject() ); v->setScheduleManager( currentScheduleManager() ); connect( this, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)), v, SLOT(setScheduleManager(ScheduleManager*)) ); connect( v, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); connect( v, SIGNAL(requestPopupMenu(QString,QPoint)), this, SLOT(slotPopupMenu(QString,QPoint)) ); v->updateReadWrite( m_readWrite ); return v; } ViewBase *View::createTaskWorkPackageView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { TaskWorkPackageView *v = new TaskWorkPackageView(getKoPart(), getPart(), m_tab ); m_tab->addWidget( v ); ViewListItem *i = m_viewlist->addView( cat, tag, name, v, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "TaskWorkPackageView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } v->setProject( &getProject() ); v->setScheduleManager( currentScheduleManager() ); connect( this, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)), v, SLOT(setScheduleManager(ScheduleManager*)) ); connect( v, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); connect( v, SIGNAL(requestPopupMenu(QString,QPoint)), this, SLOT(slotPopupMenu(QString,QPoint)) ); connect( v, SIGNAL(mailWorkpackage(Node*,Resource*)), SLOT(slotMailWorkpackage(Node*,Resource*)) ); connect( v, SIGNAL(mailWorkpackages(QList,Resource*)), SLOT(slotMailWorkpackages(QList,Resource*)) ); connect(v, SIGNAL(checkForWorkPackages()), getPart(), SLOT(checkForWorkPackages())); v->updateReadWrite( m_readWrite ); return v; } ViewBase *View::createGanttView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { GanttView *ganttview = new GanttView(getKoPart(), getPart(), m_tab, koDocument()->isReadWrite() ); m_tab->addWidget( ganttview ); ViewListItem *i = m_viewlist->addView( cat, tag, name, ganttview, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "GanttView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } ganttview->setProject( &( getProject() ) ); ganttview->setScheduleManager( currentScheduleManager() ); connect( ganttview, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); /* TODO: Review these connect( ganttview, SIGNAL(addRelation(Node*,Node*,int)), SLOT(slotAddRelation(Node*,Node*,int)) ); connect( ganttview, SIGNAL(modifyRelation(Relation*,int)), SLOT(slotModifyRelation(Relation*,int)) ); connect( ganttview, SIGNAL(modifyRelation(Relation*)), SLOT(slotModifyRelation(Relation*)) ); connect( ganttview, SIGNAL(itemDoubleClicked()), SLOT(slotOpenNode()) ); connect( ganttview, SIGNAL(itemRenamed(Node*,QString)), this, SLOT(slotRenameNode(Node*,QString)) );*/ connect( this, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)), ganttview, SLOT(setScheduleManager(ScheduleManager*)) ); connect( ganttview, SIGNAL(requestPopupMenu(QString,QPoint)), this, SLOT(slotPopupMenu(QString,QPoint)) ); ganttview->updateReadWrite( m_readWrite ); return ganttview; } ViewBase *View::createMilestoneGanttView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { MilestoneGanttView *ganttview = new MilestoneGanttView(getKoPart(), getPart(), m_tab, koDocument()->isReadWrite() ); m_tab->addWidget( ganttview ); ViewListItem *i = m_viewlist->addView( cat, tag, name, ganttview, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "MilestoneGanttView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } ganttview->setProject( &( getProject() ) ); ganttview->setScheduleManager( currentScheduleManager() ); connect( ganttview, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); connect( this, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)), ganttview, SLOT(setScheduleManager(ScheduleManager*)) ); connect( ganttview, SIGNAL(requestPopupMenu(QString,QPoint)), this, SLOT(slotPopupMenu(QString,QPoint)) ); ganttview->updateReadWrite( m_readWrite ); return ganttview; } ViewBase *View::createAccountsView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { AccountsView *accountsview = new AccountsView(getKoPart(), &getProject(), getPart(), m_tab ); m_tab->addWidget( accountsview ); ViewListItem *i = m_viewlist->addView( cat, tag, name, accountsview, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "AccountsView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } accountsview->setScheduleManager( currentScheduleManager() ); connect( this, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)), accountsview, SLOT(setScheduleManager(ScheduleManager*)) ); connect( accountsview, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); accountsview->updateReadWrite( m_readWrite ); return accountsview; } ViewBase *View::createResourceAssignmentView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { ResourceAssignmentView *resourceAssignmentView = new ResourceAssignmentView(getKoPart(), getPart(), m_tab ); m_tab->addWidget( resourceAssignmentView ); m_updateResourceAssignmentView = true; ViewListItem *i = m_viewlist->addView( cat, tag, name, resourceAssignmentView, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "ResourceAssignmentView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } resourceAssignmentView->draw( getProject() ); connect( resourceAssignmentView, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); connect( resourceAssignmentView, SIGNAL(requestPopupMenu(QString,QPoint)), this, SLOT(slotPopupMenu(QString,QPoint)) ); resourceAssignmentView->updateReadWrite( m_readWrite ); return resourceAssignmentView; } ViewBase *View::createReportsGeneratorView(ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index) { ReportsGeneratorView *v = new ReportsGeneratorView(getKoPart(), getPart(), m_tab ); m_tab->addWidget( v ); ViewListItem *i = m_viewlist->addView(cat, tag, name, v, getPart(), "", index); ViewInfo vi = defaultViewInfo( "ReportsGeneratorView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } v->setProject( &getProject() ); connect( this, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)), v, SLOT(setScheduleManager(ScheduleManager*)) ); connect( this, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)), v, SLOT(slotRefreshView())); v->setScheduleManager( currentScheduleManager() ); connect( v, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); connect( v, SIGNAL(requestPopupMenu(QString,QPoint)), this, SLOT(slotPopupMenu(QString,QPoint)) ); v->updateReadWrite( m_readWrite ); return v; } ViewBase *View::createReportView( ViewListItem *cat, const QString &tag, const QString &name, const QString &tip, int index ) { #ifdef PLAN_USE_KREPORT ReportView *v = new ReportView(getKoPart(), getPart(), m_tab ); m_tab->addWidget( v ); ViewListItem *i = m_viewlist->addView( cat, tag, name, v, getPart(), "", index ); ViewInfo vi = defaultViewInfo( "ReportView" ); if ( name.isEmpty() ) { i->setText( 0, vi.name ); } if ( tip == TIP_USE_DEFAULT_TEXT ) { i->setToolTip( 0, vi.tip ); } else { i->setToolTip( 0, tip ); } v->setProject( &getProject() ); connect( this, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)), v, SLOT(setScheduleManager(ScheduleManager*)) ); connect( this, SIGNAL(currentScheduleManagerChanged(ScheduleManager*)), v, SLOT(slotRefreshView())); v->setScheduleManager( currentScheduleManager() ); connect( v, SIGNAL(guiActivated(ViewBase*,bool)), SLOT(slotGuiActivated(ViewBase*,bool)) ); v->updateReadWrite( m_readWrite ); return v; #else return 0; #endif } Project& View::getProject() const { return getPart() ->getProject(); } KoPrintJob * View::createPrintJob() { KoView *v = qobject_cast( canvas() ); if ( v == 0 ) { return 0; } return v->createPrintJob(); } ViewBase *View::currentView() const { return qobject_cast( m_tab->currentWidget() ); } void View::slotEditCut() { ViewBase *v = currentView(); if ( v ) { v->slotEditCut(); } } void View::slotEditCopy() { ViewBase *v = currentView(); if ( v ) { v->slotEditCopy(); } } void View::slotEditPaste() { ViewBase *v = currentView(); if ( v ) { v->slotEditPaste(); } } void View::slotRefreshView() { ViewBase *v = currentView(); if ( v ) { debugPlan<slotRefreshView(); } } void View::slotViewSelector( bool show ) { //debugPlan; m_viewlist->setVisible( show ); } void View::slotInsertResourcesFile(const QString &file) { getPart()->insertResourcesFile(QUrl(file)); } void View::slotInsertFile() { InsertFileDialog *dlg = new InsertFileDialog( getProject(), currentTask(), this ); connect(dlg, SIGNAL(finished(int)), SLOT(slotInsertFileFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); } void View::slotInsertFileFinished( int result ) { InsertFileDialog *dlg = qobject_cast( sender() ); if ( dlg == 0 ) { return; } if ( result == QDialog::Accepted ) { getPart()->insertFile( dlg->url(), dlg->parentNode(), dlg->afterNode() ); } dlg->deleteLater(); } void View::slotProjectEdit() { slotOpenNode( &getProject() ); } void View::slotProjectWorktime() { StandardWorktimeDialog *dia = new StandardWorktimeDialog( getProject(), this ); connect(dia, SIGNAL(finished(int)), this, SLOT(slotProjectWorktimeFinished(int))); dia->show(); dia->raise(); dia->activateWindow(); } void View::slotProjectWorktimeFinished( int result ) { StandardWorktimeDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command * cmd = dia->buildCommand(); if ( cmd ) { //debugPlan<<"Modifying calendar(s)"; getPart() ->addCommand( cmd ); //also executes } } dia->deleteLater(); } void View::slotSelectionChanged( ScheduleManager *sm ) { debugPlan<expected() ); if ( idx < 0 ) { debugPlan<expected(); return; } QAction *a = m_scheduleActions.keys().at( idx ); Q_ASSERT( a ); a->setChecked( true ); // this doesn't trigger QActionGroup slotViewSchedule( a ); } QList View::sortedActionList() { QMap lst; foreach ( QAction *a, m_scheduleActions.keys() ) { lst.insert( a->objectName(), a ); } return lst.values(); } void View::slotScheduleRemoved( const MainSchedule *sch ) { debugPlan<name(); QAction *a = 0; QAction *checked = m_scheduleActionGroup->checkedAction(); QMapIterator i( m_scheduleActions ); while (i.hasNext()) { i.next(); if ( i.value() == sch ) { a = i.key(); break; } } if ( a ) { unplugActionList( "view_schedule_list" ); delete a; plugActionList( "view_schedule_list", sortedActionList() ); if ( checked && checked != a ) { checked->setChecked( true ); } else if ( ! m_scheduleActions.isEmpty() ) { m_scheduleActions.keys().first()->setChecked( true ); } } slotViewSchedule( m_scheduleActionGroup->checkedAction() ); } void View::slotScheduleAdded( const MainSchedule *sch ) { if ( sch->type() != Schedule::Expected ) { return; // Only view expected } MainSchedule *s = const_cast( sch ); // debugPlan<name()<<" deleted="<isDeleted()<<"scheduled="<isScheduled(); QAction *checked = m_scheduleActionGroup->checkedAction(); if ( ! sch->isDeleted() && sch->isScheduled() ) { unplugActionList( "view_schedule_list" ); QAction *act = addScheduleAction( s ); plugActionList( "view_schedule_list", sortedActionList() ); if ( checked ) { checked->setChecked( true ); } else if ( act ) { act->setChecked( true ); } else if ( ! m_scheduleActions.isEmpty() ) { m_scheduleActions.keys().first()->setChecked( true ); } } slotViewSchedule( m_scheduleActionGroup->checkedAction() ); } void View::slotScheduleChanged( MainSchedule *sch ) { // debugPlan<name()<<" deleted="<isDeleted()<<"scheduled="<isScheduled(); if ( sch->isDeleted() || ! sch->isScheduled() ) { slotScheduleRemoved( sch ); return; } if ( m_scheduleActions.values().contains( sch ) ) { slotScheduleRemoved( sch ); // hmmm, how to avoid this? } slotScheduleAdded( sch ); } QAction *View::addScheduleAction( Schedule *sch ) { QAction *act = 0; if ( ! sch->isDeleted() && sch->isScheduled() ) { QString n = sch->name(); act = new KToggleAction( n, this); actionCollection()->addAction(n, act ); m_scheduleActions.insert( act, sch ); m_scheduleActionGroup->addAction( act ); //debugPlan<<"Add:"<manager(); } emit currentScheduleManagerChanged( 0 ); setLabel( 0 ); m_nextScheduleManager = sm; // Performance is very dependent on schedule manager change since a lot is recalculated // In case of multiple changes, only issue the last change if ( ! m_trigged ) { m_trigged = true; emit currentScheduleManagerChanged( 0 ); QTimer::singleShot( 0, this, SLOT(slotViewScheduleManager()) ); } } void View::slotActionDestroyed( QObject *o ) { //debugPlan<name(); m_scheduleActions.remove( static_cast( o ) ); // slotViewSchedule( m_scheduleActionGroup->checkedAction() ); } void View::slotPlugScheduleActions() { //debugPlan<removeAction( act ); delete act; } m_scheduleActions.clear(); QAction *ca = 0; foreach( ScheduleManager *sm, getProject().allScheduleManagers() ) { Schedule *sch = sm->expected(); if ( sch == 0 ) { continue; } QAction *act = addScheduleAction( sch ); if ( act && id == sch->id() ) { ca = act; } } plugActionList( "view_schedule_list", sortedActionList() ); if ( ca == 0 && m_scheduleActionGroup->actions().count() > 0 ) { ca = m_scheduleActionGroup->actions().first(); } if ( ca ) { ca->setChecked( true ); } slotViewSchedule( ca ); } void View::slotProjectCalculated( ScheduleManager *sm ) { // we only get here if current schedule was calculated if ( sm->isScheduled() ) { slotSelectionChanged( sm ); } } void View::slotCalculateSchedule( Project *project, ScheduleManager *sm ) { if ( project == 0 || sm == 0 ) { return; } if ( sm->parentManager() && ! sm->parentManager()->isScheduled() ) { // the parent must be scheduled return; } if ( sm == currentScheduleManager() ) { connect( project, SIGNAL(projectCalculated(ScheduleManager*)), this, SLOT(slotProjectCalculated(ScheduleManager*)) ); } CalculateScheduleCmd *cmd = new CalculateScheduleCmd( *project, sm, kundo2_i18nc("@info:status 1=schedule name", "Calculate %1", sm->name() ) ); getPart() ->addCommand( cmd ); slotUpdate(); } void View::slotRemoveCommands() { while ( ! m_undocommands.isEmpty() ) { m_undocommands.last()->undo(); delete m_undocommands.takeLast(); } } void View::slotBaselineSchedule( Project *project, ScheduleManager *sm ) { if ( project == 0 || sm == 0 ) { return; } if ( ! sm->isBaselined() && project->isBaselined() ) { KMessageBox::sorry( this, i18n( "Cannot baseline. The project is already baselined." ) ); return; } KUndo2Command *cmd; if ( sm->isBaselined() ) { KMessageBox::ButtonCode res = KMessageBox::warningContinueCancel( this, i18n( "This schedule is baselined. Do you want to remove the baseline?" ) ); if ( res == KMessageBox::Cancel ) { return; } cmd = new ResetBaselineScheduleCmd( *sm, kundo2_i18n( "Reset baseline %1", sm->name() ) ); } else { cmd = new BaselineScheduleCmd( *sm, kundo2_i18n( "Baseline %1", sm->name() ) ); } getPart() ->addCommand( cmd ); } void View::slotAddScheduleManager( Project *project ) { if ( project == 0 ) { return; } ScheduleManager *sm = project->createScheduleManager(); AddScheduleManagerCmd *cmd = new AddScheduleManagerCmd( *project, sm, -1, kundo2_i18n( "Add schedule %1", sm->name() ) ); getPart() ->addCommand( cmd ); } void View::slotDeleteScheduleManager( Project *project, ScheduleManager *sm ) { if ( project == 0 || sm == 0) { return; } DeleteScheduleManagerCmd *cmd = new DeleteScheduleManagerCmd( *project, sm, kundo2_i18n( "Delete schedule %1", sm->name() ) ); getPart() ->addCommand( cmd ); } void View::slotMoveScheduleManager( ScheduleManager *sm, ScheduleManager *parent, int index ) { if ( sm == 0 ) { return; } MoveScheduleManagerCmd *cmd = new MoveScheduleManagerCmd( sm, parent, index, kundo2_i18n( "Move schedule %1", sm->name() ) ); getPart() ->addCommand( cmd ); } void View::slotAddSubTask() { Task * node = getProject().createTask( getPart() ->config().taskDefaults() ); SubTaskAddDialog *dia = new SubTaskAddDialog( getProject(), *node, currentNode(), getProject().accounts(), this ); connect(dia, SIGNAL(finished(int)), SLOT(slotAddSubTaskFinished(int))); dia->show(); dia->raise(); dia->activateWindow(); } void View::slotAddSubTaskFinished( int result ) { SubTaskAddDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command *m = dia->buildCommand(); getPart() ->addCommand( m ); // add task to project } dia->deleteLater(); } void View::slotAddTask() { Task * node = getProject().createTask( getPart() ->config().taskDefaults() ); TaskAddDialog *dia = new TaskAddDialog( getProject(), *node, currentNode(), getProject().accounts(), this ); connect(dia, SIGNAL(finished(int)), SLOT(slotAddTaskFinished(int))); dia->show(); dia->raise(); dia->activateWindow(); } void View::slotAddTaskFinished( int result ) { TaskAddDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command *m = dia->buildCommand(); getPart() ->addCommand( m ); // add task to project } dia->deleteLater(); } void View::slotAddMilestone() { Task * node = getProject().createTask(); node->estimate() ->clear(); TaskAddDialog *dia = new TaskAddDialog( getProject(), *node, currentNode(), getProject().accounts(), this ); connect(dia, SIGNAL(finished(int)), SLOT(slotAddMilestoneFinished(int))); dia->show(); dia->raise(); dia->activateWindow(); } void View::slotAddMilestoneFinished( int result ) { TaskAddDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { MacroCommand *c = new MacroCommand( kundo2_i18n( "Add milestone" ) ); c->addCommand( dia->buildCommand() ); getPart() ->addCommand( c ); // add task to project } dia->deleteLater(); } void View::slotAddSubMilestone() { Task * node = getProject().createTask(); node->estimate() ->clear(); SubTaskAddDialog *dia = new SubTaskAddDialog( getProject(), *node, currentNode(), getProject().accounts(), this ); connect(dia, SIGNAL(finished(int)), SLOT(slotAddSubMilestoneFinished(int))); dia->show(); dia->raise(); dia->activateWindow(); } void View::slotAddSubMilestoneFinished( int result ) { SubTaskAddDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { MacroCommand *c = new MacroCommand( kundo2_i18n( "Add sub-milestone" ) ); c->addCommand( dia->buildCommand() ); getPart() ->addCommand( c ); // add task to project } dia->deleteLater(); } void View::slotDefineWBS() { //debugPlan; Project &p = getProject(); WBSDefinitionDialog *dia = new WBSDefinitionDialog( p, p.wbsDefinition(), this ); connect(dia, SIGNAL(finished(int)), SLOT(slotDefineWBSFinished(int))); dia->show(); dia->raise(); dia->activateWindow(); } void View::slotDefineWBSFinished( int result ) { //debugPlan; WBSDefinitionDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted ) { KUndo2Command *cmd = dia->buildCommand(); if ( cmd ) { getPart()->addCommand( cmd ); } } dia->deleteLater(); } void View::slotConfigure() { //debugPlan; if( KConfigDialog::showDialog("Plan Settings") ) { return; } ConfigDialog *dialog = new ConfigDialog( this, "Plan Settings", KPlatoSettings::self() ); dialog->addPage(new ConfigProjectPanel(), i18n("Project Defaults"), koIconName("calligraplan") ); dialog->addPage(new ConfigWorkVacationPanel(), i18n("Work & Vacation"), koIconName("view-calendar") ); dialog->addPage(new TaskDefaultPanel(), i18n("Task Defaults"), koIconName("view-task") ); dialog->addPage(new ColorsConfigPanel(), i18n("Task Colors"), koIconName("fill-color") ); dialog->addPage(new WorkPackageConfigPanel(), i18n("Work Package"), koIconName("calligraplanwork") ); dialog->show(); } void View::slotIntroduction() { m_tab->setCurrentIndex(0); } Calendar *View::currentCalendar() { ViewBase *v = dynamic_cast( m_tab->currentWidget() ); if ( v == 0 ) { return 0; } return v->currentCalendar(); } Node *View::currentNode() const { ViewBase *v = dynamic_cast( m_tab->currentWidget() ); if ( v == 0 ) { return 0; } Node * task = v->currentNode(); if ( 0 != task ) { return task; } return &( getProject() ); } Task *View::currentTask() const { ViewBase *v = dynamic_cast( m_tab->currentWidget() ); if ( v == 0 ) { return 0; } Node * task = v->currentNode(); if ( task ) { return dynamic_cast( task ); } return 0; } Resource *View::currentResource() { ViewBase *v = dynamic_cast( m_tab->currentWidget() ); if ( v == 0 ) { return 0; } return v->currentResource(); } ResourceGroup *View::currentResourceGroup() { ViewBase *v = dynamic_cast( m_tab->currentWidget() ); if ( v == 0 ) { return 0; } return v->currentResourceGroup(); } void View::slotOpenNode() { //debugPlan; Node * node = currentNode(); slotOpenNode( node ); } void View::slotOpenNode( Node *node ) { //debugPlan; if ( !node ) return ; switch ( node->type() ) { case Node::Type_Project: { Project * project = static_cast( node ); MainProjectDialog *dia = new MainProjectDialog( *project, this ); connect(dia, SIGNAL(dialogFinished(int)), SLOT(slotProjectEditFinished(int))); connect(dia, SIGNAL(sigLoadSharedResources(const QString&)), this, SLOT(slotInsertResourcesFile(const QString&))); dia->show(); dia->raise(); dia->activateWindow(); break; } case Node::Type_Subproject: //TODO break; case Node::Type_Task: { Task *task = static_cast( node ); TaskDialog *dia = new TaskDialog( getProject(), *task, getProject().accounts(), this ); connect(dia, SIGNAL(finished(int)), SLOT(slotTaskEditFinished(int))); dia->show(); dia->raise(); dia->activateWindow(); break; } case Node::Type_Milestone: { // Use the normal task dialog for now. // Maybe milestone should have it's own dialog, but we need to be able to // enter a duration in case we accidentally set a tasks duration to zero // and hence, create a milestone Task *task = static_cast( node ); TaskDialog *dia = new TaskDialog( getProject(), *task, getProject().accounts(), this ); connect(dia, SIGNAL(finished(int)), SLOT(slotTaskEditFinished(int))); dia->show(); dia->raise(); dia->activateWindow(); break; } case Node::Type_Summarytask: { Task *task = dynamic_cast( node ); Q_ASSERT( task ); SummaryTaskDialog *dia = new SummaryTaskDialog( *task, this ); connect(dia, SIGNAL(finished(int)), SLOT(slotSummaryTaskEditFinished(int))); dia->show(); dia->raise(); dia->activateWindow(); break; } default: break; // avoid warnings } } void View::slotProjectEditFinished( int result ) { MainProjectDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command * cmd = dia->buildCommand(); if ( cmd ) { getPart() ->addCommand( cmd ); } } dia->deleteLater(); } void View::slotTaskEditFinished( int result ) { TaskDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command * cmd = dia->buildCommand(); if ( cmd ) { getPart() ->addCommand( cmd ); } } dia->deleteLater(); } void View::slotSummaryTaskEditFinished( int result ) { SummaryTaskDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command * cmd = dia->buildCommand(); if ( cmd ) { getPart() ->addCommand( cmd ); } } dia->deleteLater(); } ScheduleManager *View::currentScheduleManager() const { Schedule *s = m_scheduleActions.value( m_scheduleActionGroup->checkedAction() ); return s == 0 ? 0 : s->manager(); } long View::activeScheduleId() const { Schedule *s = m_scheduleActions.value( m_scheduleActionGroup->checkedAction() ); return s == 0 ? -1 : s->id(); } void View::setActiveSchedule( long id ) { if ( id != -1 ) { QMap::const_iterator it = m_scheduleActions.constBegin(); for (; it != m_scheduleActions.constEnd(); ++it ) { if ( it.value()->id() == id ) { it.key()->setChecked( true ); slotViewSchedule( it.key() ); // signal not emitted from group, so trigger it here break; } } } } void View::slotTaskProgress() { //debugPlan; Node * node = currentNode(); if ( !node ) return ; switch ( node->type() ) { case Node::Type_Project: { break; } case Node::Type_Subproject: //TODO break; case Node::Type_Task: { Task *task = dynamic_cast( node ); Q_ASSERT( task ); TaskProgressDialog *dia = new TaskProgressDialog( *task, currentScheduleManager(), getProject().standardWorktime(), this ); connect(dia, SIGNAL(finished(int)), SLOT(slotTaskProgressFinished(int))); dia->show(); dia->raise(); dia->activateWindow(); break; } case Node::Type_Milestone: { Task *task = dynamic_cast( node ); Q_ASSERT( task ); MilestoneProgressDialog *dia = new MilestoneProgressDialog( *task, this ); connect(dia, SIGNAL(finished(int)), SLOT(slotMilestoneProgressFinished(int))); dia->show(); dia->raise(); dia->activateWindow(); break; } case Node::Type_Summarytask: { // TODO break; } default: break; // avoid warnings } } void View::slotTaskProgressFinished( int result ) { TaskProgressDialog *dia = qobject_cast(sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command * m = dia->buildCommand(); if ( m ) { getPart() ->addCommand( m ); } } dia->deleteLater(); } void View::slotMilestoneProgressFinished( int result ) { MilestoneProgressDialog *dia = qobject_cast(sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command * m = dia->buildCommand(); if ( m ) { getPart() ->addCommand( m ); } } dia->deleteLater(); } void View::slotTaskDescription() { //debugPlan; Node * node = currentNode(); if ( !node ) return ; switch ( node->type() ) { case Node::Type_Project: { break; } case Node::Type_Subproject: //TODO break; case Node::Type_Task: case Node::Type_Milestone: case Node::Type_Summarytask: { Task *task = dynamic_cast( node ); Q_ASSERT( task ); TaskDescriptionDialog *dia = new TaskDescriptionDialog( *task, this ); connect(dia, SIGNAL(finished(int)), SLOT(slotTaskDescriptionFinished(int))); dia->show(); dia->raise(); dia->activateWindow(); break; } default: break; // avoid warnings } } void View::slotTaskDescriptionFinished( int result ) { TaskDescriptionDialog *dia = qobject_cast(sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command * m = dia->buildCommand(); if ( m ) { getPart() ->addCommand( m ); } } dia->deleteLater(); } void View::slotDeleteTask( QList lst ) { //debugPlan; foreach ( Node *n, lst ) { if ( n->isScheduled() ) { KMessageBox::ButtonCode res = KMessageBox::warningContinueCancel( this, i18n( "A task that has been scheduled will be deleted. This will invalidate the schedule." ) ); if ( res == KMessageBox::Cancel ) { return; } break; } } if ( lst.count() == 1 ) { getPart()->addCommand( new NodeDeleteCmd( lst.takeFirst(), kundo2_i18n( "Delete task" ) ) ); return; } int num = 0; MacroCommand *cmd = new MacroCommand( kundo2_i18np( "Delete task", "Delete tasks", lst.count() ) ); while ( !lst.isEmpty() ) { Node *node = lst.takeFirst(); if ( node == 0 || node->parentNode() == 0 ) { debugPlan << ( node ?"Task is main project" :"No current task" ); continue; } bool del = true; foreach ( Node *n, lst ) { if ( node->isChildOf( n ) ) { del = false; // node is going to be deleted when we delete n break; } } if ( del ) { //debugPlan<name(); cmd->addCommand( new NodeDeleteCmd( node, kundo2_i18n( "Delete task" ) ) ); num++; } } if ( num > 0 ) { getPart()->addCommand( cmd ); } else { delete cmd; } } void View::slotDeleteTask( Node *node ) { //debugPlan; if ( node == 0 || node->parentNode() == 0 ) { debugPlan << ( node ?"Task is main project" :"No current task" ); return ; } if ( node->isScheduled() ) { KMessageBox::ButtonCode res = KMessageBox::warningContinueCancel( this, i18n( "This task has been scheduled. This will invalidate the schedule." ) ); if ( res == KMessageBox::Cancel ) { return; } } NodeDeleteCmd *cmd = new NodeDeleteCmd( node, kundo2_i18n( "Delete task" ) ); getPart() ->addCommand( cmd ); } void View::slotDeleteTask() { //debugPlan; return slotDeleteTask( currentNode() ); } void View::slotIndentTask() { //debugPlan; Node * node = currentNode(); if ( node == 0 || node->parentNode() == 0 ) { debugPlan << ( node ?"Task is main project" :"No current task" ); return ; } if ( getProject().canIndentTask( node ) ) { NodeIndentCmd * cmd = new NodeIndentCmd( *node, kundo2_i18n( "Indent task" ) ); getPart() ->addCommand( cmd ); } } void View::slotUnindentTask() { //debugPlan; Node * node = currentNode(); if ( node == 0 || node->parentNode() == 0 ) { debugPlan << ( node ?"Task is main project" :"No current task" ); return ; } if ( getProject().canUnindentTask( node ) ) { NodeUnindentCmd * cmd = new NodeUnindentCmd( *node, kundo2_i18n( "Unindent task" ) ); getPart() ->addCommand( cmd ); } } void View::slotMoveTaskUp() { //debugPlan; Node * task = currentNode(); if ( 0 == task ) { // is always != 0. At least we would get the Project, but you never know who might change that // so better be careful errorPlan << "No current task" << endl; return ; } if ( Node::Type_Project == task->type() ) { debugPlan <<"The root node cannot be moved up"; return ; } if ( getProject().canMoveTaskUp( task ) ) { NodeMoveUpCmd * cmd = new NodeMoveUpCmd( *task, kundo2_i18n( "Move task up" ) ); getPart() ->addCommand( cmd ); } } void View::slotMoveTaskDown() { //debugPlan; Node * task = currentNode(); if ( 0 == task ) { // is always != 0. At least we would get the Project, but you never know who might change that // so better be careful return ; } if ( Node::Type_Project == task->type() ) { debugPlan <<"The root node cannot be moved down"; return ; } if ( getProject().canMoveTaskDown( task ) ) { NodeMoveDownCmd * cmd = new NodeMoveDownCmd( *task, kundo2_i18n( "Move task down" ) ); getPart() ->addCommand( cmd ); } } void View::slotAddRelation( Node *par, Node *child ) { //debugPlan; Relation * rel = new Relation( par, child ); AddRelationDialog *dia = new AddRelationDialog( getProject(), rel, this ); connect(dia, SIGNAL(finished(int)), SLOT(slotAddRelationFinished(int))); dia->show(); dia->raise(); dia->activateWindow(); } void View::slotAddRelationFinished( int result ) { AddRelationDialog *dia = qobject_cast(sender() ); if ( dia == 0 ) { return; } if ( result == QDialog::Accepted) { KUndo2Command * m = dia->buildCommand(); if ( m ) { getPart() ->addCommand( m ); } } dia->deleteLater(); } void View::slotAddRelation( Node *par, Node *child, int linkType ) { //debugPlan; if ( linkType == Relation::FinishStart || linkType == Relation::StartStart || linkType == Relation::FinishFinish ) { Relation * rel = new Relation( par, child, static_cast( linkType ) ); getPart() ->addCommand( new AddRelationCmd( getProject(), rel, kundo2_i18n( "Add task dependency" ) ) ); } else { slotAddRelation( par, child ); } } void View::slotModifyRelation( Relation *rel ) { //debugPlan; ModifyRelationDialog *dia = new ModifyRelationDialog( getProject(), rel, this ); connect(dia, SIGNAL(finished(int)), SLOT(slotModifyRelationFinished(int))); dia->show(); dia->raise(); dia->activateWindow(); } void View::slotModifyRelationFinished( int result ) { ModifyRelationDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return ; } if ( result == QDialog::Accepted) { KUndo2Command *cmd = dia->buildCommand(); if ( cmd ) { getPart() ->addCommand( cmd ); } } dia->deleteLater(); } void View::slotModifyRelation( Relation *rel, int linkType ) { //debugPlan; if ( linkType == Relation::FinishStart || linkType == Relation::StartStart || linkType == Relation::FinishFinish ) { getPart() ->addCommand( new ModifyRelationTypeCmd( rel, static_cast( linkType ) ) ); } else { slotModifyRelation( rel ); } } void View::slotModifyRelation() { ViewBase *v = dynamic_cast( m_tab->currentWidget() ); if ( v == 0 ) { return; } Relation *rel = v->currentRelation(); if ( rel ) { slotModifyRelation( rel ); } } void View::slotDeleteRelation() { ViewBase *v = dynamic_cast( m_tab->currentWidget() ); if ( v == 0 ) { return; } Relation *rel = v->currentRelation(); if ( rel ) { getPart()->addCommand( new DeleteRelationCmd( getProject(), rel, kundo2_i18n( "Delete task dependency" ) ) ); } } void View::slotEditResource() { //debugPlan; slotEditResource( currentResource() ); } void View::slotEditResource( Resource *resource ) { if ( resource == 0 ) { return ; } ResourceDialog *dia = new ResourceDialog( getProject(), resource, this ); connect(dia, SIGNAL(finished(int)), SLOT(slotEditResourceFinished(int))); dia->show(); dia->raise(); dia->activateWindow(); } void View::slotEditResourceFinished( int result ) { //debugPlan; ResourceDialog *dia = qobject_cast( sender() ); if ( dia == 0 ) { return ; } if ( result == QDialog::Accepted) { KUndo2Command * cmd = dia->buildCommand(); if ( cmd ) getPart() ->addCommand( cmd ); } dia->deleteLater(); } void View::slotDeleteResource( Resource *resource ) { getPart()->addCommand( new RemoveResourceCmd( resource->parentGroup(), resource, kundo2_i18n( "Delete resource" ) ) ); } void View::slotDeleteResourceGroup( ResourceGroup *group ) { getPart()->addCommand( new RemoveResourceGroupCmd( group->project(), group, kundo2_i18n( "Delete resourcegroup" ) ) ); } void View::slotDeleteResourceObjects( QObjectList lst ) { //debugPlan; foreach ( QObject *o, lst ) { Resource *r = qobject_cast( o ); if ( r && r->isScheduled() ) { KMessageBox::ButtonCode res = KMessageBox::warningContinueCancel( this, i18n( "A resource that has been scheduled will be deleted. This will invalidate the schedule." ) ); if ( res == KMessageBox::Cancel ) { return; } break; } ResourceGroup *g = qobject_cast( o ); if ( g && g->isScheduled() ) { KMessageBox::ButtonCode res = KMessageBox::warningContinueCancel( this, i18n( "A resource that has been scheduled will be deleted. This will invalidate the schedule." ) ); if ( res == KMessageBox::Cancel ) { return; } break; } } if ( lst.count() == 1 ) { Resource *r = qobject_cast( lst.first() ); if ( r ) { slotDeleteResource( r ); } else { ResourceGroup *g = qobject_cast( lst.first() ); if ( g ) { slotDeleteResourceGroup( g ); } } return; } // int num = 0; MacroCommand *cmd = 0, *rc = 0, *gc = 0; foreach ( QObject *o, lst ) { Resource *r = qobject_cast( o ); if ( r ) { if ( rc == 0 ) rc = new MacroCommand( KUndo2MagicString() ); rc->addCommand( new RemoveResourceCmd( r->parentGroup(), r ) ); continue; } ResourceGroup *g = qobject_cast( o ); if ( g ) { if ( gc == 0 ) gc = new MacroCommand( KUndo2MagicString() ); gc->addCommand( new RemoveResourceGroupCmd( g->project(), g ) ); } } if ( rc || gc ) { KUndo2MagicString s; if ( rc && gc ) { s = kundo2_i18n( "Delete resourcegroups and resources" ); } else if ( rc ) { s = kundo2_i18np( "Delete resource", "Delete resources", lst.count() ); } else { s = kundo2_i18np( "Delete resourcegroup", "Delete resourcegroups", lst.count() ); } cmd = new MacroCommand( s ); } if ( rc ) cmd->addCommand( rc ); if ( gc ) cmd->addCommand( gc ); if ( cmd ) getPart()->addCommand( cmd ); } void View::updateReadWrite( bool readwrite ) { m_readWrite = readwrite; m_viewlist->setReadWrite( readwrite ); } MainDocument *View::getPart() const { return ( MainDocument * ) koDocument(); } KoPart *View::getKoPart() const { return m_partpart; } void View::slotConnectNode() { //debugPlan; /* NodeItem *curr = ganttview->currentItem(); if (curr) { debugPlan<<"node="<getNode().name(); }*/ } QMenu * View::popupMenu( const QString& name ) { //debugPlan; if ( factory() ) { return ( ( QMenu* ) factory() ->container( name, this ) ); } debugPlan<<"No factory"; return 0L; } void View::slotUpdate() { //debugPlan<<"calculate="<currentWidget() ); } void View::slotGuiActivated( ViewBase *view, bool activate ) { //FIXME: Avoid unplug if possible, it flashes the gui // always unplug, in case they already are plugged foreach( const QString &name, view->actionListNames() ) { unplugActionList( name ); } if ( activate ) { foreach( const QString &name, view->actionListNames() ) { plugActionList( name, view->actionList( name ) ); } foreach ( DockWidget *ds, view->dockers() ) { m_dockers.append( ds ); ds->activate( mainWindow() ); } if (!m_dockers.isEmpty()) {debugPlan<<"Added dockers:"<deactivate( mainWindow() ); } } } void View::guiActivateEvent( bool activated ) { if ( activated ) { // plug my own actionlists, they may be gone slotPlugScheduleActions(); } // propagate to sub-view ViewBase *v = dynamic_cast( m_tab->currentWidget() ); if ( v ) { v->setGuiActive( activated ); } } void View::slotViewListItemRemoved( ViewListItem *item ) { getPart()->removeViewListItem( this, item ); } void View::removeViewListItem( const ViewListItem *item ) { if ( item == 0 ) { return; } ViewListItem *itm = m_viewlist->findItem( item->tag() ); if ( itm == 0 ) { return; } m_viewlist->removeViewListItem( itm ); return; } void View::slotViewListItemInserted( ViewListItem *item, ViewListItem *parent, int index ) { getPart()->insertViewListItem( this, item, parent, index ); } void View::addViewListItem( const ViewListItem *item, const ViewListItem *parent, int index ) { if ( item == 0 ) { return; } if ( parent == 0 ) { if ( item->type() != ViewListItem::ItemType_Category ) { return; } m_viewlist->blockSignals( true ); ViewListItem *cat = m_viewlist->addCategory( item->tag(), item->text( 0 ) ); cat->setToolTip( 0, item->toolTip( 0 ) ); m_viewlist->blockSignals( false ); return; } ViewListItem *cat = m_viewlist->findCategory( parent->tag() ); if ( cat == 0 ) { return; } m_viewlist->blockSignals( true ); createView( cat, item->viewType(), item->tag(), item->text( 0 ), item->toolTip( 0 ), index ); m_viewlist->blockSignals( false ); } void View::createReportView(const QDomDocument &doc) { #ifdef PLAN_USE_KREPORT QPointer vd = new ViewListReportsDialog( this, *m_viewlist, doc, this ); vd->exec(); // FIXME make non-crash delete vd; #endif } void View::slotOpenReportFile() { #ifdef PLAN_USE_KREPORT QFileDialog *dlg = new QFileDialog(this); connect(dlg, SIGNAL(finished(int)), SLOT(slotOpenReportFileFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); #endif } void View::slotOpenReportFileFinished( int result ) { #ifdef PLAN_USE_KREPORT QFileDialog *fdlg = qobject_cast( sender() ); if ( fdlg == 0 || result != QDialog::Accepted ) { return; } QString fn = fdlg->selectedFiles().value(0); if ( fn.isEmpty() ) { return; } QFile file( fn ); if ( ! file.open( QIODevice::ReadOnly | QIODevice::Text ) ) { KMessageBox::sorry( this, xi18nc( "@info", "Cannot open file:
%1", fn ) ); return; } QDomDocument doc; doc.setContent( &file ); createReportView(doc); #endif } void View::slotReportDesignFinished( int /*result */) { #ifdef PLAN_USE_KREPORT if ( sender() ) { sender()->deleteLater(); } #endif } void View::slotCreateView() { ViewListDialog *dlg = new ViewListDialog( this, *m_viewlist, this ); connect(dlg, SIGNAL(finished(int)), SLOT(slotCreateViewFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); } void View::slotCreateViewFinished( int ) { if ( sender() ) { sender()->deleteLater(); } } void View::slotViewActivated( ViewListItem *item, ViewListItem *prev ) { QApplication::setOverrideCursor( Qt::WaitCursor ); if ( prev && prev->type() == ViewListItem::ItemType_Category && m_viewlist->previousViewItem() ) { // A view is shown anyway... ViewBase *v = qobject_cast( m_viewlist->previousViewItem()->view() ); if ( v ) { v->setGuiActive( false ); } } else if ( prev && prev->type() == ViewListItem::ItemType_SubView ) { ViewBase *v = qobject_cast( prev->view() ); if ( v ) { v->setGuiActive( false ); } } if ( item && item->type() == ViewListItem::ItemType_SubView ) { //debugPlan<<"Activate:"<setCurrentWidget( item->view() ); // Add sub-view specific gui ViewBase *v = dynamic_cast( m_tab->currentWidget() ); if ( v ) { v->setGuiActive( true ); } } QApplication::restoreOverrideCursor(); } QWidget *View::canvas() const { return m_tab->currentWidget();//KoView::canvas(); } KoPageLayout View::pageLayout() const { return currentView()->pageLayout(); } QPrintDialog *View::createPrintDialog( KoPrintJob *printJob, QWidget *parent ) { debugPlan<( printJob ); if ( ! job ) { return 0; } QPrintDialog *dia = KoView::createPrintDialog( job, parent ); PrintingDialog *j = dynamic_cast( job ); if ( j ) { new PrintingControlPrivate( j, dia ); } return dia; } void View::slotCurrentChanged( int view ) { m_visitedViews << view; ViewListItem *item = m_viewlist->findItem( qobject_cast( m_tab->currentWidget() ) ); m_viewlist->setCurrentItem( item ); } void View::slotSelectDefaultView() { m_tab->setCurrentIndex(qMin(m_defaultView, m_tab->count()-1)); } void View::updateView( QWidget * ) { QApplication::setOverrideCursor( Qt::WaitCursor ); //setScheduleActionsEnabled(); QWidget *widget2; widget2 = m_viewlist->findView( "ResourceAssignmentView" ); if ( widget2 && m_updateResourceAssignmentView ) static_cast( widget2 ) ->draw( getProject() ); m_updateResourceAssignmentView = false; QApplication::restoreOverrideCursor(); } void View::slotRenameNode( Node *node, const QString& name ) { //debugPlan<type() ) { case Node::Type_Task: s = kundo2_i18n( "Modify task name" ); break; case Node::Type_Milestone: s = kundo2_i18n( "Modify milestone name" ); break; case Node::Type_Summarytask: s = kundo2_i18n( "Modify summarytask name" ); break; case Node::Type_Project: s = kundo2_i18n( "Modify project name" ); break; } NodeModifyNameCmd * cmd = new NodeModifyNameCmd( *node, name, s ); getPart() ->addCommand( cmd ); } } void View::slotPopupMenu( const QString& menuname, const QPoint & pos ) { QMenu * menu = this->popupMenu( menuname ); if ( menu ) { //debugPlan<actions().count(); ViewBase *v = qobject_cast( m_tab->currentWidget() ); //debugPlan< lst; if ( v ) { lst = v->contextActionList(); debugPlan<addSeparator(); foreach ( QAction *a, lst ) { menu->addAction( a ); } } } menu->exec( pos ); foreach ( QAction *a, lst ) { menu->removeAction( a ); } } } void View::slotPopupMenu( const QString& menuname, const QPoint &pos, ViewListItem *item ) { //debugPlan<context(); if ( ctx == 0 || ! ctx->isLoaded() ) { return false; } KoXmlElement n = ctx->context(); QString cv = n.attribute( "current-view" ); if ( ! cv.isEmpty() ) { m_viewlist->setSelected( m_viewlist->findItem( cv ) ); } else debugPlan<<"No current view"; long id = n.attribute( "current-schedule", "-1" ).toLong(); if ( id != -1 ) { setActiveSchedule( id ); } else debugPlan<<"No current schedule"; return true; } void View::saveContext( QDomElement &me ) const { //debugPlan; long id = activeScheduleId(); if ( id != -1 ) { me.setAttribute( "current-schedule", QString::number((qlonglong)id) ); } ViewListItem *item = m_viewlist->findItem( qobject_cast( m_tab->currentWidget() ) ); if ( item ) { me.setAttribute("current-view", item->tag() ); } m_viewlist->save( me ); } bool View::loadWorkPackage( Project &project, const QUrl &url ) { return getPart()->loadWorkPackage( project, url ); } void View::setLabel( ScheduleManager *sm ) { //debugPlan; Schedule *s = sm == 0 ? 0 : sm->expected(); if ( s && !s->isDeleted() && s->isScheduled() ) { m_estlabel->setText( sm->name() ); return; } m_estlabel->setText( xi18nc( "@info:status", "Not scheduled" ) ); } void View::slotWorkPackageLoaded() { debugPlan<workPackages(); } void View::slotMailWorkpackage( Node *node, Resource *resource ) { debugPlan; QTemporaryFile tmpfile(QDir::tempPath() + QLatin1String("/calligraplanwork_XXXXXX") + QLatin1String( ".planwork" )); tmpfile.setAutoRemove( false ); if ( ! tmpfile.open() ) { debugPlan<<"Failed to open file"; KMessageBox::error(0, i18n("Failed to open temporary file" ) ); return; } QUrl url = QUrl::fromLocalFile( tmpfile.fileName() ); if ( ! getPart()->saveWorkPackageUrl( url, node, activeScheduleId(), resource ) ) { debugPlan<<"Failed to save to file"; KMessageBox::error(0, xi18nc( "@info", "Failed to save to temporary file:
%1", url.url() ) ); return; } QStringList attachURLs; attachURLs << url.url(); QString to = resource == 0 ? node->leader() : ( resource->name() + " <" + resource->email() + '>' ); QString cc; QString bcc; QString subject = i18n( "Work Package: %1", node->name() ); QString body = i18nc( "1=project name, 2=task name", "%1\n%2", getProject().name(), node->name() ); QString messageFile; KToolInvocation::invokeMailer( to, cc, bcc, subject, body, messageFile, attachURLs ); } void View::slotMailWorkpackages( const QList &nodes, Resource *resource ) { debugPlan; if ( resource == 0 ) { warnPlan<<"No resource, we don't handle node->leader() yet"; return; } QString to = resource->name() + " <" + resource->email() + '>'; QString subject = i18n( "Work Package for project: %1", getProject().name() ); QString body; QStringList attachURLs; foreach ( Node *n, nodes ) { QTemporaryFile tmpfile(QDir::tempPath() + QLatin1String("/calligraplanwork_XXXXXX") + QLatin1String( ".planwork" )); tmpfile.setAutoRemove( false ); if ( ! tmpfile.open() ) { debugPlan<<"Failed to open file"; KMessageBox::error(0, i18n("Failed to open temporary file" ) ); return; } QUrl url = QUrl::fromLocalFile( tmpfile.fileName() ); if ( ! getPart()->saveWorkPackageUrl( url, n, activeScheduleId(), resource ) ) { debugPlan<<"Failed to save to file"; KMessageBox::error(0, xi18nc( "@info", "Failed to save to temporary file:
%1", url.url() ) ); return; } attachURLs << url.url(); body += n->name() + '\n'; } QString cc; QString bcc; QString messageFile; KToolInvocation::invokeMailer( to, cc, bcc, subject, body, messageFile, attachURLs ); } void View::slotCurrencyConfig() { LocaleConfigMoneyDialog *dlg = new LocaleConfigMoneyDialog( getProject().locale(), this ); connect(dlg, SIGNAL(finished(int)), SLOT(slotCurrencyConfigFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); } void View::slotCurrencyConfigFinished( int result ) { LocaleConfigMoneyDialog *dlg = qobject_cast( sender() ); if ( dlg == 0 ) { return; } if ( result == QDialog::Accepted ) { KUndo2Command *c = dlg->buildCommand( getProject() ); if ( c ) { getPart()->addCommand( c ); } } dlg->deleteLater(); } void View::saveTaskModule( const QUrl &url, Project *project ) { // NOTE: workaround: KoResourcePaths::saveLocation( "calligraplan_taskmodules" ); does not work const QString dir = KoResourcePaths::saveLocation( "appdata", "taskmodules/" ); debugPlan<<"dir="<setDocument( doc ); doc->disconnect(); // doc shall not handle feedback from openUrl() doc->setAutoSave( 0 ); //disable doc->insertProject( *project, 0, 0 ); // FIXME: destroys project, find better way doc->getProject().setName( project->name() ); doc->getProject().setLeader( project->leader() ); doc->getProject().setDescription( project->description() ); doc->saveNativeFormat( dir + url.fileName() ); part->deleteLater(); // also deletes document debugPlan<" "" "" "%1" "" "" "predefined" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "" "") .arg( i18n( "Report" ), i18nc( "Project manager", "Manager:" ), i18n( "Project:" ), i18n( "Task Status Report" ), i18nc( "As in: Page 1 of 2", "of" ), i18n( "Page" ), i18nc( "Task name", "Name" ), i18nc( "Task completion", "Completion (%)" ) ); #endif return s; } } //KPlato namespace diff --git a/plan/libs/kernel/CMakeLists.txt b/plan/libs/kernel/CMakeLists.txt index 619f1db9d8d..afa63ae4a18 100644 --- a/plan/libs/kernel/CMakeLists.txt +++ b/plan/libs/kernel/CMakeLists.txt @@ -1,58 +1,60 @@ -add_subdirectory( tests ) +if(BUILD_TESTING) + add_subdirectory( tests ) +endif() include_directories(${KOODF_INCLUDES} ${CMAKE_SOURCE_DIR}/libs/widgetutils ${KUNDO2_INCLUDES}) ########### KPlato kernel library ############### set(kplatokernel_LIB_SRCS kptglobal.cpp kptlocale.cpp kpteffortcostmap.cpp kptdocuments.cpp kptaccount.cpp kptappointment.cpp kptnode.cpp kptproject.cpp kptrelation.cpp kptresource.cpp kpttask.cpp kptduration.cpp kptdatetime.cpp kptcalendar.cpp kptschedule.cpp kptwbsdefinition.cpp kptcommand.cpp kptpackage.cpp kptdebug.cpp kptschedulerplugin.cpp kptconfigbase.cpp KPlatoXmlLoaderBase.cpp ) add_library(kplatokernel SHARED ${kplatokernel_LIB_SRCS}) generate_export_header(kplatokernel) target_link_libraries(kplatokernel PUBLIC koodf kowidgetutils kundo2 KF5::KIOWidgets ) if(KF5Holidays_FOUND) target_link_libraries(kplatokernel PUBLIC KF5::Holidays) endif() set_target_properties(kplatokernel PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) install(TARGETS kplatokernel ${INSTALL_TARGETS_DEFAULT_ARGS}) # TODO: with the new embedded JSON data for plugins there is no schema ATM to define extended properties # plan_schedulerplugin.desktop diff --git a/plan/libs/kernel/kpteffortcostmap.h b/plan/libs/kernel/kpteffortcostmap.h index 7afe279c047..3dfd69f4ec7 100644 --- a/plan/libs/kernel/kpteffortcostmap.h +++ b/plan/libs/kernel/kpteffortcostmap.h @@ -1,307 +1,307 @@ /* This file is part of the KDE project Copyright (C) 2005 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; version 2 of the License. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KPTEFFORTCOST_H #define KPTEFFORTCOST_H #include #include #include "kptduration.h" #include "kptdebug.h" #include #include namespace KPlato { class KPLATOKERNEL_EXPORT EffortCost { public: EffortCost() : m_effort(Duration::zeroDuration), m_cost(0), m_bcwpEffort(0.0), m_bcwpCost(0.0) {} - EffortCost(const Duration &effort, const double cost) + EffortCost(KPlato::Duration effort, const double cost) : m_effort(effort), m_cost(cost), m_bcwpEffort(0.0), m_bcwpCost(0.0) { //debugPlan; } ~EffortCost() { //debugPlan; } double hours() const { return m_effort.toDouble( Duration::Unit_h ); } Duration effort() const { return m_effort; } - void setEffort( const Duration &effort ) { m_effort = effort; } + void setEffort( KPlato::Duration effort ) { m_effort = effort; } double cost() const { return m_cost; } void setCost(double cost) { m_cost = cost; } void setBcwpEffort( double value ) { m_bcwpEffort = value; } double bcwpEffort() const { return m_bcwpEffort; } void setBcwpCost( double value ) { m_bcwpCost = value; } double bcwpCost() const { return m_bcwpCost; } void add(const Duration &effort, double cost, double bcwpEffort = 0.0, double bcwpCost = 0.0 ); EffortCost &operator+=(const EffortCost &ec) { add(ec.m_effort, ec.m_cost, ec.m_bcwpEffort, ec.m_bcwpCost); return *this; } #ifndef QT_NO_DEBUG_STREAM QDebug debug( QDebug dbg) const; #endif private: Duration m_effort; double m_cost; double m_bcwpEffort; double m_bcwpCost; }; typedef QMap EffortCostDayMap; class KPLATOKERNEL_EXPORT EffortCostMap { public: EffortCostMap() : m_days() { //debugPlan; } EffortCostMap( const EffortCostMap &map ); ~EffortCostMap() { //debugPlan; m_days.clear(); } void clear() { m_days.clear(); } - EffortCost effortCost(const QDate &date) const { + EffortCost effortCost(QDate date) const { EffortCost ec; if (!date.isValid()) { //errorPlan<<"Date not valid"; return ec; } EffortCostDayMap::const_iterator it = m_days.find(date); if (it != m_days.end()) ec = it.value(); return ec; } void insert(const QDate &date, const EffortCost &ec ); - void insert(const QDate &date, const Duration &effort, const double cost) { + void insert(QDate date, KPlato::Duration effort, const double cost) { if (!date.isValid()) { //errorPlan<<"Date not valid"; return; } m_days.insert(date, EffortCost(effort, cost)); } /** * If data for this date already exists add the new values to the old, * else the new values are inserted. */ - EffortCost &add(const QDate &date, const Duration &effort, const double cost) { + EffortCost &add(QDate date, KPlato::Duration effort, const double cost) { return add(date, EffortCost(effort, cost)); } /** * If data for this date already exists add the new values to the old, * else the new value is inserted. */ - EffortCost &add(const QDate &date, const EffortCost &ec) { + EffortCost &add(QDate date, const EffortCost &ec) { if (!date.isValid()) { //errorPlan<<"Date not valid"; return zero(); } //debugPlan< date ) { break; } cost += it.value().cost(); } return cost; } - Duration effortTo( const QDate &date ) const { + Duration effortTo( QDate date ) const { Duration eff; EffortCostDayMap::const_iterator it; for(it = m_days.constBegin(); it != m_days.constEnd(); ++it) { if ( it.key() > date ) { break; } eff += it.value().effort(); } return eff; } - double hoursTo( const QDate &date ) const { + double hoursTo( QDate date ) const { double eff = 0.0; EffortCostDayMap::const_iterator it; for(it = m_days.constBegin(); it != m_days.constEnd(); ++it) { if ( it.key() > date ) { break; } eff += it.value().hours(); } return eff; } /// Return the BCWP cost to @p date. (BSWP is cumulative) double bcwpCost( const QDate &date ) const; /// Return the BCWP effort to @p date. (BSWP is cumulative) double bcwpEffort( const QDate &date ) const; /// Return the BCWP total cost. Since BCWP is cumulative this is the last entry. double bcwpTotalCost() const { double cost = 0.0; if ( ! m_days.isEmpty() ) { cost = m_days.values().last().bcwpCost(); } return cost; } /// Return the BCWP total cost. Since BCWP is cumulative this is the last entry. double bcwpTotalEffort() const { double eff = 0.0; if ( ! m_days.isEmpty() ) { eff = m_days.values().last().bcwpEffort(); } return eff; } QDate startDate() const { return m_days.isEmpty() ? QDate() : m_days.keys().first(); } QDate endDate() const { return m_days.isEmpty() ? QDate() : m_days.keys().last(); } #ifndef QT_NO_DEBUG_STREAM QDebug debug( QDebug dbg) const; #endif private: EffortCost &zero() { return m_zero; } private: EffortCost m_zero; EffortCostDayMap m_days; }; } //namespace KPlato Q_DECLARE_METATYPE( KPlato::EffortCost ) Q_DECLARE_METATYPE( KPlato::EffortCostMap ) #ifndef QT_NO_DEBUG_STREAM KPLATOKERNEL_EXPORT QDebug operator<<( QDebug dbg, const KPlato::EffortCost &ec ); KPLATOKERNEL_EXPORT QDebug operator<<( QDebug dbg, const KPlato::EffortCost *ec ); KPLATOKERNEL_EXPORT QDebug operator<<( QDebug dbg, const KPlato::EffortCostMap &i ); #endif #endif diff --git a/plan/libs/kernel/kptmap.h b/plan/libs/kernel/kptmap.h index 07bb54c7137..bcd208a7f6c 100644 --- a/plan/libs/kernel/kptmap.h +++ b/plan/libs/kernel/kptmap.h @@ -1,165 +1,165 @@ /* This file is part of the KDE project Copyright (C) 2004 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; version 2 of the License. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTMAP_H #define KPTMAP_H #include "kplatokernel_export.h" #include "kptcalendar.h" #include #include #include namespace KPlato { typedef QMap DateMapType; class KPLATOKERNEL_EXPORT DateMap : public DateMapType { public: DateMap() {} virtual ~DateMap() {} - virtual bool contains(const QDate &date) const { return DateMapType::contains(date.toString(Qt::ISODate)); } + virtual bool contains(QDate date) const { return DateMapType::contains(date.toString(Qt::ISODate)); } void insert(const QString &date, int state=CalendarDay::NonWorking) { //debugPlan< IntMapType; class KPLATOKERNEL_EXPORT IntMap : public IntMapType { public: IntMap() {} virtual ~IntMap() {} void insert(int key, int state=CalendarDay::NonWorking) { if (state == CalendarDay::None) IntMapType::remove(key); else IntMapType::insert(key, state); } virtual int state(int key) const { IntMapType::ConstIterator it = IntMapType::find(key); if (it == IntMapType::end()) return 0; else return it.value(); } bool operator==(const IntMap &m) const { return keys() == m.keys() && values() == m.values(); } bool operator!=(const IntMap &m) const { return keys() != m.keys() || values() != m.values(); } // boolean use void toggle(int key, int state=CalendarDay::NonWorking) { if ( IntMapType::contains(key) ) remove(key); else insert(key, state); } void toggleClear(int key, int state=CalendarDay::NonWorking) { bool s =contains(key); clear(); if (!s) insert(key, state); } }; class KPLATOKERNEL_EXPORT WeekMap : public IntMap { public: bool contains(int week, int year) { return IntMap::contains(week*10000 + year); } - bool contains(const QPair &week) { return contains(week.first, week.second); } + bool contains(QPair week) { return contains(week.first, week.second); } void insert(int week, int year, int state=CalendarDay::NonWorking) { if (week < 1 || week > 53) { errorPlan<<"Illegal week number: "< &week, int state=CalendarDay::NonWorking) { insert(week.first, week.second, state); } + void insert(QPair week, int state=CalendarDay::NonWorking) { insert(week.first, week.second, state); } void insert(WeekMap::iterator it, int state) { insert(week(it.key()), state); } - void remove(const QPair &week) { IntMap::remove(week.first*10000 + week.second); } + void remove(QPair week) { IntMap::remove(week.first*10000 + week.second); } static QPair week(int key) { return QPair(key/10000, key%10000); } using IntMap::state; - int state(const QPair &week) const { return IntMap::state(week.first*10000 + week.second); } + int state(QPair week) const { return IntMap::state(week.first*10000 + week.second); } int state(int week, int year) const { return state(QPair(week, year)); } - void toggle(const QPair &week, int state=CalendarDay::NonWorking) { + void toggle(QPair week, int state=CalendarDay::NonWorking) { if (week.first < 1 || week.first > 53) { errorPlan<<"Illegal week number: "< &week, int state=CalendarDay::NonWorking) { + void toggleClear(QPair week, int state=CalendarDay::NonWorking) { if (week.first < 1 || week.first > 53) { errorPlan<<"Illegal week number: "< Copyright (C) 2002 - 2010, 2012 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kptnode.h" #include "kptappointment.h" #include "kptaccount.h" #include "kptwbsdefinition.h" #include "kptresource.h" #include "kptschedule.h" #include "kptxmlloaderobject.h" #include "kptdebug.h" #include #include #include namespace KPlato { Node::Node(Node *parent) : QObject( 0 ), // We don't use qobjects parent m_nodes(), m_dependChildNodes(), m_dependParentNodes(), m_estimate( 0 ), m_blockChanged(false) { //debugPlan<<"("<removeRunning(*this); if (m_startupAccount) m_startupAccount->removeStartup(*this); if (m_shutdownAccount) m_shutdownAccount->removeShutdown(*this); foreach (Schedule *s, m_schedules) { delete s; } m_schedules.clear(); m_parent = 0; //safety } void Node::init() { m_documents.node = this; m_currentSchedule = 0; m_name=""; m_constraint = Node::ASAP; m_estimate = 0; m_visitedForward = false; m_visitedBackward = false; m_runningAccount = 0; m_startupAccount = 0; m_shutdownAccount = 0; m_startupCost = 0.0; m_shutdownCost = 0.0; } QString Node::typeToString( bool trans ) const { return typeToString( (Node::NodeTypes)type(), trans ); } // static QString Node::typeToString( Node::NodeTypes typ, bool trans ) { return typeToStringList( trans ).at( typ ); } // static QStringList Node::typeToStringList( bool trans ) { return QStringList() << ( trans ? i18n( "None" ) : QString( "None" ) ) << ( trans ? i18n( "Project" ) : QString( "Project" ) ) << ( trans ? i18n( "Sub-Project" ) : QString( "Sub-Project" ) ) << ( trans ? i18n( "Task" ) : QString( "Task" ) ) << ( trans ? i18n( "Milestone" ) : QString( "Milestone" ) ) << ( trans ? i18n( "Periodic" ) : QString( "Periodic" ) ) << ( trans ? i18n( "Summary" ) : QString( "Summary-Task" ) ); } void Node::setName(const QString &n) { #ifndef NDEBUG setObjectName( n ); #endif m_name = n; changed(this); } void Node::setLeader(const QString &l) { m_leader = l; changed(this); } void Node::setDescription(const QString &d) { m_description = d; changed(this); } Node *Node::projectNode() { if ((type() == Type_Project) || (type() == Type_Subproject)) { return this; } if (m_parent) return m_parent->projectNode(); // This happends for default tasks return 0; } const Node *Node::projectNode() const { if ((type() == Type_Project) || (type() == Type_Subproject)) { return this; } if (m_parent) return m_parent->projectNode(); // This happends for default tasks return 0; } void Node::takeChildNode( Node *node) { //debugPlan<<"find="<setParentNode(0); if ( t != type() ) { changed( Type ); } } void Node::takeChildNode( int number ) { int t = type(); if (number >= 0 && number < m_nodes.size()) { Node *n = m_nodes.takeAt(number); //debugPlan<<(n?n->id():"null")<<" :"<<(n?n->name():""); if (n) { n->setParentNode( 0 ); } } if ( t != type() ) { changed( Type ); } } void Node::insertChildNode( int index, Node *node ) { int t = type(); if (index == -1) m_nodes.append(node); else m_nodes.insert(index,node); node->setParentNode( this ); if ( t != type() ) { changed( Type ); } } void Node::addChildNode( Node *node, Node *after) { int t = type(); int index = m_nodes.indexOf(after); if (index == -1) { m_nodes.append(node); node->setParentNode( this ); if ( t != type() ) { changed( Type ); } return; } m_nodes.insert(index+1, node); node->setParentNode(this); if ( t != type() ) { changed( Type ); } } int Node::findChildNode( const Node* node ) const { return m_nodes.indexOf( const_cast( node ) ); } bool Node::isChildOf( const Node* node ) const { if ( node == 0 || m_parent == 0 ) { return false; } if ( node == m_parent ) { return true; } return m_parent->isChildOf( node ); } Node* Node::childNode(int number) { //debugPlan<= m_nodes.count() ) { return 0; } return m_nodes.at( number ); } int Node::indexOf( const Node *node ) const { return m_nodes.indexOf( const_cast(node) ); } Duration *Node::getDelay() { /* TODO Calculate the delay of this node. Use the calculated startTime and the set startTime. */ return 0L; } void Node::addDependChildNode( Node *node, Relation::Type p) { addDependChildNode(node,p,Duration()); } void Node::addDependChildNode( Node *node, Relation::Type p, Duration lag) { Relation *relation = new Relation(this, node, p, lag); if (node->addDependParentNode(relation)) m_dependChildNodes.append(relation); else delete relation; } void Node::insertDependChildNode( unsigned int index, Node *node, Relation::Type p) { Relation *relation = new Relation(this, node, p, Duration()); if (node->addDependParentNode(relation)) m_dependChildNodes.insert(index, relation); else delete relation; } bool Node::addDependChildNode( Relation *relation) { if(m_dependChildNodes.indexOf(relation) != -1) return false; m_dependChildNodes.append(relation); return true; } void Node::takeDependChildNode( Relation *rel ) { int i = m_dependChildNodes.indexOf(rel); if ( i != -1 ) { //debugPlan<addDependChildNode(relation)) m_dependParentNodes.append(relation); else delete relation; } void Node::insertDependParentNode( unsigned int index, Node *node, Relation::Type p) { Relation *relation = new Relation(this, node, p, Duration()); if (node->addDependChildNode(relation)) m_dependParentNodes.insert(index,relation); else delete relation; } bool Node::addDependParentNode( Relation *relation) { if(m_dependParentNodes.indexOf(relation) != -1) return false; m_dependParentNodes.append(relation); return true; } void Node::takeDependParentNode( Relation *rel ) { int i = m_dependParentNodes.indexOf(rel); if ( i != -1 ) { //debugPlan<( node ) ) != -1) return true; QListIterator nit(childNodeIterator()); while (nit.hasNext()) { if (nit.next()->isParentOf(node)) return true; } return false; } Relation *Node::findParentRelation( const Node *node ) const { for (int i=0; iparent() == node) return rel; } return (Relation *)0; } Relation *Node::findChildRelation( const Node *node) const { for (int i=0; ichild() == node) return rel; } return (Relation *)0; } Relation *Node::findRelation( const Node *node ) const { Relation *rel = findParentRelation(node); if (!rel) rel = findChildRelation(node); return rel; } bool Node::isDependChildOf( const Node *node ) const { //debugPlan<<" '"<name()<<"'"; for (int i=0; iparent() == node) return true; if (rel->parent()->isDependChildOf(node)) return true; } return false; } QList Node::getParentNodes() { this->m_parentNodes.clear(); foreach(Relation * currentRelation, this->dependParentNodes()) { if (!this->m_parentNodes.contains(currentRelation->parent())) { this->m_parentNodes.append(currentRelation->parent()); } } return this->m_parentNodes; } bool Node::canMoveTo( const Node *newParent ) const { if ( m_parent == newParent ) { return true; } if ( newParent->isChildOf( this ) ) { return false; } if ( isDependChildOf( newParent ) || newParent->isDependChildOf( this ) ) { debugPlan<<"Can't move, node is dependent on new parent"; return false; } foreach ( Node *n, m_nodes ) { if ( !n->canMoveTo( newParent ) ) { return false; } } return true; } void Node::makeAppointments() { QListIterator nit(m_nodes); while (nit.hasNext()) { nit.next()->makeAppointments(); } } void Node::calcResourceOverbooked() { QListIterator nit(m_nodes); while (nit.hasNext()) { nit.next()->calcResourceOverbooked(); } } // Returns the (previously) calculated duration Duration Node::duration( long id ) const { Schedule *s = schedule( id ); return s ? s->duration : Duration::zeroDuration; } double Node::variance( long id, Duration::Unit unit ) const { double d = deviation( id, unit ); return d * d; } double Node::deviation( long id, Duration::Unit unit ) const { Schedule *s = schedule( id ); double d = 0.0; if ( s && m_estimate ) { d = s->duration.toDouble( unit ); double o = ( d * ( 100 + m_estimate->optimisticRatio() ) ) / 100; double p = ( d * ( 100 + m_estimate->pessimisticRatio() ) ) / 100; d = ( p - o ) / 6; } return d; } DateTime Node::startTime( long id ) const { Schedule *s = schedule( id ); return s ? s->startTime : DateTime(); } DateTime Node::endTime( long id ) const { Schedule *s = schedule( id ); return s ? s->endTime : DateTime(); } DateTime Node::appointmentStartTime( long id ) const { Schedule *s = schedule( id ); return s ? s->appointmentStartTime() : DateTime(); } DateTime Node::appointmentEndTime( long id ) const { Schedule *s = schedule( id ); return s ? s->appointmentEndTime() : DateTime(); } void Node::setDuration(const Duration &duration, long id ) { Schedule *s = schedule( id ); if ( s ) { s->duration = duration; } } void Node::setEarlyStart(const DateTime &dt, long id ) { Schedule *s = schedule( id ); if ( s ) s->earlyStart = dt; } DateTime Node::earlyStart( long id ) const { Schedule *s = schedule( id ); return s ? s->earlyStart : DateTime(); } void Node::setLateStart(const DateTime &dt, long id ) { Schedule *s = schedule( id ); if ( s ) s->lateStart = dt; } DateTime Node::lateStart( long id ) const { Schedule *s = schedule( id ); return s ? s->lateStart : DateTime(); } void Node::setEarlyFinish(const DateTime &dt, long id ) { Schedule *s = schedule( id ); if ( s ) s->earlyFinish = dt; } DateTime Node::earlyFinish( long id ) const { Schedule *s = schedule( id ); return s ? s->earlyFinish : DateTime(); } void Node::setLateFinish(const DateTime &dt, long id ) { Schedule *s = schedule( id ); if ( s ) s->lateFinish = dt; } DateTime Node::lateFinish( long id ) const { Schedule *s = schedule( id ); return s ? s->lateFinish : DateTime(); } DateTime Node::workStartTime( long id ) const { Schedule *s = schedule( id ); return s ? s->workStartTime : DateTime(); } void Node::setWorkStartTime(const DateTime &dt, long id ) { Schedule *s = schedule( id ); if ( s ) s->workStartTime = dt; } DateTime Node::workEndTime( long id ) const { Schedule *s = schedule( id ); return s ? s->workEndTime : DateTime(); } void Node::setWorkEndTime(const DateTime &dt, long id ) { Schedule *s = schedule( id ); if ( s ) s->workEndTime = dt; } bool Node::inCriticalPath( long id ) const { Schedule *s = schedule( id ); return s ? s->inCriticalPath : false; } QStringList Node::schedulingStatus( long id, bool trans ) const { Schedule *s = schedule( id ); QStringList lst; if ( s ) { lst = s->state(); } if ( lst.isEmpty() ) { lst.append( trans ? i18n( "Not scheduled" ) : QString( "Not scheduled" ) ); } return lst; } bool Node::resourceError( long id ) const { Schedule *s = schedule( id ); return s ? s->resourceError : false; } bool Node::resourceOverbooked( long id ) const { Schedule *s = schedule( id ); return s ? s->resourceOverbooked : false; } bool Node::resourceNotAvailable( long id ) const { Schedule *s = schedule( id ); return s ? s->resourceNotAvailable : false; } bool Node::constraintError( long id ) const { Schedule *s = schedule( id ); return s ? s->constraintError : false; } bool Node::schedulingError( long id ) const { Schedule *s = schedule( id ); return s ? s->schedulingError : false; } bool Node::notScheduled( long id ) const { if ( type() == Type_Summarytask ) { // i am scheduled if al least on child is scheduled foreach ( Node *n, m_nodes ) { if ( ! n->notScheduled( id ) ) { return false; } } return true; } Schedule *s = schedule( id ); return s == 0 || s->isDeleted() || s->notScheduled; } QStringList Node::overbookedResources( long id ) const { Schedule *s = schedule( id ); return s ? s->overbookedResources() : QStringList(); } void Node::saveWorkPackageXML( QDomElement &, long ) const { return; } void Node::saveRelations(QDomElement &element) const { QListIterator it(m_dependChildNodes); while (it.hasNext()) { it.next()->save(element); } QListIterator nodes(m_nodes); while (nodes.hasNext()) { nodes.next()->saveRelations(element); } } void Node::setConstraint(Node::ConstraintType type) { m_constraint = type; changed( this ); } void Node::setConstraint(const QString &type) { // Do not i18n these, they are used in load() if (type == "ASAP") setConstraint(ASAP); else if (type == "ALAP") setConstraint(ALAP); else if (type == "MustStartOn") setConstraint(MustStartOn); else if (type == "MustFinishOn") setConstraint(MustFinishOn); else if (type == "StartNotEarlier") setConstraint(StartNotEarlier); else if (type == "FinishNotLater") setConstraint(FinishNotLater); else if (type == "FixedInterval") setConstraint(FixedInterval); else setConstraint(ASAP); // default } QString Node::constraintToString( bool trans ) const { return constraintList( trans ).at( m_constraint ); } QStringList Node::constraintList( bool trans ) { // keep theses in the same order as the enum! return QStringList() << (trans ? i18n("As Soon As Possible") : QString("ASAP")) << (trans ? i18n("As Late As Possible") : QString("ALAP")) << (trans ? i18n("Must Start On") : QString("MustStartOn")) << (trans ? i18n("Must Finish On") : QString("MustFinishOn")) << (trans ? i18n("Start Not Earlier") : QString("StartNotEarlier")) << (trans ? i18n("Finish Not Later") : QString("FinishNotLater")) << (trans ? i18n("Fixed Interval") : QString("FixedInterval")); } void Node::propagateEarliestStart(DateTime &time) { if (m_currentSchedule == 0) { return; } if ( type() != Type_Project ) { m_currentSchedule->earlyStart = time; if ( m_currentSchedule->lateStart.isValid() && m_currentSchedule->lateStart < time ) { m_currentSchedule->lateStart = time; } //m_currentSchedule->logDebug( "propagateEarliestStart: " + time.toString() ); switch ( m_constraint ) { case FinishNotLater: case MustFinishOn: if ( m_constraintEndTime < time ) { m_currentSchedule->logWarning("Task constraint outside project constraint"); #ifndef PLAN_NLOGDEBUG m_currentSchedule->logDebug( QString( "%1: end constraint %2 < %3" ).arg( constraintToString( true ) ).arg( m_constraintEndTime.toString() ).arg( time.toString() ) ); #endif } break; case MustStartOn: case FixedInterval: if ( m_constraintStartTime < time ) { m_currentSchedule->logWarning("Task constraint outside project constraint"); #ifndef PLAN_NLOGDEBUG m_currentSchedule->logDebug( QString( "%1: start constraint %2 < %3" ).arg( constraintToString( true ) ).arg( m_constraintEndTime.toString() ).arg( time.toString() ) ); #endif } break; default: break; } } //debugPlan<earlyStart; QListIterator it = m_nodes; while (it.hasNext()) { it.next()->propagateEarliestStart(time); } } void Node::propagateLatestFinish(DateTime &time) { if (m_currentSchedule == 0) { return; } if ( type() != Type_Project ) { m_currentSchedule->lateFinish = time; if ( m_currentSchedule->earlyFinish.isValid() && m_currentSchedule->earlyFinish > time ) { m_currentSchedule->earlyFinish = time; } switch ( m_constraint ) { case StartNotEarlier: case MustStartOn: if ( m_constraintStartTime > time ) { m_currentSchedule->logWarning("Task constraint outside project constraint"); #ifndef PLAN_NLOGDEBUG m_currentSchedule->logDebug( QString( "%1: start constraint %2 < %3" ).arg( constraintToString( true ) ).arg( m_constraintEndTime.toString() ).arg( time.toString() ) ); #endif } break; case MustFinishOn: case FixedInterval: if ( m_constraintEndTime > time ) { m_currentSchedule->logWarning("Task constraint outside project constraint"); #ifndef PLAN_NLOGDEBUG m_currentSchedule->logDebug( QString( "%1: end constraint %2 > %3" ).arg( constraintToString( true ) ).arg( m_constraintEndTime.toString() ).arg( time.toString() ) ); #endif } break; default: break; } } //debugPlan<lateFinish; QListIterator it = m_nodes; while (it.hasNext()) { it.next()->propagateLatestFinish(time); } } void Node::moveEarliestStart(DateTime &time) { if (m_currentSchedule == 0) return; if (m_currentSchedule->earlyStart < time) { //m_currentSchedule->logDebug( "moveEarliestStart: " + m_currentSchedule->earlyStart.toString() + " -> " + time.toString() ); m_currentSchedule->earlyStart = time; } QListIterator it = m_nodes; while (it.hasNext()) { it.next()->moveEarliestStart(time); } } void Node::moveLatestFinish(DateTime &time) { if (m_currentSchedule == 0) return; if (m_currentSchedule->lateFinish > time) m_currentSchedule->lateFinish = time; QListIterator it = m_nodes; while (it.hasNext()) { it.next()->moveLatestFinish(time); } } void Node::initiateCalculation(MainSchedule &sch) { m_visitedForward = false; m_visitedBackward = false; m_durationForward = Duration::zeroDuration; m_durationBackward = Duration::zeroDuration; m_earlyStart = DateTime(); m_earlyFinish = DateTime(); m_lateFinish = DateTime(); QListIterator it = m_nodes; while (it.hasNext()) { it.next()->initiateCalculation(sch); } } void Node::resetVisited() { m_visitedForward = false; m_visitedBackward = false; QListIterator it = m_nodes; while (it.hasNext()) { it.next()->resetVisited(); } } Node *Node::siblingBefore() { //debugPlan; if (parentNode()) return parentNode()->childBefore(this); return 0; } Node *Node::childBefore(Node *node) { //debugPlan; int index = m_nodes.indexOf(node); if (index > 0){ return m_nodes.at(index-1); } return 0; } Node *Node::siblingAfter() { //debugPlan; if (parentNode()) return parentNode()->childAfter(this); return 0; } Node *Node::childAfter(Node *node) { //debugPlan; Q_ASSERT( m_nodes.contains( node ) ); int index = m_nodes.indexOf(node); if (index < m_nodes.count()-1) { return m_nodes.at(index+1); } return 0; } bool Node::moveChildUp(Node* node) { if (findChildNode(node) == -1) return false; // not my node! Node *sib = node->siblingBefore(); if (!sib) return false; sib = sib->siblingBefore(); takeChildNode(node); if (sib) { addChildNode(node, sib); } else { insertChildNode(0, node); } return true; } bool Node::moveChildDown(Node* node) { if (findChildNode(node) == -1) return false; // not my node! Node *sib = node->siblingAfter(); if (!sib) return false; takeChildNode(node); addChildNode(node, sib); return true; } bool Node::legalToLink( const Node *node ) const { Node *p = const_cast(this)->projectNode(); if (p) return p->legalToLink(this, node); return false; } bool Node::isEndNode() const { return m_dependChildNodes.isEmpty(); } bool Node::isStartNode() const { return m_dependParentNodes.isEmpty(); } void Node::setId(const QString& id) { //debugPlan<startTime = startTime; } void Node::setEndTime(const DateTime &endTime, long id ) { Schedule *s = schedule( id ); if ( s ) s->endTime = endTime; } void Node::saveAppointments(QDomElement &element, long id) const { //debugPlan<id()<<","<add(appointment); } void Node::addAppointment(ResourceSchedule *resource, const DateTime &start, const DateTime &end, double load) { Schedule *node = findSchedule(resource->id()); if (node == 0) { node = createSchedule(resource->parent()); } node->setCalculationMode( resource->calculationMode() ); node->addAppointment(resource, start, end, load); } bool Node::isBaselined( long id ) const { Schedule *s = schedule( id ); return s ? s->isBaselined() : false; } void Node::takeSchedule(const Schedule *schedule) { if (schedule == 0) return; if (m_currentSchedule == schedule) m_currentSchedule = 0; m_schedules.take(schedule->id()); } void Node::addSchedule(Schedule *schedule) { if (schedule == 0) return; m_schedules.insert(schedule->id(), schedule); } Schedule *Node::createSchedule(const QString& name, Schedule::Type type, long id) { //debugPlan<removeStartup( *this ); } m_startupAccount = acc; changed(); } void Node::setShutdownCost(double cost) { m_shutdownCost = cost; changed(ShutdownCost); } void Node::setShutdownAccount(Account *acc) { //debugPlan<removeShutdown( *this ); } m_shutdownAccount = acc; changed(); } void Node::setRunningAccount(Account *acc) { //debugPlan<removeRunning( *this ); } m_runningAccount = acc; changed(); } void Node::blockChanged(bool on) { m_blockChanged = on; } void Node::changed(Node *node, int property) { if (m_blockChanged) { return; } switch ( property) { case Type: case StartupCost: case ShutdownCost: case CompletionEntry: case CompletionStarted: case CompletionFinished: case CompletionStartTime: case CompletionFinishTime: case CompletionPercentage: case CompletionRemainingEffort: case CompletionActualEffort: case CompletionUsedEffort: foreach ( Schedule *s, m_schedules ) { s->clearPerformanceCache(); } break; default: break; } if (m_parent) { m_parent->changed(node, property); } } Duration Node::plannedEffort( const Resource *resource, long id, EffortCostCalculationType type ) const { Duration e; foreach ( Node *n, m_nodes ) { e += n->plannedEffort( resource, id, type ); } return e; } -Duration Node::plannedEffort( const Resource *resource, const QDate &date, long id, EffortCostCalculationType type ) const +Duration Node::plannedEffort( const Resource *resource, QDate date, long id, EffortCostCalculationType type ) const { Duration e; foreach ( Node *n, m_nodes ) { e += n->plannedEffort( resource, date, id, type ); } return e; } -Duration Node::plannedEffortTo( const Resource *resource, const QDate &date, long id, EffortCostCalculationType type ) const +Duration Node::plannedEffortTo( const Resource *resource, QDate date, long id, EffortCostCalculationType type ) const { Duration e; foreach ( Node *n, m_nodes ) { e += n->plannedEffortTo( resource, date, id, type ); } return e; } EffortCost Node::plannedCost( long id, EffortCostCalculationType type ) const { EffortCost ec; foreach ( Node *n, m_nodes ) { ec += n->plannedCost( id, type ); } return ec; } EffortCostMap Node::bcwsPrDay( long int id, EffortCostCalculationType type ) const { return const_cast( this )->bcwsPrDay( id, type ); } EffortCostMap Node::bcwsPrDay( long int id, EffortCostCalculationType type ) { Schedule *s = schedule( id ); if ( s == 0 ) { return EffortCostMap(); } EffortCostCache &ec = s->bcwsPrDayCache( type ); if ( ! ec.cached ) { ec.effortcostmap = EffortCostMap(); foreach ( Node *n, m_nodes ) { ec.effortcostmap += n->bcwsPrDay( id, type ); } ec.cached = true; } return ec.effortcostmap; } EffortCostMap Node::bcwpPrDay( long int id, EffortCostCalculationType type ) const { return const_cast( this )->bcwpPrDay( id, type); } EffortCostMap Node::bcwpPrDay( long int id, EffortCostCalculationType type ) { Schedule *s = schedule( id ); if ( s == 0 ) { return EffortCostMap(); } EffortCostCache &ec = s->bcwpPrDayCache( type ); if ( ! ec.cached ) { ec.effortcostmap = EffortCostMap(); foreach ( Node *n, m_nodes ) { ec.effortcostmap += n->bcwpPrDay( id, type ); } ec.cached = true; } return ec.effortcostmap; } EffortCostMap Node::acwp( long id, EffortCostCalculationType type ) const { return const_cast( this )->acwp( id, type ); } EffortCostMap Node::acwp( long id, EffortCostCalculationType type ) { Schedule *s = schedule( id ); if ( s == 0 ) { return EffortCostMap(); } EffortCostCache &ec = s->acwpCache( type ); if ( ! ec.cached ) { ec.effortcostmap = EffortCostMap(); foreach ( Node *n, m_nodes ) { ec.effortcostmap += n->acwp( id, type ); } ec.cached = true; } return ec.effortcostmap; } -EffortCost Node::acwp( const QDate &date, long id ) const +EffortCost Node::acwp( QDate date, long id ) const { EffortCost ec; foreach ( Node *n, m_nodes ) { ec += n->acwp( date, id ); } return ec; } void Node::slotStandardWorktimeChanged( StandardWorktime* ) { //debugPlan<m_expectedCached = false; m_estimate->m_optimisticCached = false; m_estimate->m_pessimisticCached = false; } } void Node::emitDocumentAdded( Node *node, Document *doc, int idx ) { if ( m_parent ) { m_parent->emitDocumentAdded( node, doc, idx ); } } void Node::emitDocumentRemoved( Node *node, Document *doc, int idx ) { if ( m_parent ) { m_parent->emitDocumentRemoved( node, doc, idx ); } } void Node::emitDocumentChanged( Node *node, Document *doc, int idx ) { if ( m_parent ) { m_parent->emitDocumentChanged( node, doc, idx ); } } ////////////////////////// Estimate ///////////////////////////////// Estimate::Estimate( Node *parent ) : m_parent( parent ) { m_pertCached = false; setUnit( Duration::Unit_h ); setExpectedEstimate( 8.0 ); setPessimisticEstimate( 8.0 ); setOptimisticEstimate( 8.0 ); m_type = Type_Effort; m_calendar = 0; m_risktype = Risk_None; } Estimate::Estimate(const Estimate &estimate, Node *parent) : m_parent( parent ) { copy( estimate ); } Estimate::~Estimate() { } void Estimate::clear() { m_pertCached = false; setExpectedEstimate( 0.0 ); setPessimisticEstimate( 0.0 ); setOptimisticEstimate( 0.0 ); m_type = Type_Effort; m_calendar = 0; m_risktype = Risk_None; m_unit = Duration::Unit_h; changed(); } Estimate &Estimate::operator=( const Estimate &estimate ) { copy( estimate ); return *this; } void Estimate::copy( const Estimate &estimate ) { //m_parent = 0; // don't touch m_expectedEstimate = estimate.m_expectedEstimate; m_optimisticEstimate = estimate.m_optimisticEstimate; m_pessimisticEstimate = estimate.m_pessimisticEstimate; m_expectedValue = estimate.m_expectedValue; m_optimisticValue = estimate.m_optimisticValue; m_pessimisticValue = estimate.m_pessimisticValue; m_expectedCached = estimate.m_expectedCached; m_optimisticCached = estimate.m_optimisticCached; m_pessimisticCached = estimate.m_pessimisticCached; m_pertExpected = estimate.m_pertExpected; m_pertCached = estimate.m_pertCached; m_type = estimate.m_type; m_calendar = estimate.m_calendar; m_risktype = estimate.m_risktype; m_unit = estimate.m_unit; changed(); } double Estimate::variance() const { double d = deviation(); return d * d; } double Estimate::variance( Duration::Unit unit ) const { double d = deviation( unit ); return d * d; } double Estimate::deviation() const { return ( m_pessimisticEstimate - m_optimisticEstimate ) / 6; } double Estimate::deviation( Duration::Unit unit ) const { if ( unit == m_unit ) { return deviation(); } double p = pessimisticValue().toDouble( unit ); double o = optimisticValue().toDouble( unit ); double v = ( p - o ) / 6; return v; } Duration Estimate::pertExpected() const { if (m_risktype == Risk_Low) { if ( ! m_pertCached ) { m_pertExpected = (optimisticValue() + pessimisticValue() + (expectedValue()*4))/6; m_pertCached = true; } return m_pertExpected; } else if (m_risktype == Risk_High) { if ( ! m_pertCached ) { m_pertExpected = (optimisticValue() + (pessimisticValue()*2) + (expectedValue()*4))/7; m_pertCached = true; } return m_pertExpected; } return expectedValue(); // risk==none } Duration Estimate::pertOptimistic() const { if (m_risktype != Risk_None) { return pertExpected() - Duration( variance( Duration::Unit_ms ) ); } return optimisticValue(); } Duration Estimate::pertPessimistic() const { if (m_risktype != Risk_None) { return pertExpected() + Duration( variance( Duration::Unit_ms ) ); } return pessimisticValue(); } Duration Estimate::value(int valueType, bool pert) const { if (valueType == Estimate::Use_Expected) { return pert ? pertExpected() : expectedValue(); } else if (valueType == Estimate::Use_Optimistic) { return pert ? pertOptimistic() : optimisticValue(); } else if (valueType == Estimate::Use_Pessimistic) { return pert ? pertPessimistic() : pessimisticValue(); } return expectedValue(); } void Estimate::setUnit( Duration::Unit unit ) { m_unit = unit; m_expectedCached = false; m_optimisticCached = false; m_pessimisticCached = false; m_pertCached = false; changed(); } bool Estimate::load(KoXmlElement &element, XMLLoaderObject &status) { setType(element.attribute("type")); setRisktype(element.attribute("risk")); if ( status.version() <= "0.6" ) { m_unit = (Duration::Unit)(element.attribute("display-unit", QString().number(Duration::Unit_h) ).toInt()); QList s = status.project().standardWorktime()->scales(); m_expectedEstimate = scale( Duration::fromString(element.attribute("expected")), m_unit, s ); m_optimisticEstimate = scale( Duration::fromString(element.attribute("optimistic")), m_unit, s ); m_pessimisticEstimate = scale( Duration::fromString(element.attribute("pessimistic")), m_unit, s ); } else { if ( status.version() <= "0.6.2" ) { // 0 pos in unit is now Unit_Y, so add 3 to get the correct new unit m_unit = (Duration::Unit)(element.attribute("unit", QString().number(Duration::Unit_ms - 3) ).toInt() + 3); } else { m_unit = Duration::unitFromString( element.attribute( "unit" ) ); } m_expectedEstimate = element.attribute("expected", "0.0").toDouble(); m_optimisticEstimate = element.attribute("optimistic", "0.0").toDouble(); m_pessimisticEstimate = element.attribute("pessimistic", "0.0").toDouble(); m_calendar = status.project().findCalendar(element.attribute("calendar-id")); } return true; } void Estimate::save(QDomElement &element) const { QDomElement me = element.ownerDocument().createElement("estimate"); element.appendChild(me); me.setAttribute("expected", QString::number(m_expectedEstimate)); me.setAttribute("optimistic", QString::number(m_optimisticEstimate)); me.setAttribute("pessimistic", QString::number(m_pessimisticEstimate)); me.setAttribute("type", typeToString()); if ( m_calendar ) { me.setAttribute("calendar-id", m_calendar->id() ); } me.setAttribute("risk", risktypeToString()); me.setAttribute("unit", Duration::unitToString( m_unit ) ); } QString Estimate::typeToString( bool trans ) const { return typeToStringList( trans ).at( m_type ); } QString Estimate::typeToString( Estimate::Type typ, bool trans ) { return typeToStringList( trans ).value( typ ); } QStringList Estimate::typeToStringList( bool trans ) { return QStringList() << (trans ? i18n("Effort") : QString("Effort")) << (trans ? i18n("Duration") : QString("Duration")); } void Estimate::setType(Type type) { m_type = type; m_expectedCached = false; m_optimisticCached = false; m_pessimisticCached = false; m_pertCached = false; changed(); } void Estimate::setType(const QString& type) { if (type == "Effort") setType(Type_Effort); else if (type == "Duration" || /*old format*/ type == "FixedDuration") setType(Type_Duration); else if (/*old format*/type == "Length") setType(Type_Duration); else if (type == "Type_FixedDuration") // Typo, keep old xml files working setType(Type_Duration); else setType(Type_Effort); // default } QString Estimate::risktypeToString( bool trans ) const { return risktypeToStringList( trans ).at( m_risktype ); } QStringList Estimate::risktypeToStringList( bool trans ) { return QStringList() << (trans ? i18n("None") : QString("None")) << (trans ? i18n("Low") : QString("Low")) << (trans ? i18n("High") : QString("High")); } void Estimate::setRisktype(const QString& type) { if (type == "High") setRisktype(Risk_High); else if (type == "Low") setRisktype(Risk_Low); else setRisktype(Risk_None); // default } void Estimate::setRisktype(Risktype type) { m_pertCached = false; m_risktype = type; changed(); } void Estimate::setCalendar( Calendar *calendar ) { m_calendar = calendar; m_expectedCached = false; m_optimisticCached = false; m_pessimisticCached = false; m_pertCached = false; changed(); } void Estimate::setExpectedEstimate( double value) { m_expectedEstimate = value; m_expectedCached = false; m_pertCached = false; changed(); } void Estimate::setOptimisticEstimate( double value ) { m_optimisticEstimate = value; m_optimisticCached = false; m_pertCached = false; changed(); } void Estimate::setPessimisticEstimate( double value ) { m_pessimisticEstimate = value; m_pessimisticCached = false; m_pertCached = false; changed(); } void Estimate::setOptimisticRatio(int percent) { int p = percent>0 ? -percent : percent; m_optimisticValue = expectedValue()*(100+p)/100; m_optimisticEstimate = scale( m_optimisticValue, m_unit, scales() ); m_optimisticCached = true; m_pertCached = false; changed(); } int Estimate::optimisticRatio() const { if (m_expectedEstimate == 0.0) return 0; return (int)((optimisticValue()*100)/expectedValue())-100; } void Estimate::setPessimisticRatio(int percent) { int p = percent<0 ? -percent : percent; m_pessimisticValue = expectedValue()*(100+p)/100; m_pessimisticEstimate = scale( m_pessimisticValue, m_unit, scales() ); m_pessimisticCached = true; m_pertCached = false; changed(); } int Estimate::pessimisticRatio() const { if (m_expectedEstimate == 0.0) return 0; return (int)((pessimisticValue()*100)/expectedValue())-100; } // internal void Estimate::setOptimisticValue() { m_optimisticValue = scale( m_optimisticEstimate, m_unit, scales() ); m_optimisticCached = true; m_pertCached = false; } // internal void Estimate::setExpectedValue() { m_expectedValue = scale( m_expectedEstimate, m_unit, scales() ); m_expectedCached = true; m_pertCached = false; } // internal void Estimate::setPessimisticValue() { m_pessimisticValue = scale( m_pessimisticEstimate, m_unit, scales() ); m_pessimisticCached = true; m_pertCached = false; } Duration Estimate::optimisticValue() const { if ( ! m_optimisticCached ) { const_cast(this)->setOptimisticValue(); } return m_optimisticValue; } Duration Estimate::pessimisticValue() const { if ( ! m_pessimisticCached ) { const_cast(this)->setPessimisticValue(); } return m_pessimisticValue; } Duration Estimate::expectedValue() const { if ( ! m_expectedCached ) { const_cast(this)->setExpectedValue(); } return m_expectedValue; } double Estimate::scale( const Duration &value, Duration::Unit unit, const QList &scales ) { //debugPlan< lst = scales; switch ( lst.count() ) { case Duration::Unit_Y: lst << (qint64)(365 * 24) * 60 * 60 * 1000; // add milliseconds in a year case Duration::Unit_M: lst << (qint64)(30 * 24) * 60 * 60 * 1000; // add milliseconds in a month case Duration::Unit_w: lst << (qint64)(7 * 24) * 60 * 60 * 1000; // add milliseconds in a week case Duration::Unit_d: lst << 24 * 60 * 60 * 1000; // add milliseconds in day case Duration::Unit_h: lst << 60 * 60 * 1000; // add milliseconds in hour case Duration::Unit_m: lst << 60 * 1000; // add milliseconds in minute case Duration::Unit_s: lst << 1000; // add milliseconds in second case Duration::Unit_ms: lst << 1; // add milliseconds in a millisecond default: break; } double v = ( double )( value.milliseconds() ); v /= lst[ unit ]; //debugPlan< &scales ) { //debugPlan< lst = scales; switch ( lst.count() ) { case Duration::Unit_Y: lst << (qint64)(365 * 24) * 60 * 60 * 1000; // add milliseconds in a year case Duration::Unit_M: lst << (qint64)(30 * 24) * 60 * 60 * 1000; // add milliseconds in a month case Duration::Unit_w: lst << (qint64)(7 * 24) * 60 * 60 * 1000; // add milliseconds in a week case Duration::Unit_d: lst << 24 * 60 * 60 * 1000; // add milliseconds in day case Duration::Unit_h: lst << 60 * 60 * 1000; // add milliseconds in hour case Duration::Unit_m: lst << 60 * 1000; // add milliseconds in minute case Duration::Unit_s: lst << 1000; // add milliseconds in second case Duration::Unit_ms: lst << 1; // add milliseconds in a millisecond default: break; } qint64 v = ( qint64 )( value * lst[ unit ] ); //debugPlan< Estimate::defaultScales() { QList lst; lst << (qint64)(365 * 24) * 60 * 60 * 1000 // add milliseconds in a year << (qint64)(30 * 24) * 60 * 60 * 1000 // add milliseconds in a month << (qint64)(7 * 24) * 60 * 60 * 1000 // add milliseconds in a week << 24 * 60 * 60 * 1000 // add milliseconds in day << 60 * 60 * 1000 // add milliseconds in hour << 60 * 1000 // add milliseconds in minute << 1000 // add milliseconds in second << 1; // add milliseconds in a millisecond return lst; } QList Estimate::scales() const { QList s; if ( m_type == Type_Duration && m_calendar == 0 ) { return s; // Use default scaling ( 24h a day...) } if ( m_parent == 0 ) { return s; } Project *p = static_cast( m_parent->projectNode() ); if ( p == 0 ) { return s; } s << p->standardWorktime()->scales(); return s; } } //KPlato namespace diff --git a/plan/libs/kernel/kptnode.h b/plan/libs/kernel/kptnode.h index 4c98accef90..8004a52363a 100644 --- a/plan/libs/kernel/kptnode.h +++ b/plan/libs/kernel/kptnode.h @@ -1,879 +1,879 @@ /* This file is part of the KDE project Copyright (C) 2001 Thomas Zander zander@kde.org Copyright (C) 2004 - 2011 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTNODE_H #define KPTNODE_H #include "kplatokernel_export.h" #include "kptglobal.h" #include "kptrelation.h" #include "kptduration.h" #include "kptdatetime.h" #include "kptschedule.h" #include "kptdocuments.h" #include "kptresource.h" #include #include #include #include class QDomElement; /// The main namespace. namespace KPlato { class Account; class Project; class Appointment; class ResourceGroup; class Resource; class ResourceGroupRequest; class Estimate; class EffortCostMap; class EffortCost; class Calendar; class KPlatoXmlLoaderBase; /** * This class represents any node in the project, a node can be a project or * a subproject or any task. * This class is basically an abstract interface to make the design more OO. */ class KPLATOKERNEL_EXPORT Node : public QObject { Q_OBJECT public: enum ConstraintType { ASAP, ALAP, MustStartOn, MustFinishOn, StartNotEarlier, FinishNotLater, FixedInterval }; enum State { State_None = 0, State_Started = 1, State_StartedLate = 2, State_StartedEarly = 4, State_Finished = 8, State_FinishedLate = 16, State_FinishedEarly = 32, State_Running = 64, State_RunningLate = 128, State_RunningEarly = 256, State_ReadyToStart = 512, // all precceeding tasks finished (if any) State_NotReadyToStart = 1024, // all precceeding tasks not finished (must be one or more) State_NotScheduled = 2048, State_Late = 4096 }; enum Properties { Type, StartupCost, ShutdownCost, CompletionEntry, CompletionStarted, CompletionFinished, CompletionStartTime, CompletionFinishTime, CompletionPercentage, CompletionRemainingEffort, CompletionActualEffort, CompletionUsedEffort }; explicit Node(Node *parent = 0); Node(const Node &node, Node *parent = 0); // Declare the class abstract virtual ~Node() = 0; void setId(const QString& id); QString id() const { return m_id; } // unique identity enum NodeTypes { Type_Node = 0, Type_Project = 1, Type_Subproject = 2, Type_Task = 3, Type_Milestone = 4, Type_Periodic = 5, Type_Summarytask = 6 }; virtual int type() const = 0; QString typeToString( bool trans = false ) const; static QString typeToString( NodeTypes typ, bool trans = false ); static QStringList typeToStringList( bool trans ); /** * Returns a pointer to the project node (main- or sub-project) * Returns 0 if no project exists. */ virtual Node *projectNode(); /** * Returns a pointer to the project node (main- or sub-project) * Returns 0 if no project exists. */ virtual const Node *projectNode() const; // The load and save methods virtual bool load(KoXmlElement &, XMLLoaderObject &) { return true; } virtual void save(QDomElement &element) const = 0; /// Save me and my childrens relations. virtual void saveRelations(QDomElement &element) const; /// Save a workpackage document containing @p node with schedule identity @p id virtual void saveWorkPackageXML( QDomElement &element, long id ) const; // simple child node management // Child nodes are things like subtasks, basically a task can exists of // several sub-tasks. Creating a table has 4 subtasks, 1) measuring // 2) cutting 3) building 4) painting. Node *parentNode() const { return m_parent; } void setParentNode( Node* newParent ) { m_parent = newParent;} const QList &childNodeIterator() const { return m_nodes; } int numChildren() const { return m_nodes.count(); } virtual void addChildNode(Node *node, Node *after=0); virtual void insertChildNode(int index, Node *node); void takeChildNode(Node *node ); void takeChildNode(int number ); Node* childNode(int number); const Node* childNode(int number) const; int findChildNode( const Node* node ) const; bool isChildOf( const Node *node ) const; int indexOf( const Node *node ) const; // Time-dependent child-node-management. // list all nodes that are dependent upon this one. // Building a house requires the table to be finished, therefore the // house-building is time dependent on the table-building. So a child // of the table-building node is the house-building node. int numDependChildNodes() const { return m_dependChildNodes.count(); } /// Adds relation of type @p p to both this node and @p node virtual void addDependChildNode( Node *node, Relation::Type p=Relation::FinishStart); /// Adds relation of type @p p with @p lag to both this node and @p node virtual void addDependChildNode( Node *node, Relation::Type p, Duration lag); /// Adds @p relation only to this node virtual bool addDependChildNode( Relation *relation); /// Inserts relation to this node at index @p index and appends relation to @p node virtual void insertDependChildNode( unsigned int index, Node *node, Relation::Type p=Relation::FinishStart); /** * Takes the relation rel from this node only. */ void takeDependChildNode( Relation *rel ); Relation *getDependChildNode( int number ) const { return m_dependChildNodes.at(number); } QList dependChildNodes() const { return m_dependChildNodes; } int numDependParentNodes() const { return m_dependParentNodes.count(); } /// Adds relation if type @p to both this node and @p node virtual void addDependParentNode(Node *node, Relation::Type p=Relation::FinishStart); /// Adds relation to both this node and @p node virtual void addDependParentNode( Node *node, Relation::Type p, Duration lag); /// Adds relation only to this node virtual bool addDependParentNode( Relation *relation); /// Inserts relation to this node at index and appends relation to @p node virtual void insertDependParentNode( unsigned int index, Node *node, Relation::Type p=Relation::FinishStart); /** * Takes the relation @p rel from this node only. */ void takeDependParentNode( Relation *rel ); Relation *getDependParentNode( int number ) const { return m_dependParentNodes.at(number); } QList dependParentNodes() const { return m_dependParentNodes; } QList getParentNodes(); bool isParentOf( const Node *node ) const; bool isDependChildOf( const Node *node ) const; virtual bool canMoveTo( const Node *newParent ) const; Relation *findParentRelation( const Node *node) const; Relation *findChildRelation( const Node *node ) const; Relation *findRelation( const Node *node ) const; /// Set the scheduled start time void setStartTime(const DateTime &startTime, long id = CURRENTSCHEDULE ); /// Return the scheduled start time virtual DateTime startTime( long id = CURRENTSCHEDULE ) const; /// Set the scheduled end time void setEndTime(const DateTime &endTime, long id = CURRENTSCHEDULE ); /// Return the scheduled end time virtual DateTime endTime( long id = CURRENTSCHEDULE ) const; /// Set the scheduled duration void setDuration(const Duration &duration, long id = CURRENTSCHEDULE ); DateTime appointmentStartTime( long id = CURRENTSCHEDULE) const; DateTime appointmentEndTime( long id = CURRENTSCHEDULE ) const; /// Return the estimate for this node Estimate *estimate() const { return m_estimate; } /** * Instead of using the expected duration, generate a random value using * the Distribution of each Task. This can be used for Monte-Carlo * estimation of Project duration. */ virtual Duration *getRandomDuration() = 0; /** * Calculate the delay of this node. * It is the difference between the actual startTime and scheduled startTime. */ Duration *getDelay(); // TODO QString &name() { return m_name; } QString &leader() { return m_leader; } QString &description() { return m_description; } const QString &name() const { return m_name; } const QString &leader() const { return m_leader; } const QString &description() const { return m_description; } void setName(const QString &n); void setLeader(const QString &l); void setDescription(const QString &d); void setConstraint(Node::ConstraintType type); void setConstraint(const QString &type); int constraint() const { return m_constraint; } QString constraintToString( bool trans=false ) const; static QStringList constraintList( bool trans ); virtual void setConstraintStartTime(const DateTime &time) { m_constraintStartTime = time; changed( this ); } virtual void setConstraintEndTime(const DateTime &time) { m_constraintEndTime = time; changed( this ); } virtual DateTime constraintStartTime() const { return m_constraintStartTime; } virtual DateTime constraintEndTime() const { return m_constraintEndTime; } virtual DateTime startNotEarlier() const { return m_constraintStartTime; } virtual DateTime finishNotLater() const { return m_constraintEndTime; } virtual DateTime mustStartOn() const { return m_constraintStartTime; } virtual DateTime mustFinishOn() const { return m_constraintEndTime; } virtual ResourceGroupRequest *resourceGroupRequest(const ResourceGroup * /*group*/) const { return 0; } virtual QStringList requestNameList() const { return QStringList(); } virtual bool containsRequest( const QString &/*identity*/ ) const { return false; } virtual ResourceRequest *resourceRequest( const QString &/*name*/ ) const { return 0; } /// Return the list of resources assigned to this task virtual QStringList assignedNameList( long /*id*/ = CURRENTSCHEDULE ) const { return QStringList(); } virtual void makeAppointments(); /// Calculates if the assigned resource is overbooked /// within the duration of this node virtual void calcResourceOverbooked(); /// Return the scheduling status of schedule @p id. If @p trans is true, text is translated QStringList schedulingStatus( long id, bool trans = false ) const; /// EstimateType == Estimate, but no resource is requested bool resourceError( long id = CURRENTSCHEDULE ) const; /// The assigned resource is overbooked virtual bool resourceOverbooked( long id = CURRENTSCHEDULE ) const; /// The requested resource is not available bool resourceNotAvailable( long id = CURRENTSCHEDULE ) const; /// The task cannot be scheduled to fullfil all the constraints virtual bool constraintError( long id = CURRENTSCHEDULE ) const; /// The task cannot be scheduled correctly virtual bool schedulingError( long id = CURRENTSCHEDULE ) const; /// The node has not been scheduled bool notScheduled( long id = CURRENTSCHEDULE ) const; /// Return a list of overbooked resources virtual QStringList overbookedResources( long id = CURRENTSCHEDULE ) const; /// The assigned resources can not fullfil the estimated effort. virtual bool effortMetError( long /*id*/ = CURRENTSCHEDULE ) const { return false; } - virtual EffortCostMap plannedEffortCostPrDay(const QDate &start, const QDate &end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const=0; - virtual EffortCostMap plannedEffortCostPrDay(const Resource *resource, const QDate &start, const QDate &end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const=0; + virtual EffortCostMap plannedEffortCostPrDay(QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const=0; + virtual EffortCostMap plannedEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const=0; /// Returns the total planned effort for @p resource on this task (or subtasks) virtual Duration plannedEffort( const Resource *resource, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the total planned effort for this task (or subtasks) virtual Duration plannedEffort( long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const { Q_UNUSED(id); return Duration::zeroDuration; } /// Returns the total planned effort for this task (or subtasks) on date - virtual Duration plannedEffort(const QDate &, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const { Q_UNUSED(id); return Duration::zeroDuration; } + virtual Duration plannedEffort(QDate , long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const { Q_UNUSED(id); return Duration::zeroDuration; } /// Returns the total planned effort for @p resource on this task (or subtasks) on date - virtual Duration plannedEffort( const Resource *resource, const QDate &date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; + virtual Duration plannedEffort( const Resource *resource, QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the planned effort up to and including date - virtual Duration plannedEffortTo(const QDate &, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const { Q_UNUSED(id); return Duration::zeroDuration; } + virtual Duration plannedEffortTo(QDate , long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const { Q_UNUSED(id); return Duration::zeroDuration; } /// Returns the planned effort for @p resource up to and including date - virtual Duration plannedEffortTo( const Resource *resource, const QDate &date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; + virtual Duration plannedEffortTo( const Resource *resource, QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the total actual effort for this task (or subtasks) virtual Duration actualEffort() const { return Duration::zeroDuration; } /// Returns the total actual effort for this task (or subtasks) on date - virtual Duration actualEffort(const QDate &/*date*/ ) const { return Duration::zeroDuration; } + virtual Duration actualEffort(QDate /*date*/ ) const { return Duration::zeroDuration; } /// Returns the total actual effort for this task (or subtasks) up to and including date - virtual Duration actualEffortTo(const QDate &/*date*/ ) const { return Duration::zeroDuration; } - virtual EffortCostMap actualEffortCostPrDay(const QDate &start, const QDate &end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const=0; + virtual Duration actualEffortTo(QDate /*date*/ ) const { return Duration::zeroDuration; } + virtual EffortCostMap actualEffortCostPrDay(QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const=0; /// Returns the actual effort and cost pr day used by @p resource - virtual EffortCostMap actualEffortCostPrDay(const Resource *resource, const QDate &start, const QDate &end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const=0; + virtual EffortCostMap actualEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const=0; /** * Planned cost is the sum total of all resources and other costs * planned for this node. */ virtual EffortCost plannedCost( long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /** * Planned cost from start of activity up to and including date * is the sum of all resource costs and other costs planned for this node. */ - virtual double plannedCostTo(const QDate &/*date*/, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const { Q_UNUSED(id); return 0; } + virtual double plannedCostTo(QDate /*date*/, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const { Q_UNUSED(id); return 0; } /// Actual cost up to and including date - virtual EffortCost actualCostTo( long int /*id*/, const QDate &/*date*/) const { return EffortCost(); } + virtual EffortCost actualCostTo( long int /*id*/, QDate /*date*/) const { return EffortCost(); } /// Returns the effort planned to be used to reach the actual percent finished - virtual Duration budgetedWorkPerformed( const QDate &, long = CURRENTSCHEDULE ) const = 0; + virtual Duration budgetedWorkPerformed( QDate , long = CURRENTSCHEDULE ) const = 0; /// Returns the cost planned to be used to reach the actual percent finished - virtual double budgetedCostPerformed( const QDate &, long = CURRENTSCHEDULE ) const { return 0.0; }; + virtual double budgetedCostPerformed( QDate , long = CURRENTSCHEDULE ) const { return 0.0; }; /// Return map of Budgeted Cost of Work Scheduled pr day virtual EffortCostMap bcwsPrDay( long id = CURRENTSCHEDULE, EffortCostCalculationType type = ECCT_All ); /// Return map of Budgeted Cost of Work Scheduled pr day virtual EffortCostMap bcwsPrDay( long id = CURRENTSCHEDULE, EffortCostCalculationType type = ECCT_All ) const; /// Budgeted Cost of Work Scheduled - virtual double bcws( const QDate &/*date*/, long id = CURRENTSCHEDULE ) const { Q_UNUSED(id); return 0.0; } + virtual double bcws( QDate /*date*/, long id = CURRENTSCHEDULE ) const { Q_UNUSED(id); return 0.0; } /// Return map of Budgeted Cost of Work Scheduled pr day (also includes bcws pr day) virtual EffortCostMap bcwpPrDay( long id = CURRENTSCHEDULE, EffortCostCalculationType type = ECCT_All ); /// Return map of Budgeted Cost of Work Scheduled pr day (also includes bcws pr day) virtual EffortCostMap bcwpPrDay( long id = CURRENTSCHEDULE, EffortCostCalculationType type = ECCT_All ) const; /// Budgeted Cost of Work Performed virtual double bcwp( long id ) const { Q_UNUSED(id); return 0.0; } /// Budgeted Cost of Work Performed ( up to @p date ) - virtual double bcwp( const QDate &/*date*/, long id = CURRENTSCHEDULE ) const { Q_UNUSED(id); return 0.0; } + virtual double bcwp( QDate /*date*/, long id = CURRENTSCHEDULE ) const { Q_UNUSED(id); return 0.0; } /// Return a map of Actual effort and Cost of Work Performed virtual EffortCostMap acwp( long id = CURRENTSCHEDULE, EffortCostCalculationType type = ECCT_All ); /// Return a map of Actual effort and Cost of Work Performed virtual EffortCostMap acwp( long id = CURRENTSCHEDULE, EffortCostCalculationType type = ECCT_All ) const; /// Return Actual effort and Cost of Work Performed upto @date - virtual EffortCost acwp( const QDate &date, long id = CURRENTSCHEDULE ) const; + virtual EffortCost acwp( QDate date, long id = CURRENTSCHEDULE ) const; /// Effort based performance index - virtual double effortPerformanceIndex(const QDate &/*date*/, long /*id*/ = CURRENTSCHEDULE ) const { return 0.0; } + virtual double effortPerformanceIndex(QDate /*date*/, long /*id*/ = CURRENTSCHEDULE ) const { return 0.0; } /// Schedule performance index - virtual double schedulePerformanceIndex(const QDate &/*date*/, long /*id*/ = CURRENTSCHEDULE ) const { return 0.0; } + virtual double schedulePerformanceIndex(QDate /*date*/, long /*id*/ = CURRENTSCHEDULE ) const { return 0.0; } /// Cost performance index - virtual double costPerformanceIndex( long int /*id*/, const QDate &/*date*/, bool * /*error=0*/ ) const { return 0.0; } + virtual double costPerformanceIndex( long int /*id*/, QDate /*date*/, bool * /*error=0*/ ) const { return 0.0; } virtual void initiateCalculationLists(MainSchedule &sch) = 0; virtual DateTime calculateForward(int /*use*/) = 0; virtual DateTime calculateEarlyFinish(int /*use*/) { return DateTime(); } virtual DateTime calculateBackward(int /*use*/) = 0; virtual DateTime calculateLateStart(int /*use*/) { return DateTime(); } virtual DateTime scheduleForward(const DateTime &, int /*use*/) = 0; virtual DateTime scheduleFromStartTime(int /*use*/) { return DateTime(); } virtual DateTime scheduleBackward(const DateTime &, int /*use*/) = 0; virtual DateTime scheduleFromEndTime(int /*use*/) { return DateTime(); } virtual void adjustSummarytask() = 0; /// Returns the (previously) calculated duration Duration duration( long id = CURRENTSCHEDULE ) const; /** * The variance is calculated based on * the optimistic/pessimistic ratio specified for the estimate. */ double variance( long id = CURRENTSCHEDULE, Duration::Unit unit = Duration::Unit_ms ) const; /** * The standard deviation is calculated based on * the optimistic/pessimistic ratio specified for the estimate. */ double deviation( long id = CURRENTSCHEDULE, Duration::Unit unit = Duration::Unit_ms ) const; Node *siblingBefore(); Node *childBefore(Node *node); Node *siblingAfter(); Node *childAfter(Node *node); bool moveChildUp(Node *node); bool moveChildDown(Node *node); /// Check if this node can be linked to node bool legalToLink( const Node *node ) const; /// Check if node par can be linked to node child. (Reimplement) virtual bool legalToLink( const Node *, const Node *) const { return false; } /// Save appointments for schedule with id virtual void saveAppointments(QDomElement &element, long id) const; ///Return the list of appointments for schedule with id. QList appointments( long id = CURRENTSCHEDULE ); /// Adds appointment to this node only (not to resource) virtual bool addAppointment(Appointment *appointment, Schedule &main); /// Return list of all resources with appointments to this task for schedule with @p id. QList assignedResources( long id ) const; /// Find the node with my id virtual Node *findNode() const { return findNode(m_id); } /// Find the node with identity id virtual Node *findNode(const QString &id) const { return (m_parent ? m_parent->findNode(id) : 0); } /// Remove myself from the id register virtual bool removeId() { return removeId(m_id); } /// Remove the registered identity @p id virtual bool removeId(const QString &id) { return (m_parent ? m_parent->removeId(id) : false); } /** * This is when work can start on this node in accordance with * the calendar of allocated resources. Normally this is the same * as @ref startTime(), but may differ if timing constraints are set. */ virtual DateTime workStartTime( long id = CURRENTSCHEDULE ) const; void setWorkStartTime(const DateTime &dt, long id = CURRENTSCHEDULE ); /** * This is when work can finish on this node in accordance with * the calendar of allocated resources. Normally this is the same * as @ref endTime(), but may differ if timing constraints are set. */ virtual DateTime workEndTime( long id = CURRENTSCHEDULE ) const; void setWorkEndTime(const DateTime &dt, long id = CURRENTSCHEDULE ); /// Returns true if this node is critical virtual bool isCritical( long id = CURRENTSCHEDULE ) const { Q_UNUSED(id); return false; } /// Returns true if this node is in a critical path virtual bool inCriticalPath( long id = CURRENTSCHEDULE ) const; /// Returns the level this node is in the hierarchy. Top node is level 0. virtual int level() const; /// Generate WBS Code virtual QString generateWBSCode( QList &indexes, bool sortable = false ) const; /// Returns the Work Breakdown Structure Code QString wbsCode(bool sortable = false) const; double startupCost() const { return m_startupCost; } void setStartupCost(double cost); Account *startupAccount() const { return m_startupAccount; } void setStartupAccount(Account *acc); double shutdownCost() const { return m_shutdownCost; } void setShutdownCost(double cost); Account *shutdownAccount() const { return m_shutdownAccount; } void setShutdownAccount(Account *acc); Account *runningAccount() const { return m_runningAccount; } void setRunningAccount(Account *acc); bool isBaselined( long int id = BASELINESCHEDULE ) const; /** * Return schedule with @p id * If @p id == CURRENTSCHEDULE, return m_currentSchedule * Return 0 if schedule with @p id doesn't exist. */ Schedule *schedule( long id = CURRENTSCHEDULE ) const; /// Return current schedule Schedule *currentSchedule() const { return m_currentSchedule; } /// Set current schedule to schedule with identity @p id, for me and my children virtual void setCurrentSchedule(long id); /// Return true if this node has a valid schedule with identity == @p id bool isScheduled( long id = CURRENTSCHEDULE ) const; /// Return the list of schedules for this node QHash &schedules() { return m_schedules; } /// Find schedule matching name and type. Does not return deleted schedule. Schedule *findSchedule(const QString &name, const Schedule::Type type); /// Find schedule matching name. Does not return deleted schedule. Schedule *findSchedule(const QString &name); /// Find schedule matching type. Does not return deleted schedule. Schedule *findSchedule(const Schedule::Type type); /// Find schedule matching id. Also returns deleted schedule. Schedule *findSchedule(long id) const; /// Take, don't delete (as in destruct). void takeSchedule(const Schedule *schedule); /// Add schedule to list, replace if schedule with same id already exists. void addSchedule(Schedule *schedule); /// Create a new schedule. Schedule *createSchedule(const QString& name, Schedule::Type type, long id); /// Create a new schedule. Schedule *createSchedule(Schedule *parent); /// Set deleted = onoff for schedule with id void setScheduleDeleted(long id, bool onoff); /// Set parent schedule recursivly virtual void setParentSchedule(Schedule *sch); const ResourceRequestCollection &requests() const { return m_requests; } ResourceRequestCollection &requests() { return m_requests; } virtual uint state( long ) const { return State_None; } const Documents &documents() const { return m_documents; } Documents &documents() { return m_documents; } virtual void emitDocumentAdded( Node *node, Document *doc, int idx ); virtual void emitDocumentRemoved( Node *node, Document *doc, int idx ); virtual void emitDocumentChanged( Node *node, Document *doc, int idx ); void blockChanged(bool on = true); public: // These shouldn't be available to other than those who inherits /// Calculate the critical path virtual bool calcCriticalPath(bool fromEnd); virtual void calcFreeFloat(); /// Check if this node has any dependent child nodes virtual bool isEndNode() const; /// Check if this node has any dependent parent nodes virtual bool isStartNode() const; virtual void initiateCalculation(MainSchedule &sch); virtual void resetVisited(); void propagateEarliestStart(DateTime &time); void propagateLatestFinish(DateTime &time); void moveEarliestStart(DateTime &time); void moveLatestFinish(DateTime &time); // Reimplement this virtual Duration summarytaskDurationForward(const DateTime &/*time*/) { return Duration::zeroDuration; } // Reimplement this virtual DateTime summarytaskEarliestStart() { return DateTime(); } // Reimplement this virtual Duration summarytaskDurationBackward(const DateTime &/*time*/) { return Duration::zeroDuration; } // Reimplement this virtual DateTime summarytaskLatestFinish() { return DateTime(); } /** * earlyStart() returns earliest time this node can start * given the constraints of the network. */ DateTime earlyStart( long id = CURRENTSCHEDULE ) const; /** * setEarlyStart() sets earliest time this node can start */ void setEarlyStart(const DateTime &dt, long id = CURRENTSCHEDULE ); /** * lateStart() returns latest time this node can start * given the constraints of the network. */ DateTime lateStart( long id = CURRENTSCHEDULE ) const; /** * setLateStart() sets the earliest time this node can start */ void setLateStart(const DateTime &dt, long id = CURRENTSCHEDULE ); /** * earlyFinish() returns earliest time this node can finish * given the constraints of the network. */ DateTime earlyFinish( long id = CURRENTSCHEDULE ) const; /** * setEarlyFinish() sets earliest time this node can finish */ void setEarlyFinish(const DateTime &dt, long id = CURRENTSCHEDULE ); /** * lateFinish() returns latest time this node can finish * given the constraints of the network. */ DateTime lateFinish( long id = CURRENTSCHEDULE ) const; /** * setLateFinish() sets latest time this node can finish */ void setLateFinish(const DateTime &dt, long id = CURRENTSCHEDULE ); /// Adds appointment to both this node and resource virtual void addAppointment(ResourceSchedule *resource, const DateTime &start, const DateTime &end, double load=100); virtual void clearProxyRelations() {} virtual void addParentProxyRelations( const QList & ) {} virtual void addChildProxyRelations( const QList & ) {} virtual void addParentProxyRelation(Node *, const Relation *) {} virtual void addChildProxyRelation(Node *, const Relation *) {} virtual void changed( int property = -1 ) { changed( this, property ); } Duration getmDurationForward(){ return this->m_durationForward;} public Q_SLOTS: void slotStandardWorktimeChanged( StandardWorktime* ); protected: friend class KPlatoXmlLoaderBase; /** * Calculates and returns the duration of the node. * Reimplement. */ virtual Duration duration(const DateTime &/*time*/, int /*use*/, bool /*backward*/) { return Duration::zeroDuration; } // NOTE: Cannot use setCurrentSchedule() due to overload/casting problems void setCurrentSchedulePtr(Schedule *schedule) { m_currentSchedule = schedule; } virtual void changed(Node *node, int property = -1 ); QList m_nodes; QList m_dependChildNodes; QList m_dependParentNodes; QListm_parentNodes; Node *m_parent; QString m_id; // unique id QString m_name; // Name of this node QString m_leader; // Person or group responsible for this node QString m_description; // Description of this node Estimate *m_estimate; ConstraintType m_constraint; /** * m_constraintTime is used if any of the constraints * FixedInterval, StartNotEarlier, MustStartOn or FixedInterval is selected */ DateTime m_constraintStartTime; /** * m_constraintEndTime is used if any of the constraints * FixedInterval, FinishNotLater, MustFinishOn or FixedInterval is selected */ DateTime m_constraintEndTime; bool m_visitedForward; bool m_visitedBackward; Duration m_durationForward; Duration m_durationBackward; DateTime m_earlyStart; DateTime m_earlyFinish; DateTime m_lateFinish; QHash m_schedules; Schedule *m_currentSchedule; double m_startupCost; Account *m_startupAccount; double m_shutdownCost; Account *m_shutdownAccount; Account *m_runningAccount; Documents m_documents; ResourceRequestCollection m_requests; private: void init(); bool m_blockChanged; }; //////////////////////////////// Estimate //////////////////////////////// /** * The Estimate class stores how much time (or effort) it takes to complete a Task. * The estimate which is needed to complete the task is not simply a single value but * is stored as an optimistic, a pessimistic and an expected value. * With statistical calculations using the PERT distribution, one can arrive at a more * realistic estimate than when using the expected value alone. */ class KPLATOKERNEL_EXPORT Estimate { public: /// Constructor explicit Estimate( Node *parent = 0 ); /// Copy constructor. Estimate (const Estimate &estimate, Node *parent = 0); /// Destructor ~Estimate(); /// Reset estimate void clear(); /// Copy values from @p estimate Estimate &operator=( const Estimate &estimate ); /// Type defines the types of estimates enum Type { Type_Effort, /// Changing amount of resources changes the task duration Type_Duration /// Changing amount of resources will not change the tasks duration }; /// Return the node that owns this Estimate Node *parentNode() const { return m_parent; } /// Set the node that owns this Estimate void setParentNode( Node* parent ) { m_parent = parent; } /// Return estimate Type Type type() const { return m_type; } /// Set estimate type to @p type void setType(Type type); /// Set estimate type to type represented by the string @p type void setType(const QString& type); /// Return estimate type as a string. If @p trans is true, it's translated QString typeToString( bool trans=false ) const; /// Return estimate type @p typ as a string. If @p trans is true, it's translated static QString typeToString( Estimate::Type typ, bool trans=false ); /// Return a stringlist of all estimate types. Translated if @p trans = true. static QStringList typeToStringList( bool trans=false ); /// Return the calendar used when Type is Duration Calendar *calendar() const { return m_calendar; } /// Set the calendar to be used when Type is Duration void setCalendar( Calendar *calendar ); enum Risktype { Risk_None, Risk_Low, Risk_High }; Risktype risktype() const { return m_risktype; } void setRisktype(Risktype type); void setRisktype(const QString& type); QString risktypeToString( bool trans=false ) const; static QStringList risktypeToStringList( bool trans=false ); /// Use defines which value to access enum Use { Use_Expected=0, Use_Optimistic=1, Use_Pessimistic=2 }; /// Return estimate (scaled) of type @p valueType. /// If @p pert is true the pert value is calculated and returned Duration value(int valueType, bool pert) const; /// Return unscaled value Duration optimisticValue() const; /// Return unscaled value Duration pessimisticValue() const; /// Return unscaled value Duration expectedValue() const; /// The unit in which the estimates were entered. Duration::Unit unit() const { return m_unit; } /// Set display unit. void setUnit( Duration::Unit unit ); /// Return the expected estimate (normally entered by user) double expectedEstimate() const { return m_expectedEstimate; } /// Return the optimistic estimate (normally entered by user) double optimisticEstimate() const { return m_optimisticEstimate; } /// Return the pessimistic estimate (normally entered by user) double pessimisticEstimate() const { return m_pessimisticEstimate; } /// Set the expected estimate void setExpectedEstimate( double value ); /// Set the optimistic estimate void setOptimisticEstimate( double value ); /// Set the pessimistic estimate void setPessimisticEstimate( double value ); /** * Set the optimistic estimate as a deviation from "expected" in percent * @param percent should be a negative value. */ void setOptimisticRatio(int percent); /** * Return the "optimistic" estimate as deviation from "expected" in percent. * This should be a negative value. */ int optimisticRatio() const; /** * Set the pessimistic estimate as a deviation from "expected" in percent * @param percent should be a positive value. */ void setPessimisticRatio(int percent); /** * Return the "pessimistic" estimate as the deviation from "expected" in percent. * This should be a positive value. */ int pessimisticRatio() const; /** * The variance is calculated based on * the optimistic/pessimistic estimates, scaled to current unit. */ double variance() const; /** * The variance is calculated based on * the optimistic/pessimistic estimates, scaled to @p unit */ double variance( Duration::Unit unit ) const; /** * The standard deviation is calculated based on * the optimistic/pessimistic estimates, scaled to current unit. */ double deviation() const; /** * The standard deviation is calculated based on * the optimistic/pessimistic estimates, scaled to @p unit */ double deviation( Duration::Unit unit ) const; /// Returns the expected duration. Calculated based on the estimates expected, optimistic and pessimistic Duration pertExpected() const; /// Returns the most optimistic duration. Calculated based on the estimates expected, optimistic and pessimistic Duration pertOptimistic() const; /// Returns the most pessimistic duration. Calculated based on the estimates expected, optimistic and pessimistic Duration pertPessimistic() const; /// Convert the duration @p value (in milliseconds) to a value in @p unit, using the scaling factors in @p scales static double scale( const Duration &value, Duration::Unit unit, const QList &scales ); /// Convert the duration @p value (in @p unit) to a value in milliseconds (base unit), using the scaling factors in @p scales static Duration scale( double value, Duration::Unit unit, const QList &scales ); /// Return a list of default scales scaling factors static QList defaultScales(); /// Return a list of scaling factors fetched from the projects standard worktime QList scales() const; /// Load from xml document bool load(KoXmlElement &element, XMLLoaderObject &status); /// Save to xml document void save(QDomElement &element) const; protected: /// Set (calculate) cached value void setOptimisticValue(); /// Set (calculate) cached value void setExpectedValue(); /// Set (calculate) cached value void setPessimisticValue(); /// Notify parent of changes void changed() { if ( m_parent ) m_parent->changed(); } /// Copy @p estimate, parentNode is not copied void copy( const Estimate &estimate ); private: friend class Node; Node *m_parent; /// Holds the unit entered by user Duration::Unit m_unit; /// Holds the value entered by user, in unit m_unit double m_expectedEstimate; /// Holds the value entered by user, in unit m_unit double m_optimisticEstimate; /// Holds the value entered by user, in unit m_unit double m_pessimisticEstimate; mutable bool m_expectedCached, m_optimisticCached, m_pessimisticCached, m_pertCached; /// Cached value in base unit (milliseconds) Duration m_expectedValue; /// Cached value in base unit (milliseconds) Duration m_optimisticValue; /// Cached value in base unit (milliseconds) Duration m_pessimisticValue; /// Cached pert expected value mutable Duration m_pertExpected; Type m_type; Risktype m_risktype; /// Calendar may be used when Type is Type_Duration Calendar *m_calendar; }; } //KPlato namespace #endif diff --git a/plan/libs/kernel/kptproject.cpp b/plan/libs/kernel/kptproject.cpp index 3cbc3f7ce56..7cf34bfe328 100644 --- a/plan/libs/kernel/kptproject.cpp +++ b/plan/libs/kernel/kptproject.cpp @@ -1,2891 +1,2891 @@ /* This file is part of the KDE project Copyright (C) 2001 Thomas zander Copyright (C) 2004 - 2010, 2012 Dag Andersen Copyright (C) 2007 Florian Piquemal Copyright (C) 2007 Alexis Ménard This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kptproject.h" #include "kptlocale.h" #include "kptappointment.h" #include "kpttask.h" #include "kptdatetime.h" #include "kpteffortcostmap.h" #include "kptschedule.h" #include "kptwbsdefinition.h" #include "kptxmlloaderobject.h" #include "kptschedulerplugin.h" #include "kptdebug.h" #include #include #include #include #include #include namespace KPlato { Project::Project( Node *parent ) : Node( parent ), m_accounts( *this ), m_defaultCalendar( 0 ), m_config( &emptyConfig ), m_schedulerPlugins(), m_sharedResourcesLoaded(false) { //debugPlan<<"("<setDefaultValues(*this); } void Project::deref() { --m_refCount; Q_ASSERT( m_refCount >= 0 ); if ( m_refCount <= 0 ) { emit aboutToBeDeleted(); deleteLater(); } } Project::~Project() { debugPlan<<"("<blockChanged(); } for (Resource *r : resourceIdDict) { r->blockChanged(); } for (ResourceGroup *g : resourceGroupIdDict) { g->blockChanged(); } delete m_standardWorktime; while ( !m_resourceGroups.isEmpty() ) delete m_resourceGroups.takeFirst(); while ( !m_calendars.isEmpty() ) delete m_calendars.takeFirst(); while ( !m_managers.isEmpty() ) delete m_managers.takeFirst(); m_config = 0; //not mine, don't delete } int Project::type() const { return Node::Type_Project; } void Project::generateUniqueNodeIds() { foreach ( Node *n, nodeIdDict ) { debugPlan<name()<<"old"<id(); QString uid = uniqueNodeId(); nodeIdDict.remove( n->id() ); n->setId( uid ); nodeIdDict[ uid ] = n; debugPlan<name()<<"new"<id(); } } void Project::generateUniqueIds() { generateUniqueNodeIds(); foreach ( ResourceGroup *g, resourceGroupIdDict ) { if (g->isShared()) { continue; } resourceGroupIdDict.remove( g->id() ); g->setId( uniqueResourceGroupId() ); resourceGroupIdDict[ g->id() ] = g; } foreach ( Resource *r, resourceIdDict ) { if (r->isShared()) { continue; } resourceIdDict.remove( r->id() ); r->setId( uniqueResourceId() ); resourceIdDict[ r->id() ] = r; } foreach ( Calendar *c, calendarIdDict ) { if (c->isShared()) { continue; } calendarIdDict.remove( c->id() ); c->setId( uniqueCalendarId() ); calendarIdDict[ c->id() ] = c; } } void Project::calculate( Schedule *schedule, const DateTime &dt ) { if ( schedule == 0 ) { errorPlan << "Schedule == 0, cannot calculate"; return ; } m_currentSchedule = schedule; calculate( dt ); } void Project::calculate( const DateTime &dt ) { if ( m_currentSchedule == 0 ) { errorPlan << "No current schedule to calculate"; return ; } stopcalculation = false; QLocale locale; DateTime time = dt.isValid() ? dt : DateTime( QDateTime::currentDateTime() ); MainSchedule *cs = static_cast( m_currentSchedule ); Estimate::Use estType = ( Estimate::Use ) cs->type(); if ( type() == Type_Project ) { cs->setPhaseName( 0, i18n( "Init" ) ); cs->logInfo( i18n( "Schedule project from: %1", locale.toString(dt, QLocale::ShortFormat) ), 0 ); initiateCalculation( *cs ); initiateCalculationLists( *cs ); // must be after initiateCalculation() !! propagateEarliestStart( time ); // Calculate lateFinish from time. If a task has started, remainingEffort is used. cs->setPhaseName( 1, i18nc( "Schedule project forward", "Forward" ) ); cs->logInfo( i18n( "Calculate finish" ), 1 ); cs->lateFinish = calculateForward( estType ); cs->lateFinish = checkEndConstraints( cs->lateFinish ); propagateLatestFinish( cs->lateFinish ); // Calculate earlyFinish. If a task has started, remainingEffort is used. cs->setPhaseName( 2, i18nc( "Schedule project backward","Backward" ) ); cs->logInfo( i18n( "Calculate start" ), 2 ); calculateBackward( estType ); // Schedule. If a task has started, remainingEffort is used and appointments are copied from parent cs->setPhaseName( 3, i18n( "Schedule" ) ); cs->logInfo( i18n( "Schedule tasks forward" ), 3 ); cs->endTime = scheduleForward( cs->startTime, estType ); cs->logInfo( i18n( "Scheduled finish: %1", locale.toString(cs->endTime, QLocale::ShortFormat) ), 3 ); if ( cs->endTime > m_constraintEndTime ) { cs->logError( i18n( "Could not finish project in time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat) ), 3 ); } else if ( cs->endTime == m_constraintEndTime ) { cs->logWarning( i18n( "Finished project exactly on time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat) ), 3 ); } else { cs->logInfo( i18n( "Finished project before time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat) ), 3 ); } calcCriticalPath( false ); calcResourceOverbooked(); cs->notScheduled = false; calcFreeFloat(); emit scheduleChanged( cs ); emit projectChanged(); } else if ( type() == Type_Subproject ) { warnPlan << "Subprojects not implemented"; } else { errorPlan << "Illegal project type: " << type(); } } void Project::calculate( ScheduleManager &sm ) { emit sigCalculationStarted( this, &sm ); sm.setScheduling( true ); m_progress = 0; int nodes = 0; foreach ( Node *n, nodeIdDict ) { if ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) { nodes++; } } int maxprogress = nodes * 3; if ( sm.recalculate() ) { emit maxProgress( maxprogress ); sm.setMaxProgress( maxprogress ); incProgress(); if ( sm.parentManager() ) { sm.expected()->startTime = sm.parentManager()->expected()->startTime; sm.expected()->earlyStart = sm.parentManager()->expected()->earlyStart; } incProgress(); calculate( sm.expected(), sm.recalculateFrom() ); } else { emit maxProgress( maxprogress ); sm.setMaxProgress( maxprogress ); calculate( sm.expected() ); emit scheduleChanged( sm.expected() ); setCurrentSchedule( sm.expected()->id() ); } emit sigProgress( maxprogress ); emit sigCalculationFinished( this, &sm ); emit scheduleManagerChanged( &sm ); emit projectCalculated( &sm ); emit projectChanged(); sm.setScheduling( false ); } void Project::calculate( Schedule *schedule ) { if ( schedule == 0 ) { errorPlan << "Schedule == 0, cannot calculate"; return ; } m_currentSchedule = schedule; calculate(); } void Project::calculate() { if ( m_currentSchedule == 0 ) { errorPlan << "No current schedule to calculate"; return ; } stopcalculation = false; MainSchedule *cs = static_cast( m_currentSchedule ); bool backwards = false; if ( cs->manager() ) { backwards = cs->manager()->schedulingDirection(); } QLocale locale; Estimate::Use estType = ( Estimate::Use ) cs->type(); if ( type() == Type_Project ) { QTime timer; timer.start(); initiateCalculation( *cs ); initiateCalculationLists( *cs ); // must be after initiateCalculation() !! if ( ! backwards ) { cs->setPhaseName( 0, i18n( "Init" ) ); cs->logInfo( i18n( "Schedule project forward from: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat) ), 0 ); cs->startTime = m_constraintStartTime; cs->earlyStart = m_constraintStartTime; // Calculate from start time propagateEarliestStart( cs->earlyStart ); cs->setPhaseName( 1, i18nc( "Schedule project forward", "Forward" ) ); cs->logInfo( i18n( "Calculate late finish" ), 1 ); cs->lateFinish = calculateForward( estType ); // cs->lateFinish = checkEndConstraints( cs->lateFinish ); cs->logInfo( i18n( "Late finish calculated: %1", locale.toString(cs->lateFinish, QLocale::ShortFormat) ), 1 ); propagateLatestFinish( cs->lateFinish ); cs->setPhaseName( 2, i18nc( "Schedule project backward", "Backward" ) ); cs->logInfo( i18n( "Calculate early start" ), 2 ); calculateBackward( estType ); cs->setPhaseName( 3, i18n( "Schedule" ) ); cs->logInfo( i18n( "Schedule tasks forward" ), 3 ); cs->endTime = scheduleForward( cs->startTime, estType ); cs->duration = cs->endTime - cs->startTime; cs->logInfo( i18n( "Scheduled finish: %1", locale.toString(cs->endTime, QLocale::ShortFormat) ), 3 ); if ( cs->endTime > m_constraintEndTime ) { cs->constraintError = true; cs->logError( i18n( "Could not finish project in time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat) ), 3 ); } else if ( cs->endTime == m_constraintEndTime ) { cs->logWarning( i18n( "Finished project exactly on time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat) ), 3 ); } else { cs->logInfo( i18n( "Finished project before time: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat) ), 3 ); } calcCriticalPath( false ); } else { cs->setPhaseName( 0, i18n( "Init" ) ); cs->logInfo( i18n( "Schedule project backward from: %1", locale.toString(m_constraintEndTime, QLocale::ShortFormat) ), 0 ); // Calculate from end time propagateLatestFinish( m_constraintEndTime ); cs->setPhaseName( 1, i18nc( "Schedule project backward", "Backward" ) ); cs->logInfo( i18n( "Calculate early start" ), 1 ); cs->earlyStart = calculateBackward( estType ); // cs->earlyStart = checkStartConstraints( cs->earlyStart ); cs->logInfo( i18n( "Early start calculated: %1", locale.toString(cs->earlyStart, QLocale::ShortFormat) ), 1 ); propagateEarliestStart( cs->earlyStart ); cs->setPhaseName( 2, i18nc( "Schedule project forward", "Forward" ) ); cs->logInfo( i18n( "Calculate late finish" ), 2 ); cs->lateFinish = qMax( m_constraintEndTime, calculateForward( estType ) ); cs->logInfo( i18n( "Late finish calculated: %1", locale.toString(cs->lateFinish, QLocale::ShortFormat) ), 2 ); cs->setPhaseName( 3, i18n( "Schedule" ) ); cs->logInfo( i18n( "Schedule tasks backward" ), 3 ); cs->startTime = scheduleBackward( cs->lateFinish, estType ); cs->endTime = cs->startTime; foreach ( Node *n, allNodes() ) { if ( n->type() == Type_Task || n->type() == Type_Milestone ) { DateTime e = n->endTime( cs->id() ); if ( cs->endTime < e ) { cs->endTime = e; } } } if ( cs->endTime > m_constraintEndTime ) { cs->constraintError = true; cs->logError( i18n( "Failed to finish project within target time" ), 3 ); } cs->duration = cs->endTime - cs->startTime; cs->logInfo( i18n( "Scheduled start: %1, target time: %2", locale.toString(cs->startTime, QLocale::ShortFormat), locale.toString(m_constraintStartTime, QLocale::ShortFormat) ), 3 ); if ( cs->startTime < m_constraintStartTime ) { cs->constraintError = true; cs->logError( i18n( "Must start project early in order to finish in time: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat) ), 3 ); } else if ( cs->startTime == m_constraintStartTime ) { cs->logWarning( i18n( "Start project exactly on time: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat) ), 3 ); } else { cs->logInfo( i18n( "Can start project later than time: %1", locale.toString(m_constraintStartTime, QLocale::ShortFormat) ), 3 ); } calcCriticalPath( true ); } cs->logInfo( i18n( "Calculation took: %1", KFormat().formatDuration( timer.elapsed() ) ) ); // TODO: fix this uncertainty, manager should *always* be available if (cs->manager()) { finishCalculation(*(cs->manager())); } } else if ( type() == Type_Subproject ) { warnPlan << "Subprojects not implemented"; } else { errorPlan << "Illegal project type: " << type(); } } void Project::finishCalculation( ScheduleManager &sm ) { MainSchedule *cs = sm.expected(); if (nodeIdDict.count() > 1) { // calculate project duration cs->startTime = m_constraintEndTime; cs->endTime = m_constraintStartTime; for (const Node *n : nodeIdDict) { cs->startTime = qMin(cs->startTime, n->startTime(cs->id())); cs->endTime = qMax(cs->endTime, n->endTime(cs->id())); } cs->duration = cs->endTime - cs->startTime; } calcCriticalPath( false ); calcResourceOverbooked(); cs->notScheduled = false; calcFreeFloat(); emit scheduleChanged( cs ); emit projectChanged(); debugPlan<startTime<endTime<<"-------------------------"; } void Project::setProgress( int progress, ScheduleManager *sm ) { m_progress = progress; if ( sm ) { sm->setProgress( progress ); } emit sigProgress( progress ); } void Project::setMaxProgress( int max, ScheduleManager *sm ) { if ( sm ) { sm->setMaxProgress( max ); } emitMaxProgress( max ); } void Project::incProgress() { m_progress += 1; emit sigProgress( m_progress ); } void Project::emitMaxProgress( int value ) { emit maxProgress( value ); } bool Project::calcCriticalPath( bool fromEnd ) { //debugPlan; MainSchedule *cs = static_cast( m_currentSchedule ); if ( cs == 0 ) { return false; } if ( fromEnd ) { QListIterator startnodes = cs->startNodes(); while ( startnodes.hasNext() ) { startnodes.next() ->calcCriticalPath( fromEnd ); } } else { QListIterator endnodes = cs->endNodes(); while ( endnodes.hasNext() ) { endnodes.next() ->calcCriticalPath( fromEnd ); } } calcCriticalPathList( cs ); return false; } void Project::calcCriticalPathList( MainSchedule *cs ) { //debugPlan<name(); cs->clearCriticalPathList(); foreach ( Node *n, allNodes() ) { if ( n->numDependParentNodes() == 0 && n->inCriticalPath( cs->id() ) ) { cs->addCriticalPath(); cs->addCriticalPathNode( n ); calcCriticalPathList( cs, n ); } } cs->criticalPathListCached = true; //debugPlan<<*(criticalPathList( cs->id() )); } void Project::calcCriticalPathList( MainSchedule *cs, Node *node ) { //debugPlan<name()<<", "<id(); bool newPath = false; QList lst = *( cs->currentCriticalPath() ); foreach ( Relation *r, node->dependChildNodes() ) { if ( r->child()->inCriticalPath( cs->id() ) ) { if ( newPath ) { cs->addCriticalPath( &lst ); //debugPlan<name()<<" new path"; } cs->addCriticalPathNode( r->child() ); calcCriticalPathList( cs, r->child() ); newPath = true; } } } const QList< QList > *Project::criticalPathList( long id ) { Schedule *s = schedule( id ); if ( s == 0 ) { //debugPlan<<"No schedule with id="<( s ); if ( ! ms->criticalPathListCached ) { initiateCalculationLists( *ms ); calcCriticalPathList( ms ); } return ms->criticalPathList(); } QList Project::criticalPath( long id, int index ) { Schedule *s = schedule( id ); if ( s == 0 ) { //debugPlan<<"No schedule with id="<(); } MainSchedule *ms = static_cast( s ); if ( ! ms->criticalPathListCached ) { initiateCalculationLists( *ms ); calcCriticalPathList( ms ); } return ms->criticalPath( index ); } DateTime Project::startTime( long id ) const { Schedule *s = schedule( id ); return s ? s->startTime : m_constraintStartTime; } DateTime Project::endTime( long id ) const { Schedule *s = schedule( id ); return s ? s->endTime : m_constraintEndTime; } Duration Project::duration( long id ) const { Schedule *s = schedule( id ); return s ? s->duration : Duration::zeroDuration; } Duration *Project::getRandomDuration() { return 0L; } DateTime Project::checkStartConstraints( const DateTime &dt ) const { DateTime t = dt; foreach ( Node *n, nodeIdDict ) { if ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) { switch ( n->constraint() ) { case Node::FixedInterval: case Node::StartNotEarlier: case Node::MustStartOn: t = qMin( t, qMax( n->constraintStartTime(), m_constraintStartTime ) ); break; default: break; } } } return t; } DateTime Project::checkEndConstraints( const DateTime &dt ) const { DateTime t = dt; foreach ( Node *n, nodeIdDict ) { if ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) { switch ( n->constraint() ) { case Node::FixedInterval: case Node::FinishNotLater: case Node::MustFinishOn: t = qMax( t, qMin( n->constraintEndTime(), m_constraintEndTime ) ); break; default: break; } } } return t; } #ifndef PLAN_NLOGDEBUG bool Project::checkParent( Node *n, const QList &list, QList &checked ) { if ( n->isStartNode() ) { debugPlan< lst = list; lst << n; foreach ( Relation *r, n->dependParentNodes() ) { if ( checked.contains( r ) ) { debugPlan<<"Depend:"<parent()<<": checked"; continue; } checked << r; if ( ! checkParent( r->parent(), lst, checked ) ) { return false; } } Task *t = static_cast( n ); foreach ( Relation *r, t->parentProxyRelations() ) { if ( checked.contains( r ) ) { debugPlan<<"Depend:"<parent()<<": checked"; continue; } checked << r; debugPlan<<"Proxy:"<parent()<<":"<parent(), lst, checked ) ) { return false; } } return true; } bool Project::checkChildren( Node *n, const QList &list, QList &checked ) { if ( n->isEndNode() ) { debugPlan< lst = list; lst << n; foreach ( Relation *r, n->dependChildNodes() ) { if ( checked.contains( r ) ) { debugPlan<<"Depend:"<parent()<<": checked"; continue; } checked << r; if ( ! checkChildren( r->child(), lst, checked ) ) { return false; } } Task *t = static_cast( n ); foreach ( Relation *r, t->childProxyRelations() ) { if ( checked.contains( r ) ) { debugPlan<<"Depend:"<parent()<<": checked"; continue; } debugPlan<<"Proxy:"<parent()<<":"<child(), lst, checked ) ) { return false; } } return true; } #endif void Project::tasksForward() { m_hardConstraints.clear(); m_softConstraints.clear(); m_terminalNodes.clear(); foreach ( Task *t, allTasks() ) { switch ( t->constraint() ) { case Node::MustStartOn: case Node::MustFinishOn: case Node::FixedInterval: m_hardConstraints.append( t ); break; case Node::StartNotEarlier: case Node::FinishNotLater: m_softConstraints.append( t ); break; default: if ( t->isEndNode() ) { m_terminalNodes.append( t ); } break; } } #ifndef PLAN_NLOGDEBUG debugPlan<<"End nodes:"< lst; QList rel; Q_ASSERT( checkParent( n, lst, rel ) ); Q_UNUSED( n ); } #endif } void Project::tasksBackward() { m_hardConstraints.clear(); m_softConstraints.clear(); m_terminalNodes.clear(); foreach ( Task *t, allTasks() ) { switch ( t->constraint() ) { case Node::MustStartOn: case Node::MustFinishOn: case Node::FixedInterval: m_hardConstraints.append( t ); break; case Node::StartNotEarlier: case Node::FinishNotLater: m_softConstraints.append( t ); break; default: if ( t->isStartNode() ) { m_terminalNodes.append( t ); } break; } } #ifndef PLAN_NLOGDEBUG debugPlan<<"Start nodes:"< lst; QList rel; Q_ASSERT( checkChildren( n, lst, rel ) ); Q_UNUSED( n ); } #endif } DateTime Project::calculateForward( int use ) { //debugPlan<( m_currentSchedule ); if ( cs == 0 ) { return finish; } if ( type() == Node::Type_Project ) { QTime timer; timer.start(); cs->logInfo( i18n( "Start calculating forward" ) ); m_visitedForward = true; if ( ! m_visitedBackward ) { // setup tasks tasksForward(); // Do all hard constrained first foreach ( Node *n, m_hardConstraints ) { cs->logDebug( "Calculate task with hard constraint:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->calculateEarlyFinish( use ); // do not do predeccessors if ( time > finish ) { finish = time; } } // do the predeccessors foreach ( Node *n, m_hardConstraints ) { cs->logDebug( "Calculate predeccessors to hard constrained task:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->calculateForward( use ); if ( time > finish ) { finish = time; } } // now try to schedule soft constrained *with* predeccessors foreach ( Node *n, m_softConstraints ) { cs->logDebug( "Calculate task with soft constraint:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->calculateForward( use ); if ( time > finish ) { finish = time; } } // and then the rest using the end nodes to calculate everything (remaining) foreach ( Task *n, m_terminalNodes ) { cs->logDebug( "Calculate using end task:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->calculateForward( use ); if ( time > finish ) { finish = time; } } } else { // tasks have been calculated backwards in this order foreach ( Node *n, cs->backwardNodes() ) { DateTime time = n->calculateForward( use ); if ( time > finish ) { finish = time; } } } cs->logInfo( i18n( "Finished calculating forward: %1 ms", timer.elapsed() ) ); } else { //TODO: subproject } return finish; } DateTime Project::calculateBackward( int use ) { //debugPlan<( m_currentSchedule ); if ( cs == 0 ) { return start; } if ( type() == Node::Type_Project ) { QTime timer; timer.start(); cs->logInfo( i18n( "Start calculating backward" ) ); m_visitedBackward = true; if ( ! m_visitedForward ) { // setup tasks tasksBackward(); // Do all hard constrained first foreach ( Task *n, m_hardConstraints ) { cs->logDebug( "Calculate task with hard constraint:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->calculateLateStart( use ); // do not do predeccessors if ( ! start.isValid() || time < start ) { start = time; } } // then do the predeccessors foreach ( Task *n, m_hardConstraints ) { cs->logDebug( "Calculate predeccessors to hard constrained task:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->calculateBackward( use ); if ( ! start.isValid() || time < start ) { start = time; } } // now try to schedule soft constrained *with* predeccessors foreach ( Task *n, m_softConstraints ) { cs->logDebug( "Calculate task with soft constraint:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->calculateBackward( use ); if ( ! start.isValid() || time < start ) { start = time; } } // and then the rest using the start nodes to calculate everything (remaining) foreach ( Task *n, m_terminalNodes ) { cs->logDebug( "Calculate using start task:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->calculateBackward( use ); if ( ! start.isValid() || time < start ) { start = time; } } } else { // tasks have been calculated forwards in this order foreach ( Node *n, cs->forwardNodes() ) { DateTime time = n->calculateBackward( use ); if ( ! start.isValid() || time < start ) { start = time; } } } cs->logInfo( i18n( "Finished calculating backward: %1 ms", timer.elapsed() ) ); } else { //TODO: subproject } return start; } DateTime Project::scheduleForward( const DateTime &earliest, int use ) { DateTime end; MainSchedule *cs = static_cast( m_currentSchedule ); if ( cs == 0 || stopcalculation ) { return DateTime(); } QTime timer; timer.start(); cs->logInfo( i18n( "Start scheduling forward" ) ); resetVisited(); // Schedule in the same order as calculated forward // Do all hard constrained first foreach ( Node *n, m_hardConstraints ) { cs->logDebug( "Schedule task with hard constraint:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->scheduleFromStartTime( use ); // do not do predeccessors if ( time > end ) { end = time; } } foreach ( Node *n, cs->forwardNodes() ) { cs->logDebug( "Schedule task:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->scheduleForward( earliest, use ); if ( time > end ) { end = time; } } // Fix summarytasks adjustSummarytask(); cs->logInfo( i18n( "Finished scheduling forward: %1 ms", timer.elapsed() ) ); foreach ( Node *n, allNodes() ) { if ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) { Q_ASSERT( n->isScheduled() ); } } return end; } DateTime Project::scheduleBackward( const DateTime &latest, int use ) { DateTime start; MainSchedule *cs = static_cast( m_currentSchedule ); if ( cs == 0 || stopcalculation ) { return start; } QTime timer; timer.start(); cs->logInfo( i18n( "Start scheduling backward" ) ); resetVisited(); // Schedule in the same order as calculated backward // Do all hard constrained first foreach ( Node *n, m_hardConstraints ) { cs->logDebug( "Schedule task with hard constraint:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->scheduleFromEndTime( use ); // do not do predeccessors if ( ! start.isValid() || time < start ) { start = time; } } foreach ( Node *n, cs->backwardNodes() ) { cs->logDebug( "Schedule task:" + n->name() + " : " + n->constraintToString() ); DateTime time = n->scheduleBackward( latest, use ); if ( ! start.isValid() || time < start ) { start = time; } } // Fix summarytasks adjustSummarytask(); cs->logInfo( i18n( "Finished scheduling backward: %1 ms", timer.elapsed() ) ); foreach ( Node *n, allNodes() ) { if ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) { Q_ASSERT( n->isScheduled() ); } } return start; } void Project::adjustSummarytask() { MainSchedule *cs = static_cast( m_currentSchedule ); if ( cs == 0 || stopcalculation ) { return; } QListIterator it( cs->summaryTasks() ); while ( it.hasNext() ) { it.next() ->adjustSummarytask(); } } void Project::initiateCalculation( MainSchedule &sch ) { //debugPlan< git( m_resourceGroups ); while ( git.hasNext() ) { git.next() ->initiateCalculation( sch ); } Node::initiateCalculation( sch ); } void Project::initiateCalculationLists( MainSchedule &sch ) { //debugPlan< it = childNodeIterator(); while ( it.hasNext() ) { it.next() ->initiateCalculationLists( sch ); } } else { //TODO: subproject } } bool Project::load( KoXmlElement &element, XMLLoaderObject &status ) { //debugPlan<<"--->"; m_useSharedResources = false; // default should off in case old project // load locale first KoXmlNode n = element.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement e = n.toElement(); if ( e.tagName() == "locale" ) { Locale *l = locale(); l->setCurrencySymbol(e.attribute( "currency-symbol", "")); if ( e.hasAttribute( "currency-digits" ) ) { l->setMonetaryDecimalPlaces(e.attribute("currency-digits").toInt()); } QLocale::Language language = QLocale::AnyLanguage; QLocale::Country country = QLocale::AnyCountry; if (e.hasAttribute("language")) { language = static_cast(e.attribute("language").toInt()); } if (e.hasAttribute("country")) { country = static_cast(e.attribute("country").toInt()); } l->setCurrencyLocale(language, country); } else if (e.tagName() == "shared-resources") { m_useSharedResources = e.attribute("use", "0").toInt(); m_sharedResourcesFile = e.attribute("file"); } } QList cals; QString s; bool ok = false; setName( element.attribute( "name" ) ); removeId( m_id ); m_id = element.attribute( "id" ); registerNodeId( this ); m_leader = element.attribute( "leader" ); m_description = element.attribute( "description" ); QTimeZone tz( element.attribute( "timezone" ).toLatin1() ); if ( tz.isValid() ) { m_timeZone = tz; } else warnPlan<<"No timezone specified, using default (local)"; status.setProjectTimeZone( m_timeZone ); // Allow for both numeric and text s = element.attribute( "scheduling", "0" ); m_constraint = ( Node::ConstraintType ) s.toInt( &ok ); if ( !ok ) setConstraint( s ); if ( m_constraint != Node::MustStartOn && m_constraint != Node::MustFinishOn ) { errorPlan << "Illegal constraint: " << constraintToString(); setConstraint( Node::MustStartOn ); } s = element.attribute( "start-time" ); if ( !s.isEmpty() ) m_constraintStartTime = DateTime::fromString( s, m_timeZone ); s = element.attribute( "end-time" ); if ( !s.isEmpty() ) m_constraintEndTime = DateTime::fromString( s, m_timeZone ); status.setProgress( 10 ); // Load the project children // Do calendars first, they only reference other calendars //debugPlan<<"Calendars--->"; n = element.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement e = n.toElement(); if ( e.tagName() == "calendar" ) { // Load the calendar. // Referenced by resources Calendar * child = new Calendar(); child->setProject( this ); if ( child->load( e, status ) ) { cals.append( child ); // temporary, reorder later } else { // TODO: Complain about this errorPlan << "Failed to load calendar"; delete child; } } else if ( e.tagName() == "standard-worktime" ) { // Load standard worktime StandardWorktime * child = new StandardWorktime(); if ( child->load( e, status ) ) { setStandardWorktime( child ); } else { errorPlan << "Failed to load standard worktime"; delete child; } } } // calendars references calendars in arbritary saved order bool added = false; do { added = false; QList lst; while ( !cals.isEmpty() ) { Calendar *c = cals.takeFirst(); c->m_blockversion = true; if ( c->parentId().isEmpty() ) { addCalendar( c, status.baseCalendar() ); // handle pre 0.6 version added = true; //debugPlan<<"added to project:"<name(); } else { Calendar *par = calendar( c->parentId() ); if ( par ) { par->m_blockversion = true; addCalendar( c, par ); added = true; //debugPlan<<"added:"<name()<<" to parent:"<name(); par->m_blockversion = false; } else { lst.append( c ); // treat later //debugPlan<<"treat later:"<name(); } } c->m_blockversion = false; } cals = lst; } while ( added ); if ( ! cals.isEmpty() ) { errorPlan<<"All calendars not saved!"; } //debugPlan<<"Calendars<---"; status.setProgress( 15 ); // Resource groups and resources, can reference calendars n = element.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement e = n.toElement(); if ( e.tagName() == "resource-group" ) { // Load the resources // References calendars ResourceGroup * child = new ResourceGroup(); if ( child->load( e, status ) ) { addResourceGroup( child ); } else { // TODO: Complain about this delete child; } } } status.setProgress( 20 ); // The main stuff n = element.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement e = n.toElement(); if ( e.tagName() == "project" ) { //debugPlan<<"Sub project--->"; /* // Load the subproject Project * child = new Project( this ); if ( child->load( e ) ) { if ( !addTask( child, this ) ) { delete child; // TODO: Complain about this } } else { // TODO: Complain about this delete child; }*/ } else if ( e.tagName() == "task" ) { //debugPlan<<"Task--->"; // Load the task (and resourcerequests). // Depends on resources already loaded Task * child = new Task( this ); if ( child->load( e, status ) ) { if ( !addTask( child, this ) ) { delete child; // TODO: Complain about this } } else { // TODO: Complain about this delete child; } } } status.setProgress( 70 ); // These go last n = element.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { debugPlan<"; // Load accounts // References tasks if ( !m_accounts.load( e, *this ) ) { errorPlan << "Failed to load accounts"; } } else if ( e.tagName() == "relation" ) { //debugPlan<<"Relation--->"; // Load the relation // References tasks Relation * child = new Relation(); if ( !child->load( e, *this ) ) { // TODO: Complain about this errorPlan << "Failed to load relation"; delete child; } //debugPlan<<"Relation<---"; } else if ( e.tagName() == "schedules" ) { //debugPlan<<"Project schedules & task appointments--->"; // References tasks and resources KoXmlNode sn = e.firstChild(); for ( ; ! sn.isNull(); sn = sn.nextSibling() ) { if ( ! sn.isElement() ) { continue; } KoXmlElement el = sn.toElement(); //debugPlan<loadXML( el, status ) ) { if ( add ) addScheduleManager( sm ); } else { errorPlan << "Failed to load schedule manager"; delete sm; } } else { debugPlan<<"No schedule manager ?!"; } } //debugPlan<<"Node schedules<---"; } else if ( e.tagName() == "resource-teams" ) { //debugPlan<<"Resource teams--->"; // References other resources KoXmlNode tn = e.firstChild(); for ( ; ! tn.isNull(); tn = tn.nextSibling() ) { if ( ! tn.isElement() ) { continue; } KoXmlElement el = tn.toElement(); if ( el.tagName() == "team" ) { Resource *r = findResource( el.attribute( "team-id" ) ); Resource *tm = findResource( el.attribute( "member-id" ) ); if ( r == 0 || tm == 0 ) { errorPlan<<"resource-teams: cannot find resources"; continue; } if ( r == tm ) { errorPlan<<"resource-teams: a team cannot be a member of itself"; continue; } r->addTeamMemberId( tm->id() ); } else { errorPlan<<"resource-teams: unhandled tag"<currencySymbolExplicit().isEmpty()) { loc.setAttribute("currency-symbol", l->currencySymbolExplicit()); } loc.setAttribute("currency-digits", l->monetaryDecimalPlaces()); loc.setAttribute("language", l->currencyLanguage()); loc.setAttribute("country", l->currencyCountry()); QDomElement share = me.ownerDocument().createElement( "shared-resources" ); me.appendChild(share); share.setAttribute("use", m_useSharedResources); share.setAttribute("file", m_sharedResourcesFile); m_accounts.save( me ); // save calendars foreach ( Calendar *c, calendarIdDict ) { c->save( me ); } // save standard worktime if ( m_standardWorktime ) m_standardWorktime->save( me ); // save project resources, must be after calendars QListIterator git( m_resourceGroups ); while ( git.hasNext() ) { git.next() ->save( me ); } // Only save parent relations QListIterator it( m_dependParentNodes ); while ( it.hasNext() ) { it.next() ->save( me ); } for ( int i = 0; i < numChildren(); i++ ) // Save all children childNode( i ) ->save( me ); // Now we can save relations assuming no tasks have relations outside the project QListIterator nodes( m_nodes ); while ( nodes.hasNext() ) { nodes.next() ->saveRelations( me ); } if ( !m_managers.isEmpty() ) { QDomElement el = me.ownerDocument().createElement( "schedules" ); me.appendChild( el ); foreach ( ScheduleManager *sm, m_managers ) { sm->saveXML( el ); } } // save resource teams QDomElement el = me.ownerDocument().createElement( "resource-teams" ); me.appendChild( el ); foreach ( Resource *r, resourceIdDict ) { if ( r->type() != Resource::Type_Team ) { continue; } foreach ( const QString &id, r->teamMemberIds() ) { QDomElement e = el.ownerDocument().createElement( "team" ); el.appendChild( e ); e.setAttribute( "team-id", r->id() ); e.setAttribute( "member-id", id ); } } } void Project::saveWorkPackageXML( QDomElement &element, const Node *node, long id ) const { QDomElement me = element.ownerDocument().createElement( "project" ); element.appendChild( me ); me.setAttribute( "name", m_name ); me.setAttribute( "leader", m_leader ); me.setAttribute( "id", m_id ); me.setAttribute( "description", m_description ); me.setAttribute( "timezone", m_timeZone.isValid() ? QString::fromLatin1(m_timeZone.id()) : QString() ); me.setAttribute( "scheduling", constraintToString() ); me.setAttribute( "start-time", m_constraintStartTime.toString( Qt::ISODate ) ); me.setAttribute( "end-time", m_constraintEndTime.toString( Qt::ISODate ) ); QListIterator git( m_resourceGroups ); while ( git.hasNext() ) { git.next() ->saveWorkPackageXML( me, node->assignedResources( id ) ); } if ( node == 0 ) { return; } node->saveWorkPackageXML( me, id ); foreach ( ScheduleManager *sm, m_managerIdMap ) { if ( sm->scheduleId() == id ) { QDomElement el = me.ownerDocument().createElement( "schedules" ); me.appendChild( el ); sm->saveWorkPackageXML( el, *node ); break; } } } void Project::setParentSchedule( Schedule *sch ) { QListIterator it = m_nodes; while ( it.hasNext() ) { it.next() ->setParentSchedule( sch ); } } void Project::addResourceGroup( ResourceGroup *group, int index ) { int i = index == -1 ? m_resourceGroups.count() : index; emit resourceGroupToBeAdded( group, i ); m_resourceGroups.insert( i, group ); setResourceGroupId( group ); group->setProject( this ); foreach ( Resource *r, group->resources() ) { setResourceId( r ); r->setProject( this ); } emit resourceGroupAdded( group ); emit projectChanged(); } ResourceGroup *Project::takeResourceGroup( ResourceGroup *group ) { int i = m_resourceGroups.indexOf( group ); Q_ASSERT( i != -1 ); if ( i == -1 ) { return 0; } emit resourceGroupToBeRemoved( group ); ResourceGroup *g = m_resourceGroups.takeAt( i ); Q_ASSERT( group == g ); g->setProject( 0 ); removeResourceGroupId( g->id() ); foreach ( Resource *r, g->resources() ) { r->setProject( 0 ); removeResourceId( r->id() ); } emit resourceGroupRemoved( g ); emit projectChanged(); return g; } QList &Project::resourceGroups() { return m_resourceGroups; } void Project::addResource( ResourceGroup *group, Resource *resource, int index ) { int i = index == -1 ? group->numResources() : index; emit resourceToBeAdded( group, i ); group->addResource( i, resource, 0 ); setResourceId( resource ); emit resourceAdded( resource ); emit projectChanged(); } Resource *Project::takeResource( ResourceGroup *group, Resource *resource ) { emit resourceToBeRemoved( resource ); bool result = removeResourceId( resource->id() ); Q_ASSERT( result == true ); if (!result) { warnPlan << "Could not remove resource with id" << resource->id(); } resource->removeRequests(); // not valid anymore Resource *r = group->takeResource( resource ); Q_ASSERT( resource == r ); if (resource != r) { warnPlan << "Cound not take resource from group"; } emit resourceRemoved( resource ); emit projectChanged(); return r; } void Project::moveResource( ResourceGroup *group, Resource *resource ) { if ( group == resource->parentGroup() ) { return; } takeResource( resource->parentGroup(), resource ); addResource( group, resource ); return; } QMap< QString, QString > Project::externalProjects() const { QMap< QString, QString > map; foreach ( Resource *r, resourceList() ) { for( QMapIterator it( r->externalProjects() ); it.hasNext(); ) { it.next(); if ( ! map.contains( it.key() ) ) { map[ it.key() ] = it.value(); } } } return map; } bool Project::addTask( Node* task, Node* position ) { // we want to add a task at the given position. => the new node will // become next sibling right after position. if ( 0 == position ) { return addSubTask( task, this ); } //debugPlan<<"Add"<name()<<" after"<name(); // in case we want to add to the main project, we make it child element // of the root element. if ( Node::Type_Project == position->type() ) { return addSubTask( task, position ); } // find the position // we have to tell the parent that we want to delete one of its children Node* parentNode = position->parentNode(); if ( !parentNode ) { debugPlan <<"parent node not found???"; return false; } int index = parentNode->findChildNode( position ); if ( -1 == index ) { // ok, it does not exist debugPlan <<"Task not found???"; return false; } return addSubTask( task, index + 1, parentNode ); } bool Project::addSubTask( Node* task, Node* parent ) { // append task to parent return addSubTask( task, -1, parent ); } bool Project::addSubTask( Node* task, int index, Node* parent, bool emitSignal ) { // we want to add a subtask to the node "parent" at the given index. // If parent is 0, add to this Node *p = parent; if ( 0 == p ) { p = this; } if ( !registerNodeId( task ) ) { errorPlan << "Failed to register node id, can not add subtask: " << task->name(); return false; } int i = index == -1 ? p->numChildren() : index; if ( emitSignal ) emit nodeToBeAdded( p, i ); p->insertChildNode( i, task ); connect( this, SIGNAL(standardWorktimeChanged(StandardWorktime*)), task, SLOT(slotStandardWorktimeChanged(StandardWorktime*)) ); if ( emitSignal ) { emit nodeAdded( task ); emit projectChanged(); if ( p != this && p->numChildren() == 1 ) { emit nodeChanged( p ); } } return true; } void Project::takeTask( Node *node, bool emitSignal ) { //debugPlan<name(); Node * parent = node->parentNode(); if ( parent == 0 ) { debugPlan <<"Node must have a parent!"; return; } removeId( node->id() ); if ( emitSignal ) emit nodeToBeRemoved( node ); disconnect( this, SIGNAL(standardWorktimeChanged(StandardWorktime*)), node, SLOT(slotStandardWorktimeChanged(StandardWorktime*)) ); parent->takeChildNode( node ); if ( emitSignal ) { emit nodeRemoved( node ); emit projectChanged(); if ( parent != this && parent->type() != Node::Type_Summarytask ) { emit nodeChanged( parent ); } } } bool Project::canMoveTask( Node* node, Node *newParent ) { //debugPlan<name()<<" to"<name(); if ( node == this ) { return false; } Node *p = newParent; while ( p && p != this ) { if ( ! node->canMoveTo( p ) ) { return false; } p = p->parentNode(); } return true; } bool Project::moveTask( Node* node, Node *newParent, int newPos ) { //debugPlan<name()<<" to"<name()<<","<parentNode(); int oldPos = oldParent->indexOf( node ); int i = newPos < 0 ? newParent->numChildren() : newPos; if ( oldParent == newParent && i == oldPos ) { // no need to move to where it already is return false; } int newRow = i; if ( oldParent == newParent && newPos > oldPos ) { ++newRow; // itemmodels wants new row *before* node is removed from old position } debugPlan<name()<<"at"<indexOf( node )<<"to"<name()<numChildren() == 0 ) { emit nodeChanged( oldParent ); } if ( newParent != this && newParent->numChildren() == 1 ) { emit nodeChanged( newParent ); } return true; } bool Project::canIndentTask( Node* node ) { if ( 0 == node ) { // should always be != 0. At least we would get the Project, // but you never know who might change that, so better be careful return false; } if ( node->type() == Node::Type_Project ) { //debugPlan<<"The root node cannot be indented"; return false; } // we have to find the parent of task to manipulate its list of children Node* parentNode = node->parentNode(); if ( !parentNode ) { return false; } if ( parentNode->findChildNode( node ) == -1 ) { errorPlan << "Tasknot found???"; return false; } Node *sib = node->siblingBefore(); if ( !sib ) { //debugPlan<<"new parent node not found"; return false; } if ( node->findParentRelation( sib ) || node->findChildRelation( sib ) ) { //debugPlan<<"Cannot have relations to parent"; return false; } return true; } bool Project::indentTask( Node* node, int index ) { if ( canIndentTask( node ) ) { Node * newParent = node->siblingBefore(); int i = index == -1 ? newParent->numChildren() : index; moveTask( node, newParent, i ); //debugPlan; return true; } return false; } bool Project::canUnindentTask( Node* node ) { if ( 0 == node ) { // is always != 0. At least we would get the Project, but you // never know who might change that, so better be careful return false; } if ( Node::Type_Project == node->type() ) { //debugPlan<<"The root node cannot be unindented"; return false; } // we have to find the parent of task to manipulate its list of children // and we need the parent's parent too Node* parentNode = node->parentNode(); if ( !parentNode ) { return false; } Node* grandParentNode = parentNode->parentNode(); if ( !grandParentNode ) { //debugPlan<<"This node already is at the top level"; return false; } int index = parentNode->findChildNode( node ); if ( -1 == index ) { errorPlan << "Tasknot found???"; return false; } return true; } bool Project::unindentTask( Node* node ) { if ( canUnindentTask( node ) ) { Node * parentNode = node->parentNode(); Node *grandParentNode = parentNode->parentNode(); int i = grandParentNode->indexOf( parentNode ) + 1; if ( i == 0 ) { i = grandParentNode->numChildren(); } moveTask( node, grandParentNode, i ); //debugPlan; return true; } return false; } bool Project::canMoveTaskUp( Node* node ) { if ( node == 0 ) return false; // safety // we have to find the parent of task to manipulate its list of children Node* parentNode = node->parentNode(); if ( !parentNode ) { //debugPlan<<"No parent found"; return false; } if ( parentNode->findChildNode( node ) == -1 ) { errorPlan << "Tasknot found???"; return false; } if ( node->siblingBefore() ) { return true; } return false; } bool Project::moveTaskUp( Node* node ) { if ( canMoveTaskUp( node ) ) { moveTask( node, node->parentNode(), node->parentNode()->indexOf( node ) - 1 ); return true; } return false; } bool Project::canMoveTaskDown( Node* node ) { if ( node == 0 ) return false; // safety // we have to find the parent of task to manipulate its list of children Node* parentNode = node->parentNode(); if ( !parentNode ) { return false; } if ( parentNode->findChildNode( node ) == -1 ) { errorPlan << "Tasknot found???"; return false; } if ( node->siblingAfter() ) { return true; } return false; } bool Project::moveTaskDown( Node* node ) { if ( canMoveTaskDown( node ) ) { moveTask( node, node->parentNode(), node->parentNode()->indexOf( node ) + 1 ); return true; } return false; } Task *Project::createTask() { Task * node = new Task(); node->setId( uniqueNodeId() ); reserveId( node->id(), node ); return node; } Task *Project::createTask( const Task &def ) { Task * node = new Task( def ); node->setId( uniqueNodeId() ); reserveId( node->id(), node ); return node; } Node *Project::findNode( const QString &id ) const { if ( m_parent == 0 ) { if ( nodeIdDict.contains( id ) ) { return nodeIdDict[ id ]; } return 0; } return m_parent->findNode( id ); } bool Project::nodeIdentExists( const QString &id ) const { return nodeIdDict.contains( id ) || nodeIdReserved.contains( id ); } QString Project::uniqueNodeId( int seed ) const { Q_UNUSED(seed); QString s = QDateTime::currentDateTime().toString( Qt::ISODate ) + ' '; QString ident = s + KRandom::randomString( 10 ); // int i = seed; while ( nodeIdentExists( ident ) ) { ident = s + KRandom::randomString( 10 ); } return ident; } QString Project::uniqueNodeId( const QList &existingIds, int seed ) { QString id = uniqueNodeId( seed ); while ( existingIds.contains( id ) ) { id = uniqueNodeId( seed ); } return id; } bool Project::removeId( const QString &id ) { //debugPlan <<"id=" << id; if ( m_parent ) { return m_parent->removeId( id ); } //debugPlan << "id=" << id<< nodeIdDict.contains(id); return nodeIdDict.remove( id ); } void Project::reserveId( const QString &id, Node *node ) { //debugPlan <<"id=" << id << node->name(); nodeIdReserved.insert( id, node ); } bool Project::registerNodeId( Node *node ) { nodeIdReserved.remove( node->id() ); if ( node->id().isEmpty() ) { warnPlan << "Node id is empty, cannot register it"; return false; } Node *rn = findNode( node->id() ); if ( rn == 0 ) { //debugPlan <<"id=" << node->id() << node->name(); nodeIdDict.insert( node->id(), node ); return true; } if ( rn != node ) { errorPlan << "Id already exists for different task: " << node->id(); return false; } //debugPlan<<"Already exists" <<"id=" << node->id() << node->name(); return true; } QList Project::allNodes() const { QList lst = nodeIdDict.values(); int me = lst.indexOf( const_cast( this ) ); if ( me != -1 ) { lst.removeAt( me ); } return lst; } QList Project::allTasks( const Node *parent ) const { QList lst; const Node *p = parent ? parent : this; foreach ( Node *n, p->childNodeIterator() ) { if ( n->type() == Node::Type_Task || n->type() == Type_Milestone ) { lst << static_cast( n ); } lst += allTasks( n ); } return lst; } bool Project::setResourceGroupId( ResourceGroup *group ) { if ( group == 0 ) { return false; } if ( ! group->id().isEmpty() ) { ResourceGroup *g = findResourceGroup( group->id() ); if ( group == g ) { return true; } else if ( g == 0 ) { insertResourceGroupId( group->id(), group ); return true; } } QString id = uniqueResourceGroupId(); group->setId( id ); if ( id.isEmpty() ) { return false; } insertResourceGroupId( id, group ); return true; } QString Project::uniqueResourceGroupId() const { QString s = QDateTime::currentDateTime().toString( Qt::ISODate ) + ' '; QString id = s + KRandom::randomString( 10 ); while ( resourceGroupIdDict.contains( id ) ) { id = s + KRandom::randomString( 10 ); } return id; } ResourceGroup *Project::group( const QString& id ) { return findResourceGroup( id ); } ResourceGroup *Project::groupByName( const QString& name ) const { foreach ( ResourceGroup *g, resourceGroupIdDict ) { if ( g->name() == name ) { return g; } } return 0; } QList Project::autoAllocateResources() const { QList lst; foreach ( Resource *r, resourceIdDict ) { if ( r->autoAllocate() ) { lst << r; } } return lst; } void Project::insertResourceId( const QString &id, Resource *resource ) { resourceIdDict.insert( id, resource ); } bool Project::removeResourceId( const QString &id ) { return resourceIdDict.remove( id ); } bool Project::setResourceId( Resource *resource ) { if ( resource == 0 ) { return false; } if ( ! resource->id().isEmpty() ) { Resource *r = findResource( resource->id() ); if ( resource == r ) { return true; } else if ( r == 0 ) { insertResourceId( resource->id(), resource ); return true; } } QString id = uniqueResourceId(); resource->setId( id ); if ( id.isEmpty() ) { return false; } insertResourceId( id, resource ); return true; } QString Project::uniqueResourceId() const { QString s = QDateTime::currentDateTime().toString( Qt::ISODate ) + ' '; QString id = s + KRandom::randomString( 10 ); while ( resourceIdDict.contains( id ) ) { id = s + KRandom::randomString( 10 ); } return id; } Resource *Project::resource( const QString& id ) { return findResource( id ); } Resource *Project::resourceByName( const QString& name ) const { foreach ( const QString &k, resourceIdDict.keys() ) { Resource *r = resourceIdDict[ k ]; if ( r->name() == name ) { Q_ASSERT( k == r->id() ); return r; } } return 0; } QStringList Project::resourceNameList() const { QStringList lst; foreach ( Resource *r, resourceIdDict ) { lst << r->name(); } return lst; } -EffortCostMap Project::plannedEffortCostPrDay( const QDate & start, const QDate &end, long id, EffortCostCalculationType typ ) const +EffortCostMap Project::plannedEffortCostPrDay( QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->plannedEffortCostPrDay( start, end, id, typ ); } return ec; } -EffortCostMap Project::plannedEffortCostPrDay( const Resource *resource, const QDate & start, const QDate &end, long id, EffortCostCalculationType typ ) const +EffortCostMap Project::plannedEffortCostPrDay( const Resource *resource, QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->plannedEffortCostPrDay( resource, start, end, id, typ ); } return ec; } -EffortCostMap Project::actualEffortCostPrDay( const QDate & start, const QDate &end, long id, EffortCostCalculationType typ ) const +EffortCostMap Project::actualEffortCostPrDay( QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->actualEffortCostPrDay( start, end, id, typ ); } return ec; } -EffortCostMap Project::actualEffortCostPrDay( const Resource *resource, const QDate & start, const QDate &end, long id, EffortCostCalculationType typ ) const +EffortCostMap Project::actualEffortCostPrDay( const Resource *resource, QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->actualEffortCostPrDay( resource, start, end, id, typ ); } return ec; } // Returns the total planned effort for this project (or subproject) Duration Project::plannedEffort( long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; QListIterator it( childNodeIterator() ); while ( it.hasNext() ) { eff += it.next() ->plannedEffort( id, typ ); } return eff; } // Returns the total planned effort for this project (or subproject) on date -Duration Project::plannedEffort( const QDate &date, long id, EffortCostCalculationType typ ) const +Duration Project::plannedEffort( QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; QListIterator it( childNodeIterator() ); while ( it.hasNext() ) { eff += it.next() ->plannedEffort( date, id, typ ); } return eff; } // Returns the total planned effort for this project (or subproject) upto and including date -Duration Project::plannedEffortTo( const QDate &date, long id, EffortCostCalculationType typ ) const +Duration Project::plannedEffortTo( QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; QListIterator it( childNodeIterator() ); while ( it.hasNext() ) { eff += it.next() ->plannedEffortTo( date, id, typ ); } return eff; } // Returns the total actual effort for this project (or subproject) upto and including date -Duration Project::actualEffortTo( const QDate &date ) const +Duration Project::actualEffortTo( QDate date ) const { //debugPlan; Duration eff; QListIterator it( childNodeIterator() ); while ( it.hasNext() ) { eff += it.next() ->actualEffortTo( date ); } return eff; } // Returns the total planned effort for this project (or subproject) upto and including date -double Project::plannedCostTo( const QDate &date, long id, EffortCostCalculationType typ ) const +double Project::plannedCostTo( QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; double c = 0; QListIterator it( childNodeIterator() ); while ( it.hasNext() ) { c += it.next() ->plannedCostTo( date, id, typ ); } return c; } // Returns the total actual cost for this project (or subproject) upto and including date -EffortCost Project::actualCostTo( long int id, const QDate &date ) const +EffortCost Project::actualCostTo( long int id, QDate date ) const { //debugPlan; EffortCost c; QListIterator it( childNodeIterator() ); while ( it.hasNext() ) { c += it.next() ->actualCostTo( id, date ); } return c; } -Duration Project::budgetedWorkPerformed( const QDate &date, long id ) const +Duration Project::budgetedWorkPerformed( QDate date, long id ) const { //debugPlan; Duration e; foreach (Node *n, childNodeIterator()) { e += n->budgetedWorkPerformed( date, id ); } return e; } -double Project::budgetedCostPerformed( const QDate &date, long id ) const +double Project::budgetedCostPerformed( QDate date, long id ) const { //debugPlan; double c = 0.0; foreach (Node *n, childNodeIterator()) { c += n->budgetedCostPerformed( date, id ); } return c; } -double Project::effortPerformanceIndex( const QDate &date, long id ) const +double Project::effortPerformanceIndex( QDate date, long id ) const { //debugPlan; debugPlan< 0.0 ) { r = p / s; } debugPlan< date ? end : date), id ); double budgetAtCompletion; double plannedCompleted; double budgetedCompleted; bool useEffort = false; //FIXME if ( useEffort ) { budgetAtCompletion = plan.totalEffort().toDouble( Duration::Unit_h ); plannedCompleted = plan.effortTo( date ).toDouble( Duration::Unit_h ); //actualCompleted = actual.effortTo( date ).toDouble( Duration::Unit_h ); budgetedCompleted = budgetedWorkPerformed( date, id ).toDouble( Duration::Unit_h ); } else { budgetAtCompletion = plan.totalCost(); plannedCompleted = plan.costTo( date ); budgetedCompleted = budgetedCostPerformed( date, id ); } double c = 0.0; if ( budgetAtCompletion > 0.0 ) { double percentageCompletion = budgetedCompleted / budgetAtCompletion; c = budgetAtCompletion * percentageCompletion; //?? debugPlan<name()<<","<<(parent?parent->name():"No parent"); int row = parent == 0 ? m_calendars.count() : parent->calendars().count(); if ( index >= 0 && index < row ) { row = index; } emit calendarToBeAdded( parent, row ); calendar->setProject( this ); if ( parent == 0 ) { calendar->setParentCal( 0 ); // in case m_calendars.insert( row, calendar ); } else { calendar->setParentCal( parent, row ); } if ( calendar->isDefault() ) { setDefaultCalendar( calendar ); } setCalendarId( calendar ); emit calendarAdded( calendar ); emit projectChanged(); } void Project::takeCalendar( Calendar *calendar ) { emit calendarToBeRemoved( calendar ); removeCalendarId( calendar->id() ); if ( calendar == m_defaultCalendar ) { m_defaultCalendar = 0; } if ( calendar->parentCal() == 0 ) { int i = indexOf( calendar ); if ( i != -1 ) { m_calendars.removeAt( i ); } } else { calendar->setParentCal( 0 ); } emit calendarRemoved( calendar ); calendar->setProject( 0 ); emit projectChanged(); } int Project::indexOf( const Calendar *calendar ) const { return m_calendars.indexOf( const_cast(calendar) ); } Calendar *Project::calendar( const QString& id ) const { return findCalendar( id ); } Calendar *Project::calendarByName( const QString& name ) const { foreach( Calendar *c, calendarIdDict ) { if ( c->name() == name ) { return c; } } return 0; } const QList &Project::calendars() const { return m_calendars; } QList Project::allCalendars() const { return calendarIdDict.values(); } QStringList Project::calendarNames() const { QStringList lst; foreach( Calendar *c, calendarIdDict ) { lst << c->name(); } return lst; } bool Project::setCalendarId( Calendar *calendar ) { if ( calendar == 0 ) { return false; } if ( ! calendar->id().isEmpty() ) { Calendar *c = findCalendar( calendar->id() ); if ( calendar == c ) { return true; } else if ( c == 0 ) { insertCalendarId( calendar->id(), calendar ); return true; } } QString id = uniqueCalendarId(); calendar->setId( id ); if ( id.isEmpty() ) { return false; } insertCalendarId( id, calendar ); return true; } QString Project::uniqueCalendarId() const { QString s = QDateTime::currentDateTime().toString( Qt::ISODate ) + ' '; QString id = s + KRandom::randomString( 10 ); while ( calendarIdDict.contains( id ) ) { id = s + KRandom::randomString( 10 ); } return id; } void Project::setDefaultCalendar( Calendar *cal ) { if ( m_defaultCalendar ) { m_defaultCalendar->setDefault( false ); } m_defaultCalendar = cal; if ( cal ) { cal->setDefault( true ); } emit defaultCalendarChanged( cal ); emit projectChanged(); } void Project::setStandardWorktime( StandardWorktime * worktime ) { if ( m_standardWorktime != worktime ) { delete m_standardWorktime; m_standardWorktime = worktime; m_standardWorktime->setProject( this ); emit standardWorktimeChanged( worktime ); } } void Project::emitDocumentAdded( Node *node , Document *doc , int index ) { emit documentAdded( node, doc, index ); } void Project::emitDocumentRemoved( Node *node , Document *doc , int index ) { emit documentRemoved( node, doc, index ); } void Project::emitDocumentChanged( Node *node , Document *doc , int index ) { emit documentChanged( node, doc, index ); } bool Project::linkExists( const Node *par, const Node *child ) const { if ( par == 0 || child == 0 || par == child || par->isDependChildOf( child ) ) { return false; } foreach ( Relation *r, par->dependChildNodes() ) { if ( r->child() == child ) { return true; } } return false; } bool Project::legalToLink( const Node *par, const Node *child ) const { //debugPlan<isDependChildOf( child ) ) { return false; } if ( linkExists( par, child ) ) { return false; } bool legal = true; // see if par/child is related if ( legal && ( par->isParentOf( child ) || child->isParentOf( par ) ) ) { legal = false; } if ( legal ) legal = legalChildren( par, child ); if ( legal ) legal = legalParents( par, child ); if ( legal ) { foreach ( Node *p, par->childNodeIterator() ) { if ( ! legalToLink( p, child ) ) { return false; } } } return legal; } bool Project::legalParents( const Node *par, const Node *child ) const { bool legal = true; //debugPlan<name()<<" ("<numDependParentNodes()<<" parents)"<name()<<" ("<numDependChildNodes()<<" children)"; for ( int i = 0; i < par->numDependParentNodes() && legal; ++i ) { Node *pNode = par->getDependParentNode( i ) ->parent(); if ( child->isParentOf( pNode ) || pNode->isParentOf( child ) ) { //debugPlan<<"Found:"<name()<<" is related to"<name(); legal = false; } else { legal = legalChildren( pNode, child ); } if ( legal ) legal = legalParents( pNode, child ); } return legal; } bool Project::legalChildren( const Node *par, const Node *child ) const { bool legal = true; //debugPlan<name()<<" ("<numDependParentNodes()<<" parents)"<name()<<" ("<numDependChildNodes()<<" children)"; for ( int j = 0; j < child->numDependChildNodes() && legal; ++j ) { Node *cNode = child->getDependChildNode( j ) ->child(); if ( par->isParentOf( cNode ) || cNode->isParentOf( par ) ) { //debugPlan<<"Found:"<name()<<" is related to"<name(); legal = false; } else { legal = legalChildren( par, cNode ); } } return legal; } WBSDefinition &Project::wbsDefinition() { return m_wbsDefinition; } void Project::setWbsDefinition( const WBSDefinition &def ) { //debugPlan; m_wbsDefinition = def; emit wbsDefinitionChanged(); emit projectChanged(); } QString Project::generateWBSCode( QList &indexes, bool sortable ) const { QString code = m_wbsDefinition.projectCode(); if (sortable) { int fw = (nodeIdDict.count() / 10) + 1; QLatin1Char fc('0'); foreach ( int index, indexes ) { code += ".%1"; code = code.arg(QString::number(index), fw, fc); } debugPlan< hash = resourceIdDict; foreach ( Resource * r, hash ) { r->setCurrentSchedule( id ); } emit currentScheduleChanged(); emit projectChanged(); } ScheduleManager *Project::scheduleManager( long id ) const { foreach ( ScheduleManager *sm, m_managers ) { if ( sm->scheduleId() == id ) { return sm; } } return 0; } ScheduleManager *Project::scheduleManager( const QString &id ) const { return m_managerIdMap.value( id ); } ScheduleManager *Project::findScheduleManagerByName( const QString &name ) const { //debugPlan; ScheduleManager *m = 0; foreach( ScheduleManager *sm, m_managers ) { m = sm->findManager( name ); if ( m ) { break; } } return m; } QList Project::allScheduleManagers() const { QList lst; foreach ( ScheduleManager *sm, m_managers ) { lst << sm; lst << sm->allChildren(); } return lst; } QString Project::uniqueScheduleName() const { //debugPlan; QString n = i18n( "Plan" ); bool unique = findScheduleManagerByName( n ) == 0; if ( unique ) { return n; } n += " %1"; int i = 1; for ( ; true; ++i ) { unique = findScheduleManagerByName( n.arg( i ) ) == 0; if ( unique ) { break; } } return n.arg( i ); } void Project::addScheduleManager( ScheduleManager *sm, ScheduleManager *parent, int index ) { int row = parent == 0 ? m_managers.count() : parent->childCount(); if ( index >= 0 && index < row ) { row = index; } if ( parent == 0 ) { emit scheduleManagerToBeAdded( parent, row ); m_managers.insert( row, sm ); } else { emit scheduleManagerToBeAdded( parent, row ); sm->setParentManager( parent, row ); } if ( sm->managerId().isEmpty() ) { sm->setManagerId( uniqueScheduleManagerId() ); } Q_ASSERT( ! m_managerIdMap.contains( sm->managerId() ) ); m_managerIdMap.insert( sm->managerId(), sm ); emit scheduleManagerAdded( sm ); emit projectChanged(); //debugPlan<<"Added:"<name()<<", now"<children() ) { takeScheduleManager( s ); } if ( sm->scheduling() ) { sm->stopCalculation(); } int index = -1; if ( sm->parentManager() ) { int index = sm->parentManager()->indexOf( sm ); if ( index >= 0 ) { emit scheduleManagerToBeRemoved( sm ); sm->setParentManager( 0 ); m_managerIdMap.remove( sm->managerId() ); emit scheduleManagerRemoved( sm ); emit projectChanged(); } } else { index = indexOf( sm ); if ( index >= 0 ) { emit scheduleManagerToBeRemoved( sm ); m_managers.removeAt( indexOf( sm ) ); m_managerIdMap.remove( sm->managerId() ); emit scheduleManagerRemoved( sm ); emit projectChanged(); } } return index; } void Project::moveScheduleManager( ScheduleManager *sm, ScheduleManager *newparent, int newindex ) { //debugPlan<name()<parentManager() ) { m_managers.removeAt( indexOf( sm ) ); } sm->setParentManager( newparent, newindex ); if ( ! newparent ) { m_managers.insert( newindex, sm ); } emit scheduleManagerMoved( sm, newindex ); } bool Project::isScheduleManager( void *ptr ) const { const ScheduleManager *sm = static_cast( ptr ); if ( indexOf( sm ) >= 0 ) { return true; } foreach ( ScheduleManager *p, m_managers ) { if ( p->isParentOf( sm ) ) { return true; } } return false; } ScheduleManager *Project::createScheduleManager( const QString &name ) { //debugPlan<isBaselined() ) { return true; } } return false; } Schedule *s = schedule( id ); return s == 0 ? false : s->isBaselined(); } MainSchedule *Project::createSchedule( const QString& name, Schedule::Type type ) { //debugPlan<<"No of schedules:"<setName( name ); sch->setType( type ); addMainSchedule( sch ); return sch; } void Project::addMainSchedule( MainSchedule *sch ) { if ( sch == 0 ) { return; } //debugPlan<<"No of schedules:"<setId( i ); sch->setNode( this ); addSchedule( sch ); } bool Project::removeCalendarId( const QString &id ) { //debugPlan <<"id=" << id; return calendarIdDict.remove( id ); } void Project::insertCalendarId( const QString &id, Calendar *calendar ) { //debugPlan <<"id=" << id <<":" << calendar->name(); calendarIdDict.insert( id, calendar ); } void Project::changed( Node *node, int property ) { if ( m_parent == 0 ) { Node::changed( node, property ); // reset cache if ( property != Node::Type ) { // add/remove node is handled elsewhere emit nodeChanged( node ); emit projectChanged(); } return; } Node::changed( node, property ); } void Project::changed( ResourceGroup *group ) { //debugPlan; emit resourceGroupChanged( group ); emit projectChanged(); } void Project::changed( ScheduleManager *sm ) { emit scheduleManagerChanged( sm ); emit projectChanged(); } void Project::changed( MainSchedule *sch ) { //debugPlan<id(); emit scheduleChanged( sch ); emit projectChanged(); } void Project::sendScheduleToBeAdded( const ScheduleManager *sm, int row ) { emit scheduleToBeAdded( sm, row ); } void Project::sendScheduleAdded( const MainSchedule *sch ) { //debugPlan<id(); emit scheduleAdded( sch ); emit projectChanged(); } void Project::sendScheduleToBeRemoved( const MainSchedule *sch ) { //debugPlan<id(); emit scheduleToBeRemoved( sch ); } void Project::sendScheduleRemoved( const MainSchedule *sch ) { //debugPlan<id(); emit scheduleRemoved( sch ); emit projectChanged(); } void Project::changed( Resource *resource ) { emit resourceChanged( resource ); emit projectChanged(); } void Project::changed( Calendar *cal ) { emit calendarChanged( cal ); emit projectChanged(); } void Project::changed( StandardWorktime *w ) { emit standardWorktimeChanged( w ); emit projectChanged(); } bool Project::addRelation( Relation *rel, bool check ) { if ( rel->parent() == 0 || rel->child() == 0 ) { return false; } if ( check && !legalToLink( rel->parent(), rel->child() ) ) { return false; } emit relationToBeAdded( rel, rel->parent()->numDependChildNodes(), rel->child()->numDependParentNodes() ); rel->parent()->addDependChildNode( rel ); rel->child()->addDependParentNode( rel ); emit relationAdded( rel ); emit projectChanged(); return true; } void Project::takeRelation( Relation *rel ) { emit relationToBeRemoved( rel ); rel->parent() ->takeDependChildNode( rel ); rel->child() ->takeDependParentNode( rel ); emit relationRemoved( rel ); emit projectChanged(); } void Project::setRelationType( Relation *rel, Relation::Type type ) { emit relationToBeModified( rel ); rel->setType( type ); emit relationModified( rel ); emit projectChanged(); } void Project::setRelationLag( Relation *rel, const Duration &lag ) { emit relationToBeModified( rel ); rel->setLag( lag ); emit relationModified( rel ); emit projectChanged(); } QList Project::flatNodeList( Node *parent ) { QList lst; Node *p = parent == 0 ? this : parent; //debugPlan<name()<childNodeIterator() ) { lst.append( n ); if ( n->numChildren() > 0 ) { lst += flatNodeList( n ); } } return lst; } void Project::setSchedulerPlugins( const QMap &plugins ) { m_schedulerPlugins = plugins; debugPlan< Copyright (C) 2007 Florian Piquemal Copyright (C) 2007 Alexis Ménard This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTPROJECT_H #define KPTPROJECT_H #include "kplatokernel_export.h" #include "kptnode.h" #include "kptglobal.h" #include "kptaccount.h" #include "kptcalendar.h" #include "kptdatetime.h" #include "kptduration.h" #include "kptresource.h" #include "kptwbsdefinition.h" #include "kptconfigbase.h" #include #include #include #include #include /// The main namespace. namespace KPlato { class Locale; class Schedule; class StandardWorktime; class ScheduleManager; class XMLLoaderObject; class Task; class SchedulerPlugin; class KPlatoXmlLoaderBase; /** * Project is the main node in a project, it contains child nodes and * possibly sub-projects. A sub-project is just another instantion of this * node however. * * A note on timezones: * To be able to handle resources working in diffierent timezones and * to facilitate data exchange with other applications like PIMs or * and groupware servers, the project has a timezone that is used for * all datetimes in nodes and schedules. * By default the local timezone is used. * * A resources timezone is defined by the associated calendar. * * Note that a projects datetimes are always displayed/modified in the timezone * it was originally created, not necessarly in your current local timezone. */ class KPLATOKERNEL_EXPORT Project : public Node { Q_OBJECT public: explicit Project( Node *parent = 0 ); explicit Project( ConfigBase &config, Node *parent = 0 ); ~Project(); /// Reference this project. void ref() { ++m_refCount; } /// De-reference this project. Deletes project of ref count <= 0 void deref(); /// Returns the node type. Can be Type_Project or Type_Subproject. virtual int type() const; /** * Calculate the schedules managed by the schedule manager * * @param sm Schedule manager */ void calculate( ScheduleManager &sm ); /** * Re-calculate the schedules managed by the schedule manager * * @param sm Schedule manager * @param dt The datetime from when the schedule shall be re-calculated */ void calculate( ScheduleManager &sm, const DateTime &dt ); virtual DateTime startTime( long id = -1 ) const; virtual DateTime endTime( long id = -1 ) const; /// Returns the calculated duration for schedule @p id Duration duration( long id = -1 ) const; using Node::duration; /** * Instead of using the expected duration, generate a random value using * the Distribution of each Task. This can be used for Monte-Carlo * estimation of Project duration. */ Duration *getRandomDuration(); virtual bool load( KoXmlElement &element, XMLLoaderObject &status ); virtual void save( QDomElement &element ) const; using Node::saveWorkPackageXML; /// Save a workpackage document containing @node with schedule identity @p id void saveWorkPackageXML( QDomElement &element, const Node *node, long id ) const; /** * Add the node @p task to the project, after node @p position * If @p postition is zero or the project node, it will be added to this project. */ bool addTask( Node* task, Node* position ); /** * Add the node @p task to the @p parent */ bool addSubTask( Node* task, Node* parent ); /** * Add the node @p task to @p parent, in position @p index * If @p parent is zero, it will be added to this project. */ bool addSubTask( Node* task, int index, Node* parent, bool emitSignal = true ); /** * Remove the @p node. * The node is not deleted. */ void takeTask( Node *node, bool emitSignal = true ); bool canMoveTask( Node* node, Node *newParent ); bool moveTask( Node* node, Node *newParent, int newPos ); bool canIndentTask( Node* node ); bool indentTask( Node* node, int index = -1 ); bool canUnindentTask( Node* node ); bool unindentTask( Node* node ); bool canMoveTaskUp( Node* node ); bool moveTaskUp( Node* node ); bool canMoveTaskDown( Node* node ); bool moveTaskDown( Node* node ); /** * Create a task with a unique id. * The task is not added to the project. Do this with addSubTask(). */ Task *createTask(); /** * Create a copy of @p def with a unique id. * The task is not added to the project. Do this with addSubTask(). */ Task *createTask( const Task &def ); int resourceGroupCount() const { return m_resourceGroups.count(); } QList &resourceGroups(); /// Adds the resource group to the project. virtual void addResourceGroup( ResourceGroup *resource, int index = -1 ); /** * Removes the resource group @p resource from the project. * The resource group is not deleted. */ ResourceGroup *takeResourceGroup( ResourceGroup *resource ); int indexOf( ResourceGroup *resource ) const { return m_resourceGroups.indexOf( resource ); } ResourceGroup *resourceGroupAt( int pos ) const { return m_resourceGroups.value( pos ); } int numResourceGroups() const { return m_resourceGroups.count(); } /// Returns the resourcegroup with identity id. ResourceGroup *group( const QString& id ); /// Returns the resource group with the matching name, 0 if no match is found. ResourceGroup *groupByName( const QString& name ) const; /** * Adds the resource to the project and resource group. * Always use this to add resources. */ void addResource( ResourceGroup *group, Resource *resource, int index = -1 ); /** * Removes the resource from the project and resource group. * The resource is not deleted. * Always use this to remove resources. */ Resource *takeResource( ResourceGroup *group, Resource *resource ); /// Move @p resource to the new @p group. Requests are removed. void moveResource( ResourceGroup *group, Resource *resource ); /// Returns the resource with identity id. Resource *resource( const QString& id ); /// Returns the resource with matching name, 0 if no match is found. Resource *resourceByName( const QString& name ) const; QStringList resourceNameList() const; /// Returns a list of all resources QList resourceList() const { return resourceIdDict.values(); } - virtual EffortCostMap plannedEffortCostPrDay( const QDate &start, const QDate &end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; - virtual EffortCostMap plannedEffortCostPrDay(const Resource *resource, const QDate &start, const QDate &end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; + virtual EffortCostMap plannedEffortCostPrDay( QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; + virtual EffortCostMap plannedEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; using Node::plannedEffort; /// Returns the total planned effort for this project (or subproject) virtual Duration plannedEffort( long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the total planned effort for this project (or subproject) on date - virtual Duration plannedEffort( const QDate &date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; + virtual Duration plannedEffort( QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; using Node::plannedEffortTo; /// Returns the planned effort up to and including date - virtual Duration plannedEffortTo( const QDate &date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; + virtual Duration plannedEffortTo( QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the actual effort up to and including @p date - virtual Duration actualEffortTo( const QDate &date ) const; + virtual Duration actualEffortTo( QDate date ) const; /** * Planned cost up to and including date * @param date The cost is calculated from the start of the project upto including date. * @param id Identity of the schedule to be used. */ - virtual double plannedCostTo( const QDate &date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; + virtual double plannedCostTo( QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /** * Actual cost up to and including @p date * @param date The cost is calculated from the start of the project upto including date. */ - virtual EffortCost actualCostTo( long int id, const QDate &date ) const; + virtual EffortCost actualCostTo( long int id, QDate date ) const; - virtual EffortCostMap actualEffortCostPrDay( const QDate &start, const QDate &end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; + virtual EffortCostMap actualEffortCostPrDay( QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; - virtual EffortCostMap actualEffortCostPrDay( const Resource *resource, const QDate &start, const QDate &end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; + virtual EffortCostMap actualEffortCostPrDay( const Resource *resource, QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; - double effortPerformanceIndex( const QDate &date, long id ) const; + double effortPerformanceIndex( QDate date, long id ) const; - double schedulePerformanceIndex( const QDate &date, long id ) const; + double schedulePerformanceIndex( QDate date, long id ) const; /// Returns the effort planned to be used to reach the actual percent finished - virtual Duration budgetedWorkPerformed( const QDate &date, long id = CURRENTSCHEDULE ) const; + virtual Duration budgetedWorkPerformed( QDate date, long id = CURRENTSCHEDULE ) const; /// Returns the cost planned to be used to reach the actual percent finished - virtual double budgetedCostPerformed( const QDate &date, long id = CURRENTSCHEDULE ) const; + virtual double budgetedCostPerformed( QDate date, long id = CURRENTSCHEDULE ) const; /// Budgeted Cost of Work Scheduled ( up to @p date ) - virtual double bcws( const QDate &date, long id = BASELINESCHEDULE ) const; + virtual double bcws( QDate date, long id = BASELINESCHEDULE ) const; /// Budgeted Cost of Work Performed virtual double bcwp( long id = BASELINESCHEDULE ) const; /// Budgeted Cost of Work Performed ( up to @p date ) - virtual double bcwp( const QDate &date, long id = BASELINESCHEDULE ) const; + virtual double bcwp( QDate date, long id = BASELINESCHEDULE ) const; Calendar *defaultCalendar() const { return m_defaultCalendar; } void setDefaultCalendar( Calendar *cal ); const QList &calendars() const; void addCalendar( Calendar *calendar, Calendar *parent = 0, int index = -1 ); void takeCalendar( Calendar *calendar ); int indexOf( const Calendar *calendar ) const; /// Returns the calendar with identity id. Calendar *calendar( const QString& id ) const; /// Returns a list of all calendars QStringList calendarNames() const; /// Find calendar by name Calendar *calendarByName( const QString &name ) const; void changed( Calendar *cal ); QList allCalendars() const; /// Return number of calendars int calendarCount() const { return m_calendars.count(); } /// Return the calendar at @p index, 0 if index out of bounds Calendar *calendarAt( int index ) const { return m_calendars.value( index ); } /** * Defines the length of days, weeks, months and years * and the standard working week. * Used for estimation and calculation of effort, * and presentation in gantt chart. */ StandardWorktime *standardWorktime() { return m_standardWorktime; } void setStandardWorktime( StandardWorktime * worktime ); void changed( StandardWorktime* ); /// Check if a link exists between node @p par and @p child. bool linkExists( const Node *par, const Node *child ) const; /// Check if node @p par can be linked to node @p child. bool legalToLink( const Node *par, const Node *child ) const; using Node::legalToLink; virtual const QHash &nodeDict() { return nodeIdDict; } /// Return a list of all nodes in the project (exluding myself) QList allNodes() const; /// Return the number of all nodes in the project (exluding myself) int nodeCount() const { return nodeIdDict.count() - 1; } /// Return a list of all tasks and milestones int the wbs order QList allTasks( const Node *parent = 0 ) const; using Node::findNode; /// Find the node with identity id virtual Node *findNode( const QString &id ) const; using Node::removeId; /// Remove the node with identity id from the registers virtual bool removeId( const QString &id ); /// Reserve @p id for the @p node virtual void reserveId( const QString &id, Node *node ); /// Register @p node. The nodes id must be unique and non-empty. bool registerNodeId( Node *node ); /// Create a unique id. QString uniqueNodeId( int seed = 1 ) const; /// Check if node @p id is used bool nodeIdentExists( const QString &id ) const; /// Create a unique id. QString uniqueNodeId( const QList &existingIds, int seed = 1 ); ResourceGroup *findResourceGroup( const QString &id ) const { if ( resourceGroupIdDict.contains( id ) ) return resourceGroupIdDict[ id ]; return 0; } /// Remove the resourcegroup with identity id from the register bool removeResourceGroupId( const QString &id ) { if ( resourceGroupIdDict.contains( id ) ) return resourceGroupIdDict.remove( id ); return false; } /// Insert the resourcegroup with identity id void insertResourceGroupId( const QString &id, ResourceGroup* group ) { resourceGroupIdDict.insert( id, group ); } /// Generate, set and insert unique id bool setResourceGroupId( ResourceGroup *group); /// returns a unique resourcegroup id QString uniqueResourceGroupId() const; /// Return a list of resources that will be allocated to new tasks QList autoAllocateResources() const; Resource *findResource( const QString &id ) const { if ( resourceIdDict.contains( id ) ) return resourceIdDict[ id ]; return 0; } /// Remove the resource with identity id from the register bool removeResourceId( const QString &id ); /// Insert the resource with identity id void insertResourceId( const QString &id, Resource *resource ); /// Generate, set and insert unique id bool setResourceId( Resource *resource ); /// returns a unique resource id QString uniqueResourceId() const; /// Find the calendar with identity id virtual Calendar *findCalendar( const QString &id ) const { if ( id.isEmpty() || !calendarIdDict.contains( id ) ) return 0; return calendarIdDict[ id ]; } /// Remove the calendar with identity id from the register virtual bool removeCalendarId( const QString &id ); /// Insert the calendar with identity id virtual void insertCalendarId( const QString &id, Calendar *calendar ); /// Set and insert a unique id for calendar bool setCalendarId( Calendar *calendar ); /// returns a unique calendar id QString uniqueCalendarId() const; /// Return reference to WBS Definition WBSDefinition &wbsDefinition(); /// Set WBS Definition to @p def void setWbsDefinition( const WBSDefinition &def ); /// Generate WBS Code virtual QString generateWBSCode( QList &indexes, bool sortable = false ) const; Accounts &accounts() { return m_accounts; } const Accounts &accounts() const { return m_accounts; } /** * Set current schedule to the schedule with identity @p id, for me and my children * Note that this is used (and may be changed) when calculating schedules */ virtual void setCurrentSchedule( long id ); /// Create new schedule with unique name and id of type Expected. MainSchedule *createSchedule(); /// Create new schedule with unique id. MainSchedule *createSchedule( const QString& name, Schedule::Type type ); /// Add the schedule to the project. A fresh id will be generated for the schedule. void addMainSchedule( MainSchedule *schedule ); /// Set parent schedule for my children virtual void setParentSchedule( Schedule *sch ); /// Find the schedule manager that manages the Schedule with @p id ScheduleManager *scheduleManager( long id ) const; /// Find the schedule manager with @p id ScheduleManager *scheduleManager( const QString &id ) const; /// Create a unique schedule name (This may later be changed by the user) QString uniqueScheduleName() const; /// Create a unique schedule manager identity QString uniqueScheduleManagerId() const; ScheduleManager *createScheduleManager(); ScheduleManager *createScheduleManager( const QString &name ); /// Returns a list of all top level schedule managers QList scheduleManagers() const { return m_managers; } int numScheduleManagers() const { return m_managers.count(); } int indexOf( const ScheduleManager *sm ) const { return m_managers.indexOf( const_cast(sm) ); } bool isScheduleManager( void* ptr ) const; void addScheduleManager( ScheduleManager *sm, ScheduleManager *parent = 0, int index = -1 ); int takeScheduleManager( ScheduleManager *sm ); void moveScheduleManager( ScheduleManager *sm, ScheduleManager *newparent = 0, int newindex = -1 ); ScheduleManager *findScheduleManagerByName( const QString &name ) const; /// Returns a list of all schedule managers QList allScheduleManagers() const; /// Return true if schedule with identity @p id is baselined bool isBaselined( long id = ANYSCHEDULED ) const; void changed( ResourceGroup *group ); void changed( Resource *resource ); void changed( ScheduleManager *sm ); void changed( MainSchedule *sch ); void sendScheduleAdded( const MainSchedule *sch ); void sendScheduleToBeAdded( const ScheduleManager *manager, int row ); void sendScheduleRemoved( const MainSchedule *sch ); void sendScheduleToBeRemoved( const MainSchedule *sch ); /// Return the time zone used in this project QTimeZone timeZone() const { return m_timeZone; } /// Set the time zone to be used in this project void setTimeZone( const QTimeZone &tz ) { m_timeZone = tz; } /** * Add a relation between the nodes specified in the relation rel. * Emits signals relationToBeAdded() before the relation is added, * and relationAdded() after it has been added. * @param rel The relation to be added. * @param check If true, the relation is checked for validity * @return true if successful. */ bool addRelation( Relation *rel, bool check=true ); /** * Removes the relation @p rel without deleting it. * Emits signals relationToBeRemoved() before the relation is removed, * and relationRemoved() after it has been removed. */ void takeRelation( Relation *rel ); /** * Modify the @p type of the @p relation. */ void setRelationType( Relation *relation, Relation::Type type ); /** * Modify the @p lag of the @p relation. */ void setRelationLag( Relation *relation, const Duration &lag ); void calcCriticalPathList( MainSchedule *cs ); void calcCriticalPathList( MainSchedule *cs, Node *node ); /** * Returns the list of critical paths for schedule @p id */ const QList< QList > *criticalPathList( long id = CURRENTSCHEDULE ); QList criticalPath( long id = CURRENTSCHEDULE, int index = 0 ); /// Returns a flat list af all nodes QList flatNodeList( Node *parent = 0 ); void generateUniqueNodeIds(); void generateUniqueIds(); const ConfigBase &config() const { return m_config ? *m_config : emptyConfig; } /// Set configuration data void setConfig( ConfigBase *config ) { m_config = config; } const Task &taskDefaults() const { return config().taskDefaults(); } /// Return locale. (Used for currency, everything else is from KGlobal::locale) Locale *locale() { return const_cast(config()).locale(); } /// Return locale. (Used for currency, everything else is from KGlobal::locale) const Locale *locale() const { return config().locale(); } /// Signal that locale data has changed void emitLocaleChanged(); void setSchedulerPlugins( const QMap &plugins ); const QMap &schedulerPlugins() const { return m_schedulerPlugins; } void initiateCalculation( MainSchedule &sch ); void initiateCalculationLists( MainSchedule &sch ); void finishCalculation( ScheduleManager &sm ); void adjustSummarytask(); /// Increments progress and emits signal sigProgress() void incProgress(); /// Emits signal maxProgress() void emitMaxProgress( int value ); bool stopcalculation; /// return a map of all external projects QMap externalProjects() const; void emitDocumentAdded( Node*, Document*, int index ); void emitDocumentRemoved( Node*, Document*, int index ); void emitDocumentChanged( Node*, Document*, int index ); bool useSharedResources() const; void setUseSharedResources(bool on); bool isSharedResourcesLoaded() const; void setSharedResourcesLoaded(bool on); void setSharedResourcesFile(const QString &file); QString sharedResourcesFile() const; public Q_SLOTS: /// Sets m_progress to @p progress and emits signal sigProgress() /// If @p sm is not 0, progress is also set for the schedule manager void setProgress( int progress, ScheduleManager *sm = 0 ); /// Sets m_maxprogress to @p max and emits signal maxProgress() /// If @p sm is not 0, max progress is also set for the schedule manager void setMaxProgress( int max, ScheduleManager *sm = 0 ); Q_SIGNALS: /// Emitted when the project is about to be deleted (The destroyed signal is disabled) void aboutToBeDeleted(); /// Emitted when anything in the project is changed (use with care) void projectChanged(); /// Emitted when the WBS code definition has changed. This may change all nodes. void wbsDefinitionChanged(); /// Emitted when a schedule has been calculated void projectCalculated( ScheduleManager *sm ); /// Emitted when the pointer to the current schedule has been changed void currentScheduleChanged(); /// Use to show progress during calculation void sigProgress( int ); /// Use to set the maximum progress (minimum always 0) void maxProgress( int ); /// Emitted when calculation starts void sigCalculationStarted( Project *project, ScheduleManager *sm ); /// Emitted when calculation is finished void sigCalculationFinished( Project *project, ScheduleManager *sm ); /// This signal is emitted when one of the nodes members is changed. void nodeChanged( Node* ); /// This signal is emitted when the node is to be added to the project. void nodeToBeAdded( Node*, int ); /// This signal is emitted when the node has been added to the project. void nodeAdded( Node* ); /// This signal is emitted when the node is to be removed from the project. void nodeToBeRemoved( Node* ); /// This signal is emitted when the node has been removed from the project. void nodeRemoved( Node* ); /// This signal is emitted when the node is to be moved up, moved down, indented or unindented. void nodeToBeMoved( Node* node, int pos, Node* newParent, int newPos ); /// This signal is emitted when the node has been moved up, moved down, indented or unindented. void nodeMoved( Node* ); /// This signal is emitted when a document is added void documentAdded( Node*, Document*, int index ); /// This signal is emitted when a document is removed void documentRemoved( Node*, Document*, int index ); /// This signal is emitted when a document is changed void documentChanged( Node*, Document*, int index ); void resourceGroupChanged( ResourceGroup *group ); void resourceGroupAdded( const ResourceGroup *group ); void resourceGroupToBeAdded( const ResourceGroup *group, int row ); void resourceGroupRemoved( const ResourceGroup *group ); void resourceGroupToBeRemoved( const ResourceGroup *group ); void resourceChanged( Resource *resource ); void resourceAdded( const Resource *resource ); void resourceToBeAdded( const ResourceGroup *group, int row ); void resourceRemoved( const Resource *resource ); void resourceToBeRemoved( const Resource *resource ); void scheduleManagerChanged( ScheduleManager *sch ); void scheduleManagerAdded( const ScheduleManager *sch ); void scheduleManagerToBeAdded( const ScheduleManager *sch, int row ); void scheduleManagerRemoved( const ScheduleManager *sch ); void scheduleManagerToBeRemoved( const ScheduleManager *sch ); void scheduleManagerMoved( const ScheduleManager *sch, int row ); void scheduleManagerToBeMoved( const ScheduleManager *sch ); void scheduleChanged( MainSchedule *sch ); void scheduleToBeAdded( const ScheduleManager *manager, int row ); void scheduleAdded( const MainSchedule *sch ); void scheduleToBeRemoved( const MainSchedule *sch ); void scheduleRemoved( const MainSchedule *sch ); // void currentViewScheduleIdChanged( long id ); void calendarChanged( Calendar *cal ); void calendarToBeAdded( const Calendar *cal, int row ); void calendarAdded( const Calendar *cal ); void calendarToBeRemoved( const Calendar *cal ); void calendarRemoved( const Calendar *cal ); /** * Emitted when the default calendar pointer has changed * @parem cal The new default calendar. May be 0. */ void defaultCalendarChanged( Calendar *cal ); /** * Emitted when the standard worktime has been changed. */ void standardWorktimeChanged( StandardWorktime* ); /// Emitted when the relation @p rel is about to be added. void relationToBeAdded( Relation *rel, int parentIndex, int childIndex ); /// Emitted when the relation @p rel has been added. void relationAdded( Relation *rel ); /// Emitted when the relation @p rel is about to be removed. void relationToBeRemoved( Relation *rel ); /// Emitted when the relation @p rel has been removed. void relationRemoved( Relation *rel ); /// Emitted when the relation @p rel shall be modified. void relationToBeModified( Relation *rel ); /// Emitted when the relation @p rel has been modified. void relationModified( Relation *rel ); /// Emitted when locale data has changed void localeChanged(); protected: /// Calculate the schedule. void calculate( Schedule *scedule ); /// Calculate current schedule void calculate(); /// Re-calculate the schedule from @p dt void calculate( Schedule *scedule, const DateTime &dt ); /// Calculate current schedule from @p dt (Always calculates forward) void calculate( const DateTime &dt ); /// Calculate critical path virtual bool calcCriticalPath( bool fromEnd ); /// Prepare task lists for scheduling void tasksForward(); /// Prepare task lists for scheduling void tasksBackward(); protected: friend class KPlatoXmlLoaderBase; using Node::changed; virtual void changed(Node *node, int property = -1); Accounts m_accounts; QList m_resourceGroups; QList m_calendars; Calendar * m_defaultCalendar; StandardWorktime *m_standardWorktime; DateTime calculateForward( int use ); DateTime calculateBackward( int use ); DateTime scheduleForward( const DateTime &earliest, int use ); DateTime scheduleBackward( const DateTime &latest, int use ); DateTime checkStartConstraints( const DateTime &dt ) const; DateTime checkEndConstraints( const DateTime &dt ) const; bool legalParents( const Node *par, const Node *child ) const; bool legalChildren( const Node *par, const Node *child ) const; #ifndef PLAN_NLOGDEBUG private: static bool checkParent( Node *n, const QList &list, QList &checked ); static bool checkChildren( Node *n, const QList &list, QList &checked ); #endif private: void init(); QHash resourceGroupIdDict; QHash resourceIdDict; QHash nodeIdDict; QMap nodeIdReserved; QMap calendarIdDict; QMap m_managerIdMap; QList m_managers; QTimeZone m_timeZone; WBSDefinition m_wbsDefinition; ConfigBase emptyConfig; QPointer m_config; // this one is not owned by me, don't delete int m_progress; QMap m_schedulerPlugins; int m_refCount; // make it possible to use the project by different threads QList m_hardConstraints; QList m_softConstraints; QList m_terminalNodes; bool m_useSharedResources; bool m_sharedResourcesLoaded; QString m_sharedResourcesFile; }; } //KPlato namespace #endif diff --git a/plan/libs/kernel/kptschedule.h b/plan/libs/kernel/kptschedule.h index 57b3a6186ba..e33e032dbf2 100644 --- a/plan/libs/kernel/kptschedule.h +++ b/plan/libs/kernel/kptschedule.h @@ -1,735 +1,735 @@ /* This file is part of the KDE project Copyright (C) 2005 - 2011 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KPTSCHEDULE_H #define KPTSCHEDULE_H #include "kplatokernel_export.h" #include "kptglobal.h" #include "kptcalendar.h" #include "kpteffortcostmap.h" #include "kptresource.h" #include #include #include //#include "KoXmlReaderForward.h" class QDomElement; class QStringList; /// The main namespace namespace KPlato { class Appointment; class DateTime; class Duration; class Node; class Project; class Task; class ScheduleManager; class XMLLoaderObject; class SchedulerPlugin; class KPlatoXmlLoaderBase; /// Caches effortcost data (bcws, bcwp, acwp) class EffortCostCache { public: EffortCostCache() : cached( false ) {} bool cached; EffortCostMap effortcostmap; }; /** * The Schedule class holds data calculated during project * calculation and scheduling, eg start- and end-times and * appointments. * There is one Schedule per node (tasks and project ) and one per resource. * Schedule is subclassed into: * MainSchedule Used by the main project. * NodeSchedule Used by all other nodes (tasks). * ResourceSchedule Used by resources. */ class KPLATOKERNEL_EXPORT Schedule { public: //NOTE: Must match Effort::Use atm. enum Type { Expected = 0 //Effort::Use_Expected }; Schedule(); explicit Schedule(Schedule *parent); Schedule( const QString& name, Type type, long id ); virtual ~Schedule(); QString name() const { return m_name; } void setName( const QString& name ) { m_name = name; } Type type() const { return m_type; } void setType( Type type ) { m_type = type; } void setType( const QString& type ); QString typeToString( bool translate = false ) const; long id() const { return m_id; } void setId( long id ) { m_id = id; } void setParent( Schedule *parent ); Schedule *parent() const { return m_parent; } virtual bool isDeleted() const; virtual void setDeleted( bool on ); virtual bool recalculate() const { return m_parent == 0 ? false : m_parent->recalculate(); } virtual DateTime recalculateFrom() const { return m_parent == 0 ? DateTime() : m_parent->recalculateFrom(); } virtual long parentScheduleId() const { return m_parent == 0 ? NOTSCHEDULED : m_parent->parentScheduleId(); } virtual Resource *resource() const { return 0; } virtual Node *node() const { return 0; } virtual bool isBaselined() const; virtual bool usePert() const; enum OBState { OBS_Parent, OBS_Allow, OBS_Deny }; /// Sets whether overbooking resources is allowed locally for this schedule /// If @p state is OBS_Parent, the parent is checked when allowOverbooking() is called virtual void setAllowOverbookingState( OBState state ); OBState allowOverbookingState() const; virtual bool allowOverbooking() const; virtual bool checkExternalAppointments() const; bool isCritical() const { return positiveFloat == Duration::zeroDuration; } virtual bool loadXML( const KoXmlElement &element, XMLLoaderObject &status ); virtual void saveXML( QDomElement &element ) const; void saveCommonXML( QDomElement &element ) const; void saveAppointments( QDomElement &element ) const; /// Return the effort available in the @p interval virtual Duration effort( const DateTimeInterval &interval ) const; virtual DateTimeInterval available( const DateTimeInterval &interval ) const; enum CalculationMode { Scheduling, CalculateForward, CalculateBackward }; /// Set calculation mode void setCalculationMode( int mode ) { m_calculationMode = mode; } /// Return calculation mode int calculationMode() const { return m_calculationMode; } /// Return the list of appointments QList appointments() const { return m_appointments; } /// Return true if the @p which list is not empty bool hasAppointments( int which ) const; /// Return the list of appointments /// @param which specifies which list is returned QList appointments(int which) const; /// Adds appointment to this schedule only virtual bool add( Appointment *appointment ); /// Adds appointment to both this resource schedule and node schedule virtual void addAppointment( Schedule * /*other*/, const DateTime & /*start*/, const DateTime & /*end*/, double /*load*/ = 100 ) {} /// Removes appointment without deleting it. virtual void takeAppointment( Appointment *appointment, int type = Scheduling ); Appointment *findAppointment( Schedule *resource, Schedule *node, int type = Scheduling ); /// Attach the appointment to appropriate list (appointment->calculationMode() specifies list) bool attatch( Appointment *appointment ); DateTime appointmentStartTime() const; DateTime appointmentEndTime() const; virtual Appointment appointmentIntervals( int which = Scheduling, const DateTimeInterval &interval = DateTimeInterval() ) const; void copyAppointments( CalculationMode from, CalculationMode to ); virtual bool isOverbooked() const { return false; } virtual bool isOverbooked( const DateTime & /*start*/, const DateTime & /*end*/ ) const { return false; } virtual QStringList overbookedResources() const; /// Returns the first booked interval to @p node that intersects @p interval (limited to @p interval) virtual DateTimeInterval firstBookedInterval( const DateTimeInterval &interval, const Schedule *node ) const; /// Return the resources that has appointments to this schedule virtual QList resources() const; /// Return the resource names that has appointments to this schedule virtual QStringList resourceNameList() const; virtual EffortCostMap bcwsPrDay( EffortCostCalculationType type = ECCT_All ); virtual EffortCostMap bcwsPrDay( EffortCostCalculationType type = ECCT_All ) const; virtual EffortCostMap plannedEffortCostPrDay( const QDate &start, const QDate &end, EffortCostCalculationType type = ECCT_All ) const; virtual EffortCostMap plannedEffortCostPrDay( const Resource *resource, const QDate &start, const QDate &end, EffortCostCalculationType type = ECCT_All ) const; /// Returns the total planned effort for @p resource this schedule virtual Duration plannedEffort( const Resource *resource, EffortCostCalculationType type = ECCT_All) const; /// Returns the total planned effort for this schedule virtual Duration plannedEffort( EffortCostCalculationType type = ECCT_All) const; /// Returns the total planned effort for this schedule on date virtual Duration plannedEffort( const QDate &date, EffortCostCalculationType type = ECCT_All ) const; /// Returns the planned effort for @p resource on the @p date date virtual Duration plannedEffort( const Resource *resource, const QDate &date, EffortCostCalculationType type = ECCT_All ) const; /// Returns the planned effort up to and including date virtual Duration plannedEffortTo( const QDate &date, EffortCostCalculationType type = ECCT_All ) const; /// Returns the planned effort for @p resource up to and including date virtual Duration plannedEffortTo( const Resource *resource, const QDate &date, EffortCostCalculationType type = ECCT_All ) const; /** * Planned cost is the sum total of all resources and other costs * planned for this node. */ virtual EffortCost plannedCost( EffortCostCalculationType type = ECCT_All ) const; /// Planned cost on date virtual double plannedCost( const QDate &date, EffortCostCalculationType type = ECCT_All ) const; /** * Planned cost from start of activity up to and including date * is the sum of all resource costs and other costs planned for this schedule. */ virtual double plannedCostTo( const QDate &date, EffortCostCalculationType type = ECCT_All ) const; virtual double normalRatePrHour() const { return 0.0; } void setEarliestStart( DateTime &dt ) { earlyStart = dt; } void setLatestFinish( DateTime &dt ) { lateFinish = dt; } virtual void initiateCalculation(); virtual void calcResourceOverbooked(); virtual void insertHardConstraint( Node * ) {} virtual void insertSoftConstraint( Node * ) {} virtual void insertForwardNode( Node *node ); virtual void insertBackwardNode( Node *node ); virtual void insertStartNode( Node * ) {} virtual void insertEndNode( Node * ) {} virtual void insertSummaryTask( Node * ) {} void setScheduled( bool on ); bool isScheduled() const { return !notScheduled; } DateTime start() const { return startTime; } DateTime end() const { return endTime; } QStringList state() const; void setResourceError( bool on ) { resourceError = on; } void setResourceOverbooked( bool on ) { resourceOverbooked = on; } void setResourceNotAvailable( bool on ) { resourceNotAvailable = on; } void setConstraintError( bool on ) { constraintError = on; } void setNotScheduled( bool on ) { notScheduled = on; } void setSchedulingError( bool on ) { schedulingError = on; } - void setPositiveFloat( const Duration &f ) { positiveFloat = f; } - void setNegativeFloat( const Duration &f ) { negativeFloat = f; } - void setFreeFloat( const Duration &f ) { freeFloat = f; } + void setPositiveFloat( KPlato::Duration f ) { positiveFloat = f; } + void setNegativeFloat( KPlato::Duration f ) { negativeFloat = f; } + void setFreeFloat( KPlato::Duration f ) { freeFloat = f; } void setInCriticalPath( bool on = true ) { inCriticalPath = on; } virtual ScheduleManager *manager() const { return 0; } class KPLATOKERNEL_EXPORT Log { public: enum Type { Type_Debug = 0, Type_Info, Type_Warning, Type_Error }; Log() : node( 0 ), resource( 0 ), severity( 0 ), phase( -1 ) {} Log( const Node *n, int sev, const QString &msg, int ph = -1 ); Log( const Node *n, const Resource *r, int sev, const QString &msg, int ph = -1 ); Log( const Log &other ); Log &operator=( const Log &other ); const Node *node; const Resource *resource; QString message; int severity; int phase; QString formatMsg() const; }; virtual void addLog( const Log &log ); virtual void clearLogs() {}; virtual void logError( const QString &, int = -1 ) {} virtual void logWarning( const QString &, int = -1 ) {} virtual void logInfo( const QString &, int = -1 ) {} virtual void logDebug( const QString &, int = -1 ) {} virtual void incProgress() { if ( m_parent ) m_parent->incProgress(); } void clearPerformanceCache(); protected: virtual void changed( Schedule * /*sch*/ ) {} protected: QString m_name; Type m_type; long m_id; bool m_deleted; Schedule *m_parent; OBState m_obstate; int m_calculationMode; QList m_appointments; QList m_forward; QList m_backward; friend class Node; friend class Task; friend class Project; friend class Resource; friend class RecalculateProjectCmd; friend class ScheduleManager; friend class KPlatoXmlLoaderBase; /** * earlyStart is calculated by PERT/CPM. * A task may be scheduled to start later because of constraints * or resource availability etc. */ DateTime earlyStart; /** * lateStart is calculated by PERT/CPM. * A task may not be scheduled to start later. */ DateTime lateStart; /** * earlyFinish is calculated by PERT/CPM. * A task may not be scheduled to finish earlier. */ DateTime earlyFinish; /** * lateFinish is calculated by PERT/CPM. * A task may be scheduled to finish earlier because of constraints * or resource availability etc. */ DateTime lateFinish; /** startTime is the scheduled start time. * It depends on constraints (i.e. ASAP/ALAP) and resource availability. * It will always be later or equal to earliestStart */ DateTime startTime; /** * m_endTime is the scheduled finish time. * It depends on constraints (i.e. ASAP/ALAP) and resource availability. * It will always be earlier or equal to latestFinish */ DateTime endTime; /** * duration is the scheduled duration which depends on * e.g. estimated effort, allocated resources and risk */ Duration duration; /// Set if EffortType == Effort, but no resource is requested bool resourceError; /// Set if the assigned resource is overbooked bool resourceOverbooked; /// Set if the requested resource is not available bool resourceNotAvailable; /// Set if the task cannot be scheduled to fullfil all the constraints bool constraintError; /// Set if the node has not been scheduled bool notScheduled; /// Set if the assigned resource cannot deliver the requered estimated effort bool effortNotMet; /// Set if some other scheduling error occurs bool schedulingError; DateTime workStartTime; DateTime workEndTime; bool inCriticalPath; Duration positiveFloat; Duration negativeFloat; Duration freeFloat; EffortCostCache &bcwsPrDayCache( int type ) { return m_bcwsPrDay[ type ]; } EffortCostCache &bcwpPrDayCache( int type ) { return m_bcwpPrDay[ type ]; } EffortCostCache &acwpCache( int type ) { return m_acwp[ type ]; } QMap m_bcwsPrDay; QMap m_bcwpPrDay; QMap m_acwp; }; /** * NodeSchedule holds scheduling information for a node (task). * */ class KPLATOKERNEL_EXPORT NodeSchedule : public Schedule { public: NodeSchedule(); NodeSchedule( Node *node, const QString& name, Schedule::Type type, long id ); NodeSchedule( Schedule *parent, Node *node ); virtual ~NodeSchedule(); virtual bool isDeleted() const { return m_parent == 0 ? true : m_parent->isDeleted(); } void setDeleted( bool on ); virtual bool loadXML( const KoXmlElement &element, XMLLoaderObject &status ); virtual void saveXML( QDomElement &element ) const; // tasks------------> virtual void addAppointment( Schedule *resource, const DateTime &start, const DateTime &end, double load = 100 ); virtual void takeAppointment( Appointment *appointment, int type = Schedule::Scheduling ); virtual Node *node() const { return m_node; } virtual void setNode( Node *n ) { m_node = n; } /// Return the resources that has appointments to this schedule virtual QList resources() const; /// Return the resource names that has appointments to this schedule virtual QStringList resourceNameList() const; virtual void logError( const QString &msg, int phase = -1 ); virtual void logWarning( const QString &msg, int phase = -1 ); virtual void logInfo( const QString &msg, int phase = -1 ); virtual void logDebug( const QString &, int = -1 ); protected: void init(); private: Node *m_node; }; /** * ResourceSchedule holds scheduling information for a resource. * */ class KPLATOKERNEL_EXPORT ResourceSchedule : public Schedule { public: ResourceSchedule(); ResourceSchedule( Resource *Resource, const QString& name, Schedule::Type type, long id ); ResourceSchedule( Schedule *parent, Resource *Resource ); virtual ~ResourceSchedule(); virtual bool isDeleted() const { return m_parent == 0 ? true : m_parent->isDeleted(); } virtual void addAppointment( Schedule *node, const DateTime &start, const DateTime &end, double load = 100 ); virtual void takeAppointment( Appointment *appointment, int type = Scheduling ); virtual bool isOverbooked() const; virtual bool isOverbooked( const DateTime &start, const DateTime &end ) const; virtual Resource *resource() const { return m_resource; } virtual double normalRatePrHour() const; /// Return the effort available in the @p interval virtual Duration effort( const DateTimeInterval &interval ) const; virtual DateTimeInterval available( const DateTimeInterval &interval ) const; virtual void logError( const QString &msg, int phase = -1 ); virtual void logWarning( const QString &msg, int phase = -1 ); virtual void logInfo( const QString &msg, int phase = -1 ); virtual void logDebug( const QString &, int = -1 ); void setNodeSchedule( const Schedule *sch ) { m_nodeSchedule = sch; } private: Resource *m_resource; Schedule *m_parent; const Schedule *m_nodeSchedule; // used during scheduling }; /** * MainSchedule holds scheduling information for the main project node. * */ class KPLATOKERNEL_EXPORT MainSchedule : public NodeSchedule { public: MainSchedule(); MainSchedule( Node *node, const QString& name, Schedule::Type type, long id ); ~MainSchedule(); virtual bool isDeleted() const { return m_deleted; } virtual bool isBaselined() const; virtual bool allowOverbooking() const; virtual bool checkExternalAppointments() const; virtual bool usePert() const; virtual bool loadXML( const KoXmlElement &element, XMLLoaderObject &status ); virtual void saveXML( QDomElement &element ) const; void setManager( ScheduleManager *sm ) { m_manager = sm; } ScheduleManager *manager() const { return m_manager; } virtual bool recalculate() const; virtual DateTime recalculateFrom() const; virtual long parentScheduleId() const; DateTime calculateForward( int use ); DateTime calculateBackward( int use ); DateTime scheduleForward( const DateTime &earliest, int use ); DateTime scheduleBackward( const DateTime &latest, int use ); void clearNodes() { m_hardconstraints.clear(); m_softconstraints.clear(); m_forwardnodes.clear(); m_backwardnodes.clear(); m_startNodes.clear(); m_endNodes.clear(); m_summarytasks.clear(); } virtual void insertHardConstraint( Node *node ) { m_hardconstraints.append( node ); } QList hardConstraints() const { return m_hardconstraints; } virtual void insertSoftConstraint( Node *node ) { m_softconstraints.append( node ); } QList softConstraints() const { return m_softconstraints; } QList forwardNodes() const { return m_forwardnodes; } virtual void insertForwardNode( Node *node ) { m_forwardnodes.append( node ); } QList backwardNodes() const { return m_backwardnodes; } virtual void insertBackwardNode( Node *node ) { m_backwardnodes.append( node ); } virtual void insertStartNode( Node *node ) { m_startNodes.append( node ); } QList startNodes() const { return m_startNodes; } virtual void insertEndNode( Node *node ) { m_endNodes.append( node ); } QList endNodes() const { return m_endNodes; } virtual void insertSummaryTask( Node *node ) { m_summarytasks.append( node ); } QList summaryTasks() const { return m_summarytasks; } void clearCriticalPathList(); QList *currentCriticalPath() const; void addCriticalPath( QList *lst = 0 ); const QList< QList > *criticalPathList() const { return &(m_pathlists); } QList criticalPath( int index = 0 ) { QList lst; return m_pathlists.count() <= index ? lst : m_pathlists[ index ]; } void addCriticalPathNode( Node *node ); QVector logs() const; void setLog( const QVector &log ) { m_log = log; } virtual void addLog( const Schedule::Log &log ); virtual void clearLogs() { m_log.clear(); m_logPhase.clear(); } void setPhaseName( int phase, const QString &name ) { m_logPhase[ phase ] = name; } QString logPhase( int phase ) const { return m_logPhase.value( phase ); } static QString logSeverity( int severity ); QMap phaseNames() const { return m_logPhase; } void setPhaseNames( const QMap &pn ) { m_logPhase = pn; } virtual void incProgress(); QStringList logMessages() const; QList< QList > m_pathlists; bool criticalPathListCached; protected: virtual void changed( Schedule *sch ); private: friend class Project; ScheduleManager *m_manager; QList m_hardconstraints; QList m_softconstraints; QList m_forwardnodes; QList m_backwardnodes; QList m_startNodes; QList m_endNodes; QList m_summarytasks; QList *m_currentCriticalPath; QVector m_log; QMap m_logPhase; }; /** * ScheduleManager is used by the Project class to manage the schedules. * The ScheduleManager is the bases for the user interface to scheduling. * A ScheduleManager can have child manager(s). */ class KPLATOKERNEL_EXPORT ScheduleManager : public QObject { Q_OBJECT public: enum CalculationResult { CalculationRunning = 0, CalculationDone, CalculationStopped, CalculationCanceled, CalculationError }; explicit ScheduleManager( Project &project, const QString name = QString() ); ~ScheduleManager(); void setName( const QString& name ); QString name() const { return m_name; } void setManagerId( const QString &id ) { m_id = id; } QString managerId() const { return m_id; } Project &project() const { return m_project; } void setParentManager( ScheduleManager *sm, int index = -1 ); ScheduleManager *parentManager() const { return m_parent; } long scheduleId() const { return m_expected == 0 ? NOTSCHEDULED : m_expected->id(); } int removeChild( const ScheduleManager *sm ); void insertChild( ScheduleManager *sm, int index = -1 ); QList children() const { return m_children; } int childCount() const { return m_children.count(); } ScheduleManager *childAt( int index ) const { return m_children.value( index ); } /// Return list of all child managers (also childrens children) QList allChildren() const; int indexOf( const ScheduleManager* child ) const; bool isParentOf( const ScheduleManager *sm ) const; ScheduleManager *findManager( const QString &name ) const; /// This sub-schedule will be re-calculated based on the parents completion data bool recalculate() const { return m_recalculate; } /// Set re-calculate to @p on. void setRecalculate( bool on ) { m_recalculate = on; } /// The datetime this schedule will be calculated from DateTime recalculateFrom() const { return m_recalculateFrom; } /// Set the datetime this schedule will be calculated from to @p dt void setRecalculateFrom( const DateTime &dt ) { m_recalculateFrom = dt; } long parentScheduleId() const { return m_parent == 0 ? NOTSCHEDULED : m_parent->scheduleId(); } void createSchedules(); void setDeleted( bool on ); bool isScheduled() const { return m_expected == 0 ? false : m_expected->isScheduled(); } void setExpected( MainSchedule *sch ); MainSchedule *expected() const { return m_expected; } QStringList state() const; void setBaselined( bool on ); bool isBaselined() const { return m_baselined; } bool isChildBaselined() const; void setAllowOverbooking( bool on ); bool allowOverbooking() const; void setCheckExternalAppointments( bool on ); bool checkExternalAppointments() const; void setUsePert( bool on ); bool usePert() const { return m_usePert; } void setSchedulingDirection( bool on ); bool schedulingDirection() const { return m_schedulingDirection; } void setScheduling( bool on ); bool scheduling() const { return m_scheduling; } bool loadXML( KoXmlElement &element, XMLLoaderObject &status ); void saveXML( QDomElement &element ) const; /// Save a workpackage document void saveWorkPackageXML( QDomElement &element, const Node &node ) const; void scheduleChanged( MainSchedule *sch ); const QList schedulerPlugins() const; QString schedulerPluginId() const; void setSchedulerPluginId( const QString &id ); SchedulerPlugin *schedulerPlugin() const; QStringList schedulerPluginNames() const; int schedulerPluginIndex() const; void setSchedulerPlugin( int index ); /// Stop calculation. Use result if possible. void stopCalculation(); /// Terminate calculation. Forget any results. void haltCalculation(); void calculateSchedule(); int calculationResult() const { return m_calculationresult; } void setCalculationResult( int r ) { m_calculationresult = r; } /// Increments progress in the project void incProgress(); /// Returns current progress int progress() const { return m_progress; } /// Returns maximum progress value int maxProgress() const { return m_maxprogress; } /// Log added by MainSchedule /// Emits sigLogAdded() to enable synchronization between schedules void logAdded( const Schedule::Log &log ); /// Create and load a MainSchedule MainSchedule *loadMainSchedule( KoXmlElement &element, XMLLoaderObject &status ); /// Load an existing MainSchedule bool loadMainSchedule( MainSchedule *schedule, KoXmlElement &element, XMLLoaderObject &status ); QMap< int, QString > phaseNames() const; /// Return a list of the supported granularities of the current scheduler QList supportedGranularities() const; /// Return current index of supported granularities of the selected scheduler int granularity() const; /// Set current index of supported granularities of the selected scheduler void setGranularity( int duration ); public Q_SLOTS: /// Set maximum progress. Emits signal maxProgressChanged void setMaxProgress( int value ); /// Set progress. Emits signal progressChanged void setProgress( int value ); /// Add the lis of logs @p log to expected() void slotAddLog( const QVector &log ); void setPhaseNames( const QMap &phasenames ); Q_SIGNALS: void maxProgressChanged( int ); void progressChanged( int ); /// Emitted by logAdded() when new log entries are added void logInserted( MainSchedule*, int firstrow, int lastrow ); /// Emitted by logAdded() /// Used by scheduling thread void sigLogAdded( const Schedule::Log &log ); protected: Project &m_project; ScheduleManager *m_parent; QString m_name; QString m_id; bool m_baselined; bool m_allowOverbooking; bool m_checkExternalAppointments; bool m_usePert; bool m_recalculate; DateTime m_recalculateFrom; bool m_schedulingDirection; bool m_scheduling; int m_progress; int m_maxprogress; MainSchedule *m_expected; QList m_children; QString m_schedulerPluginId; int m_calculationresult; }; } //namespace KPlato Q_DECLARE_TYPEINFO(KPlato::Schedule::Log, Q_MOVABLE_TYPE); KPLATOKERNEL_EXPORT QDebug operator<<( QDebug dbg, const KPlato::Schedule *s ); KPLATOKERNEL_EXPORT QDebug operator<<( QDebug dbg, const KPlato::Schedule &s ); KPLATOKERNEL_EXPORT QDebug operator<<( QDebug dbg, const KPlato::Schedule::Log &log ); #endif diff --git a/plan/libs/kernel/kpttask.cpp b/plan/libs/kernel/kpttask.cpp index 47e30acf6a3..b15ee849e4d 100644 --- a/plan/libs/kernel/kpttask.cpp +++ b/plan/libs/kernel/kpttask.cpp @@ -1,3836 +1,3836 @@ /* This file is part of the KDE project Copyright (C) 2001 Thomas zander Copyright (C) 2004 - 2007 Dag Andersen Copyright (C) 2007 Florian Piquemal Copyright (C) 2007 Alexis Ménard This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kpttask.h" #include "kptappointment.h" #include "kptproject.h" #include "kptduration.h" #include "kptrelation.h" #include "kptdatetime.h" #include "kptcalendar.h" #include "kpteffortcostmap.h" #include "kptschedule.h" #include "kptxmlloaderobject.h" #include #include #include namespace KPlato { Task::Task(Node *parent) : Node(parent), m_resource(), m_workPackage( this ) { //debugPlan<<"("<setOptimisticRatio(-10); m_estimate->setPessimisticRatio(20); m_estimate->setParentNode( this ); if (m_parent) m_leader = m_parent->leader(); } Task::Task(const Task &task, Node *parent) : Node(task, parent), m_resource(), m_workPackage( this ) { //debugPlan<<"("<setParentNode( this ); } Task::~Task() { while (!m_resource.isEmpty()) { delete m_resource.takeFirst(); } while (!m_parentProxyRelations.isEmpty()) { delete m_parentProxyRelations.takeFirst(); } while (!m_childProxyRelations.isEmpty()) { delete m_childProxyRelations.takeFirst(); } } int Task::type() const { if ( numChildren() > 0) { return Node::Type_Summarytask; } else if ( m_constraint == Node::FixedInterval ) { if ( m_constraintEndTime == m_constraintStartTime ) { return Node::Type_Milestone; } } else if ( m_estimate->expectedEstimate() == 0.0 ) { return Node::Type_Milestone; } return Node::Type_Task; } Duration *Task::getRandomDuration() { return 0L; } ResourceGroupRequest *Task::resourceGroupRequest(const ResourceGroup *group) const { return m_requests.find(group); } void Task::clearResourceRequests() { m_requests.clear(); changed( this ); } void Task::addRequest(ResourceGroup *group, int numResources) { addRequest(new ResourceGroupRequest(group, numResources)); } void Task::addRequest(ResourceGroupRequest *request) { //debugPlan<group()<group()->id()<group()->name(); m_requests.addRequest(request); changed( this ); } void Task::takeRequest(ResourceGroupRequest *request) { //debugPlan< Task::requestedResources() const { return m_requests.requestedResources(); } bool Task::containsRequest( const QString &identity ) const { return m_requests.contains( identity ); } ResourceRequest *Task::resourceRequest( const QString &name ) const { return m_requests.resourceRequest( name ); } QStringList Task::assignedNameList( long id) const { Schedule *s = schedule( id ); if ( s == 0 ) { return QStringList(); } return s->resourceNameList(); } void Task::makeAppointments() { if (m_currentSchedule == 0) return; if (type() == Node::Type_Task) { //debugPlan<startTime<<","<endTime<<";"<duration.toString(); m_requests.makeAppointments(m_currentSchedule); //debugPlan<startTime<<","<endTime<<";"<duration.toString(); } else if (type() == Node::Type_Summarytask) { foreach (Node *n, m_nodes) { n->makeAppointments(); } } else if (type() == Node::Type_Milestone) { //debugPlan<<"Milestone not implemented"; // Well, shouldn't have resources anyway... } } void Task::copySchedule() { if ( m_currentSchedule == 0 || type() != Node::Type_Task ) { return; } int id = m_currentSchedule->parentScheduleId(); NodeSchedule *ns = static_cast( findSchedule( id ) ); if ( ns == 0 ) { return; } if ( type() == Node::Type_Task ) { copyAppointments( ns->startTime, ns->endTime ); } m_currentSchedule->startTime = ns->startTime; m_currentSchedule->earlyStart = ns->earlyStart; m_currentSchedule->endTime = ns->endTime; m_currentSchedule->lateFinish = ns->lateFinish; m_currentSchedule->duration = ns->duration; // TODO: status flags, etc //debugPlan; } void Task::copyAppointments() { copyAppointments( DateTime(), m_currentSchedule->startTime ); } void Task::copyAppointments( const DateTime &start, const DateTime &end ) { if ( m_currentSchedule == 0 || type() != Node::Type_Task ) { return; } int id = m_currentSchedule->parentScheduleId(); NodeSchedule *ns = static_cast( findSchedule( id ) ); if ( ns == 0 ) { return; } DateTime st = start.isValid() ? start : ns->startTime; DateTime et = end.isValid() ? end : ns->endTime; //debugPlan<calculationMode(); foreach ( const Appointment *a, ns->appointments() ) { Resource *r = a->resource() == 0 ? 0 : a->resource()->resource(); if ( r == 0 ) { errorPlan<<"No resource"; continue; } AppointmentIntervalList lst = a->intervals( st, et ); if ( lst.isEmpty() ) { //debugPlan<<"No intervals to copy from"<appointments() ) { if ( c->resource()->resource() == r ) { //debugPlan<<"Found current appointment to"<resource()->resource()->name()<add( curr ); curr->setNode( m_currentSchedule ); //debugPlan<<"Created new appointment"<( r->findSchedule( m_currentSchedule->id() ) ); if ( rs == 0 ) { rs = r->createSchedule( m_currentSchedule->parent() ); rs->setId( m_currentSchedule->id() ); rs->setName( m_currentSchedule->name() ); rs->setType( m_currentSchedule->type() ); //debugPlan<<"Resource schedule not found, id="<id(); } rs->setCalculationMode( m_currentSchedule->calculationMode() ); if ( ! rs->appointments().contains( curr ) ) { //debugPlan<<"add to resource"<add( curr ); curr->setResource( rs ); } Appointment app; app.setIntervals( lst ); //foreach ( AppointmentInterval *i, curr->intervals() ) { debugPlan<startTime().toString()<endTime().toString(); } curr->merge( app ); //debugPlan<<"Appointments added"; } m_currentSchedule->startTime = ns->startTime; m_currentSchedule->earlyStart = ns->earlyStart; } void Task::calcResourceOverbooked() { if (m_currentSchedule) m_currentSchedule->calcResourceOverbooked(); } bool Task::load(KoXmlElement &element, XMLLoaderObject &status ) { QString s; bool ok = false; - m_id = element.attribute("id"); + m_id = element.attribute(QStringLiteral("id")); - setName( element.attribute("name") ); - m_leader = element.attribute("leader"); - m_description = element.attribute("description"); + setName( element.attribute(QStringLiteral("name")) ); + m_leader = element.attribute(QStringLiteral("leader")); + m_description = element.attribute(QStringLiteral("description")); //debugPlan<load(e)) { if (!project.addSubTask(child, this)) { delete child; // TODO: Complain about this } } else { // TODO: Complain about this delete child; }*/ - } else if (e.tagName() == "task") { + } else if (e.tagName() == QLatin1String("task")) { // Load the task Task *child = new Task(this); if (child->load(e, status)) { if (!status.project().addSubTask(child, this)) { delete child; // TODO: Complain about this } } else { // TODO: Complain about this delete child; } - } else if (e.tagName() == "resource") { + } else if (e.tagName() == QLatin1String("resource")) { // TODO: Load the resource (projects don't have resources yet) - } else if (e.tagName() == "estimate" || - ( /*status.version() < "0.6" &&*/ e.tagName() == "effort" ) ) { + } else if (e.tagName() == QLatin1String("estimate") || + ( /*status.version() < "0.6" &&*/ e.tagName() == QLatin1String("effort") ) ) { // Load the estimate m_estimate->load(e, status); - } else if (e.tagName() == "resourcegroup-request") { + } else if (e.tagName() == QLatin1String("resourcegroup-request")) { // Load the resource request // Handle multiple requests to same group gracefully (Not really allowed) - ResourceGroupRequest *r = m_requests.findGroupRequestById( e.attribute("group-id") ); + ResourceGroupRequest *r = m_requests.findGroupRequestById( e.attribute(QStringLiteral("group-id")) ); if ( r ) { warnPlan<<"Multiple requests to same group, loading into existing group"; if ( ! r->load( e, status ) ) { errorPlan<<"Failed to load resource request"; } } else { r = new ResourceGroupRequest(); if (r->load(e, status)) { addRequest(r); } else { errorPlan<<"Failed to load resource request"; delete r; } } - } else if (e.tagName() == "workpackage") { + } else if (e.tagName() == QLatin1String("workpackage")) { m_workPackage.loadXML( e, status ); - } else if (e.tagName() == "progress") { + } else if (e.tagName() == QLatin1String("progress")) { completion().loadXML( e, status ); - } else if (e.tagName() == "schedules") { + } else if (e.tagName() == QLatin1String("schedules")) { KoXmlNode n = e.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement el = n.toElement(); - if (el.tagName() == "schedule") { + if (el.tagName() == QLatin1String("schedule")) { NodeSchedule *sch = new NodeSchedule(); if (sch->loadXML(el, status)) { sch->setNode(this); addSchedule(sch); } else { errorPlan<<"Failed to load schedule"; delete sch; } } } - } else if (e.tagName() == "documents") { + } else if (e.tagName() == QLatin1String("documents")) { m_documents.load( e, status ); - } else if (e.tagName() == "workpackage-log") { + } else if (e.tagName() == QLatin1String("workpackage-log")) { KoXmlNode n = e.firstChild(); for ( ; ! n.isNull(); n = n.nextSibling() ) { if ( ! n.isElement() ) { continue; } KoXmlElement el = n.toElement(); - if (el.tagName() == "workpackage") { + if (el.tagName() == QLatin1String("workpackage")) { WorkPackage *wp = new WorkPackage( this ); if ( wp->loadLoggedXML( el, status ) ) { m_packageLog << wp; } else { errorPlan<<"Failed to load logged workpackage"; delete wp; } } } } } //debugPlan<save(me); m_workPackage.saveXML(me); completion().saveXML( me ); if (!m_schedules.isEmpty()) { - QDomElement schs = me.ownerDocument().createElement("schedules"); + QDomElement schs = me.ownerDocument().createElement(QStringLiteral("schedules")); me.appendChild(schs); foreach (const Schedule *s, m_schedules) { if (!s->isDeleted()) { s->saveXML(schs); } } } if ( ! m_requests.isEmpty() ) { m_requests.save(me); } m_documents.save( me ); // The workpackage log if (!m_packageLog.isEmpty()) { - QDomElement log = me.ownerDocument().createElement("workpackage-log"); + QDomElement log = me.ownerDocument().createElement(QStringLiteral("workpackage-log")); me.appendChild(log); foreach (const WorkPackage *wp, m_packageLog) { wp->saveLoggedXML( log ); } } for (int i=0; isave(me); } } void Task::saveAppointments(QDomElement &element, long id) const { //debugPlan<save(me); completion().saveXML( me ); if ( m_schedules.contains( id ) && ! m_schedules[ id ]->isDeleted() ) { - QDomElement schs = me.ownerDocument().createElement("schedules"); + QDomElement schs = me.ownerDocument().createElement(QStringLiteral("schedules")); me.appendChild(schs); m_schedules[ id ]->saveXML( schs ); } m_documents.save( me ); // TODO: copying documents } -EffortCostMap Task::plannedEffortCostPrDay(const QDate &start, const QDate &end, long id, EffortCostCalculationType typ ) const { +EffortCostMap Task::plannedEffortCostPrDay(QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->plannedEffortCostPrDay( start, end, id, typ ); } return ec; } Schedule *s = schedule( id ); if ( s ) { return s->plannedEffortCostPrDay( start, end, typ ); } return EffortCostMap(); } -EffortCostMap Task::plannedEffortCostPrDay(const Resource *resource, const QDate &start, const QDate &end, long id, EffortCostCalculationType typ ) const { +EffortCostMap Task::plannedEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->plannedEffortCostPrDay( resource, start, end, id, typ ); } return ec; } Schedule *s = schedule( id ); if ( s ) { return s->plannedEffortCostPrDay( resource, start, end, typ ); } return EffortCostMap(); } -EffortCostMap Task::actualEffortCostPrDay(const QDate &start, const QDate &end, long id, EffortCostCalculationType typ ) const { +EffortCostMap Task::actualEffortCostPrDay(QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->actualEffortCostPrDay( start, end, id, typ ); } return ec; } switch ( completion().entrymode() ) { case Completion::FollowPlan: return plannedEffortCostPrDay( start, end, id, typ ); default: return completion().effortCostPrDay( start, end, id ); } return EffortCostMap(); } -EffortCostMap Task::actualEffortCostPrDay(const Resource *resource, const QDate &start, const QDate &end, long id, EffortCostCalculationType typ ) const { +EffortCostMap Task::actualEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id, EffortCostCalculationType typ ) const { //debugPlan< it( childNodeIterator() ); while ( it.hasNext() ) { ec += it.next() ->actualEffortCostPrDay( resource, start, end, id, typ ); } return ec; } switch ( completion().entrymode() ) { case Completion::FollowPlan: return plannedEffortCostPrDay( resource, start, end, id, typ ); default: return completion().effortCostPrDay( resource, start, end ); } return EffortCostMap(); } // Returns the total planned effort for this task (or subtasks) Duration Task::plannedEffort( const Resource *resource, long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffort( resource, id, typ ); } return eff; } Schedule *s = schedule( id ); if ( s ) { eff = s->plannedEffort( resource, typ ); } return eff; } // Returns the total planned effort for this task (or subtasks) Duration Task::plannedEffort( long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffort( id, typ ); } return eff; } Schedule *s = schedule( id ); if ( s ) { eff = s->plannedEffort( typ ); } return eff; } // Returns the total planned effort for this task (or subtasks) on date -Duration Task::plannedEffort( const Resource *resource, const QDate &date, long id, EffortCostCalculationType typ ) const { +Duration Task::plannedEffort( const Resource *resource, QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffort( resource, date, id, typ ); } return eff; } Schedule *s = schedule( id ); if ( s ) { eff = s->plannedEffort( resource, date, typ ); } return eff; } // Returns the total planned effort for this task (or subtasks) on date -Duration Task::plannedEffort(const QDate &date, long id, EffortCostCalculationType typ ) const { +Duration Task::plannedEffort(QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffort( date, id, typ ); } return eff; } Schedule *s = schedule( id ); if ( s ) { eff = s->plannedEffort( date, typ ); } return eff; } // Returns the total planned effort for this task (or subtasks) upto and including date -Duration Task::plannedEffortTo( const QDate &date, long id, EffortCostCalculationType typ ) const { +Duration Task::plannedEffortTo( QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffortTo( date, id, typ ); } return eff; } Schedule *s = schedule( id ); if ( s ) { eff = s->plannedEffortTo( date, typ ); } return eff; } // Returns the total planned effort for this task (or subtasks) upto and including date -Duration Task::plannedEffortTo( const Resource *resource, const QDate &date, long id, EffortCostCalculationType typ ) const { +Duration Task::plannedEffortTo( const Resource *resource, QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->plannedEffortTo( resource, date, id, typ ); } return eff; } Schedule *s = schedule( id ); if ( s ) { eff = s->plannedEffortTo( resource, date, typ ); } return eff; } // Returns the total actual effort for this task (or subtasks) Duration Task::actualEffort() const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->actualEffort(); } } return completion().actualEffort(); } // Returns the total actual effort for this task (or subtasks) on date -Duration Task::actualEffort( const QDate &date ) const { +Duration Task::actualEffort( QDate date ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->actualEffort( date ); } return eff; } return completion().actualEffort( date ); } // Returns the total actual effort for this task (or subtasks) to date -Duration Task::actualEffortTo( const QDate &date ) const { +Duration Task::actualEffortTo( QDate date ) const { //debugPlan; Duration eff; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { eff += n->actualEffortTo( date ); } return eff; } return completion().actualEffortTo( date ); } EffortCost Task::plannedCost( long id, EffortCostCalculationType typ ) const { //debugPlan; if (type() == Node::Type_Summarytask) { return Node::plannedCost( id, typ ); } EffortCost c; Schedule *s = schedule( id ); if ( s ) { c = s->plannedCost( typ ); } return c; } -double Task::plannedCostTo( const QDate &date, long id, EffortCostCalculationType typ ) const { +double Task::plannedCostTo( QDate date, long id, EffortCostCalculationType typ ) const { //debugPlan; double c = 0; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { c += n->plannedCostTo( date, id, typ ); } return c; } Schedule *s = schedule( id ); if ( s == 0 ) { return c; } c = s->plannedCostTo( date, typ ); if ( date >= s->startTime.date() ) { c += m_startupCost; } if ( date >= s->endTime.date() ) { c += m_shutdownCost; } return c; } -EffortCost Task::actualCostTo( long int id, const QDate &date ) const { +EffortCost Task::actualCostTo( long int id, QDate date ) const { //debugPlan; EffortCostMap ecm = acwp( id ); return EffortCost( ecm.effortTo( date ), ecm.costTo( date ) ); } -double Task::bcws( const QDate &date, long id ) const +double Task::bcws( QDate date, long id ) const { //debugPlan; double c = plannedCostTo( date, id ); //debugPlan<bcwsPrDayCache( typ ); if ( ! cache.cached ) { EffortCostMap ec = s->bcwsPrDay( typ ); if ( typ != ECCT_Work ) { if ( m_startupCost > 0.0 ) { ec.add( s->startTime.date(), Duration::zeroDuration, m_startupCost ); } if ( m_shutdownCost > 0.0 ) { ec.add( s->endTime.date(), Duration::zeroDuration, m_shutdownCost ); } cache.effortcostmap = ec; cache.cached = true; } } return cache.effortcostmap; } EffortCostMap Task::bcwpPrDay( long int id, EffortCostCalculationType typ ) { //debugPlan; if ( type() == Node::Type_Summarytask ) { return Node::bcwpPrDay( id, typ ); } Schedule *s = schedule( id ); if ( s == 0 ) { return EffortCostMap(); } EffortCostCache cache = s->bcwpPrDayCache( typ ); if ( ! cache.cached ) { // do not use bcws cache, it includes startup/shutdown cost EffortCostMap e = s->plannedEffortCostPrDay( s->appointmentStartTime().date(), s->appointmentEndTime().date(), typ ); if ( completion().isStarted() && ! e.isEmpty() ) { // calculate bcwp on bases of bcws *without* startup/shutdown cost double totEff = e.totalEffort().toDouble( Duration::Unit_h ); double totCost = e.totalCost(); QDate sd = completion().entries().keys().value( 0 ); if ( ! sd.isValid() || e.startDate() < sd ) { sd = e.startDate(); } QDate ed = qMax( e.endDate(), completion().entryDate() ); for ( QDate d = sd; d <= ed; d = d.addDays( 1 ) ) { double p = (double)(completion().percentFinished( d )) / 100.0; EffortCost ec = e.days()[ d ]; ec.setBcwpEffort( totEff * p ); ec.setBcwpCost( totCost * p ); e.insert( d, ec ); } } if ( typ != ECCT_Work ) { // add bcws startup/shutdown cost if ( m_startupCost > 0.0 ) { e.add( s->startTime.date(), Duration::zeroDuration, m_startupCost ); } if ( m_shutdownCost > 0.0 ) { e.add( s->endTime.date(), Duration::zeroDuration, m_shutdownCost ); } // add bcwp startup/shutdown cost if ( m_shutdownCost > 0.0 && completion().finishIsValid() ) { QDate finish = completion().finishTime().date(); e.addBcwpCost( finish, m_shutdownCost ); debugPlan<<"addBcwpCost:"< finish ) { e.addBcwpCost( date, m_shutdownCost ); debugPlan<<"addBcwpCost:"< 0.0 && completion().startIsValid() ) { QDate start = completion().startTime().date(); e.addBcwpCost( start, m_startupCost ); // bcwp is cumulative so add to all entries after start for ( EffortCostDayMap::const_iterator it = e.days().constBegin(); it != e.days().constEnd(); ++it ) { const QDate date = it.key(); if ( date > start ) { e.addBcwpCost( date, m_startupCost ); } } } } cache.effortcostmap = e; cache.cached = true; } return cache.effortcostmap; } -Duration Task::budgetedWorkPerformed( const QDate &date, long id ) const +Duration Task::budgetedWorkPerformed( QDate date, long id ) const { //debugPlan; Duration e; if (type() == Node::Type_Summarytask) { foreach (const Node *n, childNodeIterator()) { e += n->budgetedWorkPerformed( date, id ); } return e; } e = plannedEffort( id ) * (double)completion().percentFinished( date ) / 100.0; //debugPlan<budgetedCostPerformed( date, id ); } return c; } c = plannedCost( id ).cost() * (double)completion().percentFinished( date ) / 100.0; if ( completion().isStarted() && date >= completion().startTime().date() ) { c += m_startupCost; } if ( completion().isFinished() && date >= completion().finishTime().date() ) { c += m_shutdownCost; } //debugPlan<acwpCache( typ ); if ( ! ec.cached ) { //debugPlan<= completion().startTime().date() ) { c.add( Duration::zeroDuration, m_startupCost ); } if ( completion().isFinished() && date >= completion().finishTime().date() ) { c.add( Duration::zeroDuration, m_shutdownCost ); } return c; } -double Task::schedulePerformanceIndex( const QDate &date, long id ) const { +double Task::schedulePerformanceIndex( QDate date, long id ) const { //debugPlan; double r = 1.0; double s = bcws( date, id ); double p = bcwp( date, id ); if ( s > 0.0 ) { r = p / s; } return r; } -double Task::effortPerformanceIndex( const QDate &date, long id ) const { +double Task::effortPerformanceIndex( QDate date, long id ) const { //debugPlan; double r = 1.0; Duration a, b; if ( m_estimate->type() == Estimate::Type_Effort ) { Duration b = budgetedWorkPerformed( date, id ); if ( b == Duration::zeroDuration ) { return r; } Duration a = actualEffortTo( date ); if ( b == Duration::zeroDuration ) { return 1.0; } r = b.toDouble() / a.toDouble(); } else if ( m_estimate->type() == Estimate::Type_Duration ) { //TODO } return r; } //FIXME Handle summarytasks -double Task::costPerformanceIndex( long int id, const QDate &date, bool *error ) const +double Task::costPerformanceIndex( long int id, QDate date, bool *error ) const { double res = 0.0; double ac = actualCostTo( id, date ).cost(); bool e = ( ac == 0.0 || completion().percentFinished() == 0 ); if (error) { *error = e; } if (!e) { res = ( plannedCostTo( date, id ) * completion().percentFinished() ) / ( 100 * ac ); } return res; } void Task::initiateCalculation(MainSchedule &sch) { //debugPlan< &list, int use) { DateTime time; // do them forward foreach (Relation *r, list) { if (r->parent()->type() == Type_Summarytask) { //debugPlan<<"Skip summarytask:"<parent()->name(); continue; // skip summarytasks } DateTime t = r->parent()->calculateForward(use); // early finish switch (r->type()) { case Relation::StartStart: // I can't start earlier than my predesseccor t = r->parent()->earlyStart() + r->lag(); break; case Relation::FinishFinish: { // I can't finish earlier than my predeccessor, so // I can't start earlier than it's (earlyfinish+lag)- my duration t += r->lag(); Schedule::OBState obs = m_currentSchedule->allowOverbookingState(); m_currentSchedule->setAllowOverbookingState( Schedule::OBS_Allow ); #ifndef PLAN_NLOGDEBUG - m_currentSchedule->logDebug( "FinishFinish: get duration to calculate early finish" ); + m_currentSchedule->logDebug( QStringLiteral("FinishFinish: get duration to calculate early finish") ); #endif t -= duration(t, use, true); m_currentSchedule->setAllowOverbookingState( obs ); break; } default: t += r->lag(); break; } if (!time.isValid() || t > time) time = t; } //debugPlan<earlyFinish; } if (m_currentSchedule == 0) { return DateTime(); } Schedule *cs = m_currentSchedule; cs->setCalculationMode( Schedule::CalculateForward ); //cs->logDebug( "calculateForward: earlyStart=" + cs->earlyStart.toString() ); // calculate all predecessors if (!dependParentNodes().isEmpty()) { DateTime time = calculatePredeccessors(dependParentNodes(), use); if (time.isValid() && time > cs->earlyStart) { cs->earlyStart = time; //cs->logDebug( QString( "calculate forward: early start moved to: %1" ).arg( cs->earlyStart.toString() ) ); } } if (!m_parentProxyRelations.isEmpty()) { DateTime time = calculatePredeccessors(m_parentProxyRelations, use); if (time.isValid() && time > cs->earlyStart) { cs->earlyStart = time; //cs->logDebug( QString( "calculate forward: early start moved to: %1" ).arg( cs->earlyStart.toString() ) ); } } m_calculateForwardRun = true; //cs->logDebug( "calculateForward: earlyStart=" + cs->earlyStart.toString() ); return calculateEarlyFinish( use ); } DateTime Task::calculateEarlyFinish(int use) { //debugPlan<usePert(); cs->setCalculationMode( Schedule::CalculateForward ); #ifndef PLAN_NLOGDEBUG QTime timer; timer.start(); - cs->logDebug( QString( "Start calculate forward: %1 " ).arg( constraintToString( true ) ) ); + cs->logDebug( QStringLiteral( "Start calculate forward: %1 " ).arg( constraintToString( true ) ) ); #endif QLocale locale; cs->logInfo( i18n( "Calculate early finish " ) ); //debugPlan<<"------>"<earlyStart; if (type() == Node::Type_Task) { m_durationForward = m_estimate->value(use, pert); switch (constraint()) { case Node::ASAP: case Node::ALAP: { //debugPlan<earlyStart; cs->earlyStart = workTimeAfter( cs->earlyStart ); m_durationForward = duration(cs->earlyStart, use, false); m_earlyFinish = cs->earlyStart + m_durationForward; #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP/ALAP: " + cs->earlyStart.toString() + '+' + m_durationForward.toString() + '=' + m_earlyFinish.toString() ); #endif if ( !cs->allowOverbooking() ) { cs->startTime = cs->earlyStart; cs->endTime = m_earlyFinish; makeAppointments(); // calculate duration wo checking booking = the earliest finish possible Schedule::OBState obs = cs->allowOverbookingState(); cs->setAllowOverbookingState( Schedule::OBS_Allow ); m_durationForward = duration(cs->earlyStart, use, false); cs->setAllowOverbookingState( obs ); #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP/ALAP earliest possible: " + cs->earlyStart.toString() + '+' + m_durationForward.toString() + '=' + (cs->earlyStart+m_durationForward).toString() ); #endif } break; } case Node::MustFinishOn: { cs->earlyStart = workTimeAfter( cs->earlyStart ); m_durationForward = duration(cs->earlyStart, use, false); cs->earlyFinish = cs->earlyStart + m_durationForward; //debugPlan<<"MustFinishOn:"<earlyStart<earlyFinish; if (cs->earlyFinish > m_constraintEndTime) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } cs->earlyFinish = qMax( cs->earlyFinish, m_constraintEndTime ); if ( !cs->allowOverbooking() ) { cs->endTime = cs->earlyFinish; cs->startTime = cs->earlyFinish - duration( cs->earlyFinish, use, true ); makeAppointments(); } m_earlyFinish = cs->earlyFinish; m_durationForward = m_earlyFinish - cs->earlyStart; break; } case Node::FinishNotLater: { m_durationForward = duration(cs->earlyStart, use, false); cs->earlyFinish = cs->earlyStart + m_durationForward; //debugPlan<<"FinishNotLater:"<earlyStart<earlyFinish; if (cs->earlyFinish > m_constraintEndTime) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } if ( !cs->allowOverbooking() ) { cs->startTime = cs->earlyStart; cs->endTime = cs->earlyFinish; makeAppointments(); } m_earlyFinish = cs->earlyStart + m_durationForward; break; } case Node::MustStartOn: case Node::StartNotEarlier: { //debugPlan<<"MSO/SNE:"<earlyStart; cs->logDebug( constraintToString() + ": " + m_constraintStartTime.toString() + ' ' + cs->earlyStart.toString() ); cs->earlyStart = workTimeAfter( qMax( cs->earlyStart, m_constraintStartTime ) ); if ( cs->earlyStart < m_constraintStartTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } m_durationForward = duration( cs->earlyStart, use, false ); m_earlyFinish = cs->earlyStart + m_durationForward; if ( !cs->allowOverbooking() ) { cs->startTime = cs->earlyStart; cs->endTime = m_earlyFinish; makeAppointments(); // calculate duration wo checking booking = the earliest finish possible Schedule::OBState obs = cs->allowOverbookingState(); cs->setAllowOverbookingState( Schedule::OBS_Allow ); m_durationForward = duration(cs->startTime, use, false); cs->setAllowOverbookingState( obs ); m_earlyFinish = cs->earlyStart + m_durationForward; #ifndef PLAN_NLOGDEBUG cs->logDebug("MSO/SNE earliest possible: " + cs->earlyStart.toString() + '+' + m_durationForward.toString() + '=' + (cs->earlyStart+m_durationForward).toString() ); #endif } break; } case Node::FixedInterval: { if ( cs->earlyStart > m_constraintStartTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } //cs->earlyStart = m_constraintStartTime; m_durationForward = m_constraintEndTime - m_constraintStartTime; if ( cs->earlyStart < m_constraintStartTime ) { m_durationForward = m_constraintEndTime - cs->earlyStart; } if ( !cs->allowOverbooking() ) { cs->startTime = m_constraintStartTime; cs->endTime = m_constraintEndTime; makeAppointments(); } m_earlyFinish = cs->earlyStart + m_durationForward; break; } } } else if (type() == Node::Type_Milestone) { m_durationForward = Duration::zeroDuration; switch (constraint()) { case Node::MustFinishOn: //debugPlan<<"MustFinishOn:"<earlyStart; //cs->logDebug( QString( "%1: %2, early start: %3" ).arg( constraintToString() ).arg( m_constraintEndTime.toString() ).arg( cs->earlyStart.toString() ) ); if ( cs->earlyStart < m_constraintEndTime ) { m_durationForward = m_constraintEndTime - cs->earlyStart; } if ( cs->earlyStart > m_constraintEndTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } m_earlyFinish = cs->earlyStart + m_durationForward; break; case Node::FinishNotLater: //debugPlan<<"FinishNotLater:"<earlyStart; if ( cs->earlyStart > m_constraintEndTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } m_earlyFinish = cs->earlyStart; break; case Node::MustStartOn: //debugPlan<<"MustStartOn:"<earlyStart; if ( cs->earlyStart < m_constraintStartTime ) { m_durationForward = m_constraintStartTime - cs->earlyStart; } if ( cs->earlyStart > m_constraintStartTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } m_earlyFinish = cs->earlyStart + m_durationForward; break; case Node::StartNotEarlier: //debugPlan<<"StartNotEarlier:"<earlyStart; if ( cs->earlyStart < m_constraintStartTime ) { m_durationForward = m_constraintStartTime - cs->earlyStart; } m_earlyFinish = cs->earlyStart + m_durationForward; break; case Node::FixedInterval: m_earlyFinish = cs->earlyStart + m_durationForward; break; default: m_earlyFinish = cs->earlyStart + m_durationForward; break; } //debugPlan<insertForwardNode( this ); cs->earlyFinish = cs->earlyStart + m_durationForward; foreach ( const Appointment *a, cs->appointments( Schedule::CalculateForward ) ) { cs->logInfo( i18n( "Resource %1 booked from %2 to %3", a->resource()->resource()->name(), locale.toString(a->startTime(), QLocale::ShortFormat), locale.toString(a->endTime(), QLocale::ShortFormat) ) ); } // clean up temporary usage cs->startTime = DateTime(); cs->endTime = DateTime(); cs->duration = Duration::zeroDuration; cs->logInfo( i18n( "Early finish calculated: %1", locale.toString(cs->earlyFinish, QLocale::ShortFormat) ) ); cs->incProgress(); #ifndef PLAN_NLOGDEBUG - cs->logDebug( QString( "Finished calculate forward: %1 ms" ).arg( timer.elapsed() ) ); + cs->logDebug( QStringLiteral( "Finished calculate forward: %1 ms" ).arg( timer.elapsed() ) ); #endif return m_earlyFinish; } DateTime Task::calculateSuccessors(const QList &list, int use) { DateTime time; foreach (Relation *r, list) { if (r->child()->type() == Type_Summarytask) { //debugPlan<<"Skip summarytask:"<parent()->name(); continue; // skip summarytasks } DateTime t = r->child()->calculateBackward(use); switch (r->type()) { case Relation::StartStart: { // I must start before my successor, so // I can't finish later than it's (starttime-lag) + my duration t -= r->lag(); Schedule::OBState obs = m_currentSchedule->allowOverbookingState(); m_currentSchedule->setAllowOverbookingState( Schedule::OBS_Allow ); #ifndef PLAN_NLOGDEBUG - m_currentSchedule->logDebug( "StartStart: get duration to calculate late start" ); + m_currentSchedule->logDebug( QStringLiteral("StartStart: get duration to calculate late start") ); #endif t += duration(t, use, false); m_currentSchedule->setAllowOverbookingState( obs ); break; } case Relation::FinishFinish: // My successor cannot finish before me, so // I can't finish later than it's latest finish - lag t = r->child()->lateFinish() - r->lag(); break; default: t -= r->lag(); break; } if (!time.isValid() || t < time) time = t; } //debugPlan<lateStart; } if (m_currentSchedule == 0) { return DateTime(); } Schedule *cs = m_currentSchedule; cs->setCalculationMode( Schedule::CalculateBackward ); //cs->lateFinish = projectNode()->constraintEndTime(); // calculate all successors if (!dependChildNodes().isEmpty()) { DateTime time = calculateSuccessors(dependChildNodes(), use); if (time.isValid() && time < cs->lateFinish) { cs->lateFinish = time; } } if (!m_childProxyRelations.isEmpty()) { DateTime time = calculateSuccessors(m_childProxyRelations, use); if (time.isValid() && time < cs->lateFinish) { cs->lateFinish = time; } } m_calculateBackwardRun = true; return calculateLateStart( use ); } DateTime Task::calculateLateStart(int use) { //debugPlan<lateStart; } bool pert = cs->usePert(); cs->setCalculationMode( Schedule::CalculateBackward ); #ifndef PLAN_NLOGDEBUG QTime timer; timer.start(); - cs->logDebug( QString( "Start calculate backward: %1 " ).arg( constraintToString( true ) ) ); + cs->logDebug( QStringLiteral( "Start calculate backward: %1 " ).arg( constraintToString( true ) ) ); #endif QLocale locale; cs->logInfo( i18n( "Calculate late start" ) ); - cs->logDebug( QString( "%1: late finish= %2" ).arg( constraintToString() ).arg( cs->lateFinish.toString() ) ); + cs->logDebug( QStringLiteral( "%1: late finish= %2" ).arg( constraintToString() ).arg( cs->lateFinish.toString() ) ); //debugPlan<lateFinish; cs->lateFinish = workTimeBefore( cs->lateFinish ); m_durationBackward = duration(cs->lateFinish, use, true); cs->lateStart = cs->lateFinish - m_durationBackward; #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP/ALAP: " + cs->lateFinish.toString() + '-' + m_durationBackward.toString() + '=' + cs->lateStart.toString() ); #endif if ( !cs->allowOverbooking() ) { cs->startTime = cs->lateStart; cs->endTime = cs->lateFinish; makeAppointments(); // calculate wo checking bookings = latest start possible Schedule::OBState obs = cs->allowOverbookingState(); cs->setAllowOverbookingState( Schedule::OBS_Allow ); m_durationBackward = duration(cs->lateFinish, use, true); cs->setAllowOverbookingState( obs ); #ifndef PLAN_NLOGDEBUG cs->logDebug("ASAP/ALAP latest start possible: " + cs->lateFinish.toString() + '-' + m_durationBackward.toString() + '=' + (cs->lateFinish-m_durationBackward).toString() ); #endif } break; case Node::MustStartOn: case Node::StartNotEarlier: { //debugPlan<<"MustStartOn:"<lateFinish; cs->lateFinish = workTimeBefore( cs->lateFinish ); m_durationBackward = duration(cs->lateFinish, use, true); cs->lateStart = cs->lateFinish - m_durationBackward; if ( cs->lateStart < m_constraintStartTime) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } else { cs->lateStart = qMax( cs->earlyStart, m_constraintStartTime ); } if ( !cs->allowOverbooking() ) { if ( constraint() == MustStartOn ) { cs->startTime = m_constraintStartTime; cs->endTime = m_constraintStartTime + duration( m_constraintStartTime, use, false ); } else { cs->startTime = qMax( cs->lateStart, m_constraintStartTime ); cs->endTime = qMax( cs->lateFinish, cs->startTime ); // safety } makeAppointments(); } cs->lateStart = cs->lateFinish - m_durationBackward; break; } case Node::MustFinishOn: case Node::FinishNotLater: //debugPlan<<"MustFinishOn:"<lateFinish; cs->lateFinish = workTimeBefore( cs->lateFinish ); cs->endTime = cs->lateFinish; if ( cs->lateFinish < m_constraintEndTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } else { cs->endTime = qMax( cs->earlyFinish, m_constraintEndTime ); } m_durationBackward = duration(cs->endTime, use, true); cs->startTime = cs->endTime - m_durationBackward; if ( !cs->allowOverbooking() ) { makeAppointments(); } m_durationBackward = cs->lateFinish - cs->startTime; cs->lateStart = cs->lateFinish - m_durationBackward; break; case Node::FixedInterval: { //cs->lateFinish = m_constraintEndTime; if ( cs->lateFinish < m_constraintEndTime ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } m_durationBackward = m_constraintEndTime - m_constraintStartTime; if ( cs->lateFinish > m_constraintEndTime ) { m_durationBackward = cs->lateFinish - m_constraintStartTime; } if ( !cs->allowOverbooking() ) { cs->startTime = m_constraintStartTime; cs->endTime = m_constraintEndTime; makeAppointments(); } cs->lateStart = cs->lateFinish - m_durationBackward; break; } } } else if (type() == Node::Type_Milestone) { m_durationBackward = Duration::zeroDuration; switch (constraint()) { case Node::MustFinishOn: //debugPlan<<"MustFinishOn:"<lateFinish; if ( m_constraintEndTime < cs->lateFinish ) { m_durationBackward = cs->lateFinish - m_constraintEndTime; } else if ( m_constraintEndTime > cs->lateFinish ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } cs->lateStart = cs->lateFinish - m_durationBackward; break; case Node::FinishNotLater: //debugPlan<<"FinishNotLater:"<lateFinish; if ( m_constraintEndTime < cs->lateFinish ) { m_durationBackward = cs->lateFinish - m_constraintEndTime; } else if ( m_constraintEndTime > cs->lateFinish ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } cs->lateStart = cs->lateFinish - m_durationBackward; break; case Node::MustStartOn: //debugPlan<<"MustStartOn:"<lateFinish; if ( m_constraintStartTime < cs->lateFinish ) { m_durationBackward = cs->lateFinish - m_constraintStartTime; } else if ( m_constraintStartTime > cs->lateFinish ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } cs->lateStart = cs->lateFinish - m_durationBackward; //cs->logDebug( QString( "%1: constraint:%2, start=%3, finish=%4" ).arg( constraintToString() ).arg( m_constraintStartTime.toString() ).arg( cs->lateStart.toString() ).arg( cs->lateFinish.toString() ) ); break; case Node::StartNotEarlier: //debugPlan<<"MustStartOn:"<lateFinish; if ( m_constraintStartTime > cs->lateFinish ) { cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to meet constraint", constraintToString( true ) ) ); } cs->lateStart = cs->lateFinish; break; case Node::FixedInterval: cs->lateStart = cs->lateFinish - m_durationBackward; break; default: cs->lateStart = cs->lateFinish - m_durationBackward; break; } //debugPlan<lateFinish; } else if (type() == Node::Type_Summarytask) { warnPlan<<"Summarytasks should not be calculated here: "<insertBackwardNode( this ); cs->lateStart = cs->lateFinish - m_durationBackward; foreach ( const Appointment *a, cs->appointments( Schedule::CalculateBackward ) ) { cs->logInfo( i18n( "Resource %1 booked from %2 to %3", a->resource()->resource()->name(), locale.toString(a->startTime(), QLocale::ShortFormat), locale.toString(a->endTime(), QLocale::ShortFormat) ) ); } // clean up temporary usage cs->startTime = DateTime(); cs->endTime = DateTime(); cs->duration = Duration::zeroDuration; cs->logInfo( i18n( "Late start calculated: %1", locale.toString(cs->lateStart, QLocale::ShortFormat) ) ); cs->incProgress(); #ifndef PLAN_NLOGDEBUG - cs->logDebug( QString( "Finished calculate backward: %1 ms" ).arg( timer.elapsed() ) ); + cs->logDebug( QStringLiteral( "Finished calculate backward: %1 ms" ).arg( timer.elapsed() ) ); #endif return cs->lateStart; } DateTime Task::schedulePredeccessors(const QList &list, int use) { DateTime time; foreach (Relation *r, list) { if (r->parent()->type() == Type_Summarytask) { //debugPlan<<"Skip summarytask:"<parent()->name(); continue; // skip summarytasks } // schedule the predecessors DateTime earliest = r->parent()->earlyStart(); DateTime t = r->parent()->scheduleForward(earliest, use); switch (r->type()) { case Relation::StartStart: // I can't start before my predesseccor t = r->parent()->startTime() + r->lag(); break; case Relation::FinishFinish: // I can't end before my predecessor, so // I can't start before it's endtime - my duration #ifndef PLAN_NLOGDEBUG - m_currentSchedule->logDebug( "FinishFinish: get duration to calculate earliest start" ); + m_currentSchedule->logDebug( QStringLiteral("FinishFinish: get duration to calculate earliest start") ); #endif t -= duration(t + r->lag(), use, true); break; default: t += r->lag(); break; } if (!time.isValid() || t > time) time = t; } //debugPlan<endTime; } if (m_currentSchedule == 0) { return DateTime(); } Schedule *cs = m_currentSchedule; //cs->logDebug( QString( "Schedule forward (early start: %1)" ).arg( cs->earlyStart.toString() ) ); cs->setCalculationMode( Schedule::Scheduling ); DateTime startTime = earliest > cs->earlyStart ? earliest : cs->earlyStart; // First, calculate all my own predecessors DateTime time = schedulePredeccessors(dependParentNodes(), use); if ( time > startTime ) { startTime = time; //debugPlan<earlyStart.toString() ) ); cs->startTime = cs->earlyStart; } QTime timer; timer.start(); cs->logInfo( i18n( "Start schedule forward: %1 ", constraintToString( true ) ) ); QLocale locale; cs->logInfo( i18n( "Schedule from start %1", locale.toString(cs->startTime, QLocale::ShortFormat) ) ); //debugPlan<startTime<<"earliest:"<earlyStart; if ( false/*useCalculateForwardAppointments*/ && m_estimate->type() == Estimate::Type_Effort && ! cs->allowOverbooking() && cs->hasAppointments( Schedule::CalculateForward ) ) { #ifndef PLAN_NLOGDEBUG cs->logDebug( "ASAP: " + cs->startTime.toString() + " earliest: " + cs->earlyStart.toString() ); #endif cs->copyAppointments( Schedule::CalculateForward, Schedule::Scheduling ); if ( cs->recalculate() && completion().isStarted() ) { // copy start times + appointments from parent schedule copyAppointments(); } cs->startTime = cs->appointmentStartTime(); cs->endTime = cs->appointmentEndTime(); Q_ASSERT( cs->startTime.isValid() ); Q_ASSERT( cs->endTime.isValid() ); cs->duration = cs->endTime - cs->startTime; if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } cs->logInfo( i18n( "Scheduled: %1 to %2", locale.toString(cs->startTime, QLocale::ShortFormat), locale.toString(cs->endTime, QLocale::ShortFormat) ) ); return cs->endTime; } cs->startTime = workTimeAfter( cs->startTime, cs ); #ifndef PLAN_NLOGDEBUG cs->logDebug( "ASAP: " + cs->startTime.toString() + " earliest: " + cs->earlyStart.toString() ); #endif cs->duration = duration(cs->startTime, use, false); cs->endTime = cs->startTime + cs->duration; makeAppointments(); if ( cs->recalculate() && completion().isStarted() ) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } break; case Node::ALAP: // cs->startTime calculated above //debugPlan<startTime<endTime<<" latest="<lateFinish; cs->endTime = workTimeBefore( cs->lateFinish, cs ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; //debugPlan<endTime = workTimeBefore( cs->earlyFinish, cs ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; makeAppointments(); } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if ( cs->recalculate() && completion().isStarted() ) { cs->earlyStart = cs->startTime = completion().startTime(); } break; case Node::StartNotEarlier: // cs->startTime calculated above //debugPlan<<"StartNotEarlier:"<startTime<lateStart; cs->startTime = workTimeAfter( qMax( cs->startTime, m_constraintStartTime ), cs ); cs->duration = duration(cs->startTime, use, false); cs->endTime = cs->startTime + cs->duration; makeAppointments(); if ( cs->recalculate() && completion().isStarted() ) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if (cs->startTime < m_constraintStartTime) { cs->constraintError = true; cs->negativeFloat = cs->startTime - m_constraintStartTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } break; case Node::FinishNotLater: // cs->startTime calculated above //debugPlan<<"FinishNotLater:"<startTime; cs->startTime = workTimeAfter( cs->startTime, cs ); cs->duration = duration(cs->startTime, use, false); cs->endTime = cs->startTime + cs->duration; makeAppointments(); if ( cs->recalculate() && completion().isStarted() ) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if (cs->endTime > m_constraintEndTime) { //warnPlan<<"cs->endTime > m_constraintEndTime"; cs->constraintError = true; cs->negativeFloat = cs->endTime - m_constraintEndTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } break; case Node::MustStartOn: // Always try to put it on time cs->startTime = workTimeAfter( m_constraintStartTime, cs ); //debugPlan<<"MustStartOn="<startTime; cs->duration = duration(cs->startTime, use, false); cs->endTime = cs->startTime + cs->duration; #ifndef PLAN_NLOGDEBUG - cs->logDebug( QString( "%1: Schedule from %2 to %3" ).arg( constraintToString() ).arg( cs->startTime.toString() ).arg( cs->endTime.toString() ) ); + cs->logDebug( QStringLiteral( "%1: Schedule from %2 to %3" ).arg( constraintToString() ).arg( cs->startTime.toString() ).arg( cs->endTime.toString() ) ); #endif makeAppointments(); if ( cs->recalculate() && completion().isStarted() ) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if (m_constraintStartTime < cs->startTime ) { cs->constraintError = true; cs->negativeFloat = cs->startTime - m_constraintStartTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } break; case Node::MustFinishOn: // Just try to schedule on time cs->endTime = workTimeBefore( m_constraintEndTime, cs ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; //debugPlan<<"MustFinishOn:"<lateFinish<<":"<startTime<endTime; makeAppointments(); if ( cs->recalculate() && completion().isStarted() ) { // copy start times + appointments from parent schedule copyAppointments(); cs->duration = cs->endTime - cs->startTime; } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } if ( cs->endTime != m_constraintEndTime ) { cs->constraintError = true; cs->negativeFloat = cs->endTime - m_constraintEndTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } break; case Node::FixedInterval: { // cs->startTime calculated above //debugPlan<<"FixedInterval="<startTime; cs->duration = m_constraintEndTime - m_constraintStartTime; if ( m_constraintStartTime >= cs->earlyStart ) { cs->startTime = m_constraintStartTime; cs->endTime = m_constraintEndTime; } else { cs->startTime = cs->earlyStart; cs->endTime = cs->startTime + cs->duration; cs->constraintError = true; } if ( m_constraintStartTime < cs->startTime ) { cs->constraintError = true; cs->negativeFloat = cs->startTime - m_constraintStartTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } else { cs->positiveFloat = Duration::zeroDuration; } cs->workStartTime = m_constraintStartTime; cs->workEndTime = m_constraintEndTime; //debugPlan<<"FixedInterval="<startTime<<","<endTime; makeAppointments(); break; } default: break; } if ( m_estimate->type() == Estimate::Type_Effort ) { // HACK scheduling may accept deviation less than 5 mins to improve performance cs->effortNotMet = ( m_estimate->value( use, cs->usePert() ) - cs->plannedEffort() ) > ( 5 * 60000 ); if ( cs->effortNotMet ) { cs->logError( i18n( "Effort not met. Estimate: %1, planned: %2", estimate()->value( use, cs->usePert() ).toHours(), cs->plannedEffort().toHours() ) ); } } } else if (type() == Node::Type_Milestone) { if ( cs->recalculate() && completion().isFinished() ) { cs->startTime = completion().startTime(); cs->endTime = completion().finishTime(); m_visitedForward = true; return cs->endTime; } switch (m_constraint) { case Node::ASAP: { cs->endTime = cs->startTime; // TODO check, do we need to check succeccors earliestStart? cs->positiveFloat = cs->lateFinish - cs->endTime; break; } case Node::ALAP: { cs->startTime = qMax( cs->lateFinish, cs->earlyFinish ); cs->endTime = cs->startTime; cs->positiveFloat = Duration::zeroDuration; break; } case Node::MustStartOn: case Node::MustFinishOn: case Node::FixedInterval: { //debugPlan<<"MustStartOn:"<startTime; DateTime contime = m_constraint == Node::MustFinishOn ? m_constraintEndTime : m_constraintStartTime; #ifndef PLAN_NLOGDEBUG - cs->logDebug( QString( "%1: constraint time=%2, start time=%3" ).arg( constraintToString() ).arg( contime.toString() ).arg( cs->startTime.toString() ) ); + cs->logDebug( QStringLiteral( "%1: constraint time=%2, start time=%3" ).arg( constraintToString() ).arg( contime.toString() ).arg( cs->startTime.toString() ) ); #endif if ( cs->startTime < contime ) { if ( contime <= cs->lateFinish || contime <= cs->earlyFinish ) { cs->startTime = contime; } } cs->negativeFloat = cs->startTime > contime ? cs->startTime - contime : contime - cs->startTime; if ( cs->negativeFloat != 0 ) { cs->constraintError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } cs->endTime = cs->startTime; if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; } case Node::StartNotEarlier: if ( cs->startTime < m_constraintStartTime ) { if ( m_constraintStartTime <= cs->lateFinish || m_constraintStartTime <= cs->earlyFinish ) { cs->startTime = m_constraintStartTime; } } if ( cs->startTime < m_constraintStartTime ) { cs->constraintError = true; cs->negativeFloat = m_constraintStartTime - cs->startTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } cs->endTime = cs->startTime; if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; case Node::FinishNotLater: //debugPlan<startTime; if (cs->startTime > m_constraintEndTime) { cs->constraintError = true; cs->negativeFloat = cs->startTime - m_constraintEndTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } cs->endTime = cs->startTime; if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; default: break; } cs->duration = Duration::zeroDuration; //debugPlan<startTime<<","<endTime; } else if (type() == Node::Type_Summarytask) { //shouldn't come here cs->endTime = cs->startTime; cs->duration = cs->endTime - cs->startTime; warnPlan<<"Summarytasks should not be calculated here: "<startTime<<" :"<endTime<<""<startTime < projectNode()->constraintStartTime() || cs->endTime > projectNode()->constraintEndTime() ) { cs->logError( i18n( "Failed to schedule within project target time" ) ); } foreach ( const Appointment *a, cs->appointments() ) { cs->logInfo( i18n( "Resource %1 booked from %2 to %3", a->resource()->resource()->name(), locale.toString(a->startTime(), QLocale::ShortFormat), locale.toString(a->endTime(), QLocale::ShortFormat) ) ); } if ( cs->startTime < cs->earlyStart ) { cs->logWarning( i18n( "Starting earlier than early start" ) ); } if ( cs->endTime > cs->lateFinish ) { cs->logWarning( i18n( "Finishing later than late finish" ) ); } cs->logInfo( i18n( "Scheduled: %1 to %2", locale.toString(cs->startTime, QLocale::ShortFormat), locale.toString(cs->endTime, QLocale::ShortFormat) ) ); m_visitedForward = true; cs->incProgress(); m_requests.resetDynamicAllocations(); cs->logInfo( i18n( "Finished schedule forward: %1 ms", timer.elapsed() ) ); return cs->endTime; } DateTime Task::scheduleSuccessors(const QList &list, int use) { DateTime time; foreach (Relation *r, list) { if (r->child()->type() == Type_Summarytask) { //debugPlan<<"Skip summarytask:"<child()->name(); continue; } // get the successors starttime DateTime latest = r->child()->lateFinish(); DateTime t = r->child()->scheduleBackward(latest, use); switch (r->type()) { case Relation::StartStart: // I can't start before my successor, so // I can't finish later than it's starttime + my duration #ifndef PLAN_NLOGDEBUG - m_currentSchedule->logDebug( "StartStart: get duration to calculate late finish" ); + m_currentSchedule->logDebug( QStringLiteral("StartStart: get duration to calculate late finish") ); #endif t += duration(t - r->lag(), use, false); break; case Relation::FinishFinish: t = r->child()->endTime() - r->lag(); break; default: t -= r->lag(); break; } if (!time.isValid() || t < time) time = t; } return time; } DateTime Task::scheduleBackward(const DateTime &latest, int use) { if ( m_scheduleBackwardRun ) { return m_currentSchedule->startTime; } if (m_currentSchedule == 0) { return DateTime(); } Schedule *cs = m_currentSchedule; cs->setCalculationMode( Schedule::Scheduling ); DateTime endTime = latest < cs->lateFinish ? latest : cs->lateFinish; // First, calculate all my own successors DateTime time = scheduleSuccessors(dependChildNodes(), use); if (time.isValid() && time < endTime) { endTime = time; } // Then my parents time = scheduleSuccessors(m_childProxyRelations, use); if (time.isValid() && time < endTime) { endTime = time; } if ( ! m_visitedBackward ) { cs->endTime = endTime; } m_scheduleBackwardRun = true; return scheduleFromEndTime( use ); } DateTime Task::scheduleFromEndTime(int use) { //debugPlan<setCalculationMode( Schedule::Scheduling ); bool pert = cs->usePert(); if (m_visitedBackward) { return cs->startTime; } cs->notScheduled = false; if ( !cs->endTime.isValid() ) { cs->endTime = cs->lateFinish; } #ifndef PLAN_NLOGDEBUG QTime timer; timer.start(); - cs->logDebug( QString( "Start schedule backward: %1 " ).arg( constraintToString( true ) ) ); + cs->logDebug( QStringLiteral( "Start schedule backward: %1 " ).arg( constraintToString( true ) ) ); #endif QLocale locale; cs->logInfo( i18n( "Schedule from end time: %1", cs->endTime.toString() ) ); if (type() == Node::Type_Task) { cs->duration = m_estimate->value(use, pert); switch (m_constraint) { case Node::ASAP: { // cs->endTime calculated above //debugPlan<logDebug( QStringLiteral( "%1: Latest allowed end time earlier than early start").arg( constraintToString() ) ); #endif cs->duration = duration( cs->endTime, use, true ); e = cs->endTime; cs->startTime = e - cs->duration; } if ( e > cs->lateFinish ) { cs->schedulingError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to schedule within late finish.", constraintToString() ) ); #ifndef PLAN_NLOGDEBUG cs->logDebug( "ASAP: late finish=" + cs->lateFinish.toString() + " end time=" + e.toString() ); #endif } else if ( e > cs->endTime ) { cs->schedulingError = true; cs->logWarning( i18nc( "1=type of constraint", "%1: Failed to schedule within successors start time", constraintToString() ) ); #ifndef PLAN_NLOGDEBUG cs->logDebug( "ASAP: succ. start=" + cs->endTime.toString() + " end time=" + e.toString() ); #endif } if ( cs->lateFinish > e ) { DateTime w = workTimeBefore( cs->lateFinish ); if ( w > e ) { cs->positiveFloat = w - e; } #ifndef PLAN_NLOGDEBUG cs->logDebug( "ASAP: positiveFloat=" + cs->positiveFloat.toString() ); #endif } cs->endTime = e; makeAppointments(); break; } case Node::ALAP: { // cs->endTime calculated above //debugPlan<logDebug( "ALAP: earlyStart=" + cs->earlyStart.toString() + " cs->startTime=" + cs->startTime.toString() ); #endif } else if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; #ifndef PLAN_NLOGDEBUG cs->logDebug( "ALAP: positiveFloat=" + cs->positiveFloat.toString() ); #endif } //debugPlan<endTime; cs->endTime = workTimeBefore( cs->endTime, cs ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; if ( cs->startTime < m_constraintStartTime ) { //warnPlan<<"m_constraintStartTime > cs->lateStart"; cs->constraintError = true; cs->negativeFloat = m_constraintStartTime - cs->startTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } makeAppointments(); if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } break; case Node::FinishNotLater: // cs->endTime calculated above //debugPlan<<"FinishNotLater:"<endTime; if (cs->endTime > m_constraintEndTime) { cs->endTime = qMax( qMin( m_constraintEndTime, cs->lateFinish ), cs->earlyFinish ); } cs->endTime = workTimeBefore( cs->endTime, cs ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; if ( cs->endTime > m_constraintEndTime ) { cs->negativeFloat = cs->endTime - m_constraintEndTime; cs->constraintError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } makeAppointments(); if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } break; case Node::MustStartOn: // Just try to schedule on time //debugPlan<<"MustStartOn="<startTime.toString(); cs->startTime = workTimeAfter( m_constraintStartTime, cs ); cs->duration = duration(cs->startTime, use, false); if ( cs->endTime >= cs->startTime + cs->duration ) { cs->endTime = cs->startTime + cs->duration; } else { cs->endTime = workTimeBefore( cs->endTime ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; } if (m_constraintStartTime != cs->startTime) { cs->constraintError = true; cs->negativeFloat = m_constraintStartTime - cs->startTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } makeAppointments(); if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } break; case Node::MustFinishOn: // Just try to schedule on time //debugPlan<endTime<earlyFinish; cs->endTime = workTimeBefore( m_constraintEndTime, cs ); cs->duration = duration(cs->endTime, use, true); cs->startTime = cs->endTime - cs->duration; if (m_constraintEndTime != cs->endTime ) { cs->negativeFloat = m_constraintEndTime - cs->endTime; cs->constraintError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); //warnPlan<<"m_constraintEndTime > cs->endTime"; } makeAppointments(); if ( cs->lateFinish > cs->endTime ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } break; case Node::FixedInterval: { // cs->endTime calculated above //debugPlan<endTime; cs->duration = m_constraintEndTime - m_constraintStartTime; if ( cs->endTime > m_constraintEndTime ) { cs->endTime = qMax( m_constraintEndTime, cs->earlyFinish ); } cs->startTime = cs->endTime - cs->duration; if (m_constraintEndTime != cs->endTime) { cs->negativeFloat = m_constraintEndTime - cs->endTime; cs->constraintError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } cs->workStartTime = workTimeAfter( cs->startTime ); cs->workEndTime = workTimeBefore( cs->endTime ); makeAppointments(); if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = workTimeBefore( cs->lateFinish ) - cs->endTime; } break; } default: break; } m_requests.reserve(cs->startTime, cs->duration); if ( m_estimate->type() == Estimate::Type_Effort ) { // HACK scheduling may accept deviation less than 5 mins to improve performance cs->effortNotMet = ( m_estimate->value( use, cs->usePert() ) - cs->plannedEffort() ) > ( 5 * 60000 ); if ( cs->effortNotMet ) { cs->logError( i18n( "Effort not met. Estimate: %1, planned: %2", estimate()->value( use, cs->usePert() ).toHours(), cs->plannedEffort().toHours() ) ); } } } else if (type() == Node::Type_Milestone) { switch (m_constraint) { case Node::ASAP: if ( cs->endTime < cs->earlyStart ) { cs->schedulingError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to schedule after early start.", constraintToString() ) ); cs->endTime = cs->earlyStart; } else { cs->positiveFloat = cs->lateFinish - cs->endTime; } //cs->endTime = cs->earlyStart; FIXME need to follow predeccessors. Defer scheduling? cs->startTime = cs->endTime; break; case Node::ALAP: cs->startTime = cs->endTime; cs->positiveFloat = cs->lateFinish - cs->endTime; break; case Node::MustStartOn: case Node::MustFinishOn: case Node::FixedInterval: { DateTime contime = m_constraint == Node::MustFinishOn ? m_constraintEndTime : m_constraintStartTime; if ( contime < cs->earlyStart ) { if ( cs->earlyStart < cs->endTime ) { cs->endTime = cs->earlyStart; } } else if ( contime < cs->endTime ) { cs->endTime = contime; } cs->negativeFloat = cs->endTime > contime ? cs->endTime - contime : contime - cs->endTime; if ( cs->negativeFloat != 0 ) { cs->constraintError = true; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } cs->startTime = cs->endTime; if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; } case Node::StartNotEarlier: cs->startTime = cs->endTime; if ( m_constraintStartTime > cs->startTime) { cs->constraintError = true; cs->negativeFloat = m_constraintStartTime - cs->startTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; case Node::FinishNotLater: if ( m_constraintEndTime < cs->earlyStart ) { if ( cs->earlyStart < cs->endTime ) { cs->endTime = cs->earlyStart; } } else if ( m_constraintEndTime < cs->endTime ) { cs->endTime = m_constraintEndTime; } if ( m_constraintEndTime > cs->endTime ) { cs->constraintError = true; cs->negativeFloat = cs->endTime - m_constraintEndTime; cs->logError( i18nc( "1=type of constraint", "%1: Failed to meet constraint. Negative float=%2", constraintToString( true ), cs->negativeFloat.toString( Duration::Format_i18nHour ) ) ); } cs->startTime = cs->endTime; if ( cs->negativeFloat == Duration::zeroDuration ) { cs->positiveFloat = cs->lateFinish - cs->endTime; } break; default: break; } cs->duration = Duration::zeroDuration; } else if (type() == Node::Type_Summarytask) { //shouldn't come here cs->startTime = cs->endTime; cs->duration = cs->endTime - cs->startTime; warnPlan<<"Summarytasks should not be calculated here: "<startTime < projectNode()->constraintStartTime() || cs->endTime > projectNode()->constraintEndTime() ) { cs->logError( i18n( "Failed to schedule within project target time" ) ); } foreach ( const Appointment *a, cs->appointments() ) { cs->logInfo( i18n( "Resource %1 booked from %2 to %3", a->resource()->resource()->name(), locale.toString(a->startTime(), QLocale::ShortFormat ), locale.toString(a->endTime(), QLocale::ShortFormat) ) ); } if ( cs->startTime < cs->earlyStart ) { cs->logWarning( i18n( "Starting earlier than early start" ) ); } if ( cs->endTime > cs->lateFinish ) { cs->logWarning( i18n( "Finishing later than late finish" ) ); } cs->logInfo( i18n( "Scheduled: %1 to %2", locale.toString(cs->startTime, QLocale::ShortFormat), locale.toString(cs->endTime, QLocale::ShortFormat) ) ); m_visitedBackward = true; cs->incProgress(); m_requests.resetDynamicAllocations(); #ifndef PLAN_NLOGDEBUG - cs->logDebug( QString( "Finished schedule backward: %1 ms" ).arg( timer.elapsed() ) ); + cs->logDebug( QStringLiteral( "Finished schedule backward: %1 ms" ).arg( timer.elapsed() ) ); #endif return cs->startTime; } void Task::adjustSummarytask() { if (m_currentSchedule == 0) return; if (type() == Type_Summarytask) { DateTime start = m_currentSchedule->lateFinish; DateTime end = m_currentSchedule->earlyStart; foreach (Node *n, m_nodes) { n->adjustSummarytask(); if (n->startTime() < start) start = n->startTime(); if (n->endTime() > end) end = n->endTime(); } m_currentSchedule->startTime = start; m_currentSchedule->endTime = end; m_currentSchedule->duration = end - start; m_currentSchedule->notScheduled = false; //debugPlan<name<<":"<startTime.toString()<<" :"<endTime.toString(); } } Duration Task::duration(const DateTime &time, int use, bool backward) { //debugPlan; // TODO: handle risc if (m_currentSchedule == 0) { errorPlan<<"No current schedule"; return Duration::zeroDuration; } if (!time.isValid()) { #ifndef PLAN_NLOGDEBUG - m_currentSchedule->logDebug( "Calculate duration: Start time is not valid" ); + m_currentSchedule->logDebug( QStringLiteral("Calculate duration: Start time is not valid") ); #endif return Duration::zeroDuration; } //debugPlan< calcDuration"<<(backward?"(B)":"(F)")<resourceNotAvailable = true; dur = effort; //??? } return dur; } if (m_estimate->type() == Estimate::Type_Duration) { return length( time, dur, backward ); } errorPlan<<"Unsupported estimate type: "<type(); return dur; } -Duration Task::length(const DateTime &time, const Duration &duration, bool backward) +Duration Task::length(const DateTime &time, KPlato::Duration duration, bool backward) { return length( time, duration, m_currentSchedule, backward ); } -Duration Task::length(const DateTime &time, const Duration &duration, Schedule *sch, bool backward) { +Duration Task::length(const DateTime &time, KPlato::Duration duration, Schedule *sch, bool backward) { //debugPlan<<"--->"<<(backward?"(B)":"(F)")<logDebug( "Calculate length: estimate == 0" ); + if ( sch ) sch->logDebug( QStringLiteral("Calculate length: estimate == 0") ); #else Q_UNUSED(sch) #endif return l; } Calendar *cal = m_estimate->calendar(); if ( cal == 0) { #ifndef PLAN_NLOGDEBUG if ( sch ) sch->logDebug( "Calculate length: No calendar, return estimate " + duration.toString() ); #endif return duration; } #ifndef PLAN_NLOGDEBUG if ( sch ) sch->logDebug( "Calculate length from: " + time.toString() ); #endif DateTime logtime = time; bool sts=true; bool match = false; DateTime start = time; int inc = backward ? -1 : 1; DateTime end = start; Duration l1; int nDays = backward ? projectNode()->constraintStartTime().daysTo( time ) : time.daysTo( projectNode()->constraintEndTime() ); for (int i=0; !match && i <= nDays; ++i) { // days end = end.addDays(inc); l1 = backward ? cal->effort(end, start) : cal->effort(start, end); //debugPlan<<"["<logDebug( "Days: duration " + logtime.toString() + " - " + end.toString() + " = " + l.toString() + " (" + (duration - l).toString() + ')' ); #endif logtime = start; for (int i=0; !match && i < 24; ++i) { // hours end = end.addSecs(inc*60*60); l1 = backward ? cal->effort(end, start) : cal->effort(start, end); if (l + l1 < duration) { l += l1; start = end; } else if (l + l1 == duration) { l += l1; match = true; } else { end = start; break; } //debugPlan<<"duration(h)["<effort(end, start) : cal->effort(start, end); if (l + l1 < duration) { l += l1; start = end; } else if (l + l1 == duration) { l += l1; match = true; } else if (l + l1 > duration) { end = start; break; } //debugPlan<<"duration(m)"<<(backward?"backward":"forward:")<<" time="<effort(end, start) : cal->effort(start, end); if (l + l1 < duration) { l += l1; start = end; } else if (l + l1 == duration) { l += l1; match = true; } else if (l + l1 > duration) { end = start; break; } //debugPlan<<"duration(s)["<logDebug( "Seconds: duration " + logtime.toString() + " - " + end.toString() + " l " + l.toString() + " (" + (duration - l).toString() + ')' ); #endif for (int i=0; !match && i < 1000; ++i) { //milliseconds end.setTime(end.time().addMSecs(inc)); l1 = backward ? cal->effort(end, start) : cal->effort(start, end); if (l + l1 < duration) { l += l1; start = end; } else if (l + l1 == duration) { l += l1; match = true; } else { #ifndef PLAN_NLOGDEBUG if ( sch ) sch->logDebug( "Got more than asked for, should not happen! Want: " + duration.toString(Duration::Format_Hour) + " got: " + l.toString(Duration::Format_Hour) ); #endif break; } //debugPlan<<"duration(ms)["<logError( i18n( "Could not match work duration. Want: %1 got: %2", l.toString( Duration::Format_i18nHour ), duration.toString( Duration::Format_i18nHour ) ) ); } DateTime t = end; if (l != Duration::zeroDuration) { if ( backward ) { if ( end < projectNode()->constraintEndTime() ) { t = cal->firstAvailableAfter(end, projectNode()->constraintEndTime()); } } else { if ( end > projectNode()->constraintStartTime() ) { t = cal->firstAvailableBefore(end, projectNode()->constraintStartTime()); } } #ifndef PLAN_NLOGDEBUG if ( sch ) sch->logDebug( "Moved end to work: " + end.toString() + " -> " + t.toString() ); #endif } end = t.isValid() ? t : time; //debugPlan<<"<---"<<(backward?"(B)":"(F)")<time ? end-time : time-end; if ( match ) { #ifndef PLAN_NLOGDEBUG if ( sch ) sch->logDebug( "Calculated length: " + time.toString() + " - " + end.toString() + " = " + l.toString() ); #endif } return l; } void Task::clearProxyRelations() { m_parentProxyRelations.clear(); m_childProxyRelations.clear(); } void Task::addParentProxyRelations( const QList &list ) { //debugPlan<addParentProxyRelations(list); n->addParentProxyRelations(dependParentNodes()); } } else { // add 'this' as child relation to the relations parent //debugPlan<parent()->addChildProxyRelation(this, r); // add a parent relation to myself addParentProxyRelation(r->parent(), r); } } } void Task::addChildProxyRelations( const QList &list) { //debugPlan<addChildProxyRelations(list); n->addChildProxyRelations(dependChildNodes()); } } else { // add 'this' as parent relation to the relations child //debugPlan<child()->addParentProxyRelation(this, r); // add a child relation to myself addChildProxyRelation(r->child(), r); } } } void Task::addParentProxyRelation(Node *node, const Relation *rel) { if (node->type() != Type_Summarytask) { if (type() == Type_Summarytask) { //debugPlan<<"Add parent proxy from my children"<name(); foreach (Node *n, m_nodes) { n->addParentProxyRelation(node, rel); } } else { //debugPlan<<"Add parent proxy from"<name()<<" to (me)"<type(), rel->lag())); } } } void Task::addChildProxyRelation(Node *node, const Relation *rel) { if (node->type() != Type_Summarytask) { if (type() == Type_Summarytask) { //debugPlan<<"Add child proxy from my children"<name(); foreach (Node *n, m_nodes) { n->addChildProxyRelation(node, rel); } } else { //debugPlan<<"Add child proxy from (me)"<name(); m_childProxyRelations.append(new ProxyRelation(this, node, rel->type(), rel->lag())); } } } bool Task::isEndNode() const { return m_dependChildNodes.isEmpty() && m_childProxyRelations.isEmpty(); } bool Task::isStartNode() const { return m_dependParentNodes.isEmpty() && m_parentProxyRelations.isEmpty(); } DateTime Task::workTimeAfter(const DateTime &dt, Schedule *sch) const { DateTime t; if ( m_estimate->type() == Estimate::Type_Duration ) { if ( m_estimate->calendar() ) { t = m_estimate->calendar()->firstAvailableAfter( dt, projectNode()->constraintEndTime() ); } } else { t = m_requests.workTimeAfter(dt, sch); #ifndef PLAN_NLOGDEBUG - if ( sch ) sch->logDebug( QString( "workTimeAfter: %1 = %2" ).arg( dt.toString() ).arg( t.toString() ) ); + if ( sch ) sch->logDebug( QStringLiteral( "workTimeAfter: %1 = %2" ).arg( dt.toString() ).arg( t.toString() ) ); #endif } return t.isValid() ? t : dt; } DateTime Task::workTimeBefore(const DateTime &dt, Schedule *sch) const { DateTime t; if ( m_estimate->type() == Estimate::Type_Duration ) { if ( m_estimate->calendar() ) { t = m_estimate->calendar()->firstAvailableBefore( dt, projectNode()->constraintStartTime() ); } } else { t = m_requests.workTimeBefore(dt, sch); } return t.isValid() ? t : dt; } Duration Task::positiveFloat( long id ) const { Schedule *s = schedule( id ); return s == 0 ? Duration::zeroDuration : s->positiveFloat; } -void Task::setPositiveFloat( const Duration &fl, long id ) const +void Task::setPositiveFloat( KPlato::Duration fl, long id ) const { Schedule *s = schedule( id ); if ( s ) s->positiveFloat = fl; } Duration Task::negativeFloat( long id ) const { Schedule *s = schedule( id ); return s == 0 ? Duration::zeroDuration : s->negativeFloat; } -void Task::setNegativeFloat( const Duration &fl, long id ) const +void Task::setNegativeFloat( KPlato::Duration fl, long id ) const { Schedule *s = schedule( id ); if ( s ) s->negativeFloat = fl; } Duration Task::freeFloat( long id ) const { Schedule *s = schedule( id ); return s == 0 ? Duration::zeroDuration : s->freeFloat; } -void Task::setFreeFloat( const Duration &fl, long id ) const +void Task::setFreeFloat( KPlato::Duration fl, long id ) const { Schedule *s = schedule( id ); if ( s ) s->freeFloat = fl; } Duration Task::startFloat( long id ) const { Schedule *s = schedule( id ); return s == 0 || s->earlyStart > s->lateStart ? Duration::zeroDuration : ( s->earlyStart - s->lateStart ); } Duration Task::finishFloat( long id ) const { Schedule *s = schedule( id ); return s == 0 || s->lateFinish < s->earlyFinish ? Duration::zeroDuration : ( s->lateFinish - s->earlyFinish ); } bool Task::isCritical( long id ) const { Schedule *s = schedule( id ); return s == 0 ? false : s->isCritical(); } bool Task::calcCriticalPath(bool fromEnd) { if (m_currentSchedule == 0) return false; //debugPlan<child()->calcCriticalPath(fromEnd)) { m_currentSchedule->inCriticalPath = true; } } foreach (Relation *r, m_dependChildNodes) { if (r->child()->calcCriticalPath(fromEnd)) { m_currentSchedule->inCriticalPath = true; } } } else { if (isStartNode() && startFloat() == 0 && finishFloat() == 0) { m_currentSchedule->inCriticalPath = true; //debugPlan<parent()->calcCriticalPath(fromEnd)) { m_currentSchedule->inCriticalPath = true; } } foreach (Relation *r, m_dependParentNodes) { if (r->parent()->calcCriticalPath(fromEnd)) { m_currentSchedule->inCriticalPath = true; } } } //debugPlan<freeFloat.toString(); } } void Task::setCurrentSchedule(long id) { setCurrentSchedulePtr(findSchedule(id)); Node::setCurrentSchedule(id); } bool Task::effortMetError( long id ) const { Schedule *s = schedule( id ); if (s == 0 || s->notScheduled || m_estimate->type() != Estimate::Type_Effort) { return false; } return s->effortNotMet; } uint Task::state( long id ) const { int st = Node::State_None; if ( ! isScheduled( id ) ) { st |= State_NotScheduled; } if ( completion().isFinished() ) { st |= Node::State_Finished; if ( completion().finishTime() > endTime( id ) ) { st |= State_FinishedLate; } if ( completion().finishTime() < endTime( id ) ) { st |= State_FinishedEarly; } } else if ( completion().isStarted() ) { st |= Node::State_Started; if ( completion().startTime() > startTime( id ) ) { st |= State_StartedLate; } if ( completion().startTime() < startTime( id ) ) { st |= State_StartedEarly; } if ( completion().percentFinished() > 0 ) { st |= State_Running; } if ( endTime( id ) < QDateTime::currentDateTime() ) { st |= State_Late; } } else if ( isScheduled( id ) ) { if ( startTime( id ) < QDateTime::currentDateTime() ) { st |= State_Late; } } st |= State_ReadyToStart; //TODO: check proxy relations foreach ( const Relation *r, m_dependParentNodes ) { if ( ! static_cast( r->parent() )->completion().isFinished() ) { st &= ~Node::State_ReadyToStart; st |= Node::State_NotReadyToStart; break; } } return st; } void Task::addWorkPackage( WorkPackage *wp ) { emit workPackageToBeAdded( this, m_packageLog.count() ); m_packageLog.append( wp ); emit workPackageAdded( this ); } void Task::removeWorkPackage( WorkPackage *wp ) { int index = m_packageLog.indexOf( wp ); if ( index < 0 ) { return; } emit workPackageToBeRemoved( this, index ); m_packageLog.removeAt( index ); emit workPackageRemoved( this ); } WorkPackage *Task::workPackageAt( int index ) const { Q_ASSERT ( index >= 0 && index < m_packageLog.count() ); return m_packageLog.at( index ); } QString Task::wpOwnerName() const { if ( m_packageLog.isEmpty() ) { return m_workPackage.ownerName(); } return m_packageLog.last()->ownerName(); } WorkPackage::WPTransmitionStatus Task::wpTransmitionStatus() const { if ( m_packageLog.isEmpty() ) { return m_workPackage.transmitionStatus(); } return m_packageLog.last()->transmitionStatus(); } DateTime Task::wpTransmitionTime() const { if ( m_packageLog.isEmpty() ) { return m_workPackage.transmitionTime(); } return m_packageLog.last()->transmitionTime(); } //------------------------------------------ Completion::Completion( Node *node ) : m_node( node ), m_started( false ), m_finished( false ), m_entrymode( EnterCompleted ) {} Completion::Completion( const Completion &c ) { copy( c ); } Completion::~Completion() { qDeleteAll( m_entries ); qDeleteAll( m_usedEffort ); } void Completion::copy( const Completion &p ) { m_node = 0; //NOTE m_started = p.isStarted(); m_finished = p.isFinished(); m_startTime = p.startTime(); m_finishTime = p.finishTime(); m_entrymode = p.entrymode(); qDeleteAll( m_entries ); m_entries.clear(); Completion::EntryList::ConstIterator entriesIt = p.entries().constBegin(); const Completion::EntryList::ConstIterator entriesEnd = p.entries().constEnd(); for (; entriesIt != entriesEnd; ++entriesIt) { addEntry( entriesIt.key(), new Entry( *entriesIt.value() ) ); } qDeleteAll( m_usedEffort ); m_usedEffort.clear(); Completion::ResourceUsedEffortMap::ConstIterator usedEffortMapIt = p.usedEffortMap().constBegin(); const Completion::ResourceUsedEffortMap::ConstIterator usedEffortMapEnd = p.usedEffortMap().constEnd(); for (; usedEffortMapIt != usedEffortMapEnd; ++usedEffortMapIt) { addUsedEffort( usedEffortMapIt.key(), new UsedEffort( *usedEffortMapIt.value() ) ); } } bool Completion::operator==( const Completion &p ) { return m_started == p.isStarted() && m_finished == p.isFinished() && m_startTime == p.startTime() && m_finishTime == p.finishTime() && m_entries == p.entries() && m_usedEffort == p.usedEffortMap(); } Completion &Completion::operator=( const Completion &p ) { copy( p ); return *this; } void Completion::changed( int property) { if ( m_node ) { m_node->changed(property); } } void Completion::setStarted( bool on ) { m_started = on; changed(Node::CompletionStarted); } void Completion::setFinished( bool on ) { m_finished = on; changed(Node::CompletionFinished); } void Completion::setStartTime( const DateTime &dt ) { m_startTime = dt; changed(Node::CompletionStartTime); } void Completion::setFinishTime( const DateTime &dt ) { m_finishTime = dt; changed(Node::CompletionFinishTime); } -void Completion::setPercentFinished( const QDate &date, int value ) +void Completion::setPercentFinished( QDate date, int value ) { Entry *e = 0; if ( m_entries.contains( date ) ) { e = m_entries[ date ]; } else { e = new Entry(); m_entries[ date ] = e; } e->percentFinished = value; changed(Node::CompletionPercentage); } -void Completion::setRemainingEffort( const QDate &date, const Duration &value ) +void Completion::setRemainingEffort( QDate date, KPlato::Duration value ) { Entry *e = 0; if ( m_entries.contains( date ) ) { e = m_entries[ date ]; } else { e = new Entry(); m_entries[ date ] = e; } e->remainingEffort = value; changed(Node::CompletionRemainingEffort); } -void Completion::setActualEffort( const QDate &date, const Duration &value ) +void Completion::setActualEffort( QDate date, KPlato::Duration value ) { Entry *e = 0; if ( m_entries.contains( date ) ) { e = m_entries[ date ]; } else { e = new Entry(); m_entries[ date ] = e; } e->totalPerformed = value; changed(Node::CompletionActualEffort); } -void Completion::addEntry( const QDate &date, Entry *entry ) +void Completion::addEntry( QDate date, Entry *entry ) { m_entries.insert( date, entry ); //debugPlan<percentFinished; } -int Completion::percentFinished( const QDate &date ) const +int Completion::percentFinished( QDate date ) const { int x = 0; - foreach ( const QDate &d, m_entries.keys() ) { + foreach ( QDate d, m_entries.keys() ) { if ( d <= date ) { x = m_entries[ d ]->percentFinished; } if ( d >= date ) { break; } } return x; } Duration Completion::remainingEffort() const { return m_entries.isEmpty() ? Duration::zeroDuration : m_entries.values().last()->remainingEffort; } -Duration Completion::remainingEffort( const QDate &date ) const +Duration Completion::remainingEffort( QDate date ) const { Duration x; foreach ( const QDate &d, m_entries.keys() ) { if ( d <= date ) { x = m_entries[ d ]->remainingEffort; } if ( d >= date ) { break; } } return x; } Duration Completion::actualEffort() const { Duration eff; if ( m_entrymode == EnterEffortPerResource ) { foreach( const UsedEffort *ue, m_usedEffort ) { foreach ( const QDate &d, ue->actualEffortMap().keys() ) { eff += ue->actualEffortMap()[ d ].effort(); } } } else if ( ! m_entries.isEmpty() ) { eff = m_entries.values().last()->totalPerformed; } return eff; } -Duration Completion::actualEffort( const Resource *resource, const QDate &date ) const +Duration Completion::actualEffort( const Resource *resource, QDate date ) const { UsedEffort *ue = usedEffort( resource ); if ( ue == 0 ) { return Duration::zeroDuration; } UsedEffort::ActualEffort ae = ue->effort( date ); return ae.effort(); } -Duration Completion::actualEffort( const QDate &date ) const +Duration Completion::actualEffort( QDate date ) const { Duration eff; if ( m_entrymode == EnterEffortPerResource ) { foreach( const UsedEffort *ue, m_usedEffort ) { if ( ue && ue->actualEffortMap().contains( date ) ) { eff += ue->actualEffortMap().value( date ).effort(); } } } else { // Hmmm: How to really knowon a specific date? if ( m_entries.contains( date ) ) { eff = m_entries[ date ]->totalPerformed; } } return eff; } -Duration Completion::actualEffortTo( const QDate &date ) const +Duration Completion::actualEffortTo( QDate date ) const { //debugPlan<effortTo( date ); } } else { QListIterator it( m_entries.uniqueKeys() ); it.toBack(); while ( it.hasPrevious() ) { QDate d = it.previous(); if ( d <= date ) { eff = m_entries[ d ]->totalPerformed; break; } } } return eff; } -double Completion::averageCostPrHour( const QDate &date, long id ) const +double Completion::averageCostPrHour( QDate date, long id ) const { Schedule *s = m_node->schedule( id ); if ( s == 0 ) { return 0.0; } double cost = 0.0; double eff = 0.0; QList cl; foreach ( const Appointment *a, s->appointments() ) { cl << a->resource()->resource()->normalRate(); double e = a->plannedEffort( date ).toDouble( Duration::Unit_h ); if ( e > 0.0 ) { eff += e; cost += e * cl.last(); } } if ( eff > 0.0 ) { cost /= eff; } else { foreach ( double c, cl ) { cost += c; } cost /= cl.count(); } return cost; } -EffortCostMap Completion::effortCostPrDay(const QDate &start, const QDate &end, long id ) const +EffortCostMap Completion::effortCostPrDay(QDate start, QDate end, long id ) const { //debugPlan<name()< et ) { break; } Duration e = m_entries[ d ]->totalPerformed; if ( e != Duration::zeroDuration && e != last ) { Duration eff = e - last; ec.insert( d, eff, eff.toDouble( Duration::Unit_h ) * averageCostPrHour( d, id ) ); last = e; } } break; } case EnterEffortPerResource: { QPair dates = actualStartEndDates(); if ( ! dates.first.isValid() ) { // no data, so just break break; } QDate st = start.isValid() ? start : dates.first; QDate et = end.isValid() ? end : dates.second; for ( QDate d = st; d <= et; d = d.addDays( 1 ) ) { ec.add( d, actualEffort( d ), actualCost( d ) ); } break; } } return ec; } -EffortCostMap Completion::effortCostPrDay(const Resource *resource, const QDate &start, const QDate &end, long id ) const +EffortCostMap Completion::effortCostPrDay(const Resource *resource, QDate start, QDate end, long id ) const { Q_UNUSED(id); //debugPlan<name()< dates = actualStartEndDates(); if ( ! dates.first.isValid() ) { // no data, so just break break; } QDate st = start.isValid() ? start : dates.first; QDate et = end.isValid() ? end : dates.second; for ( QDate d = st; d <= et; d = d.addDays( 1 ) ) { ec.add( d, actualEffort( resource, d ), actualCost( resource, d ) ); } break; } } return ec; } void Completion::addUsedEffort( const Resource *resource, Completion::UsedEffort *value ) { UsedEffort *v = value == 0 ? new UsedEffort() : value; if ( m_usedEffort.contains( resource ) ) { m_usedEffort[ resource ]->mergeEffort( *v ); delete v; } else { m_usedEffort.insert( resource, v ); } changed(Node::CompletionUsedEffort); } QString Completion::note() const { return m_entries.isEmpty() ? QString() : m_entries.values().last()->note; } void Completion::setNote( const QString &str ) { if ( ! m_entries.isEmpty() ) { m_entries.values().last()->note = str; changed(); } } QPair Completion::actualStartEndDates() const { QPair p; foreach ( const Resource *r, m_usedEffort.keys() ) { if ( ! m_usedEffort[ r ]->actualEffortMap().isEmpty() ) { QDate d = m_usedEffort[ r ]->actualEffortMap().keys().first(); if ( ! p.first.isValid() || d < p.first ) p.first = d; d = m_usedEffort[ r ]->actualEffortMap().keys().last(); if ( ! p.second.isValid() || d > p.second ) p.second = d; } } return p; } -double Completion::actualCost( const QDate &date ) const +double Completion::actualCost( QDate date ) const { //debugPlan<normalRate(); double oc = r->overtimeRate(); if ( m_usedEffort[ r ]->actualEffortMap().contains( date ) ) { UsedEffort::ActualEffort a = m_usedEffort[ r ]->effort( date ); c += a.normalEffort().toDouble( Duration::Unit_h ) * nc; c += a.overtimeEffort().toDouble( Duration::Unit_h ) * oc; } } return c; } double Completion::actualCost( const Resource *resource ) const { UsedEffort *ue = usedEffort( resource ); if ( ue == 0 ) { return 0.0; } double c = 0.0; double nc = resource->normalRate(); double oc = resource->overtimeRate(); foreach ( const UsedEffort::ActualEffort &a, ue->actualEffortMap() ) { c += a.normalEffort().toDouble( Duration::Unit_h ) * nc; c += a.overtimeEffort().toDouble( Duration::Unit_h ) * oc; } return c; } double Completion::actualCost() const { double c = 0.0; foreach ( const Resource *r, m_usedEffort.keys() ) { c += actualCost( r ); } return c; } -double Completion::actualCost( const Resource *resource, const QDate &date ) const +double Completion::actualCost( const Resource *resource, QDate date ) const { UsedEffort *ue = usedEffort( resource ); if ( ue == 0 ) { return 0.0; } UsedEffort::ActualEffort a = ue->actualEffortMap().value( date ); double c = a.normalEffort().toDouble( Duration::Unit_h ) * resource->normalRate(); c += a.overtimeEffort().toDouble( Duration::Unit_h ) * resource->overtimeRate(); return c; } EffortCostMap Completion::actualEffortCost( long int id, KPlato::EffortCostCalculationType type ) const { //debugPlan; EffortCostMap map; if ( ! isStarted() ) { return map; } QList< QMap > lst; QList< double > rate; QDate start, end; foreach ( const Resource *r, m_usedEffort.keys() ) { //debugPlan<name()<name(); lst << usedEffort( r )->actualEffortMap(); if ( lst.last().isEmpty() ) { lst.takeLast(); continue; } if ( r->type() == Resource::Type_Material ) { if ( type == ECCT_All ) { rate.append( r->normalRate() ); } else if ( type == ECCT_EffortWork ) { rate.append( 0.0 ); } else { lst.takeLast(); continue; } } else { rate.append( r->normalRate() ); } if ( ! start.isValid() || start > lst.last().keys().first() ) { start = lst.last().keys().first(); } if ( ! end.isValid() || end < lst.last().keys().last() ) { end = lst.last().keys().last(); } } if ( ! lst.isEmpty() && start.isValid() && end.isValid() ) { for ( QDate d = start; d <= end; d = d.addDays( 1 ) ) { EffortCost c; for ( int i = 0; i < lst.count(); ++i ) { UsedEffort::ActualEffort a = lst.at( i ).value( d ); double nc = rate.value( i ); Duration eff = a.normalEffort(); double cost = eff.toDouble( Duration::Unit_h ) * nc; c.add( eff, cost ); } if ( c.effort() != Duration::zeroDuration || c.cost() != 0.0 ) { map.add( d, c ); } } } else if ( ! m_entries.isEmpty() ) { QDate st = start.isValid() ? start : m_startTime.date(); QDate et = end.isValid() ? end : m_finishTime.date(); Duration last; foreach ( const QDate &d, m_entries.uniqueKeys() ) { if ( d < st ) { continue; } Duration e = m_entries[ d ]->totalPerformed; if ( e != Duration::zeroDuration && e != last ) { //debugPlan<name()< et ) { break; } } } return map; } -EffortCost Completion::actualCostTo( long int id, const QDate &date ) const +EffortCost Completion::actualCostTo( long int id, QDate date ) const { //debugPlan<( m ); } QString Completion::entryModeToString() const { return entrymodeList().value( m_entrymode ); } bool Completion::loadXML( KoXmlElement &element, XMLLoaderObject &status ) { //debugPlan; QString s; - m_started = (bool)element.attribute("started", "0").toInt(); - m_finished = (bool)element.attribute("finished", "0").toInt(); - s = element.attribute("startTime"); + m_started = (bool)element.attribute(QStringLiteral("started"), QStringLiteral("0")).toInt(); + m_finished = (bool)element.attribute(QStringLiteral("finished"), QStringLiteral("0")).toInt(); + s = element.attribute(QStringLiteral("startTime")); if (!s.isEmpty()) { m_startTime = DateTime::fromString(s, status.projectTimeZone()); } - s = element.attribute("finishTime"); + s = element.attribute(QStringLiteral("finishTime")); if (!s.isEmpty()) { m_finishTime = DateTime::fromString(s, status.projectTimeZone()); } - setEntrymode( element.attribute( "entrymode" ) ); - if (status.version() < "0.6") { + setEntrymode( element.attribute( QStringLiteral("entrymode") ) ); + if (status.version() < QLatin1String("0.6")) { if ( m_started ) { - Entry *entry = new Entry( element.attribute("percent-finished", "0").toInt(), Duration::fromString(element.attribute("remaining-effort")), Duration::fromString(element.attribute("performed-effort")) ); - entry->note = element.attribute("note"); + Entry *entry = new Entry( element.attribute(QStringLiteral("percent-finished"), QStringLiteral("0")).toInt(), Duration::fromString(element.attribute(QStringLiteral("remaining-effort"))), Duration::fromString(element.attribute(QStringLiteral("performed-effort"))) ); + entry->note = element.attribute(QStringLiteral("note")); QDate date = m_startTime.date(); if ( m_finished ) { date = m_finishTime.date(); } // almost the best we can do ;) addEntry( date, entry ); } } else { KoXmlElement e; forEachElement(e, element) { - if (e.tagName() == "completion-entry") { + if (e.tagName() == QLatin1String("completion-entry")) { QDate date; - s = e.attribute("date"); + s = e.attribute(QStringLiteral("date")); if ( !s.isEmpty() ) { date = QDate::fromString( s, Qt::ISODate ); } if ( !date.isValid() ) { warnPlan<<"Invalid date: "<percentFinished ); - elm.setAttribute( "remaining-effort", e->remainingEffort.toString() ); - elm.setAttribute( "performed-effort", e->totalPerformed.toString() ); - elm.setAttribute( "note", e->note ); + elm.setAttribute( QStringLiteral("date"), date.toString( Qt::ISODate ) ); + elm.setAttribute( QStringLiteral("percent-finished"), e->percentFinished ); + elm.setAttribute( QStringLiteral("remaining-effort"), e->remainingEffort.toString() ); + elm.setAttribute( QStringLiteral("performed-effort"), e->totalPerformed.toString() ); + elm.setAttribute( QStringLiteral("note"), e->note ); } if ( ! m_usedEffort.isEmpty() ) { - QDomElement elm = el.ownerDocument().createElement("used-effort"); + QDomElement elm = el.ownerDocument().createElement(QStringLiteral("used-effort")); el.appendChild(elm); ResourceUsedEffortMap::ConstIterator i = m_usedEffort.constBegin(); for ( ; i != m_usedEffort.constEnd(); ++i ) { if ( i.value() == 0 ) { continue; } - QDomElement e = elm.ownerDocument().createElement("resource"); + QDomElement e = elm.ownerDocument().createElement(QStringLiteral("resource")); elm.appendChild(e); - e.setAttribute( "id", i.key()->id() ); + e.setAttribute( QStringLiteral("id"), i.key()->id() ); i.value()->saveXML( e ); } } } //-------------- Completion::UsedEffort::UsedEffort() { } Completion::UsedEffort::UsedEffort( const UsedEffort &e ) { mergeEffort( e ); } Completion::UsedEffort::~UsedEffort() { } void Completion::UsedEffort::mergeEffort( const Completion::UsedEffort &value ) { foreach ( const QDate &d, value.actualEffortMap().keys() ) { setEffort( d, value.actualEffortMap()[ d ] ); } } -void Completion::UsedEffort::setEffort( const QDate &date, const ActualEffort &value ) +void Completion::UsedEffort::setEffort( QDate date, const ActualEffort &value ) { m_actual.insert( date, value ); } -Duration Completion::UsedEffort::effortTo( const QDate &date ) const +Duration Completion::UsedEffort::effortTo( QDate date ) const { Duration eff; foreach ( const QDate &d, m_actual.keys() ) { if ( d > date ) { break; } eff += m_actual[ d ].effort(); } return eff; } Duration Completion::UsedEffort::effort() const { Duration eff; foreach ( const ActualEffort &e, m_actual ) { eff += e.effort(); } return eff; } bool Completion::UsedEffort::operator==( const Completion::UsedEffort &e ) const { return m_actual == e.actualEffortMap(); } bool Completion::UsedEffort::loadXML(KoXmlElement &element, XMLLoaderObject & ) { //debugPlan; KoXmlElement e; forEachElement(e, element) { - if (e.tagName() == "actual-effort") { - QDate date = QDate::fromString( e.attribute("date"), Qt::ISODate ); + if (e.tagName() == QLatin1String("actual-effort")) { + QDate date = QDate::fromString( e.attribute(QStringLiteral("date")), Qt::ISODate ); if ( date.isValid() ) { ActualEffort a; - a.setNormalEffort( Duration::fromString( e.attribute( "normal-effort" ) ) ); - a.setOvertimeEffort( Duration::fromString( e.attribute( "overtime-effort" ) ) ); + a.setNormalEffort( Duration::fromString( e.attribute( QStringLiteral("normal-effort") ) ) ); + a.setOvertimeEffort( Duration::fromString( e.attribute( QStringLiteral("overtime-effort") ) ) ); setEffort( date, a ); } } } return true; } void Completion::UsedEffort::saveXML(QDomElement &element ) const { if ( m_actual.isEmpty() ) { return; } DateUsedEffortMap::ConstIterator i = m_actual.constBegin(); for ( ; i != m_actual.constEnd(); ++i ) { - QDomElement el = element.ownerDocument().createElement("actual-effort"); + QDomElement el = element.ownerDocument().createElement(QStringLiteral("actual-effort")); element.appendChild( el ); - el.setAttribute( "overtime-effort", i.value().overtimeEffort().toString() ); - el.setAttribute( "normal-effort", i.value().normalEffort().toString() ); - el.setAttribute( "date", i.key().toString( Qt::ISODate ) ); + el.setAttribute( QStringLiteral("overtime-effort"), i.value().overtimeEffort().toString() ); + el.setAttribute( QStringLiteral("normal-effort"), i.value().normalEffort().toString() ); + el.setAttribute( QStringLiteral("date"), i.key().toString( Qt::ISODate ) ); } } //---------------------------------- WorkPackage::WorkPackage( Task *task ) : m_task( task ), m_manager( 0 ), m_transmitionStatus( TS_None ) { m_completion.setNode( task ); } WorkPackage::WorkPackage( const WorkPackage &wp ) : m_task( 0 ), m_manager( 0 ), m_completion( wp.m_completion ), m_ownerName( wp.m_ownerName ), m_ownerId( wp.m_ownerId ), m_transmitionStatus( wp.m_transmitionStatus ), m_transmitionTime( wp.m_transmitionTime ) { } WorkPackage::~WorkPackage() { } bool WorkPackage::loadXML(KoXmlElement &element, XMLLoaderObject &status ) { Q_UNUSED(status); - m_ownerName = element.attribute( "owner" ); - m_ownerId = element.attribute( "owner-id" ); + m_ownerName = element.attribute( QStringLiteral("owner") ); + m_ownerId = element.attribute( QStringLiteral("owner-id") ); return true; } void WorkPackage::saveXML(QDomElement &element) const { - QDomElement el = element.ownerDocument().createElement("workpackage"); + QDomElement el = element.ownerDocument().createElement(QStringLiteral("workpackage")); element.appendChild(el); - el.setAttribute( "owner", m_ownerName ); - el.setAttribute( "owner-id", m_ownerId ); + el.setAttribute( QStringLiteral("owner"), m_ownerName ); + el.setAttribute( QStringLiteral("owner-id"), m_ownerId ); } bool WorkPackage::loadLoggedXML(KoXmlElement &element, XMLLoaderObject &status ) { - m_ownerName = element.attribute( "owner" ); - m_ownerId = element.attribute( "owner-id" ); - m_transmitionStatus = transmitionStatusFromString( element.attribute( "status" ) ); - m_transmitionTime = DateTime( QDateTime::fromString( element.attribute( "time" ), Qt::ISODate ) ); + m_ownerName = element.attribute( QStringLiteral("owner") ); + m_ownerId = element.attribute( QStringLiteral("owner-id") ); + m_transmitionStatus = transmitionStatusFromString( element.attribute( QStringLiteral("status") ) ); + m_transmitionTime = DateTime( QDateTime::fromString( element.attribute( QStringLiteral("time") ), Qt::ISODate ) ); return m_completion.loadXML( element, status ); } void WorkPackage::saveLoggedXML(QDomElement &element) const { - QDomElement el = element.ownerDocument().createElement("workpackage"); + QDomElement el = element.ownerDocument().createElement(QStringLiteral("workpackage")); element.appendChild(el); - el.setAttribute( "owner", m_ownerName ); - el.setAttribute( "owner-id", m_ownerId ); - el.setAttribute( "status", transmitionStatusToString( m_transmitionStatus ) ); - el.setAttribute( "time", m_transmitionTime.toString( Qt::ISODate ) ); + el.setAttribute( QStringLiteral("owner"), m_ownerName ); + el.setAttribute( QStringLiteral("owner-id"), m_ownerId ); + el.setAttribute( QStringLiteral("status"), transmitionStatusToString( m_transmitionStatus ) ); + el.setAttribute( QStringLiteral("time"), m_transmitionTime.toString( Qt::ISODate ) ); m_completion.saveXML( el ); } QList WorkPackage::fetchResources() { return fetchResources( id() ); } QList WorkPackage::fetchResources( long id ) { //debugPlan< lst; if ( id == NOTSCHEDULED ) { if ( m_task ) { lst << m_task->requestedResources(); } } else { if ( m_task ) lst = m_task->assignedResources( id ); foreach ( const Resource *r, m_completion.resources() ) { if ( ! lst.contains( const_cast( r ) ) ) { lst << const_cast( r ); } } } return lst; } Completion &WorkPackage::completion() { return m_completion; } const Completion &WorkPackage::completion() const { return m_completion; } void WorkPackage::setScheduleManager( ScheduleManager *sm ) { m_manager = sm; } QString WorkPackage::transmitionStatusToString( WorkPackage::WPTransmitionStatus sts, bool trans ) { - QString s = trans ? i18n( "None" ) : "None"; + QString s = trans ? i18n( "None" ) : QStringLiteral("None"); switch ( sts ) { case TS_Send: - s = trans ? i18n( "Send" ) : "Send"; + s = trans ? i18n( "Send" ) : QStringLiteral("Send"); break; case TS_Receive: - s = trans ? i18n( "Receive" ) : "Receive"; + s = trans ? i18n( "Receive" ) : QStringLiteral("Receive"); break; default: break; } return s; } WorkPackage::WPTransmitionStatus WorkPackage::transmitionStatusFromString( const QString &sts ) { QStringList lst; - lst << "None" << "Send" << "Receive"; + lst << QStringLiteral("None") << QStringLiteral("Send") << QStringLiteral("Receive"); int s = lst.indexOf( sts ); return s < 0 ? TS_None : static_cast( s ); } void WorkPackage::clear() { //m_task = 0; m_manager = 0; m_ownerName.clear(); m_ownerId.clear(); m_transmitionStatus = TS_None; m_transmitionTime = DateTime(); m_log.clear(); m_completion = Completion(); m_completion.setNode( m_task ); } //-------------------------------- WorkPackageSettings::WorkPackageSettings() : usedEffort( true ), progress( false ), documents( true ) { } void WorkPackageSettings::saveXML( QDomElement &element ) const { - QDomElement el = element.ownerDocument().createElement("settings"); + QDomElement el = element.ownerDocument().createElement(QStringLiteral("settings")); element.appendChild( el ); - el.setAttribute( "used-effort", QString::number(usedEffort) ); - el.setAttribute( "progress", QString::number(progress) ); - el.setAttribute( "documents", QString::number(documents) ); + el.setAttribute( QStringLiteral("used-effort"), QString::number(usedEffort) ); + el.setAttribute( QStringLiteral("progress"), QString::number(progress) ); + el.setAttribute( QStringLiteral("documents"), QString::number(documents) ); } bool WorkPackageSettings::loadXML( const KoXmlElement &element ) { - usedEffort = (bool)element.attribute( "used-effort" ).toInt(); - progress = (bool)element.attribute( "progress" ).toInt(); - documents = (bool)element.attribute( "documents" ).toInt(); + usedEffort = (bool)element.attribute( QStringLiteral("used-effort") ).toInt(); + progress = (bool)element.attribute( QStringLiteral("progress") ).toInt(); + documents = (bool)element.attribute( QStringLiteral("documents") ).toInt(); return true; } -bool WorkPackageSettings::operator==( const WorkPackageSettings &s ) const +bool WorkPackageSettings::operator==( KPlato::WorkPackageSettings s ) const { return usedEffort == s.usedEffort && progress == s.progress && documents == s.documents; } -bool WorkPackageSettings::operator!=( const WorkPackageSettings &s ) const +bool WorkPackageSettings::operator!=( KPlato::WorkPackageSettings s ) const { return ! operator==( s ); } } //KPlato namespace #ifndef QT_NO_DEBUG_STREAM QDebug operator<<( QDebug dbg, const KPlato::Completion::UsedEffort::ActualEffort &ae ) { - dbg << QString( "%1" ).arg( ae.normalEffort().toDouble( KPlato::Duration::Unit_h ), 1 ); + dbg << QStringLiteral( "%1" ).arg( ae.normalEffort().toDouble( KPlato::Duration::Unit_h ), 1 ); return dbg; } #endif diff --git a/plan/libs/kernel/kpttask.h b/plan/libs/kernel/kpttask.h index 59aa8654105..b005746b30f 100644 --- a/plan/libs/kernel/kpttask.h +++ b/plan/libs/kernel/kpttask.h @@ -1,761 +1,761 @@ /* This file is part of the KDE project Copyright (C) 2001 Thomas Zander zander@kde.org Copyright (C) 2004 - 2007 Dag Andersen Copyright (C) 2007 Florian Piquemal Copyright (C) 2007 Alexis Ménard This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPTTASK_H #define KPTTASK_H #include "kplatokernel_export.h" #include "kptnode.h" #include "kptglobal.h" #include "kptdatetime.h" #include "kptduration.h" #include "kptresource.h" #include #include #include /// The main namespace. namespace KPlato { class Completion; /** * The Completion class holds information about the tasks progress. */ class KPLATOKERNEL_EXPORT Completion { public: class KPLATOKERNEL_EXPORT UsedEffort { public: class KPLATOKERNEL_EXPORT ActualEffort : public QPair { public: - explicit ActualEffort( const Duration &ne = Duration::zeroDuration, const Duration &oe = Duration::zeroDuration ) + explicit ActualEffort( KPlato::Duration ne = Duration::zeroDuration, KPlato::Duration oe = Duration::zeroDuration ) : QPair( ne, oe ) {} ActualEffort( const ActualEffort &e ) : QPair( e.first, e.second ) {} ~ActualEffort() {} Duration normalEffort() const { return first; } - void setNormalEffort( const Duration &e ) { first = e; } + void setNormalEffort( KPlato::Duration e ) { first = e; } Duration overtimeEffort() const { return second; } - void setOvertimeEffort( const Duration &e ) { second = e; } + void setOvertimeEffort( KPlato::Duration e ) { second = e; } /// Returns the sum of normalEffort + overtimeEffort Duration effort() const { return first + second; } - void setEffort( const Duration &ne, const Duration &oe = Duration::zeroDuration ) { first = ne; second = oe; } + void setEffort( KPlato::Duration ne, KPlato::Duration oe = Duration::zeroDuration ) { first = ne; second = oe; } }; UsedEffort(); UsedEffort( const UsedEffort &e ); ~UsedEffort(); bool operator==( const UsedEffort &e ) const; bool operator!=( const UsedEffort &e ) const { return !operator==( e ); } void mergeEffort( const UsedEffort &value ); - void setEffort( const QDate &date, const ActualEffort &value ); + void setEffort( QDate date, const ActualEffort &value ); /// Returns the total effort up to @p date - Duration effortTo( const QDate &date ) const; + Duration effortTo( QDate date ) const; /// Returns the total effort on @p date - ActualEffort effort( const QDate &date ) const { return m_actual.value( date ); } - ActualEffort takeEffort( const QDate &date ) { return m_actual.take( date ); } + ActualEffort effort( QDate date ) const { return m_actual.value( date ); } + ActualEffort takeEffort( QDate date ) { return m_actual.take( date ); } /// Returns the total effort for all registered dates Duration effort() const; QMap actualEffortMap() const { return m_actual; } /// Load from document bool loadXML(KoXmlElement &element, XMLLoaderObject &status ); /// Save to document void saveXML(QDomElement &element) const; - bool contains( const QDate &date ) const { return m_actual.contains( date ); } + bool contains( QDate date ) const { return m_actual.contains( date ); } private: QMap m_actual; }; typedef QMap DateUsedEffortMap; class KPLATOKERNEL_EXPORT Entry { public: Entry() : percentFinished( 0 ), remainingEffort( Duration::zeroDuration ), totalPerformed( Duration::zeroDuration ) {} Entry( int percent, Duration remaining, Duration performed ) : percentFinished( percent ), remainingEffort( remaining ), totalPerformed( performed ) {} Entry( const Entry &e ) { copy( e ); } bool operator==( const Entry &e ) const { return percentFinished == e.percentFinished && remainingEffort == e.remainingEffort && totalPerformed == e.totalPerformed && note == e.note; } bool operator!=( const Entry &e ) const { return ! operator==( e ); } Entry &operator=(const Entry &e ) { copy( e ); return *this; } int percentFinished; Duration remainingEffort; Duration totalPerformed; QString note; protected: void copy( const Entry &e ) { percentFinished = e.percentFinished; remainingEffort = e.remainingEffort; totalPerformed = e.totalPerformed; note = e.note; } }; typedef QMap EntryList; typedef QMap ResourceUsedEffortMap; explicit Completion( Node *node = 0 ); // review * or &, or at all? Completion( const Completion © ); virtual ~Completion(); bool operator==(const Completion &p); bool operator!=(Completion &p) { return !(*this == p); } Completion &operator=(const Completion &p); /// Load from document bool loadXML(KoXmlElement &element, XMLLoaderObject &status ); /// Save to document void saveXML(QDomElement &element) const; bool startIsValid() const { return m_started && m_startTime.isValid(); } bool isStarted() const { return m_started; } void setStarted( bool on ); bool finishIsValid() const { return m_finished && m_finishTime.isValid(); } bool isFinished() const { return m_finished; } void setFinished( bool on ); DateTime startTime() const { return m_startTime; } void setStartTime( const DateTime &dt ); DateTime finishTime() const { return m_finishTime; } void setFinishTime( const DateTime &dt ); - void setPercentFinished( const QDate &date, int value ); - void setRemainingEffort( const QDate &date, const Duration &value ); - void setActualEffort( const QDate &date, const Duration &value ); + void setPercentFinished( QDate date, int value ); + void setRemainingEffort( QDate date, Duration value ); + void setActualEffort( QDate date, Duration value ); /// Return a list of the resource that has done any work on this task QList resources() { return m_usedEffort.keys(); } const EntryList &entries() const { return m_entries; } - void addEntry( const QDate &date, Entry *entry ); - Entry *takeEntry( const QDate &date ) { return m_entries.take( date ); changed(); } - Entry *entry( const QDate &date ) const { return m_entries[ date ]; } + void addEntry( QDate date, Entry *entry ); + Entry *takeEntry( QDate date ) { return m_entries.take( date ); changed(); } + Entry *entry( QDate date ) const { return m_entries[ date ]; } /// Returns the date of the latest entry QDate entryDate() const; /// Returns the percentFinished of the latest entry int percentFinished() const; /// Returns the percentFinished on @p date - int percentFinished( const QDate &date ) const; + int percentFinished( QDate date ) const; /// Returns the estimated remaining effort Duration remainingEffort() const; /// Returns the estimated remaining effort on @p date - Duration remainingEffort( const QDate &date ) const; + Duration remainingEffort( QDate date ) const; /// Returns the total actual effort Duration actualEffort() const; /// Returns the total actual effort on @p date - Duration actualEffort( const QDate &date ) const; + Duration actualEffort( QDate date ) const; /// Returns the total actual effort upto and including @p date - Duration actualEffortTo( const QDate &date ) const; + Duration actualEffortTo( QDate date ) const; /// Returns the actual effort for @p resource on @p date - Duration actualEffort( const Resource *resource, const QDate &date ) const; + Duration actualEffort( const Resource *resource, QDate date ) const; /// TODO QString note() const; /// TODO void setNote( const QString &str ); /// Returns the total actual cost double actualCost() const; /// Returns the actual cost for @p resource double actualCost( const Resource *resource ) const; /// Returns the actual cost on @p date - double actualCost( const QDate &date ) const; + double actualCost( QDate date ) const; /// Returns the total actual cost for @p resource on @p date - double actualCost( const Resource *resource, const QDate &date ) const; + double actualCost( const Resource *resource, QDate date ) const; /// Returns the total actual effort and cost upto and including @p date - EffortCost actualCostTo( long int id, const QDate &date ) const; + EffortCost actualCostTo( long int id, QDate date ) const; /** * Returns a map of all actual effort and cost entered */ virtual EffortCostMap actualEffortCost( long id, EffortCostCalculationType type = ECCT_All ) const; void addUsedEffort( const Resource *resource, UsedEffort *value = 0 ); UsedEffort *takeUsedEffort( const Resource *r ) { return m_usedEffort.take( const_cast( r ) ); changed(); } UsedEffort *usedEffort( const Resource *r ) const { return m_usedEffort.value( const_cast( r ) ); } const ResourceUsedEffortMap &usedEffortMap() const { return m_usedEffort; } void changed( int property = -1 ); Node *node() const { return m_node; } void setNode( Node *node ) { m_node = node; } enum Entrymode { FollowPlan, EnterCompleted, EnterEffortPerTask, EnterEffortPerResource }; void setEntrymode( Entrymode mode ) { m_entrymode = mode; } Entrymode entrymode() const { return m_entrymode; } void setEntrymode( const QString &mode ); QString entryModeToString() const; QStringList entrymodeList() const; - EffortCostMap effortCostPrDay(const QDate &start, const QDate &end, long id = -1 ) const; + EffortCostMap effortCostPrDay(QDate start, QDate end, long id = -1 ) const; /// Returns the actual effort and cost pr day used by @p resource - EffortCostMap effortCostPrDay(const Resource *resource, const QDate &start, const QDate &end, long id = CURRENTSCHEDULE ) const; + EffortCostMap effortCostPrDay(const Resource *resource, QDate start, QDate end, long id = CURRENTSCHEDULE ) const; protected: void copy( const Completion ©); - double averageCostPrHour( const QDate &date, long id ) const; + double averageCostPrHour( QDate date, long id ) const; QPair actualStartEndDates() const; private: Node *m_node; bool m_started, m_finished; DateTime m_startTime, m_finishTime; EntryList m_entries; ResourceUsedEffortMap m_usedEffort; Entrymode m_entrymode; #ifndef NDEBUG public: void printDebug( const QByteArray &ident ) const; #endif }; /** * The WorkPackage class controls work flow for a task */ class KPLATOKERNEL_EXPORT WorkPackage { public: /// @enum WPTransmitionStatus describes if this package was sent or received enum WPTransmitionStatus { TS_None, /// Not sent nor received TS_Send, /// Package was sent to resource TS_Receive /// Package was received from resource }; explicit WorkPackage( Task *task = 0 ); explicit WorkPackage( const WorkPackage &wp ); virtual ~WorkPackage(); Task *parentTask() const { return m_task; } void setParentTask( Task *task ) { m_task = task; } /// Returns the transmission status of this package WPTransmitionStatus transmitionStatus() const { return m_transmitionStatus; } void setTransmitionStatus( WPTransmitionStatus sts ) { m_transmitionStatus = sts; } static QString transmitionStatusToString( WPTransmitionStatus sts, bool trans = false ); static WPTransmitionStatus transmitionStatusFromString( const QString &sts ); /// Load from document virtual bool loadXML(KoXmlElement &element, XMLLoaderObject &status ); /// Save the full workpackage virtual void saveXML(QDomElement &element) const; /// Load from document virtual bool loadLoggedXML(KoXmlElement &element, XMLLoaderObject &status ); /// Save the full workpackage virtual void saveLoggedXML(QDomElement &element) const; /// Set schedule manager void setScheduleManager( ScheduleManager *sm ); /// Return schedule manager ScheduleManager *scheduleManager() const { return m_manager; } /// Return the schedule id, or NOTSCHEDULED if no schedule manager is set long id() const { return m_manager ? m_manager->scheduleId() : NOTSCHEDULED; } Completion &completion(); const Completion &completion() const; void addLogEntry( DateTime &dt, const QString &str ); QMap log() const; QStringList log(); /// Return a list of resources fetched from the appointements or requests /// merged with resources added to completion QList fetchResources(); /// Return a list of resources fetched from the appointements or requests /// merged with resources added to completion QList fetchResources( long id ); /// Returns id of the resource that owns this package. If empty, task leader owns it. QString ownerId() const { return m_ownerId; } /// Set the resource that owns this package to @p owner. If empty, task leader owns it. void setOwnerId( const QString &id ) { m_ownerId = id; } /// Returns the name of the resource that owns this package. QString ownerName() const { return m_ownerName; } /// Set the name of the resource that owns this package. void setOwnerName( const QString &name ) { m_ownerName = name; } DateTime transmitionTime() const { return m_transmitionTime; } void setTransmitionTime( const DateTime &dt ) { m_transmitionTime = dt; } /// Clear workpackage data void clear(); private: Task *m_task; ScheduleManager *m_manager; Completion m_completion; QString m_ownerName; QString m_ownerId; WPTransmitionStatus m_transmitionStatus; DateTime m_transmitionTime; QMap m_log; }; class KPLATOKERNEL_EXPORT WorkPackageSettings { public: WorkPackageSettings(); bool loadXML( const KoXmlElement &element ); void saveXML( QDomElement &element) const; - bool operator==( const WorkPackageSettings &settings ) const; - bool operator!=( const WorkPackageSettings &settings ) const; + bool operator==( WorkPackageSettings settings ) const; + bool operator!=( WorkPackageSettings settings ) const; bool usedEffort; bool progress; bool documents; }; /** * A task in the scheduling software is represented by this class. A task * can be anything from 'build house' to 'drill hole' It will always mean * an activity. */ class KPLATOKERNEL_EXPORT Task : public Node { Q_OBJECT public: explicit Task(Node *parent = 0); explicit Task(const Task &task, Node *parent = 0); ~Task(); /// Return task type. Can be Type_Task, Type_Summarytask ot Type_Milestone. virtual int type() const; /** * Instead of using the expected duration, generate a random value using * the Distribution of each Task. This can be used for Monte-Carlo * estimation of Project duration. */ Duration *getRandomDuration(); /** * Return the resource request made to group * (There should be only one) */ ResourceGroupRequest *resourceGroupRequest(const ResourceGroup *group) const; void clearResourceRequests(); void addRequest(ResourceGroup *group, int numResources); void addRequest(ResourceGroupRequest *request); void takeRequest(ResourceGroupRequest *request); void makeAppointments(); virtual QStringList requestNameList() const; virtual QList requestedResources() const; virtual bool containsRequest( const QString &/*identity*/ ) const; virtual ResourceRequest *resourceRequest( const QString &/*name*/ ) const; /// Return the list of resources assigned to this task virtual QStringList assignedNameList( long id = CURRENTSCHEDULE ) const; /** * Calculates if the assigned resource is overbooked * within the duration of this task */ void calcResourceOverbooked(); /// Load from document virtual bool load(KoXmlElement &element, XMLLoaderObject &status ); /// Save to document virtual void save(QDomElement &element) const; /// Save appointments for schedule with id virtual void saveAppointments(QDomElement &element, long id) const; /// Save a workpackage document with schedule identity @p id void saveWorkPackageXML( QDomElement &element, long id ) const; /** * Returns a list of planned effort and cost for this task * for the interval start, end inclusive */ - virtual EffortCostMap plannedEffortCostPrDay(const QDate &start, const QDate &end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; + virtual EffortCostMap plannedEffortCostPrDay(QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /** * Returns a list of planned effort and cost for the @p resource * for the interval @p start, @p end inclusive, useing schedule with identity @p id */ - virtual EffortCostMap plannedEffortCostPrDay(const Resource *resource, const QDate &start, const QDate &end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; + virtual EffortCostMap plannedEffortCostPrDay(const Resource *resource, QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the total planned effort for @p reosurce on this task (or subtasks) virtual Duration plannedEffort( const Resource *resource, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the total planned effort for this task (or subtasks) virtual Duration plannedEffort( long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the total planned effort for this task (or subtasks) on date - virtual Duration plannedEffort(const QDate &date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; + virtual Duration plannedEffort(QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the total planned effort for @p resource on this task (or subtasks) on date - virtual Duration plannedEffort( const Resource *resource, const QDate &date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; + virtual Duration plannedEffort( const Resource *resource, QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the planned effort up to and including date - virtual Duration plannedEffortTo(const QDate &date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; + virtual Duration plannedEffortTo(QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the planned effort for @p resource up to and including date - virtual Duration plannedEffortTo( const Resource *resource, const QDate &date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; + virtual Duration plannedEffortTo( const Resource *resource, QDate date, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the total actual effort for this task (or subtasks) virtual Duration actualEffort() const; /// Returns the total actual effort for this task (or subtasks) on date - virtual Duration actualEffort(const QDate &date ) const; + virtual Duration actualEffort(QDate date ) const; /// Returns the actual effort up to and including date - virtual Duration actualEffortTo(const QDate &date ) const; + virtual Duration actualEffortTo(QDate date ) const; /** * Returns the total planned cost for this task (or subtasks) */ virtual EffortCost plannedCost( long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Planned cost up to and including date - virtual double plannedCostTo(const QDate &/*date*/, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; + virtual double plannedCostTo(QDate /*date*/, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns actual effort and cost up to and including @p date - virtual EffortCost actualCostTo( long int id, const QDate &date ) const; + virtual EffortCost actualCostTo( long int id, QDate date ) const; /** * Returns a list of actual effort and cost for this task * for the interval start, end inclusive */ - virtual EffortCostMap actualEffortCostPrDay( const QDate &start, const QDate &end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; + virtual EffortCostMap actualEffortCostPrDay( QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the actual effort and cost pr day used by @p resource - virtual EffortCostMap actualEffortCostPrDay( const Resource *resource, const QDate &start, const QDate &end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; + virtual EffortCostMap actualEffortCostPrDay( const Resource *resource, QDate start, QDate end, long id = CURRENTSCHEDULE, EffortCostCalculationType = ECCT_All ) const; /// Returns the effort planned to be used to reach the actual percent finished - virtual Duration budgetedWorkPerformed( const QDate &date, long id = CURRENTSCHEDULE ) const; + virtual Duration budgetedWorkPerformed( QDate date, long id = CURRENTSCHEDULE ) const; /// Returns the cost planned to be used to reach the actual percent finished - virtual double budgetedCostPerformed( const QDate &date, long id = CURRENTSCHEDULE ) const; + virtual double budgetedCostPerformed( QDate date, long id = CURRENTSCHEDULE ) const; using Node::bcwsPrDay; /// Return map of Budgeted Cost of Work Scheduled pr day virtual EffortCostMap bcwsPrDay( long id = CURRENTSCHEDULE, EffortCostCalculationType type = ECCT_All ); /// Budgeted Cost of Work Scheduled - virtual double bcws( const QDate &date, long id = CURRENTSCHEDULE ) const; + virtual double bcws( QDate date, long id = CURRENTSCHEDULE ) const; using Node::bcwpPrDay; /// Return map of Budgeted Cost of Work Performed pr day (also includes bcwsPrDay) virtual EffortCostMap bcwpPrDay( long id = CURRENTSCHEDULE, EffortCostCalculationType type = ECCT_All ); /// Budgeted Cost of Work Performed virtual double bcwp( long id = CURRENTSCHEDULE ) const; /// Budgeted Cost of Work Performed ( up to @p date ) - virtual double bcwp( const QDate &date, long id = CURRENTSCHEDULE ) const; + virtual double bcwp( QDate date, long id = CURRENTSCHEDULE ) const; using Node::acwp; /// Map of Actual Cost of Work Performed virtual EffortCostMap acwp( long id = CURRENTSCHEDULE, EffortCostCalculationType type = ECCT_All ); /// Actual Cost of Work Performed up to dat - virtual EffortCost acwp( const QDate &date, long id = CURRENTSCHEDULE ) const; + virtual EffortCost acwp( QDate date, long id = CURRENTSCHEDULE ) const; /// Effort based performance index - virtual double effortPerformanceIndex( const QDate &date, long id = CURRENTSCHEDULE ) const; + virtual double effortPerformanceIndex( QDate date, long id = CURRENTSCHEDULE ) const; /// Schedule performance index - virtual double schedulePerformanceIndex( const QDate &date, long id = CURRENTSCHEDULE ) const; + virtual double schedulePerformanceIndex( QDate date, long id = CURRENTSCHEDULE ) const; /// Cost performance index - virtual double costPerformanceIndex( long int id, const QDate &date, bool *error=0 ) const; + virtual double costPerformanceIndex( long int id, QDate date, bool *error=0 ) const; /** * Return the duration that an activity's start can be delayed * without affecting the project completion date. * An activity with positive float is not on the critical path. * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ Duration positiveFloat( long id = CURRENTSCHEDULE ) const; - void setPositiveFloat( const Duration &fl, long id = CURRENTSCHEDULE ) const; + void setPositiveFloat( Duration fl, long id = CURRENTSCHEDULE ) const; /** * Return the duration by which the duration of an activity or path * has to be reduced in order to fullfil a timing- or dependency constraint. * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ Duration negativeFloat( long id = CURRENTSCHEDULE ) const; - void setNegativeFloat( const Duration &fl, long id = CURRENTSCHEDULE ) const; + void setNegativeFloat( Duration fl, long id = CURRENTSCHEDULE ) const; /** * Return the duration by which an activity can be delayed or extended * without affecting the start of any succeeding activity. * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ Duration freeFloat( long id = CURRENTSCHEDULE ) const; - void setFreeFloat( const Duration &fl, long id = CURRENTSCHEDULE ) const; + void setFreeFloat( Duration fl, long id = CURRENTSCHEDULE ) const; /** * Return the duration from Early Start to Late Start. * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ Duration startFloat( long id = CURRENTSCHEDULE ) const; /** * Return the duration from Early Finish to Late Finish. * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ Duration finishFloat( long id = CURRENTSCHEDULE ) const; /** * A task is critical if positive float equals 0 * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ virtual bool isCritical( long id = CURRENTSCHEDULE ) const; /** * Set current schedule to schedule with identity id, for me and my children. * @param id Schedule identity */ virtual void setCurrentSchedule(long id); /** * The assigned resources can not fullfil the estimated effort. * @param id Schedule identity. If id is CURRENTSCHEDULE, use current schedule. */ virtual bool effortMetError( long id = CURRENTSCHEDULE ) const; Completion &completion() { return m_workPackage.completion(); } const Completion &completion() const { return m_workPackage.completion(); } WorkPackage &workPackage() { return m_workPackage; } const WorkPackage &workPackage() const { return m_workPackage; } int workPackageLogCount() const { return m_packageLog.count(); } QList workPackageLog() const { return m_packageLog; } void addWorkPackage( WorkPackage *wp ); void removeWorkPackage( WorkPackage *wp ); WorkPackage *workPackageAt( int index ) const; QString wpOwnerName() const; WorkPackage::WPTransmitionStatus wpTransmitionStatus() const; DateTime wpTransmitionTime() const; /** * Returns the state of the task * @param id The identity of the schedule used when calculating the state */ virtual uint state( long id = CURRENTSCHEDULE ) const; /// Check if this node has any dependent child nodes virtual bool isEndNode() const; /// Check if this node has any dependent parent nodes virtual bool isStartNode() const; QList parentProxyRelations() const { return m_parentProxyRelations; } QList childProxyRelations() const { return m_childProxyRelations; } /** * Calculates and returns the duration of the node. * Uses the correct expected-, optimistic- or pessimistic effort * dependent on @p use. * @param time Where to start calculation. * @param use Calculate using expected-, optimistic- or pessimistic estimate. * @param backward If true, time specifies when the task should end. */ virtual Duration duration(const DateTime &time, int use, bool backward); /** * Return the duration calculated on bases of the estimates calendar */ - Duration length(const DateTime &time, const Duration &duration, bool backward); - Duration length(const DateTime &time, const Duration &duration, Schedule *sch, bool backward); + Duration length(const DateTime &time, Duration duration, bool backward); + Duration length(const DateTime &time, Duration uration, Schedule *sch, bool backward); /// Copy info from parent schedule void copySchedule(); /// Copy intervals from parent schedule void copyAppointments(); /// Copy intervals from parent schedule in the range @p start, @p end void copyAppointments( const DateTime &start, const DateTime &end = DateTime() ); Q_SIGNALS: void workPackageToBeAdded( Node *node, int row ); void workPackageAdded( Node *node ); void workPackageToBeRemoved( Node *node, int row ); void workPackageRemoved( Node *node ); public: virtual void initiateCalculation(MainSchedule &sch); /** * Sets up the lists used for calculation. * This includes adding summarytasks relations to subtasks * and lists for start- and endnodes. */ virtual void initiateCalculationLists(MainSchedule &sch); /** * Calculates early start and early finish, first for all predeccessors, * then for this task. * @param use Calculate using expected-, optimistic- or pessimistic estimate. */ virtual DateTime calculateForward(int use); /** * Calculates ref m_durationForward from ref earliestStart and * returns the resulting end time (early finish), * which will be used as the successors ref earliestStart. * * @param use Calculate using expected-, optimistic- or pessimistic estimate. */ virtual DateTime calculateEarlyFinish(int use); /** * Calculates late start and late finish, first for all successors, * then for this task. * @param use Calculate using expected-, optimistic- or pessimistic estimate. */ virtual DateTime calculateBackward(int use); /** * Calculates ref m_durationBackward from ref latestFinish and * returns the resulting start time (late start), * which will be used as the predecessors ref latestFinish. * * @param use Calculate using expected-, optimistic- or pessimistic estimate. */ virtual DateTime calculateLateStart(int use); /** * Schedules the task within the limits of earliestStart and latestFinish. * Calculates ref m_startTime, ref m_endTime and ref m_duration, * Assumes ref calculateForward() and ref calculateBackward() has been run. * * @param earliest The task is not scheduled to start earlier than this * @param use Calculate using expected-, optimistic- or pessimistic estimate. * @return The tasks endtime which can be used for scheduling the successor. */ virtual DateTime scheduleForward(const DateTime &earliest, int use); /** * Schedules the task within the limits of start time and latestFinish, * Calculates end time and duration. * Assumes ref calculateForward() and ref calculateBackward() has been run. * * @param use Calculate using expected-, optimistic- or pessimistic estimate. * @return The tasks endtime which can be used for scheduling the successor. */ virtual DateTime scheduleFromStartTime(int use); /** * Schedules the task within the limits of earliestStart and latestFinish. * Calculates ref m_startTime, ref m_endTime and ref m_duration, * Assumes ref calculateForward() and ref calculateBackward() has been run. * * @param latest The task is not scheduled to end later than this * @param use Calculate using expected-, optimistic- or pessimistic estimate. * @return The tasks starttime which can be used for scheduling the predeccessor. */ virtual DateTime scheduleBackward(const DateTime &latest, int use); /** * Schedules the task within the limits of end time and latestFinish. * Calculates endTime and duration. * Assumes ref calculateForward() and ref calculateBackward() has been run. * * @param latest The task is not scheduled to end later than this * @param use Calculate using expected-, optimistic- or pessimistic estimate. * @return The tasks starttime which can be used for scheduling the predeccessor. */ virtual DateTime scheduleFromEndTime(int use); /** * Summarytasks (with milestones) need special treatment because * milestones are always 'glued' to their predecessors. */ virtual void adjustSummarytask(); /// Calculate the critical path virtual bool calcCriticalPath(bool fromEnd); virtual void calcFreeFloat(); // Proxy relations are relations to/from summarytasks. // These relations are distributed to the child tasks before calculation. virtual void clearProxyRelations(); virtual void addParentProxyRelations( const QList & ); virtual void addChildProxyRelations( const QList & ); virtual void addParentProxyRelation(Node *, const Relation *); virtual void addChildProxyRelation(Node *, const Relation *); public: DateTime earlyStartDate(); void setEarlyStartDate(DateTime value); DateTime earlyFinishDate(); void setEarlyFinishDate(DateTime value); DateTime lateStartDate(); void setLateStartDate(DateTime value); DateTime lateFinishDate(); void setLateFinishDate(DateTime value); int activitySlack(); void setActivitySlack(int value); int activityFreeMargin(); void setActivityFreeMargin(int value); protected: /** * Return the duration calculated on bases of the requested resources */ - Duration calcDuration(const DateTime &time, const Duration &effort, bool backward); + Duration calcDuration(const DateTime &time, Duration effort, bool backward); private: DateTime calculateSuccessors(const QList &list, int use); DateTime calculatePredeccessors(const QList &list, int use); DateTime scheduleSuccessors(const QList &list, int use); DateTime schedulePredeccessors(const QList &list, int use); /// Fixed duration: Returns @p dt /// Duration with calendar: Returns first available after @p dt /// Has working resource(s) allocated: Returns the earliest time a resource can start work after @p dt, and checks appointments if @p sch is not null. DateTime workTimeAfter(const DateTime &dt, Schedule *sch = 0) const; /// Fixed duration: Returns @p dt /// Duration with calendar: Returns first available before @p dt /// Has working resource(s) allocated: Returns the latest time a resource can finish work, and checks appointments if @p sch is not null. DateTime workTimeBefore(const DateTime &dt, Schedule *sch = 0) const; private: QList m_resource; QList m_parentProxyRelations; QList m_childProxyRelations; // This list store pointers to linked task QList m_requiredTasks; WorkPackage m_workPackage; QList m_packageLog; bool m_calculateForwardRun; bool m_calculateBackwardRun; bool m_scheduleForwardRun; bool m_scheduleBackwardRun; }; } //KPlato namespace Q_DECLARE_METATYPE( KPlato::Completion::UsedEffort::ActualEffort ) #ifndef QT_NO_DEBUG_STREAM KPLATOKERNEL_EXPORT QDebug operator<<( QDebug dbg, const KPlato::Completion::UsedEffort::ActualEffort &ae ); #endif #endif diff --git a/plan/libs/kernel/kptxmlloaderobject.h b/plan/libs/kernel/kptxmlloaderobject.h index b8770047cfb..8e7154cf68a 100644 --- a/plan/libs/kernel/kptxmlloaderobject.h +++ b/plan/libs/kernel/kptxmlloaderobject.h @@ -1,139 +1,139 @@ /* This file is part of the KDE project Copyright (C) 2006 - 2007 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; version 2 of the License. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef XMLLOADEROBJECT_H #define XMLLOADEROBJECT_H #include "kplatokernel_export.h" #include "kptproject.h" #include "kptdatetime.h" #include #include #include #include #include namespace KPlato { class KPLATOKERNEL_EXPORT XMLLoaderObject { public: enum Severity { None=0, Errors=1, Warnings=2, Diagnostics=3, Debug=4 }; XMLLoaderObject() : m_project(0), m_errors(0), m_warnings(0), m_logLevel(Diagnostics), m_log(), m_baseCalendar( 0 ) {} ~XMLLoaderObject() {} void setProject(Project *proj) { m_project = proj; } Project &project() const { return *m_project; } QString version() const { return m_version; } void setVersion( const QString &ver ) { m_version = ver; } QString workVersion() const { return m_workversion; } void setWorkVersion( const QString &ver ) { m_workversion = ver; } QString mimetype() const { return m_mimetype; } void setMimetype( const QString &mime ) { m_mimetype = mime; } const QTimeZone &projectTimeZone() const { return m_projectTimeZone; } void setProjectTimeZone( const QTimeZone &timeZone ) { m_projectTimeZone = timeZone; } void startLoad() { m_timer.start(); m_starttime = QDateTime::currentDateTime(); m_errors = m_warnings = 0; m_log.clear(); - addMsg(QString("Loading started at %1").arg(m_starttime.toString())); + addMsg(QStringLiteral("Loading started at %1").arg(m_starttime.toString())); } void stopLoad() { m_elapsed = m_timer.elapsed(); - addMsg(QString("Loading finished at %1, took %2").arg(QDateTime::currentDateTime().toString()).arg(formatElapsed())); + addMsg(QStringLiteral("Loading finished at %1, took %2").arg(QDateTime::currentDateTime().toString()).arg(formatElapsed())); } QDateTime lastLoaded() const { return m_starttime; } int elapsed() const { return m_elapsed; } - QString formatElapsed() { return QString("%1 seconds").arg((double)m_elapsed/1000); } + QString formatElapsed() { return QStringLiteral("%1 seconds").arg((double)m_elapsed/1000); } void setLogLevel(Severity sev) { m_logLevel = sev; } const QStringList &log() const { return m_log; } void error( const QString &msg ) { addMsg( Errors, msg ); } void warning( const QString &msg ) { addMsg( Errors, msg ); } void diagnostic( const QString &msg ) { addMsg( Diagnostics, msg ); } void debug( const QString &msg ) { addMsg( Debug, msg ); } void message( const QString &msg ) { addMsg( None, msg ); } void addMsg(int sev, const QString& msg) { increment(sev); if (m_logLevel < sev) return; QString s; - if (sev == Errors) s = "ERROR"; - else if (sev == Warnings) s = "WARNING"; - else if (sev == Diagnostics) s = "Diagnostic"; - else if (sev == Debug) s = "Debug"; - else s = "Message"; - m_log< 0; } void incWarnings() { ++m_warnings; } int warnings() const { return m_warnings; } bool warning() const { return m_warnings > 0; } // help to handle version < 0.6 void setBaseCalendar( Calendar *cal ) { m_baseCalendar = cal; } Calendar *baseCalendar() const { return m_baseCalendar; } void setUpdater( KoUpdater *updater ) { m_updater = updater; } void setProgress( int value ) { if ( m_updater ) m_updater->setProgress( value ); } protected: Project *m_project; int m_errors; int m_warnings; int m_logLevel; QStringList m_log; QDateTime m_starttime; QTime m_timer; int m_elapsed; QString m_version; QString m_workversion; QString m_mimetype; QTimeZone m_projectTimeZone; Calendar *m_baseCalendar; // help to handle version < 0.6 QPointer m_updater; }; } //namespace KPlato #endif diff --git a/plan/libs/models/CMakeLists.txt b/plan/libs/models/CMakeLists.txt index eed57ab4644..3e5066bfc37 100644 --- a/plan/libs/models/CMakeLists.txt +++ b/plan/libs/models/CMakeLists.txt @@ -1,71 +1,73 @@ -add_subdirectory( tests ) +if(BUILD_TESTING) + add_subdirectory( tests ) +endif() include_directories( ../kernel ${CMAKE_SOURCE_DIR}/libs/widgetutils ${KOODF_INCLUDES} ${KUNDO2_INCLUDES} ) if (KF5Contacts_FOUND) add_definitions(-DPLAN_KCONTACTS_FOUND) endif () ########### KPlato data models library ############### set(kplatomodels_LIB_SRCS reportgenerator/ReportGenerator.cpp reportgenerator/ReportGeneratorOdt.cpp kptcommonstrings.cpp kpttreecombobox.cpp kcalendar/kdatetable.cpp kcalendar/kdatepicker.cpp kptnodechartmodel.cpp kptflatproxymodel.cpp kptrelationmodel.cpp kptworkpackagemodel.cpp kptdocumentmodel.cpp kptitemmodelbase.cpp kptnodeitemmodel.cpp kptdurationspinbox.cpp kpttaskstatusmodel.cpp kptresourcemodel.cpp kptcalendarmodel.cpp kptschedulemodel.cpp kptaccountsmodel.cpp kptpertcpmmodel.cpp kptresourceappointmentsmodel.cpp kptresourceallocationmodel.cpp kpttaskcompletedelegate.cpp ) add_library(kplatomodels SHARED ${kplatomodels_LIB_SRCS}) generate_export_header(kplatomodels) target_link_libraries(kplatomodels PUBLIC kplatokernel koodf kundo2 KChart KF5::KIOWidgets PRIVATE KGantt KF5::Notifications KF5::TextWidgets ) if(KF5Contacts_FOUND) target_link_libraries(kplatomodels PRIVATE KF5::Contacts) endif() set_target_properties(kplatomodels PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) install(TARGETS kplatomodels ${INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/plan/libs/models/kptcalendarmodel.cpp b/plan/libs/models/kptcalendarmodel.cpp index 2ee9c3c4091..d4727f3dcb3 100644 --- a/plan/libs/models/kptcalendarmodel.cpp +++ b/plan/libs/models/kptcalendarmodel.cpp @@ -1,1467 +1,1474 @@ /* This file is part of the KDE project * Copyright (C) 2007, 2012 Dag Andersen * Copyright (C) 2017 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kptcalendarmodel.h" #include "kptglobal.h" #include "kptcommonstrings.h" #include "kptcommand.h" #include "kptitemmodelbase.h" #include "kptcalendar.h" #include "kptduration.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptdatetime.h" #include "kcalendar/kdatetable.h" #include "kptdebug.h" #include #include #include #include #include #ifdef HAVE_KHOLIDAYS #include #endif namespace KPlato { //----------------------------------------- CalendarDayItemModelBase::CalendarDayItemModelBase( QObject *parent ) : ItemModelBase( parent ), m_calendar( 0 ) { } CalendarDayItemModelBase::~CalendarDayItemModelBase() { } void CalendarDayItemModelBase::slotCalendarToBeRemoved( const Calendar *calendar ) { if ( calendar && calendar == m_calendar ) { setCalendar( 0 ); } } void CalendarDayItemModelBase::setCalendar( Calendar *calendar ) { m_calendar = calendar; } void CalendarDayItemModelBase::setProject( Project *project ) { setCalendar( 0 ); if ( m_project ) { disconnect(m_project, SIGNAL(aboutToBeDeleted()), this, SLOT(projectDeleted())); disconnect( m_project, SIGNAL(calendarToBeRemoved(const Calendar*)), this, SLOT(slotCalendarToBeRemoved(const Calendar*)) ); } m_project = project; if ( project ) { connect(m_project, SIGNAL(aboutToBeDeleted()), this, SLOT(projectDeleted())); connect( m_project, SIGNAL(calendarToBeRemoved(const Calendar*)), this, SLOT(slotCalendarToBeRemoved(const Calendar*)) ); } reset(); } //------------------------------------- CalendarItemModel::CalendarItemModel( QObject *parent ) : ItemModelBase( parent ), m_calendar( 0 ) { } CalendarItemModel::~CalendarItemModel() { } const QMetaEnum CalendarItemModel::columnMap() const { return metaObject()->enumerator( metaObject()->indexOfEnumerator("Properties") ); } void CalendarItemModel::slotCalendarToBeInserted( const Calendar *parent, int row ) { //debugPlan<<(parent?parent->name():"Top level")<<","<(parent); beginInsertRows( index( parent ), row, row ); } void CalendarItemModel::slotCalendarInserted( const Calendar *calendar ) { //debugPlan<name(); Q_ASSERT( calendar->parentCal() == m_calendar ); #ifdef NDEBUG Q_UNUSED(calendar) #endif endInsertRows(); m_calendar = 0; } void CalendarItemModel::slotCalendarToBeRemoved( const Calendar *calendar ) { //debugPlan<name(); int row = index( calendar ).row(); beginRemoveRows( index( calendar->parentCal() ), row, row ); } void CalendarItemModel::slotCalendarRemoved( const Calendar * ) { //debugPlan<name(); endRemoveRows(); } void CalendarItemModel::setProject( Project *project ) { if ( m_project ) { disconnect(m_project, SIGNAL(aboutToBeDeleted()), this, SLOT(projectDeleted())); disconnect( m_project , SIGNAL(calendarChanged(Calendar*)), this, SLOT(slotCalendarChanged(Calendar*)) ); disconnect( m_project, SIGNAL(calendarAdded(const Calendar*)), this, SLOT(slotCalendarInserted(const Calendar*)) ); disconnect( m_project, SIGNAL(calendarToBeAdded(const Calendar*,int)), this, SLOT(slotCalendarToBeInserted(const Calendar*,int)) ); disconnect( m_project, SIGNAL(calendarRemoved(const Calendar*)), this, SLOT(slotCalendarRemoved(const Calendar*)) ); disconnect( m_project, SIGNAL(calendarToBeRemoved(const Calendar*)), this, SLOT(slotCalendarToBeRemoved(const Calendar*)) ); } m_project = project; if ( project ) { connect(m_project, SIGNAL(aboutToBeDeleted()), this, SLOT(projectDeleted())); connect( m_project, SIGNAL(calendarChanged(Calendar*)), this, SLOT(slotCalendarChanged(Calendar*)) ); connect( m_project, SIGNAL(calendarAdded(const Calendar*)), this, SLOT(slotCalendarInserted(const Calendar*)) ); connect( m_project, SIGNAL(calendarToBeAdded(const Calendar*,int)), this, SLOT(slotCalendarToBeInserted(const Calendar*,int)) ); connect( m_project, SIGNAL(calendarRemoved(const Calendar*)), this, SLOT(slotCalendarRemoved(const Calendar*)) ); connect( m_project, SIGNAL(calendarToBeRemoved(const Calendar*)), this, SLOT(slotCalendarToBeRemoved(const Calendar*)) ); } reset(); } Qt::ItemFlags CalendarItemModel::flags( const QModelIndex &index ) const { Qt::ItemFlags flags = ItemModelBase::flags( index ); if ( !m_readWrite ) { return flags &= ~Qt::ItemIsEditable; } flags |= Qt::ItemIsDropEnabled; if ( !index.isValid() ) { return flags; } Calendar *c = calendar(index); if (!c || c->isShared()) { if (index.column() == Name) { flags |= Qt::ItemIsUserCheckable; } return flags; } flags |= Qt::ItemIsDragEnabled; if ( calendar ( index ) ) { switch ( index.column() ) { case Name: flags |= ( Qt::ItemIsEditable | Qt::ItemIsUserCheckable ); break; + case Scope: + flags &= ~Qt::ItemIsEditable; + break; case TimeZone: if ( parent( index ).isValid() ) { flags &= ~Qt::ItemIsEditable; } else { flags |= Qt::ItemIsEditable; } break; #ifdef HAVE_KHOLIDAYS case HolidayRegion: flags |= Qt::ItemIsEditable; break; #endif default: flags |= Qt::ItemIsEditable; break; } } return flags; } QModelIndex CalendarItemModel::parent( const QModelIndex &index ) const { if ( !index.isValid() || m_project == 0 ) { return QModelIndex(); } //debugPlan<parentCal(); if ( par ) { a = par->parentCal(); int row = -1; if ( a ) { row = a->indexOf( par ); } else { row = m_project->indexOf( par ); } //debugPlan<name()<<":"<= columnCount() || row < 0 ) { return QModelIndex(); } Calendar *par = calendar( parent ); if ( par == 0 ) { if ( row < m_project->calendars().count() ) { return createIndex( row, column, m_project->calendars().at( row ) ); } } else if ( row < par->calendars().count() ) { return createIndex( row, column, par->calendars().at( row ) ); } return QModelIndex(); } QModelIndex CalendarItemModel::index( const Calendar *calendar, int column ) const { if ( m_project == 0 || calendar == 0 ) { return QModelIndex(); } Calendar *a = const_cast(calendar); int row = -1; Calendar *par = a->parentCal(); if ( par == 0 ) { row = m_project->calendars().indexOf( a ); } else { row = par->indexOf( a ); } if ( row == -1 ) { return QModelIndex(); } return createIndex( row, column, a ); } int CalendarItemModel::columnCount( const QModelIndex & ) const { return columnMap().keyCount(); } int CalendarItemModel::rowCount( const QModelIndex &parent ) const { if ( m_project == 0 ) { return 0; } Calendar *par = calendar( parent ); if ( par == 0 ) { return m_project->calendars().count(); } return par->calendars().count(); } QVariant CalendarItemModel::name( const Calendar *a, int role ) const { //debugPlan<name()<<","<name(); case Qt::ToolTipRole: if ( a->isDefault() ) { return xi18nc( "1=calendar name", "%1 (Default calendar)", a->name() ); } return a->name(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Qt::CheckStateRole: return a->isDefault() ? Qt::Checked : Qt::Unchecked; } return QVariant(); } QVariant CalendarItemModel::scope( const Calendar *a, int role ) const { //debugPlan<name()<<","<isShared() ? i18n("Shared") : i18n("Local"); case Qt::EditRole: return a->isShared() ? "Shared" : "Local"; case Qt::ToolTipRole: if ( !a->isShared() ) { return xi18nc( "@info:tooltip 1=calendar name", "%1 is a Local calendar", a->name() ); } return xi18nc( "@info:tooltip 1=calendar name", "%1 is a Shared calendar", a->name() ); + case Role::EnumList: + return QStringList() << i18n("Shared") << i18n("Local"); + case Role::EnumListValue: + return a->isShared() ? 0 : 1; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } bool CalendarItemModel::setName( Calendar *a, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: if ( value.toString() != a->name() ) { emit executeCommand( new CalendarModifyNameCmd( a, value.toString(), kundo2_i18n( "Modify calendar name" ) ) ); return true; } break; case Qt::CheckStateRole: { switch ( value.toInt() ) { case Qt::Unchecked: if ( a->isDefault() ) { emit executeCommand( new ProjectModifyDefaultCalendarCmd( m_project, 0, kundo2_i18n( "De-select as default calendar" ) ) ); return true; } break; case Qt::Checked: if ( ! a->isDefault() ) { emit executeCommand( new ProjectModifyDefaultCalendarCmd( m_project, a, kundo2_i18n( "Select as default calendar" ) ) ); return true; } break; default: break; } } default: break; } return false; } QVariant CalendarItemModel::timeZone( const Calendar *a, int role ) const { //debugPlan<name()<<","<timeZone().id() ); case Role::EnumList: { QStringList lst; foreach ( const QByteArray &id, QTimeZone::availableTimeZoneIds() ) { lst << i18n( id ); } lst.sort(); return lst; } case Role::EnumListValue: { QStringList lst = timeZone( a, Role::EnumList ).toStringList(); return lst.indexOf( i18n ( a->timeZone().id() ) ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } bool CalendarItemModel::setTimeZone( Calendar *a, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { if ( timeZone( a, Role::EnumListValue ) == value.toInt() ) { return false; } QStringList lst = timeZone( a, Role::EnumList ).toStringList(); QString name = lst.value( value.toInt() ); QTimeZone tz; foreach ( const QByteArray &id, QTimeZone::availableTimeZoneIds() ) { if ( name == i18n( id ) ) { tz = QTimeZone( id ); break; } } if ( !tz.isValid() ) { return false; } emit executeCommand( new CalendarModifyTimeZoneCmd( a, tz, kundo2_i18n( "Modify calendar timezone" ) ) ); return true; } } return false; } #ifdef HAVE_KHOLIDAYS QVariant CalendarItemModel::holidayRegion( const Calendar *a, int role ) const { //debugPlan<name()<<","<holidayRegionCode().isEmpty() || !a->holidayRegion()->isValid()) { return i18n("None"); } if (a->holidayRegionCode() == "Default") { return i18n("Default"); } return a->holidayRegion()->name(); case Qt::EditRole: if (a->holidayRegionCode().isEmpty()) { return "None"; } return a->holidayRegionCode(); case Qt::ToolTipRole: if (!a->holidayRegion()->isValid()) { return xi18nc("@info:tooltip", "No holidays"); } else if (a->holidayRegionCode() == "Default") { return xi18nc("@info:tooltip", "Default region: %1", a->holidayRegion()->name()); } return a->holidayRegion()->description(); case Role::EnumList: { QStringList lst; lst << i18n("None") << i18n("Default"); for (const QString &code : a->holidayRegionCodes()) { lst << KHolidays::HolidayRegion::name(code); } return lst; } case Role::EnumListValue: { if (!a->holidayRegion()->isValid()) { return 0; // None } if (a->holidayRegionCode() == "Default") { return 1; } return a->holidayRegionCodes().indexOf(a->holidayRegionCode()) + 2; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } bool CalendarItemModel::setHolidayRegion( Calendar *a, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { QString code = "None"; if (value.toInt() == 1) { code = "Default"; } else if (value.toInt() > 1) { code = a->holidayRegionCodes().value(value.toInt() - 2); } if (a->holidayRegionCode() == code || (code == "None" && a->holidayRegionCode().isEmpty())) { return false; } emit executeCommand(new CalendarModifyHolidayRegionCmd(a, code, kundo2_i18n("Modify calendar holiday region"))); return true; } } return false; } #endif QVariant CalendarItemModel::data( const QModelIndex &index, int role ) const { QVariant result; Calendar *a = calendar( index ); if ( a == 0 ) { return QVariant(); } switch ( index.column() ) { case Name: result = name( a, role ); break; case Scope: result = scope( a, role ); break; case TimeZone: result = timeZone( a, role ); break; #ifdef HAVE_KHOLIDAYS case HolidayRegion: result = holidayRegion( a, role ); break; #endif default: debugPlan<<"data: invalid display value column"<( index.internalPointer() ); } void CalendarItemModel::slotCalendarChanged( Calendar *calendar ) { Calendar *par = calendar->parentCal(); if ( par ) { int row = par->indexOf( calendar ); emit dataChanged( createIndex( row, 0, calendar ), createIndex( row, columnCount() - 1, calendar ) ); } else { int row = m_project->indexOf( calendar ); emit dataChanged( createIndex( row, 0, calendar ), createIndex( row, columnCount() - 1, calendar ) ); } } Qt::DropActions CalendarItemModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } QStringList CalendarItemModel::mimeTypes() const { return QStringList() << "application/x-vnd.kde.plan.calendarid.internal"; } QMimeData *CalendarItemModel::mimeData( const QModelIndexList & indexes ) const { QMimeData *m = new QMimeData(); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); QList rows; foreach (const QModelIndex &index, indexes) { if ( index.isValid() && !rows.contains( index.row() ) ) { debugPlan<id(); } } } m->setData("application/x-vnd.kde.plan.calendarid.internal", encodedData); return m; } bool CalendarItemModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int /*column*/, const QModelIndex &parent ) { debugPlan<hasFormat( "application/x-vnd.kde.plan.calendarid.internal" ) ) { return false; } if ( action == Qt::MoveAction ) { debugPlan<<"MoveAction"; QByteArray encodedData = data->data( "application/x-vnd.kde.plan.calendarid.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); Calendar *par = 0; if ( parent.isValid() ) { par = calendar( parent ); } MacroCommand *cmd = 0; QList lst = calendarList( stream ); foreach ( Calendar *c, lst ) { if ( c->parentCal() != par ) { if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Re-parent calendar" ) ); cmd->addCommand( new CalendarModifyParentCmd( m_project, c, par ) ); } else { if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Move calendar" ) ); cmd->addCommand( new CalendarMoveCmd( m_project, c, row, par ) ); } } if ( cmd ) { emit executeCommand( cmd ); return true; } //debugPlan<name(); } return false; } QList CalendarItemModel::calendarList( QDataStream &stream ) const { QList lst; while (!stream.atEnd()) { QString id; stream >> id; Calendar *c = m_project->findCalendar( id ); if ( c ) { lst << c; } } return lst; } bool CalendarItemModel::dropAllowed( Calendar *on, const QMimeData *data ) { debugPlan<hasFormat("application/x-vnd.kde.plan.calendarid.internal"); if ( !data->hasFormat("application/x-vnd.kde.plan.calendarid.internal") ) { return false; } if ( on == 0 && ! ( flags( QModelIndex() ) & (int)Qt::ItemIsDropEnabled ) ) { return false; } QByteArray encodedData = data->data( "application/x-vnd.kde.plan.calendarid.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); QList lst = calendarList( stream ); foreach ( Calendar *c, lst ) { if ( (flags( index( c ) ) & (int)Qt::ItemIsDropEnabled) == 0 ) { return false; } if ( on != 0 && on == c->parentCal() ) { return false; } if ( on != 0 && ( on == c || on->isChildOf( c ) ) ) { return false; } } return true; } QModelIndex CalendarItemModel::insertCalendar ( Calendar *calendar, int pos, Calendar *parent ) { //debugPlan<indexOf( calendar ); } else { row = m_project->indexOf( calendar ); } if ( row != -1 ) { //debugPlan<<"Inserted:"<name()<<"row="< /*lst*/ ) { } void CalendarItemModel::removeCalendar( Calendar *calendar ) { if ( calendar == 0 ) { return; } emit executeCommand( new CalendarRemoveCmd( m_project, calendar, kundo2_i18n( "Delete calendar" ) ) ); } //------------------------------------------ CalendarDayItemModel::CalendarDayItemModel( QObject *parent ) : CalendarDayItemModelBase( parent ) { } CalendarDayItemModel::~CalendarDayItemModel() { } void CalendarDayItemModel::slotWorkIntervalAdded( CalendarDay *day, TimeInterval *ti ) { Q_UNUSED(ti); //debugPlan<indexOfWeekday( day ); if ( c == -1 ) { return; } dataChanged( createIndex( 0, c, day ), createIndex( 0, c, day ) ); } void CalendarDayItemModel::slotWorkIntervalRemoved( CalendarDay *day, TimeInterval *ti ) { Q_UNUSED(ti); int c = m_calendar->indexOfWeekday( day ); if ( c == -1 ) { return; } dataChanged( createIndex( 0, c, day ), createIndex( 0, c, day ) ); } void CalendarDayItemModel::slotDayChanged( CalendarDay *day ) { int c = m_calendar->indexOfWeekday( day ); if ( c == -1 ) { return; } debugPlan<indexOf( ti ); emit dataChanged( createIndex( row, 0, ti ), createIndex( row, columnCount() - 1, ti ) );*/ } void CalendarDayItemModel::setCalendar( Calendar *calendar ) { //debugPlan<"<weekday( column + 1 ); // weekdays are 1..7 if ( d == 0 ) { return QModelIndex(); } return createIndex( row, column, d ); } QModelIndex CalendarDayItemModel::index( const CalendarDay *d) const { if ( m_project == 0 || m_calendar == 0 ) { return QModelIndex(); } int col = m_calendar->indexOfWeekday( d ); if ( col == -1 ) { return QModelIndex(); } return createIndex( 0, col, const_cast( d ) ); } int CalendarDayItemModel::columnCount( const QModelIndex &/*parent*/ ) const { return 7; } int CalendarDayItemModel::rowCount( const QModelIndex &parent ) const { if ( m_project == 0 || m_calendar == 0 || parent.isValid() ) { return 0; } return 1; } QVariant CalendarDayItemModel::name( int weekday, int role ) const { //debugPlan<name()<<","<= 1 && weekday <= 7 ) { return QLocale().dayName( weekday, QLocale::ShortFormat ); } break; case Qt::ToolTipRole: if ( weekday >= 1 && weekday <= 7 ) { return QLocale().dayName( weekday, QLocale::LongFormat ); } break; case Qt::EditRole: case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant CalendarDayItemModel::dayState( const CalendarDay *d, int role ) const { switch ( role ) { case Qt::DisplayRole: switch ( d->state() ) { case CalendarDay::Undefined: return i18nc( "Undefined", "U" ); case CalendarDay::NonWorking: return i18nc( "NonWorking", "NW" ); case CalendarDay::Working: return i18nc( "Working", "W" ); } break; case Qt::ToolTipRole: return CalendarDay::stateToString( d->state(), true ); case Role::EnumList: { QStringList lst = CalendarDay::stateList( true ); return lst; } case Qt::EditRole: case Role::EnumListValue: { return d->state(); } case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::EditorType: return Delegate::EnumEditor; } return QVariant(); } bool CalendarDayItemModel::setDayState( CalendarDay *d, const QVariant &value, int role ) { //debugPlan; switch ( role ) { case Qt::EditRole: int v = value.toInt(); emit executeCommand( new CalendarModifyStateCmd( m_calendar, d, static_cast( v ), kundo2_i18n( "Modify calendar state" ) ) ); return true; } return false; } QVariant CalendarDayItemModel::workDuration( const CalendarDay *day, int role ) const { //debugPlan<date()<<","<state() == CalendarDay::Working ) { return QLocale().toString( day->workDuration().toDouble( Duration::Unit_h ), 'f', 1 ); } return QVariant(); } case Qt::ToolTipRole: { if ( day->state() == CalendarDay::Working ) { QLocale locale; QStringList tip; foreach ( TimeInterval *i, day->timeIntervals() ) { tip << i18nc( "1=time 2=The number of hours of work duration (non integer)", "%1, %2 hours", locale.toString( i->startTime(), QLocale::ShortFormat ), locale.toString( i->hours(), 'f', 2 ) ); } return tip.join( "\n" ); } return QVariant(); } case Qt::EditRole: break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Qt::TextAlignmentRole: return Qt::AlignCenter; } return QVariant(); } QVariant CalendarDayItemModel::data( const QModelIndex &index, int role ) const { QVariant result; if ( ! index.isValid() ) { return result; } CalendarDay *d = day( index ); if ( d == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: { switch ( d->state() ) { case CalendarDay::Working: result = workDuration( d, role ); break; case CalendarDay::NonWorking: result = dayState( d, role ); break; default: { // Return parent value (if any) for ( Calendar *c = m_calendar->parentCal(); c != 0; c = c->parentCal() ) { d = c->weekday( index.column() + 1 ); Q_ASSERT( d ); if ( d->state() == CalendarDay::Working ) { return workDuration( d, role ); } if ( d->state() == CalendarDay::NonWorking ) { return dayState( d, role ); } } break; } } break; } case Qt::ToolTipRole: { if ( d->state() == CalendarDay::Undefined ) { return xi18nc( "@info:tooltip", "Undefined" ); } if ( d->state() == CalendarDay::NonWorking ) { return xi18nc( "@info:tooltip", "Non-working" ); } QLocale locale; KFormat format(locale); QStringList tip; foreach ( TimeInterval *i, d->timeIntervals() ) { tip << xi18nc( "@info:tooltip 1=time 2=The work duration (non integer)", "%1, %2", locale.toString( i->startTime(), QLocale::ShortFormat ), format.formatDuration( i->second ) ); } return tip.join( "" ); } case Qt::FontRole: { if ( d->state() != CalendarDay::Undefined ) { return QVariant(); } // If defined in parent, return italic for ( Calendar *c = m_calendar->parentCal(); c != 0; c = c->parentCal() ) { d = c->weekday( index.column() + 1 ); Q_ASSERT( d ); if ( d->state() != CalendarDay::Undefined ) { QFont f; f.setItalic( true ); return f; } } break; } } return result; } bool CalendarDayItemModel::setData( const QModelIndex &index, const QVariant &value, int role ) { return ItemModelBase::setData( index, value, role ); } QVariant CalendarDayItemModel::headerData( int section, Qt::Orientation orientation, int role ) const { if ( orientation == Qt::Horizontal ) { if ( role == Qt::DisplayRole ) { switch ( section ) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: return name( section + 1, role ); default: return QVariant(); } } else if ( role == Qt::TextAlignmentRole ) { switch (section) { default: return Qt::AlignCenter; } } } if ( role == Qt::ToolTipRole ) { switch ( section ) { /* case 0: return ToolTip::Calendar Name;*/ default: return QVariant(); } } return ItemModelBase::headerData(section, orientation, role); } CalendarDay *CalendarDayItemModel::day( const QModelIndex &index ) const { return static_cast( index.internalPointer() ); } QAbstractItemDelegate *CalendarDayItemModel::createDelegate( int column, QWidget *parent ) const { Q_UNUSED(parent); switch ( column ) { default: return 0; } return 0; } //----------------------- DateTableDataModel::DateTableDataModel( QObject *parent ) : KDateTableDataModel( parent ), m_calendar( 0 ) { } void DateTableDataModel::setCalendar( Calendar *calendar ) { if ( m_calendar ) { disconnect( m_calendar, SIGNAL(dayAdded(CalendarDay*)), this, SIGNAL(reset()) ); disconnect( m_calendar, SIGNAL(dayRemoved(CalendarDay*)), this, SIGNAL(reset()) ); disconnect( m_calendar, SIGNAL(changed(CalendarDay*)), this, SIGNAL(reset()) ); } m_calendar = calendar; if ( m_calendar ) { connect( m_calendar, SIGNAL(dayAdded(CalendarDay*)), this, SIGNAL(reset()) ); connect( m_calendar, SIGNAL(dayRemoved(CalendarDay*)), this, SIGNAL(reset()) ); connect( m_calendar, SIGNAL(changed(CalendarDay*)), this, SIGNAL(reset()) ); } emit reset(); } QVariant DateTableDataModel::data( const Calendar &cal, const QDate &date, int role ) const { switch ( role ) { case Qt::DisplayRole: { CalendarDay *day = cal.findDay( date ); if ( day == 0 || day->state() == CalendarDay::Undefined ) { #ifdef HAVE_KHOLIDAYS if (cal.isHoliday(date)) { return i18nc( "NonWorking", "NW" ); } #endif if ( cal.parentCal() ) { return data( *( cal.parentCal() ), date, role ); } return ""; } if ( day->state() == CalendarDay::NonWorking ) { return i18nc( "NonWorking", "NW" ); } double v; v = day->workDuration().toDouble( Duration::Unit_h ); return QLocale().toString( v, 'f', 1 ); } case Qt::TextAlignmentRole: return (uint)( Qt::AlignHCenter | Qt::AlignBottom ); case Qt::FontRole: { CalendarDay *day = cal.findDay( date ); if ( day && day->state() != CalendarDay::Undefined ) { if ( &cal != m_calendar ) { QFont f; f.setItalic( true ); return f; } return QVariant(); } if ( cal.parentCal() ) { return data( *( cal.parentCal() ), date, role ); } break; } default: break; } return QVariant(); } QVariant DateTableDataModel::data( const QDate &date, int role, int dataType ) const { //debugPlan<findDay( date ); if ( day == 0 || day->state() == CalendarDay::Undefined ) { #ifdef HAVE_KHOLIDAYS if (m_calendar->isHoliday(date)) { return xi18nc( "@info:tooltip", "Holiday" ); } #endif return xi18nc( "@info:tooltip", "Undefined" ); } if ( day->state() == CalendarDay::NonWorking ) { return xi18nc( "@info:tooltip", "Non-working" ); } QLocale locale; KFormat format(locale); QStringList tip; foreach ( TimeInterval *i, day->timeIntervals() ) { tip << xi18nc( "@info:tooltip 1=time 2=The work duration (non integer)", "%1, %2", locale.toString( i->startTime(), QLocale::ShortFormat ), format.formatDuration( i->second ) ); } return tip.join( "\n" ); } switch ( dataType ) { case -1: { //default (date) switch ( role ) { case Qt::DisplayRole: { return QVariant(); } case Qt::TextAlignmentRole: return (uint)Qt::AlignLeft | Qt::AlignTop; case Qt::FontRole: break;//return QFont( "Helvetica", 6 ); case Qt::BackgroundRole: break;//return QColor( "red" ); default: break; } break; } case 0: { if ( m_calendar == 0 ) { return ""; } return data( *m_calendar, date, role ); } default: break; } return QVariant(); } QVariant DateTableDataModel::weekDayData( int day, int role ) const { Q_UNUSED(day); Q_UNUSED(role); return QVariant(); } QVariant DateTableDataModel::weekNumberData( int week, int role ) const { Q_UNUSED(week); Q_UNUSED(role); return QVariant(); } //------------- DateTableDateDelegate::DateTableDateDelegate( QObject *parent ) : KDateTableDateDelegate( parent ) { } QRectF DateTableDateDelegate::paint( QPainter *painter, const StyleOptionViewItem &option, const QDate &date, KDateTableDataModel *model ) { //debugPlan<save(); painter->translate( r.width(), 0.0 ); QRectF rect( 1, 1, option.rectF.right() - r.width(), option.rectF.bottom() ); //debugPlan<<" rects: "<data( date, Qt::DisplayRole, 0 ).toString(); int align = model->data( date, Qt::TextAlignmentRole, 0 ).toInt(); QFont f = option.font; QVariant v = model->data( date, Qt::FontRole, 0 ); if ( v.isValid() ) { f = v.value(); } painter->setFont( f ); if ( option.state & QStyle::State_Selected ) { painter->setPen( option.palette.highlightedText().color() ); } else { painter->setPen( option.palette.color( QPalette::Text ) ); } painter->drawText(rect, align, text, &r); painter->restore(); return r; } //------------------------------------- CalendarExtendedItemModel::CalendarExtendedItemModel( QObject *parent ) : CalendarItemModel( parent ) { } Qt::ItemFlags CalendarExtendedItemModel::flags( const QModelIndex &index ) const { Qt::ItemFlags flags = CalendarItemModel::flags( index ); if ( ! m_readWrite || ! index.isValid() || calendar( index ) == 0 ) { return flags; } return flags |= Qt::ItemIsEditable; } QModelIndex CalendarExtendedItemModel::index( int row, int column, const QModelIndex &parent ) const { if ( m_project == 0 || column < 0 || column >= columnCount() || row < 0 ) { return QModelIndex(); } Calendar *par = calendar( parent ); if ( par == 0 ) { if ( row < m_project->calendars().count() ) { return createIndex( row, column, m_project->calendars().at( row ) ); } } else if ( row < par->calendars().count() ) { return createIndex( row, column, par->calendars().at( row ) ); } return QModelIndex(); } int CalendarExtendedItemModel::columnCount( const QModelIndex & ) const { return CalendarItemModel::columnCount() + 2; // weekdays + date } QVariant CalendarExtendedItemModel::data( const QModelIndex &index, int role ) const { QVariant result; Calendar *a = calendar( index ); if ( a == 0 ) { return QVariant(); } int col = index.column() - CalendarItemModel::columnCount( index ); if ( col < 0 ) { return CalendarItemModel::data( index, role ); } switch ( col ) { default: debugPlan<<"Fetching data from weekdays and date is not supported"; break; } return result; } bool CalendarExtendedItemModel::setData( const QModelIndex &index, const QVariant &value, int role ) { int col = index.column() - CalendarItemModel::columnCount( index ); if ( col < 0 ) { return CalendarItemModel::setData( index, value, role ); } if ( ( flags( index ) &( Qt::ItemIsEditable ) ) == 0 ) { return false; } Calendar *cal = calendar( index ); if ( cal == 0 || col > 2 ) { return false; } switch ( col ) { case 0: { // weekday if ( value.type() != QVariant::List ) { return false; } QVariantList lst = value.toList(); if ( lst.count() < 2 ) { return false; } int wd = CalendarWeekdays::dayOfWeek( lst.at( 0 ).toString() ); if ( wd < 1 || wd > 7 ) { return false; } CalendarDay *day = new CalendarDay(); if ( lst.count() == 2 ) { QString state = lst.at( 1 ).toString(); if ( state == "NonWorking" ) { day->setState( CalendarDay::NonWorking ); } else if ( state == "Undefined" ) { day->setState( CalendarDay::Undefined ); } else { delete day; return false; } CalendarModifyWeekdayCmd *cmd = new CalendarModifyWeekdayCmd( cal, wd, day, kundo2_i18n( "Modify calendar weekday" ) ); emit executeCommand( cmd ); return true; } if ( lst.count() % 2 == 0 ) { delete day; return false; } day->setState( CalendarDay::Working ); for ( int i = 1; i < lst.count(); i = i + 2 ) { QTime t1 = lst.at( i ).toTime(); QTime t2 = lst.at( i + 1 ).toTime(); int length = t1.msecsTo( t2 ); if ( t1 == QTime( 0, 0, 0 ) && t2 == t1 ) { length = 24 * 60 * 60 *1000; } else if ( length < 0 && t2 == QTime( 0, 0, 0 ) ) { length += 24 * 60 * 60 *1000; } else if ( length == 0 || ( length < 0 && t2 != QTime( 0, 0, 0 ) ) ) { delete day; return false; } length = qAbs( length ); day->addInterval( t1, length ); } CalendarModifyWeekdayCmd *cmd = new CalendarModifyWeekdayCmd( cal, wd, day, kundo2_i18n( "Modify calendar weekday" ) ); emit executeCommand( cmd ); return true; } case 1: { // day if ( value.type() != QVariant::List ) { return false; } CalendarDay *day = new CalendarDay(); QVariantList lst = value.toList(); if ( lst.count() < 2 ) { return false; } day->setDate( lst.at( 0 ).toDate() ); if ( ! day->date().isValid() ) { delete day; return false; } if ( lst.count() == 2 ) { QString state = lst.at( 1 ).toString(); if ( state == "NonWorking" ) { day->setState( CalendarDay::NonWorking ); } else if ( state == "Undefined" ) { day->setState( CalendarDay::Undefined ); } else { delete day; return false; } CalendarModifyDayCmd *cmd = new CalendarModifyDayCmd( cal, day, kundo2_i18n( "Modify calendar date" ) ); emit executeCommand( cmd ); return true; } if ( lst.count() % 2 == 0 ) { delete day; return false; } day->setState( CalendarDay::Working ); for ( int i = 1; i < lst.count(); i = i + 2 ) { QTime t1 = lst.at( i ).toTime(); QTime t2 = lst.at( i + 1 ).toTime(); int length = t1.msecsTo( t2 ); if ( t1 == QTime( 0, 0, 0 ) && t2 == t1 ) { length = 24 * 60 * 60 *1000; } else if ( length < 0 && t2 == QTime( 0, 0, 0 ) ) { length += 24 * 60 * 60 *1000; } else if ( length == 0 || ( length < 0 && t2 != QTime( 0, 0, 0 ) ) ) { delete day; return false; } length = qAbs( length ); day->addInterval( t1, length ); } CalendarModifyDayCmd *cmd = new CalendarModifyDayCmd( cal, day, kundo2_i18n( "Modify calendar date" ) ); emit executeCommand( cmd ); return true; } } return false; } QVariant CalendarExtendedItemModel::headerData( int section, Qt::Orientation orientation, int role ) const { int col = section - CalendarItemModel::columnCount(); if ( col < 0 ) { return CalendarItemModel::headerData( section, orientation, role ); } if ( orientation == Qt::Horizontal ) { if ( role == Qt::DisplayRole ) { switch ( col ) { case 0: return xi18nc( "@title:column", "Weekday" ); case 1: return xi18nc( "@title:column", "Date" ); default: return QVariant(); } } else if ( role == Qt::TextAlignmentRole ) { switch ( col ) { default: return QVariant(); } } } if ( role == Qt::ToolTipRole ) { switch ( section ) { default: return QVariant(); } } return QVariant(); } int CalendarExtendedItemModel::columnNumber(const QString& name) const { QStringList lst; lst << "Weekday" << "Date"; if ( lst.contains( name ) ) { return lst.indexOf( name ) + CalendarItemModel::columnCount(); } return CalendarItemModel::columnMap().keyToValue( name.toUtf8() ); } } // namespace KPlato diff --git a/plan/libs/models/kptnodeitemmodel.cpp b/plan/libs/models/kptnodeitemmodel.cpp index dcbc58e2e4f..5830f83914d 100644 --- a/plan/libs/models/kptnodeitemmodel.cpp +++ b/plan/libs/models/kptnodeitemmodel.cpp @@ -1,5192 +1,5194 @@ /* This file is part of the KDE project Copyright (C) 2007 - 2009, 2012 Dag Andersen Copyright (C) 2016 Dag Andersen This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library 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 Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kptnodeitemmodel.h" #include "kptglobal.h" #include "kptlocale.h" #include "kptcommonstrings.h" #include "kptcommand.h" #include "kptduration.h" #include "kptproject.h" #include "kptnode.h" #include "kpttaskcompletedelegate.h" #include "kptxmlloaderobject.h" #include "kptdebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace KPlato { //-------------------------------------- NodeModel::NodeModel() : QObject(), m_project( 0 ), m_manager( 0 ), m_now( QDate::currentDate() ), m_prec( 1 ) { } const QMetaEnum NodeModel::columnMap() const { return metaObject()->enumerator( metaObject()->indexOfEnumerator("Properties") ); } void NodeModel::setProject( Project *project ) { debugPlan<"<"<name(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Qt::DecorationRole: if ( node->isBaselined() ) { return koIcon("view-time-schedule-baselined"); } break; case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return static_cast( node )->completion().isFinished() ? m_project->config().taskFinishedColor() : m_project->config().taskNormalColor(); case Node::Type_Milestone: return static_cast( node )->completion().isFinished() ? m_project->config().milestoneFinishedColor() : m_project->config().milestoneNormalColor(); case Node::Type_Summarytask: return m_project->config().summaryTaskLevelColor( node->level() ); default: break; } break; } } return QVariant(); } QVariant NodeModel::leader( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: case Qt::ToolTipRole: return node->leader(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::allocation( const Node *node, int role ) const { if ( node->type() == Node::Type_Task ) { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->requests().requestNameList().join( "," ); case Qt::ToolTipRole: { QMap lst; foreach ( ResourceRequest *rr, node->requests().resourceRequests( false ) ) { QStringList sl; foreach( Resource *r, rr->requiredResources() ) { sl << r->name(); } lst.insert( rr->resource()->name(), sl ); } if ( lst.isEmpty() ) { return xi18nc( "@info:tooltip", "No resources has been allocated" ); } QStringList sl; for ( QMap::ConstIterator it = lst.constBegin(); it != lst.constEnd(); ++it ) { if ( it.value().isEmpty() ) { sl << it.key(); } else { sl << xi18nc( "@info:tooltip 1=resource name, 2=list of required resources", "%1 (%2)", it.key(), it.value().join(", ") ); } } if ( sl.count() == 1 ) { return xi18nc( "@info:tooltip 1=resource name", "Allocated resource:%1", sl.first() ); } return xi18nc( "@info:tooltip 1=list of resources", "Allocated resources:%1", sl.join( "" ) ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::description( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { KRichTextWidget w( node->description(), 0 ); w.switchToPlainText(); QString s = w.textOrHtml(); int i = s.indexOf( '\n' ); s = s.left( i ); if ( i > 0 ) { s += "..."; } return s; } case Qt::ToolTipRole: { KRichTextWidget w( node->description(), 0 ); w.switchToPlainText(); if ( w.textOrHtml().isEmpty() ) { return QVariant(); } return node->description(); } case Qt::EditRole: return node->description(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::type( const Node *node, int role ) const { //debugPlan<name()<<", "<typeToString( true ); case Qt::EditRole: return node->type(); case Qt::TextAlignmentRole: return (int)(Qt::AlignLeft|Qt::AlignVCenter); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::constraint( const Node *node, int role ) const { if ( node->type() == Node::Type_Project ) { switch ( role ) { case Qt::DisplayRole: return i18n( "Target times" ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Earliest start and latest finish" ); case Role::EnumList: case Qt::EditRole: case Role::EnumListValue: return QVariant(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } else if ( node->type() != Node::Type_Summarytask ) { switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: return node->constraintToString( true ); case Role::EnumList: return Node::constraintList( true ); case Qt::EditRole: return node->constraint(); case Role::EnumListValue: return (int)node->constraint(); case Qt::TextAlignmentRole: return Qt::AlignCenter; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::constraintStartTime( const Node *node, int role ) const { if ( node->type() == Node::Type_Project ) { switch ( role ) { case Qt::DisplayRole: { return QLocale().toString( node->constraintStartTime(), QLocale::ShortFormat ); } case Qt::ToolTipRole: { return QLocale().toString( node->constraintStartTime(), QLocale::LongFormat ); } case Qt::EditRole: return node->constraintStartTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } else if ( node->type() != Node::Type_Summarytask ) { switch ( role ) { case Qt::DisplayRole: { QString s = QLocale().toString( node->constraintStartTime(), QLocale::ShortFormat ); switch ( node->constraint() ) { case Node::StartNotEarlier: case Node::MustStartOn: case Node::FixedInterval: return s; default: break; } return QString( "(%1)" ).arg( s ); } case Qt::ToolTipRole: { int c = node->constraint(); if ( c == Node::MustStartOn || c == Node::StartNotEarlier || c == Node::FixedInterval ) { return QLocale().toString( node->constraintStartTime(), QLocale::LongFormat ); } break; } case Qt::EditRole: return node->constraintStartTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::constraintEndTime( const Node *node, int role ) const { if ( node->type() == Node::Type_Project ) { switch ( role ) { case Qt::DisplayRole: { return QLocale().toString( node->constraintEndTime(), QLocale::ShortFormat ); } case Qt::ToolTipRole: { return QLocale().toString( node->constraintEndTime(), QLocale::LongFormat ); } case Qt::EditRole: return node->constraintEndTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } else if ( node->type() != Node::Type_Summarytask ) { switch ( role ) { case Qt::DisplayRole: { QString s = QLocale().toString( node->constraintEndTime(), QLocale::ShortFormat ); switch ( node->constraint() ) { case Node::FinishNotLater: case Node::MustFinishOn: case Node::FixedInterval: return s; default: break; } return QString( "(%1)" ).arg( s ); } case Qt::ToolTipRole: { int c = node->constraint(); if ( c == Node::FinishNotLater || c == Node::MustFinishOn || c == Node::FixedInterval ) { return QLocale().toString( node->constraintEndTime(), QLocale::LongFormat ); } break; } case Qt::EditRole: return node->constraintEndTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } } return QVariant(); } QVariant NodeModel::estimateType( const Node *node, int role ) const { if ( node->estimate() == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { return node->estimate()->typeToString( true ); } return QString(); case Role::EnumList: return Estimate::typeToStringList( true ); case Qt::EditRole: if ( node->type() == Node::Type_Task ) { return node->estimate()->typeToString(); } return QString(); case Role::EnumListValue: return (int)node->estimate()->type(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::estimateCalendar( const Node *node, int role ) const { if ( node->estimate() == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task ) { if ( node->estimate()->calendar() ) { return node->estimate()->calendar()->name(); } return i18n( "None" ); } return QString(); case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { if ( node->estimate()->type() == Estimate::Type_Effort ) { return xi18nc( "@info:tooltip", "Not applicable, estimate type is Effort" ); } if ( node->estimate()->calendar() ) { return node->estimate()->calendar()->name(); } return QVariant(); } return QString(); case Role::EnumList: { QStringList lst; lst << i18n( "None" ); const Node *n = const_cast( node )->projectNode(); if ( n ) { lst += static_cast( n )->calendarNames(); } return lst; } case Qt::EditRole: if ( node->type() == Node::Type_Task ) { if ( node->estimate()->calendar() == 0 ) { return i18n( "None" ); } return node->estimate()->calendar()->name(); } return QString(); case Role::EnumListValue: { if ( node->estimate()->calendar() == 0 ) { return 0; } QStringList lst; const Node *n = const_cast( node )->projectNode(); if ( n ) { lst = static_cast( n )->calendarNames(); } return lst.indexOf( node->estimate()->calendar()->name() ) + 1; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::estimate( const Node *node, int role ) const { if ( node->estimate() == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString( node->estimate()->expectedEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ); if ( node->constraint() == Node::FixedInterval && node->estimate()->type() == Estimate::Type_Duration ) { s = '(' + s + ')'; } return s; } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString( node->estimate()->expectedEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ); Estimate::Type t = node->estimate()->type(); if ( node->constraint() == Node::FixedInterval && t == Estimate::Type_Duration ) { s = xi18nc( "@info:tooltip", "Not applicable, constraint is Fixed Interval" ); } else if ( t == Estimate::Type_Effort ) { s = xi18nc( "@info:tooltip", "Estimated effort: %1", s ); } else { s = xi18nc( "@info:tooltip", "Estimated duration: %1", s ); } return s; } break; case Qt::EditRole: return node->estimate()->expectedEstimate(); case Role::DurationUnit: return static_cast( node->estimate()->unit() ); case Role::Minimum: return m_project->config().minimumDurationUnit(); case Role::Maximum: return m_project->config().maximumDurationUnit(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::optimisticRatio( const Node *node, int role ) const { if ( node->estimate() == 0 || node->type() == Node::Type_Summarytask || node->type() == Node::Type_Milestone ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task && node->constraint() == Node::FixedInterval && node->estimate()->type() == Estimate::Type_Duration ) { QString s = QString::number( node->estimate()->optimisticRatio() ); s = '(' + s + ')'; return s; } if ( node->estimate() ) { return node->estimate()->optimisticRatio(); } break; case Qt::EditRole: if ( node->estimate() ) { return node->estimate()->optimisticRatio(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString( node->estimate()->optimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ); Estimate::Type t = node->estimate()->type(); if ( node->constraint() == Node::FixedInterval && t == Estimate::Type_Duration ) { s = xi18nc( "@info:tooltip", "Not applicable, constraint is Fixed Interval" ); } else if ( t == Estimate::Type_Effort ) { s = xi18nc( "@info:tooltip", "Optimistic effort: %1", s ); } else { s = xi18nc( "@info:tooltip", "Optimistic duration: %1", s ); } return s; } break; case Role::Minimum: return -99; case Role::Maximum: return 0; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::pessimisticRatio( const Node *node, int role ) const { if ( node->estimate() == 0 || node->type() == Node::Type_Summarytask || node->type() == Node::Type_Milestone ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task && node->constraint() == Node::FixedInterval && node->estimate()->type() == Estimate::Type_Duration ) { QString s = QString::number( node->estimate()->pessimisticRatio() ); s = '(' + s + ')'; return s; } if ( node->estimate() ) { return node->estimate()->pessimisticRatio(); } break; case Qt::EditRole: if ( node->estimate() ) { return node->estimate()->pessimisticRatio(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); QString s = QLocale().toString( node->estimate()->pessimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ); Estimate::Type t = node->estimate()->type(); if ( node->constraint() == Node::FixedInterval && t == Estimate::Type_Duration ) { s = xi18nc( "@info:tooltip", "Not applicable, constraint is Fixed Interval" ); } else if ( t == Estimate::Type_Effort ) { s = xi18nc( "@info:tooltip", "Pessimistic effort: %1", s ); } else { s = xi18nc( "@info:tooltip", "Pessimistic duration: %1", s ); } return s; } break; case Role::Minimum: return 0; case Role::Maximum: return INT_MAX; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::riskType( const Node *node, int role ) const { if ( node->estimate() == 0 ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { return node->estimate()->risktypeToString( true ); } return QString(); case Role::EnumList: return Estimate::risktypeToStringList( true ); case Qt::EditRole: if ( node->type() == Node::Type_Task ) { return node->estimate()->risktypeToString(); } return QString(); case Role::EnumListValue: return (int)node->estimate()->risktype(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::runningAccount( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task ) { Account *a = node->runningAccount(); return a == 0 ? i18n( "None" ) : a->name(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Account *a = node->runningAccount(); return a ? xi18nc( "@info:tooltip", "Account for resource cost: %1", a->name() ) : xi18nc( "@info:tooltip", "Account for resource cost" ); } break; case Role::EnumListValue: case Qt::EditRole: { Account *a = node->runningAccount(); return a == 0 ? 0 : ( m_project->accounts().costElements().indexOf( a->name() ) + 1 ); } case Role::EnumList: { QStringList lst; lst << i18n("None"); lst += m_project->accounts().costElements(); return lst; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startupAccount( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Account *a = node->startupAccount(); //debugPlan<name()<<": "<name(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Account *a = node->startupAccount(); //debugPlan<name()<<": "<name() ) : xi18nc( "@info:tooltip", "Account for task startup cost" ); } break; case Role::EnumListValue: case Qt::EditRole: { Account *a = node->startupAccount(); return a == 0 ? 0 : ( m_project->accounts().costElements().indexOf( a->name() ) + 1 ); } case Role::EnumList: { QStringList lst; lst << i18n("None"); lst += m_project->accounts().costElements(); return lst; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startupCost( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { return m_project->locale()->formatMoney( node->startupCost() ); } break; case Qt::EditRole: return node->startupCost(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::shutdownAccount( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Account *a = node->shutdownAccount(); return a == 0 ? i18n( "None" ) : a->name(); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { Account *a = node->shutdownAccount(); return a ? xi18nc( "@info:tooltip", "Account for task shutdown cost: %1", a->name() ) : xi18nc( "@info:tooltip", "Account for task shutdown cost" ); } break; case Role::EnumListValue: case Qt::EditRole: { Account *a = node->shutdownAccount(); return a == 0 ? 0 : ( m_project->accounts().costElements().indexOf( a->name() ) + 1 ); } case Role::EnumList: { QStringList lst; lst << i18n("None"); lst += m_project->accounts().costElements(); return lst; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::shutdownCost( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) { return m_project->locale()->formatMoney( node->shutdownCost() ); } break; case Qt::EditRole: return node->shutdownCost(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startTime( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return QLocale().toString( node->startTime( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: //debugPlan<name()<<", "<startTime( id() ), QLocale::LongFormat ) ); case Qt::EditRole: return node->startTime( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::endTime( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return QLocale().toString( node->endTime( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: //debugPlan<name()<<", "<endTime( id() ), QLocale::LongFormat ) ); case Qt::EditRole: return node->endTime( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::duration( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); double v = node->duration( id() ).toDouble( unit ); return QVariant(QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true )); } else if ( node->type() == Node::Type_Project ) { Duration::Unit unit = Duration::Unit_d; double v = node->duration( id() ).toDouble( unit ); return QVariant(QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true )); } break; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); double v = node->duration( id() ).toDouble( unit ); return xi18nc( "@info:tooltip", "Scheduled duration: %1", QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true ) ); } else if ( node->type() == Node::Type_Project ) { Duration::Unit unit = Duration::Unit_d; double v = node->duration( id() ).toDouble( unit ); return xi18nc( "@info:tooltip", "Scheduled duration: %1", QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true ) ); } break; case Qt::EditRole: { return node->duration( id() ).toDouble( Duration::Unit_h ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::varianceDuration( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); double v = node->variance( id(), unit ); return QLocale().toString( v, 'f', 2 ); } break; case Qt::EditRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); return node->variance( id(), unit ); } return 0.0; case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Duration::Unit unit = node->estimate()->unit(); double v = node->variance( id(), unit ); return xi18nc( "@info:tooltip", "PERT duration variance: %1", QLocale().toString( v ,'f', 2 ) ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::varianceEstimate( const Estimate *est, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); double v = est->variance( unit ); //debugPlan<name()<<": "<variance( est->unit() ); } case Qt::ToolTipRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); double v = est->variance( unit ); return xi18nc( "@info:tooltip", "PERT estimate variance: %1", QLocale().toString( v, 'f', 2 ) + Duration::unitToString( unit, true ) ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::optimisticDuration( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( node->type() != Node::Type_Task ) { return QVariant(); } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->optimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble( unit ); //debugPlan<name()<<": "<type() != Node::Type_Task ) { return 0.0; } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->optimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); return d.toDouble( unit ); } case Qt::ToolTipRole: { if ( node->type() != Node::Type_Task ) { return QVariant(); } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->optimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble( unit ); //debugPlan<name()<<": "<unit(); return QVariant(QLocale().toString( est->optimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true )); break; } case Qt::EditRole: { if ( est == 0 ) { return 0.0; } return est->optimisticEstimate(); } case Qt::ToolTipRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); return xi18nc( "@info:tooltip", "Optimistic estimate: %1", QLocale().toString( est->optimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ) ); break; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::pertExpected( const Estimate *est, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); double v = Estimate::scale( est->pertExpected(), unit, est->scales() ); return QVariant(QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true )); } case Qt::EditRole: { if ( est == 0 ) { return 0.0; } return Estimate::scale( est->pertExpected(), est->unit(), est->scales() ); } case Qt::ToolTipRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); double v = Estimate::scale( est->pertExpected(), unit, est->scales() ); return xi18nc( "@info:tooltip", "PERT expected estimate: %1", QLocale().toString( v, 'f', m_prec ) + Duration::unitToString( unit, true ) ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::pessimisticDuration( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { if ( node->type() != Node::Type_Task ) { return QVariant(); } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->pessimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble( unit ); //debugPlan<name()<<": "<type() != Node::Type_Task ) { return 0.0; } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->pessimisticRatio() ) ) / 100; return d.toDouble( node->estimate()->unit() ); } case Qt::ToolTipRole: { if ( node->type() != Node::Type_Task ) { return QVariant(); } Duration d = node->duration( id() ); d = ( d * ( 100 + node->estimate()->pessimisticRatio() ) ) / 100; Duration::Unit unit = node->estimate()->unit(); double v = d.toDouble( unit ); //debugPlan<name()<<": "<unit(); return QVariant(QLocale().toString( est->pessimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true )); break; } case Qt::EditRole: { if ( est == 0 ) { return 0.0; } return est->pessimisticEstimate(); } case Qt::ToolTipRole: { if ( est == 0 ) { return QVariant(); } Duration::Unit unit = est->unit(); return xi18nc( "@info:tooltip", "Pessimistic estimate: %1", QLocale().toString( est->pessimisticEstimate(), 'f', m_prec ) + Duration::unitToString( unit, true ) ); break; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::earlyStart( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return QLocale().toString( t->earlyStart( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: return QLocale().toString( t->earlyStart( id() ).date(), QLocale::ShortFormat ); case Qt::EditRole: return t->earlyStart( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::earlyFinish( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return QLocale().toString( t->earlyFinish( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: return QLocale().toString( t->earlyFinish( id() ).date(), QLocale::ShortFormat ); case Qt::EditRole: return t->earlyFinish( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::lateStart( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return QLocale().toString( t->lateStart( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: return QLocale().toString( t->lateStart( id() ).date(), QLocale::ShortFormat ); case Qt::EditRole: return t->lateStart( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::lateFinish( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return QLocale().toString( t->lateFinish( id() ), QLocale::ShortFormat ); case Qt::ToolTipRole: return QLocale().toString( t->lateFinish( id() ).date(), QLocale::ShortFormat ); case Qt::EditRole: return t->lateFinish( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::positiveFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->positiveFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->positiveFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: return t->positiveFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::freeFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->freeFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->freeFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: return t->freeFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::negativeFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->negativeFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->negativeFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: return t->negativeFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->startFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->startFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: return t->startFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::finishFloat( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->finishFloat( id() ).toString( Duration::Format_i18nHourFraction ); case Qt::ToolTipRole: return t->finishFloat( id() ).toString( Duration::Format_i18nDayTime ); case Qt::EditRole: - t->finishFloat( id() ).toDouble( Duration::Unit_h ); + return t->finishFloat( id() ).toDouble( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::assignedResources( const Node *node, int role ) const { if ( node->type() != Node::Type_Task ) { return QVariant(); } switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->assignedNameList( id() ).join(","); case Qt::ToolTipRole: { QStringList lst = node->assignedNameList( id() ); if ( ! lst.isEmpty() ) { return xi18nc( "@info:tooltip 1=list of resources", "Assigned resources:%1", node->assignedNameList( id() ).join("") ); } break; } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::completed( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: return t->completion().percentFinished(); case Qt::EditRole: return t->completion().percentFinished(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Task is %1% completed", t->completion().percentFinished() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::status( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: { int st = t->state( id() ); if ( st & Node::State_NotScheduled ) { return SchedulingState::notScheduled(); } if ( st & Node::State_Finished ) { if ( st & Node::State_FinishedLate ) { return i18n( "Finished late" ); } if ( st & Node::State_FinishedEarly ) { return i18n( "Finished early" ); } return i18n( "Finished" ); } if ( st & Node::State_Running ) { if ( st & Node::State_Late ) { return i18n( "Running late" ); } return i18n( "Running" ); } if ( st & Node::State_Started ) { if ( st & Node::State_StartedLate ) { return i18n( "Started late" ); } if ( st & Node::State_StartedEarly ) { return i18n( "Started early" ); } if ( st & Node::State_Late ) { return i18n( "Running late" ); } return i18n( "Started" ); } if ( st & Node::State_ReadyToStart ) { if ( st & Node::State_Late ) { return i18n( "Not started" ); } return i18n( "Can start" ); } if ( st & Node::State_NotReadyToStart ) { if ( st & Node::State_Late ) { return i18n( "Delayed" ); } return i18n( "Cannot start" ); } return i18n( "Not started" ); break; } case Qt::ToolTipRole: { int st = t->state( id() ); if ( st & Node::State_NotScheduled ) { return SchedulingState::notScheduled(); } if ( st & Node::State_Finished ) { if ( st & Node::State_FinishedLate ) { Duration d = t->completion().finishTime() - t->endTime( id() ); return xi18nc( "@info:tooltip", "Finished %1 late", d.toString( Duration::Format_i18nDay ) ); } if ( st & Node::State_FinishedEarly ) { Duration d = t->endTime( id() ) - t->completion().finishTime(); return xi18nc( "@info:tooltip", "Finished %1 early", d.toString( Duration::Format_i18nDay ) ); } return xi18nc( "@info:tooltip", "Finished" ); } if ( st & Node::State_Started ) { if ( st & Node::State_StartedLate ) { Duration d = t->completion().startTime() - t->startTime( id() ); return xi18nc( "@info:tooltip", "Started %1 late", d.toString( Duration::Format_i18nDay ) ); } if ( st & Node::State_StartedEarly ) { Duration d = t->startTime( id() ) - t->completion().startTime(); return xi18nc( "@info:tooltip", "Started %1 early", d.toString( Duration::Format_i18nDay ) ); } return xi18nc( "@info:tooltip", "Started" ); } if ( st & Node::State_Running ) { return xi18nc( "@info:tooltip", "Running" ); } if ( st & Node::State_ReadyToStart ) { return xi18nc( "@info:tooltip", "Can start" ); } if ( st & Node::State_NotReadyToStart ) { QStringList names; // TODO: proxy relations foreach ( Relation *r, node->dependParentNodes() ) { switch ( r->type() ) { case Relation::FinishFinish: case Relation::FinishStart: if ( ! static_cast( r->parent() )->completion().isFinished() ) { if ( ! names.contains( r->parent()->name() ) ) { names << r->parent()->name(); } } break; case Relation::StartStart: if ( ! static_cast( r->parent() )->completion().isStarted() ) { if ( ! names.contains( r->parent()->name() ) ) { names << r->parent()->name(); } } break; } } return names.isEmpty() ? xi18nc( "@info:tooltip", "Cannot start" ) : xi18nc( "@info:tooltip 1=list of task names", "Cannot start, waiting for:%1", names.join( "" ) ); } return xi18nc( "@info:tooltip", "Not started" ); break; } case Qt::EditRole: return t->state( id() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::startedTime( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: if ( t->completion().isStarted() ) { return QLocale().toString( t->completion().startTime(), QLocale::ShortFormat ); } break; case Qt::ToolTipRole: if ( t->completion().isStarted() ) { return xi18nc( "@info:tooltip", "Actual start: %1", QLocale().toString( t->completion().startTime().date(), QLocale::LongFormat ) ); } break; case Qt::EditRole: if ( t->completion().isStarted() ) { return t->completion().startTime(); } return QDateTime::currentDateTime(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::isStarted( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return t->completion().isStarted(); case Qt::ToolTipRole: if ( t->completion().isStarted() ) { return xi18nc( "@info:tooltip", "The task started at: %1", QLocale().toString( t->completion().startTime().date(), QLocale::LongFormat ) ); } return xi18nc( "@info:tooltip", "The task is not started" ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::finishedTime( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: if ( t->completion().isFinished() ) { return QLocale().toString( t->completion().finishTime(), QLocale::ShortFormat ); } break; case Qt::ToolTipRole: if ( t->completion().isFinished() ) { return xi18nc( "@info:tooltip", "Actual finish: %1", QLocale().toString( t->completion().finishTime(), QLocale::LongFormat ) ); } break; case Qt::EditRole: if ( t->completion().isFinished() ) { return t->completion().finishTime(); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::isFinished( const Node *node, int role ) const { if ( ! ( node->type() == Node::Type_Task || node->type() == Node::Type_Milestone ) ) { return QVariant(); } const Task *t = static_cast( node ); switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return t->completion().isFinished(); case Qt::ToolTipRole: if ( t->completion().isFinished() ) { return xi18nc( "@info:tooltip", "The task finished at: %1", QLocale().toString( t->completion().finishTime().date(), QLocale::LongFormat ) ); } return xi18nc( "@info:tooltip", "The task is not finished" ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::plannedEffortTo( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return node->plannedEffortTo( m_now, id() ).format(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Planned effort until %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), node->plannedEffortTo( m_now, id() ).toString( Duration::Format_i18nHour ) ); case Qt::EditRole: return node->plannedEffortTo( m_now, id() ).toDouble( Duration::Unit_h ); case Role::DurationUnit: return static_cast( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::actualEffortTo( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return node->actualEffortTo( m_now ).format(); case Qt::ToolTipRole: //debugPlan<actualEffortTo( m_now ).toString( Duration::Format_i18nHour ) ); case Qt::EditRole: return node->actualEffortTo( m_now ).toDouble( Duration::Unit_h ); case Role::DurationUnit: return static_cast( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::remainingEffort( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { const Task *t = dynamic_cast( node ); if ( t ) { return t->completion().remainingEffort().format(); } break; } case Qt::ToolTipRole: { const Task *t = dynamic_cast( node ); if ( t ) { return xi18nc( "@info:tooltip", "Remaining effort: %1", t->completion().remainingEffort().toString( Duration::Format_i18nHour ) ); } break; } case Qt::EditRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } return t->completion().remainingEffort().toDouble( Duration::Unit_h ); } case Role::DurationUnit: return static_cast( Duration::Unit_h ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::plannedCostTo( const Node *node, int role ) const { Locale *l = m_project->locale(); switch ( role ) { case Qt::DisplayRole: return l->formatMoney( node->plannedCostTo( m_now, id() ) ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Planned cost until %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), l->formatMoney( node->plannedCostTo( m_now, id() ) ) ); case Qt::EditRole: return node->plannedCostTo( m_now ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::actualCostTo( const Node *node, int role ) const { Locale *l = m_project->locale(); switch ( role ) { case Qt::DisplayRole: return l->formatMoney( node->actualCostTo( id(), m_now ).cost() ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Actual cost until %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), l->formatMoney( node->actualCostTo( id(), m_now ).cost() ) ); case Qt::EditRole: return node->actualCostTo( id(), m_now ).cost(); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::note( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: case Qt::ToolTipRole: if ( node->type() == Node::Type_Task ) { Node *n = const_cast( node ); return static_cast( n )->completion().note(); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeSchedulingStatus( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return node->schedulingStatus( id(), true ).value( 0 ); case Qt::EditRole: return node->schedulingStatus( id(), false ).value( 0 ); case Qt::ToolTipRole: return node->schedulingStatus( id(), true ).join( "\n" ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::resourceIsMissing( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->resourceError( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->resourceError( id() ); case Qt::ToolTipRole: if ( node->resourceError( id() ) ) { return xi18nc( "@info:tooltip", "Resource allocation is expected when the task estimate type is Effort" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::resourceIsOverbooked( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->resourceOverbooked( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->resourceOverbooked( id() ); case Qt::ToolTipRole: if ( node->resourceOverbooked( id() ) ) { return xi18nc( "@info:tooltip", "A resource has been overbooked" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::resourceIsNotAvailable( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->resourceNotAvailable( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->resourceNotAvailable( id() ); case Qt::ToolTipRole: if ( node->resourceNotAvailable( id() ) ) { return xi18nc( "@info:tooltip", "No resource is available for this task" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::schedulingConstraintsError( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->constraintError( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->constraintError( id() ); case Qt::ToolTipRole: if ( node->constraintError( id() ) ) { return xi18nc( "@info:tooltip", "Failed to comply with a timing constraint" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::nodeIsNotScheduled( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->notScheduled( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->notScheduled( id() ); case Qt::ToolTipRole: if ( node->notScheduled( id() ) ) { return xi18nc( "@info:tooltip", "This task has not been scheduled" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::effortNotMet( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->effortMetError( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->effortMetError( id() ); case Qt::ToolTipRole: if ( node->effortMetError( id() ) ) { return xi18nc( "@info:tooltip", "The assigned resources cannot deliver the required estimated effort" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::schedulingError( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: if ( node->schedulingError( id() ) ) { return i18n( "Error" ); } break; case Qt::EditRole: return node->schedulingError( id() ); case Qt::ToolTipRole: if ( node->schedulingError( id() ) ) { return xi18nc( "@info:tooltip", "Scheduling error" ); } break; case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskErrorColor(); case Node::Type_Milestone: return m_project->config().milestoneErrorColor(); default: break; } } } return QVariant(); } QVariant NodeModel::wbsCode( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->wbsCode(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Work breakdown structure code: %1", node->wbsCode() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case SortableRole: return node->wbsCode(true); } return QVariant(); } QVariant NodeModel::nodeLevel( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->level(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Task level: %1", node->level() ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeBCWS( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return m_project->locale()->formatMoney( node->bcws( m_now, id() ), QString(), 0 ); case Qt::EditRole: return node->bcws( m_now, id() ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Budgeted Cost of Work Scheduled at %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), m_project->locale()->formatMoney( node->bcws( m_now, id() ), QString(), 0 ) ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeBCWP( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return m_project->locale()->formatMoney( node->bcwp( id() ), QString(), 0 ); case Qt::EditRole: return node->bcwp( id() ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Budgeted Cost of Work Performed at %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), m_project->locale()->formatMoney( node->bcwp( id() ), QString(), 0 ) ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodeACWP( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return m_project->locale()->formatMoney( node->acwp( m_now, id() ).cost(), QString(), 0 ); case Qt::EditRole: return node->acwp( m_now, id() ).cost(); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Actual Cost of Work Performed at %1: %2", QLocale().toString( m_now, QLocale::ShortFormat ), m_project->locale()->formatMoney( node->acwp( m_now, id() ).cost() ) ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::nodePerformanceIndex( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: return QLocale().toString( node->schedulePerformanceIndex( m_now, id() ), 'f', 2 ); case Qt::EditRole: return node->schedulePerformanceIndex( m_now, id() ); case Qt::ToolTipRole: return xi18nc( "@info:tooltip", "Schedule Performance Index at %1: %2", m_now.toString(), QLocale().toString( node->schedulePerformanceIndex( m_now, id() ), 'f', 2 ) ); case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Qt::ForegroundRole: return QColor(node->schedulePerformanceIndex( m_now, id() ) < 1.0 ? Qt::red : Qt::black); } return QVariant(); } QVariant NodeModel::nodeIsCritical( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->isCritical( id() ); case Qt::ToolTipRole: case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskNormalColor(); case Node::Type_Milestone: return m_project->config().milestoneNormalColor(); default: break; } } } return QVariant(); } QVariant NodeModel::nodeInCriticalPath( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: return node->inCriticalPath( id() ); case Qt::ToolTipRole: case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); case Role::Foreground: { if ( ! m_project ) { break; } switch ( node->type() ) { case Node::Type_Task: return m_project->config().taskNormalColor(); case Node::Type_Milestone: return m_project->config().milestoneNormalColor(); default: break; } } } return QVariant(); } QVariant NodeModel::wpOwnerName( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } if ( t->wpTransmitionStatus() == WorkPackage::TS_None ) { return xi18nc( "Not available", "NA" ); } return t->wpOwnerName(); } case Qt::ToolTipRole: { const Task *task = dynamic_cast( node ); if ( task == 0 ) { return QVariant(); } int sts = task->wpTransmitionStatus(); QString t = wpTransmitionTime( node, Qt::DisplayRole ).toString(); if ( sts == WorkPackage::TS_Send ) { return xi18nc( "@info:tooltip", "Latest work package sent to %1 at %2", static_cast( node )->wpOwnerName(), t ); } if ( sts == WorkPackage::TS_Receive ) { return xi18nc( "@info:tooltip", "Latest work package received from %1 at %2", static_cast( node )->wpOwnerName(), t ); } return xi18nc( "@info:tooltip", "Not available" ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::wpTransmitionStatus( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } if ( t->wpTransmitionStatus() == WorkPackage::TS_None ) { return xi18nc( "Not available", "NA" ); } return WorkPackage::transmitionStatusToString( t->wpTransmitionStatus(), true ); } case Qt::EditRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } return WorkPackage::transmitionStatusToString( t->wpTransmitionStatus(), false ); } case Qt::ToolTipRole: case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::wpTransmitionTime( const Node *node, int role ) const { switch ( role ) { case Qt::DisplayRole: case Qt::EditRole: { const Task *t = dynamic_cast( node ); if ( t == 0 ) { return QVariant(); } if ( t->wpTransmitionStatus() == WorkPackage::TS_None ) { return xi18nc( "Not available", "NA" ); } return QLocale().toString( t->wpTransmitionTime(), QLocale::ShortFormat ); } case Qt::ToolTipRole: { const Task *task = dynamic_cast( node ); if ( task == 0 ) { return QVariant(); } int sts = task->wpTransmitionStatus(); QString t = wpTransmitionTime( node, Qt::DisplayRole ).toString(); if ( sts == WorkPackage::TS_Send ) { return xi18nc( "@info:tooltip", "Latest work package sent: %1", t ); } if ( sts == WorkPackage::TS_Receive ) { return xi18nc( "@info:tooltip", "Latest work package received: %1", t ); } return xi18nc( "@info:tooltip", "Not available" ); } case Qt::StatusTipRole: case Qt::WhatsThisRole: return QVariant(); } return QVariant(); } QVariant NodeModel::data( const Node *n, int property, int role ) const { QVariant result; switch ( property ) { // Edited by user case NodeName: result = name( n, role ); break; case NodeType: result = type( n, role ); break; case NodeResponsible: result = leader( n, role ); break; case NodeAllocation: result = allocation( n, role ); break; case NodeEstimateType: result = estimateType( n, role ); break; case NodeEstimateCalendar: result = estimateCalendar( n, role ); break; case NodeEstimate: result = estimate( n, role ); break; case NodeOptimisticRatio: result = optimisticRatio( n, role ); break; case NodePessimisticRatio: result = pessimisticRatio( n, role ); break; case NodeRisk: result = riskType( n, role ); break; case NodeConstraint: result = constraint( n, role ); break; case NodeConstraintStart: result = constraintStartTime( n, role ); break; case NodeConstraintEnd: result = constraintEndTime( n, role ); break; case NodeRunningAccount: result = runningAccount( n, role ); break; case NodeStartupAccount: result = startupAccount( n, role ); break; case NodeStartupCost: result = startupCost( n, role ); break; case NodeShutdownAccount: result = shutdownAccount( n, role ); break; case NodeShutdownCost: result = shutdownCost( n, role ); break; case NodeDescription: result = description( n, role ); break; // Based on edited values case NodeExpected: result = pertExpected( n->estimate(), role ); break; case NodeVarianceEstimate: result = varianceEstimate( n->estimate(), role ); break; case NodeOptimistic: result = optimisticEstimate( n->estimate(), role ); break; case NodePessimistic: result = pessimisticEstimate( n->estimate(), role ); break; // After scheduling case NodeStartTime: result = startTime( n, role ); break; case NodeEndTime: result = endTime( n, role ); break; case NodeEarlyStart: result = earlyStart( n, role ); break; case NodeEarlyFinish: result = earlyFinish( n, role ); break; case NodeLateStart: result = lateStart( n, role ); break; case NodeLateFinish: result = lateFinish( n, role ); break; case NodePositiveFloat: result = positiveFloat( n, role ); break; case NodeFreeFloat: result = freeFloat( n, role ); break; case NodeNegativeFloat: result = negativeFloat( n, role ); break; case NodeStartFloat: result = startFloat( n, role ); break; case NodeFinishFloat: result = finishFloat( n, role ); break; case NodeAssignments: result = assignedResources( n, role ); break; // Based on scheduled values case NodeDuration: result = duration( n, role ); break; case NodeVarianceDuration: result = varianceDuration( n, role ); break; case NodeOptimisticDuration: result = optimisticDuration( n, role ); break; case NodePessimisticDuration: result = pessimisticDuration( n, role ); break; // Completion case NodeStatus: result = status( n, role ); break; case NodeCompleted: result = completed( n, role ); break; case NodePlannedEffort: result = plannedEffortTo( n, role ); break; case NodeActualEffort: result = actualEffortTo( n, role ); break; case NodeRemainingEffort: result = remainingEffort( n, role ); break; case NodePlannedCost: result = plannedCostTo( n, role ); break; case NodeActualCost: result = actualCostTo( n, role ); break; case NodeActualStart: result = startedTime( n, role ); break; case NodeStarted: result = isStarted( n, role ); break; case NodeActualFinish: result = finishedTime( n, role ); break; case NodeFinished: result = isFinished( n, role ); break; case NodeStatusNote: result = note( n, role ); break; // Scheduling errors case NodeSchedulingStatus: result = nodeSchedulingStatus( n, role ); break; case NodeNotScheduled: result = nodeIsNotScheduled( n, role ); break; case NodeAssignmentMissing: result = resourceIsMissing( n, role ); break; case NodeResourceOverbooked: result = resourceIsOverbooked( n, role ); break; case NodeResourceUnavailable: result = resourceIsNotAvailable( n, role ); break; case NodeConstraintsError: result = schedulingConstraintsError( n, role ); break; case NodeEffortNotMet: result = effortNotMet( n, role ); break; case NodeSchedulingError: result = schedulingError( n, role ); break; case NodeWBSCode: result = wbsCode( n, role ); break; case NodeLevel: result = nodeLevel( n, role ); break; // Performance case NodeBCWS: result = nodeBCWS( n, role ); break; case NodeBCWP: result = nodeBCWP( n, role ); break; case NodeACWP: result = nodeACWP( n, role ); break; case NodePerformanceIndex: result = nodePerformanceIndex( n, role ); break; case NodeCritical: result = nodeIsCritical( n, role ); break; case NodeCriticalPath: result = nodeInCriticalPath( n, role ); break; case WPOwnerName: result = wpOwnerName( n, role ); break; case WPTransmitionStatus: result = wpTransmitionStatus( n, role ); break; case WPTransmitionTime: result = wpTransmitionTime( n, role ); break; default: //debugPlan<<"Invalid property number: "<name() ) { return 0; } KUndo2MagicString s = kundo2_i18n( "Modify name" ); switch ( node->type() ) { case Node::Type_Task: s = kundo2_i18n( "Modify task name" ); break; case Node::Type_Milestone: s = kundo2_i18n( "Modify milestone name" ); break; case Node::Type_Summarytask: s = kundo2_i18n( "Modify summarytask name" ); break; case Node::Type_Project: s = kundo2_i18n( "Modify project name" ); break; } return new NodeModifyNameCmd( *node, value.toString(), s ); } } return 0; } KUndo2Command *NodeModel::setLeader( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { if ( value.toString() != node->leader() ) { return new NodeModifyLeaderCmd( *node, value.toString(), kundo2_i18n( "Modify responsible" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setAllocation( Node */*node*/, const QVariant &/*value*/, int /*role*/ ) { return 0; } KUndo2Command *NodeModel::setDescription( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: if ( value.toString() == node->description() ) { return 0; } return new NodeModifyDescriptionCmd( *node, value.toString(), kundo2_i18n( "Modify task description" ) ); } return 0; } KUndo2Command *NodeModel::setType( Node *, const QVariant &, int ) { return 0; } KUndo2Command *NodeModel::setConstraint( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { Node::ConstraintType v; QStringList lst = node->constraintList( false ); if ( lst.contains( value.toString() ) ) { v = Node::ConstraintType( lst.indexOf( value.toString() ) ); } else { v = Node::ConstraintType( value.toInt() ); } //debugPlan<constraint() ) { return new NodeModifyConstraintCmd( *node, v, kundo2_i18n( "Modify constraint type" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setConstraintStartTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { QDateTime dt = value.toDateTime(); dt.setTime( QTime( dt.time().hour(), dt.time().minute(), 0 ) ); // reset possible secs/msecs if ( dt != node->constraintStartTime() ) { return new NodeModifyConstraintStartTimeCmd( *node, dt, kundo2_i18n( "Modify constraint start time" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setConstraintEndTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { QDateTime dt = value.toDateTime(); dt.setTime( QTime( dt.time().hour(), dt.time().minute(), 0 ) ); // reset possible secs/msecs if ( dt != node->constraintEndTime() ) { return new NodeModifyConstraintEndTimeCmd( *node, dt, kundo2_i18n( "Modify constraint end time" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setEstimateType( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: { Estimate::Type v; QStringList lst = node->estimate()->typeToStringList( false ); if ( lst.contains( value.toString() ) ) { v = Estimate::Type( lst.indexOf( value.toString() ) ); } else { v = Estimate::Type( value.toInt() ); } if ( v != node->estimate()->type() ) { return new ModifyEstimateTypeCmd( *node, node->estimate()->type(), v, kundo2_i18n( "Modify estimate type" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setEstimateCalendar( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: { Calendar *c = 0; Calendar *old = node->estimate()->calendar(); if ( value.toInt() > 0 ) { QStringList lst = estimateCalendar( node, Role::EnumList ).toStringList(); if ( value.toInt() < lst.count() ) { c = m_project->calendarByName( lst.at( value.toInt() ) ); } } if ( c != old ) { return new ModifyEstimateCalendarCmd( *node, old, c, kundo2_i18n( "Modify estimate calendar" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setEstimate( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: { double d; Duration::Unit unit; if ( value.toList().count() == 2 ) { d = value.toList()[0].toDouble(); unit = static_cast( value.toList()[1].toInt() ); } else if ( value.canConvert() ) { bool ok = Duration::valueFromString( value.toString(), d, unit ); if ( ! ok ) { return 0; } } else { return 0; } //debugPlan<"<estimate()->expectedEstimate() ) { if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Modify estimate" ) ); cmd->addCommand( new ModifyEstimateCmd( *node, node->estimate()->expectedEstimate(), d ) ); } if ( unit != node->estimate()->unit() ) { if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Modify estimate" ) ); cmd->addCommand( new ModifyEstimateUnitCmd( *node, node->estimate()->unit(), unit ) ); } if ( cmd ) { return cmd; } break; } default: break; } return 0; } KUndo2Command *NodeModel::setOptimisticRatio( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: if ( value.toInt() != node->estimate()->optimisticRatio() ) { return new EstimateModifyOptimisticRatioCmd( *node, node->estimate()->optimisticRatio(), value.toInt(), kundo2_i18n( "Modify optimistic estimate" ) ); } break; default: break; } return 0; } KUndo2Command *NodeModel::setPessimisticRatio( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: if ( value.toInt() != node->estimate()->pessimisticRatio() ) { return new EstimateModifyPessimisticRatioCmd( *node, node->estimate()->pessimisticRatio(), value.toInt(), kundo2_i18n( "Modify pessimistic estimate" ) ); } default: break; } return 0; } KUndo2Command *NodeModel::setRiskType( Node *node, const QVariant &value, int role ) { if ( node->estimate() == 0 ) { return 0; } switch ( role ) { case Qt::EditRole: { int val = 0; QStringList lst = node->estimate()->risktypeToStringList( false ); if ( lst.contains( value.toString() ) ) { val = lst.indexOf( value.toString() ); } else { val = value.toInt(); } if ( val != node->estimate()->risktype() ) { Estimate::Risktype v = Estimate::Risktype( val ); return new EstimateModifyRiskCmd( *node, node->estimate()->risktype(), v, kundo2_i18n( "Modify risk type" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setRunningAccount( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { //debugPlan<name(); QStringList lst = runningAccount( node, Role::EnumList ).toStringList(); if ( value.toInt() < lst.count() ) { Account *a = m_project->accounts().findAccount( lst.at( value.toInt() ) ); Account *old = node->runningAccount(); if ( old != a ) { return new NodeModifyRunningAccountCmd( *node, old, a, kundo2_i18n( "Modify running account" ) ); } } break; } default: break; } return 0; } KUndo2Command *NodeModel::setStartupAccount( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { //debugPlan<name(); QStringList lst = startupAccount( node, Role::EnumList ).toStringList(); if ( value.toInt() < lst.count() ) { Account *a = m_project->accounts().findAccount( lst.at( value.toInt() ) ); Account *old = node->startupAccount(); //debugPlan<<(value.toInt())<<";"<<(lst.at( value.toInt()))<<":"<startupCost() ) { return new NodeModifyStartupCostCmd( *node, v, kundo2_i18n( "Modify startup cost" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setShutdownAccount( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { //debugPlan<name(); QStringList lst = shutdownAccount( node, Role::EnumList ).toStringList(); if ( value.toInt() < lst.count() ) { Account *a = m_project->accounts().findAccount( lst.at( value.toInt() ) ); Account *old = node->shutdownAccount(); if ( old != a ) { return new NodeModifyShutdownAccountCmd( *node, old, a, kundo2_i18n( "Modify shutdown account" ) ); } } break; } default: break; } return 0; } KUndo2Command *NodeModel::setShutdownCost( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { double v = value.toDouble(); if ( v != node->shutdownCost() ) { return new NodeModifyShutdownCostCmd( *node, v, kundo2_i18n( "Modify shutdown cost" ) ); } break; } default: break; } return 0; } KUndo2Command *NodeModel::setCompletion( Node */*node*/, const QVariant &/*value*/, int /*role*/ ) { return 0; } KUndo2Command *NodeModel::setRemainingEffort( Node *node, const QVariant &value, int role ) { if ( role == Qt::EditRole && node->type() == Node::Type_Task ) { Task *t = static_cast( node ); double d( value.toList()[0].toDouble() ); Duration::Unit unit = static_cast( value.toList()[1].toInt() ); Duration dur( d, unit ); return new ModifyCompletionRemainingEffortCmd( t->completion(), QDate::currentDate(), dur, kundo2_i18n( "Modify remaining effort" ) ); } return 0; } KUndo2Command *NodeModel::setActualEffort( Node *node, const QVariant &value, int role ) { if ( role == Qt::EditRole && node->type() == Node::Type_Task ) { Task *t = static_cast( node ); double d( value.toList()[0].toDouble() ); Duration::Unit unit = static_cast( value.toList()[1].toInt() ); Duration dur( d, unit ); return new ModifyCompletionActualEffortCmd( t->completion(), QDate::currentDate(), dur, kundo2_i18n( "Modify actual effort" ) ); } return 0; } KUndo2Command *NodeModel::setStartedTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { Task *t = qobject_cast( node ); if ( t == 0 ) { return 0; } MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify actual start time" ) ); if ( ! t->completion().isStarted() ) { m->addCommand( new ModifyCompletionStartedCmd( t->completion(), true ) ); } m->addCommand( new ModifyCompletionStartTimeCmd( t->completion(), value.toDateTime() ) ); if ( t->type() == Node::Type_Milestone ) { m->addCommand( new ModifyCompletionFinishedCmd( t->completion(), true ) ); m->addCommand( new ModifyCompletionFinishTimeCmd( t->completion(), value.toDateTime() ) ); if ( t->completion().percentFinished() < 100 ) { Completion::Entry *e = new Completion::Entry( 100, Duration::zeroDuration, Duration::zeroDuration ); m->addCommand( new AddCompletionEntryCmd( t->completion(), value.toDate(), e ) ); } } return m; } default: break; } return 0; } KUndo2Command *NodeModel::setFinishedTime( Node *node, const QVariant &value, int role ) { switch ( role ) { case Qt::EditRole: { Task *t = qobject_cast( node ); if ( t == 0 ) { return 0; } MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify actual finish time" ) ); if ( ! t->completion().isFinished() ) { m->addCommand( new ModifyCompletionFinishedCmd( t->completion(), true ) ); if ( t->completion().percentFinished() < 100 ) { Completion::Entry *e = new Completion::Entry( 100, Duration::zeroDuration, Duration::zeroDuration ); m->addCommand( new AddCompletionEntryCmd( t->completion(), value.toDate(), e ) ); } } m->addCommand( new ModifyCompletionFinishTimeCmd( t->completion(), value.toDateTime() ) ); if ( t->type() == Node::Type_Milestone ) { m->addCommand( new ModifyCompletionStartedCmd( t->completion(), true ) ); m->addCommand( new ModifyCompletionStartTimeCmd( t->completion(), value.toDateTime() ) ); } return m; } default: break; } return 0; } //---------------------------- NodeItemModel::NodeItemModel( QObject *parent ) : ItemModelBase( parent ), m_node( 0 ), m_projectshown( false ) { setReadOnly( NodeModel::NodeDescription, true ); } NodeItemModel::~NodeItemModel() { } void NodeItemModel::setShowProject( bool on ) { m_projectshown = on; reset(); emit projectShownChanged( on ); } void NodeItemModel::slotNodeToBeInserted( Node *parent, int row ) { //debugPlan<name()<<"; "<parentNode()->name()<<"-->"<name(); Q_ASSERT( node->parentNode() == m_node ); endInsertRows(); m_node = 0; emit nodeInserted( node ); } void NodeItemModel::slotNodeToBeRemoved( Node *node ) { //debugPlan<name(); Q_ASSERT( m_node == 0 ); m_node = node; int row = index( node ).row(); beginRemoveRows( index( node->parentNode() ), row, row ); } void NodeItemModel::slotNodeRemoved( Node *node ) { //debugPlan<name(); Q_ASSERT( node == m_node ); #ifdef NDEBUG Q_UNUSED(node) #endif endRemoveRows(); m_node = 0; } void NodeItemModel::slotNodeToBeMoved( Node *node, int pos, Node *newParent, int newPos ) { //debugPlan<parentNode()->name()<name()<parentNode() ), pos, pos, index( newParent ), newPos ); } void NodeItemModel::slotNodeMoved( Node *node ) { Q_UNUSED( node ); //debugPlan<parentNode()->name()<parentNode()->indexOf( node ); endMoveRows(); } void NodeItemModel::slotLayoutChanged() { //debugPlan<name(); emit layoutAboutToBeChanged(); emit layoutChanged(); } void NodeItemModel::slotProjectCalculated(ScheduleManager *sm) { debugPlan<allNodes() ) { int row = n->parentNode()->indexOf( n ); QModelIndex idx = createIndex( row, NodeModel::NodeWBSCode, n ); emit dataChanged( idx, idx ); } } void NodeItemModel::setProject( Project *project ) { if ( m_project ) { disconnect(m_project, SIGNAL(aboutToBeDeleted()), this, SLOT(projectDeleted())); disconnect( m_project, SIGNAL(localeChanged()), this, SLOT(slotLayoutChanged()) ); disconnect( m_project, SIGNAL(wbsDefinitionChanged()), this, SLOT(slotWbsDefinitionChanged()) ); disconnect( m_project, SIGNAL(nodeChanged(Node*)), this, SLOT(slotNodeChanged(Node*)) ); disconnect( m_project, SIGNAL(nodeToBeAdded(Node*,int)), this, SLOT(slotNodeToBeInserted(Node*,int)) ); disconnect( m_project, SIGNAL(nodeToBeRemoved(Node*)), this, SLOT(slotNodeToBeRemoved(Node*)) ); disconnect( m_project, SIGNAL(nodeToBeMoved(Node*,int,Node*,int)), this, SLOT(slotNodeToBeMoved(Node*,int,Node*,int)) ); disconnect( m_project, SIGNAL(nodeMoved(Node*)), this, SLOT(slotNodeMoved(Node*)) ); disconnect( m_project, SIGNAL(nodeAdded(Node*)), this, SLOT(slotNodeInserted(Node*)) ); disconnect( m_project, SIGNAL(nodeRemoved(Node*)), this, SLOT(slotNodeRemoved(Node*)) ); disconnect( m_project, SIGNAL(projectCalculated(ScheduleManager*)), this, SLOT(slotProjectCalculated(ScheduleManager*))); } m_project = project; debugPlan<"<isBaselined(); flags |= Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; switch ( index.column() ) { case NodeModel::NodeName: // name flags |= Qt::ItemIsEditable; break; case NodeModel::NodeType: break; // Node type case NodeModel::NodeResponsible: // Responsible flags |= Qt::ItemIsEditable; break; case NodeModel::NodeAllocation: // allocation if ( n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeEstimateType: // estimateType { if ( ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeEstimate: // estimate { if ( ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeOptimisticRatio: // optimisticRatio case NodeModel::NodePessimisticRatio: // pessimisticRatio { if ( ! baselined && n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeEstimateCalendar: { if ( ! baselined && n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeRisk: // risktype { if ( ! baselined && n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeConstraint: // constraint type if ( ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeConstraintStart: { // constraint start if ( ! baselined && n->type() == Node::Type_Project ) { flags |= Qt::ItemIsEditable; break; } if ( ! baselined && ! ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { break; } flags |= Qt::ItemIsEditable; break; } case NodeModel::NodeConstraintEnd: { // constraint end if ( ! baselined && n->type() == Node::Type_Project ) { flags |= Qt::ItemIsEditable; break; } if ( ! baselined && ! ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { break; } flags |= Qt::ItemIsEditable; break; } case NodeModel::NodeRunningAccount: // running account if ( ! baselined && n->type() == Node::Type_Task ) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeStartupAccount: // startup account case NodeModel::NodeStartupCost: // startup cost case NodeModel::NodeShutdownAccount: // shutdown account case NodeModel::NodeShutdownCost: { // shutdown cost if ( ! baselined && ( n->type() == Node::Type_Task || n->type() == Node::Type_Milestone ) ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeDescription: // description flags |= Qt::ItemIsEditable; break; default: break; } Task *t = static_cast( n ); if ( manager() && t->isScheduled( id() ) ) { if ( ! t->completion().isStarted() ) { switch ( index.column() ) { case NodeModel::NodeActualStart: flags |= Qt::ItemIsEditable; break; case NodeModel::NodeActualFinish: if ( t->type() == Node::Type_Milestone ) { flags |= Qt::ItemIsEditable; } break; case NodeModel::NodeCompleted: if ( t->state() & Node::State_ReadyToStart ) { flags |= Qt::ItemIsEditable; } break; default: break; } } else if ( ! t->completion().isFinished() ) { switch ( index.column() ) { case NodeModel::NodeActualFinish: case NodeModel::NodeCompleted: case NodeModel::NodeRemainingEffort: flags |= Qt::ItemIsEditable; break; case NodeModel::NodeActualEffort: if ( t->completion().entrymode() == Completion::EnterEffortPerTask || t->completion().entrymode() == Completion::EnterEffortPerResource ) { flags |= Qt::ItemIsEditable; } break; default: break; } } } } return flags; } QModelIndex NodeItemModel::parent( const QModelIndex &index ) const { if ( ! index.isValid() ) { return QModelIndex(); } Node *n = node( index ); if ( n == 0 || n == m_project ) { return QModelIndex(); } Node *p = n->parentNode(); if ( p == m_project ) { return m_projectshown ? createIndex( 0, 0, p ) : QModelIndex(); } int row = p->parentNode()->indexOf( p ); if ( row == -1 ) { return QModelIndex(); } return createIndex( row, 0, p ); } QModelIndex NodeItemModel::index( int row, int column, const QModelIndex &parent ) const { if ( parent.isValid() ) { Q_ASSERT( parent.model() == this ); } //debugPlan<= columnCount() || row < 0 ) { //debugPlan<= p->numChildren() ) { errorPlan<name()<<" row too high"<childNode( row ); QModelIndex idx = createIndex(row, column, n); //debugPlan<parentNode(); if ( par ) { //debugPlan<"<indexOf( node ), column, const_cast(node) ); } if ( m_projectshown && node == m_project ) { return createIndex( 0, column, m_project ); } //debugPlan<( node ); if ( task == 0 ) { return false; } switch ( role ) { case Qt::EditRole: { MacroCommand *cmd = 0; QStringList res = m_project->resourceNameList(); QStringList req = node->requestNameList(); QStringList alloc; foreach ( const QString &s, value.toString().split( QRegExp(" *, *"), QString::SkipEmptyParts ) ) { alloc << s.trimmed(); } // first add all new resources (to "default" group) ResourceGroup *pargr = m_project->groupByName( i18n( "Resources" ) ); foreach ( const QString &s, alloc ) { Resource *r = m_project->resourceByName( s.trimmed() ); if ( r != 0 ) { continue; } if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Add resource" ) ); if ( pargr == 0 ) { pargr = new ResourceGroup(); pargr->setName( i18n( "Resources" ) ); cmd->addCommand( new AddResourceGroupCmd( m_project, pargr ) ); //debugPlan<<"add group:"<name(); } r = new Resource(); r->setName( s.trimmed() ); cmd->addCommand( new AddResourceCmd( pargr, r ) ); //debugPlan<<"add resource:"<name(); emit executeCommand( cmd ); cmd = 0; } KUndo2MagicString c = kundo2_i18n( "Modify resource allocations" ); // Handle deleted requests foreach ( const QString &s, req ) { // if a request is not in alloc, it must have been be removed by the user if ( alloc.indexOf( s ) == -1 ) { // remove removed resource request ResourceRequest *r = node->resourceRequest( s ); if ( r ) { if ( cmd == 0 ) cmd = new MacroCommand( c ); //debugPlan<<"delete request:"<resource()->name()<<" group:"<parent()->group()->name(); cmd->addCommand( new RemoveResourceRequestCmd( r->parent(), r ) ); } } } // Handle new requests QMap groupmap; foreach ( const QString &s, alloc ) { // if an allocation is not in req, it must be added if ( req.indexOf( s ) == -1 ) { ResourceGroup *pargr = 0; Resource *r = m_project->resourceByName( s ); if ( r == 0 ) { // Handle request to non exixting resource pargr = m_project->groupByName( i18n( "Resources" ) ); if ( pargr == 0 ) { pargr = new ResourceGroup(); pargr->setName( i18n( "Resources" ) ); cmd->addCommand( new AddResourceGroupCmd( m_project, pargr ) ); //debugPlan<<"add group:"<name(); } r = new Resource(); r->setName( s ); cmd->addCommand( new AddResourceCmd( pargr, r ) ); //debugPlan<<"add resource:"<name(); emit executeCommand( cmd ); cmd = 0; } else { pargr = r->parentGroup(); //debugPlan<<"add '"<name()<<"' to group:"<resourceGroupRequest( pargr ); if ( g == 0 ) { g = groupmap.value( pargr ); } if ( g == 0 ) { // create a group request if ( cmd == 0 ) cmd = new MacroCommand( c ); g = new ResourceGroupRequest( pargr ); cmd->addCommand( new AddResourceGroupRequestCmd( *task, g ) ); groupmap.insert( pargr, g ); //debugPlan<<"add group request:"<addCommand( new AddResourceRequestCmd( g, new ResourceRequest( r, r->units() ) ) ); //debugPlan<<"add request:"<name()<<" group:"<name()<type() == Node::Type_Task ) { Completion &c = static_cast( node )->completion(); QDateTime dt = QDateTime::currentDateTime(); QDate date = dt.date(); MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify completion" ) ); if ( ! c.isStarted() ) { m->addCommand( new ModifyCompletionStartTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionStartedCmd( c, true ) ); } m->addCommand( new ModifyCompletionPercentFinishedCmd( c, date, value.toInt() ) ); if ( value.toInt() == 100 ) { m->addCommand( new ModifyCompletionFinishTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionFinishedCmd( c, true ) ); } emit executeCommand( m ); // also adds a new entry if necessary if ( c.entrymode() == Completion::EnterCompleted ) { Duration planned = static_cast( node )->plannedEffort( m_nodemodel.id() ); Duration actual = ( planned * value.toInt() ) / 100; debugPlan<execute(); m->addCommand( cmd ); cmd = new ModifyCompletionRemainingEffortCmd( c, date, planned - actual ); cmd->execute(); m->addCommand( cmd ); } return true; } if ( node->type() == Node::Type_Milestone ) { Completion &c = static_cast( node )->completion(); if ( value.toInt() > 0 ) { QDateTime dt = QDateTime::currentDateTime(); QDate date = dt.date(); MacroCommand *m = new MacroCommand( kundo2_i18n( "Set finished" ) ); m->addCommand( new ModifyCompletionStartTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionStartedCmd( c, true ) ); m->addCommand( new ModifyCompletionFinishTimeCmd( c, dt ) ); m->addCommand( new ModifyCompletionFinishedCmd( c, true ) ); m->addCommand( new ModifyCompletionPercentFinishedCmd( c, date, 100 ) ); emit executeCommand( m ); // also adds a new entry if necessary return true; } return false; } return false; } QVariant NodeItemModel::data( const QModelIndex &index, int role ) const { if ( role == Qt::TextAlignmentRole ) { return headerData( index.column(), Qt::Horizontal, role ); } Node *n = node( index ); if ( role == Role::Object ) { return n ? QVariant::fromValue( static_cast( n ) ) : QVariant(); } QVariant result; if ( n != 0 ) { result = m_nodemodel.data( n, index.column(), role ); //debugPlan<name()<<": "<numChildren(); } Qt::DropActions NodeItemModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } QStringList NodeItemModel::mimeTypes() const { return QStringList() << "application/x-vnd.kde.plan.nodeitemmodel.internal" << "application/x-vnd.kde.plan.resourceitemmodel.internal" << "application/x-vnd.kde.plan.project" << "text/uri-list"; } QMimeData *NodeItemModel::mimeData( const QModelIndexList & indexes ) const { QMimeData *m = new QMimeData(); QByteArray encodedData; QDataStream stream(&encodedData, QIODevice::WriteOnly); QList rows; foreach (const QModelIndex &index, indexes) { if ( index.isValid() && !rows.contains( index.row() ) ) { //debugPlan<id(); } } } m->setData("application/x-vnd.kde.plan.nodeitemmodel.internal", encodedData); return m; } bool NodeItemModel::dropAllowed( const QModelIndex &index, int dropIndicatorPosition, const QMimeData *data ) { debugPlan; if ( m_projectshown && ! index.isValid() ) { return false; } Node *dn = node( index ); // returns project if ! index.isValid() if ( dn == 0 ) { errorPlan<<"no node (or project) to drop on!"; return false; // hmmm } if ( data->hasFormat("application/x-vnd.kde.plan.resourceitemmodel.internal") ) { switch ( dropIndicatorPosition ) { case ItemModelBase::OnItem: if ( index.column() == NodeModel::NodeAllocation ) { debugPlan<<"resource:"<type() == Node::Type_Task); return dn->type() == Node::Type_Task; } else if ( index.column() == NodeModel::NodeResponsible ) { debugPlan<<"resource:"<hasFormat( "application/x-vnd.kde.plan.nodeitemmodel.internal") || data->hasFormat( "application/x-vnd.kde.plan.project" ) || data->hasUrls() ) { switch ( dropIndicatorPosition ) { case ItemModelBase::AboveItem: case ItemModelBase::BelowItem: // dn == sibling, if not project if ( dn == m_project ) { return dropAllowed( dn, data ); } return dropAllowed( dn->parentNode(), data ); case ItemModelBase::OnItem: // dn == new parent return dropAllowed( dn, data ); default: break; } } else { debugPlan<<"Unknown mimetype"; } return false; } QList NodeItemModel::resourceList( QDataStream &stream ) { QList lst; while (!stream.atEnd()) { QString id; stream >> id; debugPlan<<"id"<findResource( id ); if ( r ) { lst << r; } } debugPlan<isBaselined() && on->type() != Node::Type_Summarytask ) { return false; } if ( data->hasFormat( "application/x-vnd.kde.plan.nodeitemmodel.internal" ) ) { QByteArray encodedData = data->data( "application/x-vnd.kde.plan.nodeitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); QList lst = nodeList( stream ); foreach ( Node *n, lst ) { if ( n->type() == Node::Type_Project || on == n || on->isChildOf( n ) ) { return false; } } lst = removeChildNodes( lst ); foreach ( Node *n, lst ) { if ( ! m_project->canMoveTask( n, on ) ) { return false; } } } return true; } QList NodeItemModel::nodeList( QDataStream &stream ) { QList lst; while (!stream.atEnd()) { QString id; stream >> id; Node *node = m_project->findNode( id ); if ( node ) { lst << node; } } return lst; } QList NodeItemModel::removeChildNodes( const QList &nodes ) { QList lst; foreach ( Node *node, nodes ) { bool ins = true; foreach ( Node *n, lst ) { if ( node->isChildOf( n ) ) { //debugPlan<name()<<" is child of"<name(); ins = false; break; } } if ( ins ) { //debugPlan<<" insert"<name(); lst << node; } } QList nl = lst; QList nlst = lst; foreach ( Node *node, nl ) { foreach ( Node *n, nlst ) { if ( n->isChildOf( node ) ) { //debugPlan<name()<<" is child of"<name(); int i = nodes.indexOf( n ); lst.removeAt( i ); } } } return lst; } bool NodeItemModel::dropResourceMimeData( const QMimeData *data, Qt::DropAction action, int /*row*/, int /*column*/, const QModelIndex &parent ) { QByteArray encodedData = data->data( "application/x-vnd.kde.plan.resourceitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); Node *n = node( parent ); debugPlan<name(); if ( parent.column() == NodeModel::NodeResponsible ) { QString s; foreach ( Resource *r, resourceList( stream ) ) { s += r->name(); } if ( ! s.isEmpty() ) { if ( action == Qt::CopyAction && ! n->leader().isEmpty() ) { s += ',' + n->leader(); } KUndo2Command *cmd = m_nodemodel.setLeader( n, s, Qt::EditRole ); if ( cmd ) { emit executeCommand( cmd ); } debugPlan<type() == Node::Type_Task ) { QList lst = resourceList( stream ); if ( action == Qt::CopyAction ) { lst += static_cast( n )->requestedResources(); } KUndo2Command *cmd = createAllocationCommand( static_cast( *n ), lst ); if ( cmd ) { emit executeCommand( cmd ); } return true; } return true; } bool NodeItemModel::dropProjectMimeData( const QMimeData *data, Qt::DropAction action, int row, int /*column*/, const QModelIndex &parent ) { Node *n = node( parent ); if ( n == 0 ) { n = m_project; } debugPlan<data( "application/x-vnd.kde.plan.project" ) ); KoXmlElement element = doc.documentElement().namedItem( "project" ).toElement(); Project project; XMLLoaderObject status; status.setVersion( doc.documentElement().attribute( "version", PLAN_FILE_SYNTAX_VERSION ) ); status.setProject( &project ); if ( ! project.load( element, status ) ) { debugPlan<<"Failed to load project"; return false; } project.generateUniqueNodeIds(); KUndo2Command *cmd = new InsertProjectCmd( project, n, n->childNode( row - 1 ), kundo2_i18nc("1=project or task name", "Insert %1", project.name() ) ); emit executeCommand( cmd ); return true; } bool NodeItemModel::dropUrlMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent ) { if ( data->hasUrls() ) { QList urls = data->urls(); debugPlan<bad() ) { // d->lastErrorMessage = i18n( "Not a valid Calligra file: %1", file ); debugPlan<<"bad store"<open( "root" ) ) { // maindoc.xml debugPlan<<"No root"<device() ); KoXmlElement element = doc.documentElement().namedItem( "project" ).toElement(); Project project; XMLLoaderObject status; status.setVersion( doc.documentElement().attribute( "version", PLAN_FILE_SYNTAX_VERSION ) ); status.setProject( &project ); if ( ! project.load( element, status ) ) { debugPlan<<"Failed to load project from:"<childNode( row - 1 ), kundo2_i18n( "Insert %1", url.fileName() ) ); emit executeCommand( cmd ); return true; } KUndo2Command *NodeItemModel::createAllocationCommand( Task &task, const QList &lst ) { MacroCommand *cmd = new MacroCommand( kundo2_i18n( "Modify resource allocations" ) ); QMap groups; foreach ( Resource *r, lst ) { if ( ! groups.contains( r->parentGroup() ) && task.resourceGroupRequest( r->parentGroup() ) == 0 ) { ResourceGroupRequest *gr = new ResourceGroupRequest( r->parentGroup() ); groups[ r->parentGroup() ] = gr; cmd->addCommand( new AddResourceGroupRequestCmd( task, gr ) ); } } QList resources = task.requestedResources(); foreach ( Resource *r, lst ) { if ( resources.contains( r ) ) { continue; } ResourceGroupRequest *gr = groups.value( r->parentGroup() ); if ( gr == 0 ) { gr = task.resourceGroupRequest( r->parentGroup() ); } if ( gr == 0 ) { errorPlan<<"No group request found, cannot add resource request:"<name(); continue; } cmd->addCommand( new AddResourceRequestCmd( gr, new ResourceRequest( r, 100 ) ) ); } foreach ( Resource *r, resources ) { if ( ! lst.contains( r ) ) { ResourceGroupRequest *gr = task.resourceGroupRequest( r->parentGroup() ); ResourceRequest *rr = task.requests().find( r ); if ( gr && rr ) { cmd->addCommand( new RemoveResourceRequestCmd( gr, rr ) ); } } } if ( cmd->isEmpty() ) { delete cmd; return 0; } return cmd; } bool NodeItemModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent ) { debugPlan<hasFormat( "application/x-vnd.kde.plan.resourceitemmodel.internal" ) ) { return dropResourceMimeData( data, action, row, column, parent ); } if ( data->hasFormat( "application/x-vnd.kde.plan.nodeitemmodel.internal" ) ) { if ( action == Qt::MoveAction ) { //debugPlan<<"MoveAction"; QByteArray encodedData = data->data( "application/x-vnd.kde.plan.nodeitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); Node *par = 0; if ( parent.isValid() ) { par = node( parent ); } else { par = m_project; } QList lst = nodeList( stream ); QList nodes = removeChildNodes( lst ); // children goes with their parent foreach ( Node *n, nodes ) { if ( ! m_project->canMoveTask( n, par ) ) { //debugPlan<<"Can't move task:"<name(); return false; } } int offset = 0; MacroCommand *cmd = 0; foreach ( Node *n, nodes ) { if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Move tasks" ) ); // append nodes if dropped *on* another node, insert if dropped *after* int pos = row == -1 ? -1 : row + offset; if ( pos >= 0 && n->parentNode() == par && par->indexOf( n ) < pos ) { --pos; } if ( n->parentNode() == par ) { // avoid drop into the same position, QAbstractItemModel does not like it int crow = par->indexOf( n ); if ( ( ( pos == -1 ) && ( crow == par->numChildren() - 1 ) ) || ( pos == crow ) ) { delete cmd; cmd = 0; continue; } } cmd->addCommand( new NodeMoveCmd( m_project, n, par, pos ) ); offset++; } if ( cmd ) { emit executeCommand( cmd ); } //debugPlan<name(); return true; } } if ( data->hasFormat( "application/x-vnd.kde.plan.project" ) ) { debugPlan; return dropProjectMimeData( data, action, row, column, parent ); } if ( data->hasUrls() ) { return dropUrlMimeData( data, action, row, column, parent ); } return false; } Node *NodeItemModel::node( const QModelIndex &index ) const { Node *n = m_project; if ( index.isValid() ) { //debugPlan<( index.internalPointer() ); Q_ASSERT( n ); } return n; } void NodeItemModel::slotNodeChanged( Node *node ) { if ( node == 0 || ( ! m_projectshown && node->type() == Node::Type_Project ) ) { return; } if ( node->type() == Node::Type_Project ) { emit dataChanged( createIndex( 0, 0, node ), createIndex( 0, columnCount()-1, node ) ); return; } int row = node->parentNode()->findChildNode( node ); Q_ASSERT( row >= 0 ); emit dataChanged( createIndex( row, 0, node ), createIndex( row, columnCount()-1, node ) ); } QModelIndex NodeItemModel::insertTask( Node *node, Node *after ) { MacroCommand *cmd = new MacroCommand( kundo2_i18n( "Add task" ) ); cmd->addCommand( new TaskAddCmd( m_project, node, after ) ); if ( m_project && node->type() == Node::Type_Task ) { QMap groups; foreach ( Resource *r, m_project->autoAllocateResources() ) { if ( ! groups.contains( r->parentGroup() ) ) { ResourceGroupRequest *gr = new ResourceGroupRequest( r->parentGroup() ); cmd->addCommand( new AddResourceGroupRequestCmd( static_cast(*node), gr ) ); groups[ r->parentGroup() ] = gr; } ResourceRequest *rr = new ResourceRequest( r, 100 ); cmd->addCommand( new AddResourceRequestCmd( groups[ r->parentGroup() ], rr ) ); } } emit executeCommand( cmd ); int row = -1; if ( node->parentNode() ) { row = node->parentNode()->indexOf( node ); } if ( row != -1 ) { //debugPlan<<"Inserted: "<name()<<"; "<name(); return QModelIndex(); } QModelIndex NodeItemModel::insertSubtask( Node *node, Node *parent ) { emit executeCommand( new SubtaskAddCmd( m_project, node, parent, kundo2_i18n( "Add sub-task" ) ) ); int row = -1; if ( node->parentNode() ) { row = node->parentNode()->indexOf( node ); } if ( row != -1 ) { //debugPlan<parentNode()<<" inserted: "<name()<<"; "<name(); return QModelIndex(); } int NodeItemModel::sortRole( int column ) const { int v = Qt::DisplayRole; switch ( column ) { case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: case NodeModel::NodeActualStart: case NodeModel::NodeActualFinish: case NodeModel::NodeEarlyStart: case NodeModel::NodeEarlyFinish: case NodeModel::NodeLateStart: case NodeModel::NodeLateFinish: case NodeModel::NodeConstraintStart: case NodeModel::NodeConstraintEnd: v = Qt::EditRole; + break; case NodeModel::NodeWBSCode: v = NodeModel::SortableRole; + break; default: break; } debugPlan< lst = parentmap.values(); while ( ! lst.isEmpty() ) delete (int*)(lst.takeFirst()); } int GanttItemModel::rowCount( const QModelIndex &parent ) const { if ( m_showSpecial ) { if ( parentmap.values().contains( parent.internalPointer() ) ) { return 0; } Node *n = node( parent ); if ( n && n->type() == Node::Type_Task ) { return 5; // the task + early start + late finish ++ } } return NodeItemModel::rowCount( parent ); } QModelIndex GanttItemModel::index( int row, int column, const QModelIndex &parent ) const { if ( m_showSpecial && parent.isValid() ) { Node *p = node( parent ); if ( p->type() == Node::Type_Task ) { void *v = 0; foreach ( void *i, parentmap.values( p ) ) { if ( *( (int*)( i ) ) == row ) { v = i; break; } } if ( v == 0 ) { v = new int( row ); const_cast( this )->parentmap.insertMulti( p, v ); } return createIndex( row, column, v ); } } return NodeItemModel::index( row, column, parent ); } QModelIndex GanttItemModel::parent( const QModelIndex &idx ) const { if ( m_showSpecial ) { QList lst = parentmap.keys( idx.internalPointer() ); if ( ! lst.isEmpty() ) { Q_ASSERT( lst.count() == 1 ); return index( lst.first() ); } } return NodeItemModel::parent( idx ); } QVariant GanttItemModel::data( const QModelIndex &index, int role ) const { if ( ! index.isValid() ) { return QVariant(); } if ( role == Qt::TextAlignmentRole ) { return headerData( index.column(), Qt::Horizontal, role ); } QModelIndex idx = index; QList lst; if ( m_showSpecial ) { lst = parentmap.keys( idx.internalPointer() ); } if ( ! lst.isEmpty() ) { Q_ASSERT( lst.count() == 1 ); int row = *((int*)(idx.internalPointer())); Node *n = lst.first(); if ( role == SpecialItemTypeRole ) { return row; // 0=task, 1=early start, 2=late finish... } switch ( row ) { case 0: // the task if ( idx.column() == NodeModel::NodeType && role == KGantt::ItemTypeRole ) { switch ( n->type() ) { case Node::Type_Task: return KGantt::TypeTask; default: break; } } break; case 1: { // early start if ( role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole ) { return QVariant(); } switch ( idx.column() ) { case NodeModel::NodeName: return "Early Start"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->earlyStart( id() ); default: break; } } case 2: { // late finish if ( role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole ) { return QVariant(); } switch ( idx.column() ) { case NodeModel::NodeName: return "Late Finish"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->lateFinish( id() ); default: break; } } case 3: { // late start if ( role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole ) { return QVariant(); } switch ( idx.column() ) { case NodeModel::NodeName: return "Late Start"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->lateStart( id() ); default: break; } } case 4: { // early finish if ( role != Qt::DisplayRole && role != Qt::EditRole && role != KGantt::ItemTypeRole ) { return QVariant(); } switch ( idx.column() ) { case NodeModel::NodeName: return "Early Finish"; case NodeModel::NodeType: return KGantt::TypeEvent; case NodeModel::NodeStartTime: case NodeModel::NodeEndTime: return n->earlyFinish( id() ); default: break; } } default: return QVariant(); } idx = createIndex( idx.row(), idx.column(), n ); } else { if ( role == SpecialItemTypeRole ) { return 0; // task of some type } if ( idx.column() == NodeModel::NodeType && role == KGantt::ItemTypeRole ) { QVariant result = NodeItemModel::data( idx, Qt::EditRole ); switch ( result.toInt() ) { case Node::Type_Project: return KGantt::TypeSummary; case Node::Type_Summarytask: return KGantt::TypeSummary; case Node::Type_Milestone: return KGantt::TypeEvent; default: return m_showSpecial ? KGantt::TypeMulti : KGantt::TypeTask; } } } return NodeItemModel::data( idx, role ); } //---------------------------- MilestoneItemModel::MilestoneItemModel( QObject *parent ) : ItemModelBase( parent ) { } MilestoneItemModel::~MilestoneItemModel() { } QList MilestoneItemModel::mileStones() const { QList lst; foreach( Node* n, m_nodemap ) { if ( n->type() == Node::Type_Milestone ) { lst << n; } } return lst; } void MilestoneItemModel::slotNodeToBeInserted( Node *parent, int row ) { Q_UNUSED(parent); Q_UNUSED(row); } void MilestoneItemModel::slotNodeInserted( Node *node ) { Q_UNUSED(node); resetModel(); } void MilestoneItemModel::slotNodeToBeRemoved( Node *node ) { Q_UNUSED(node); //debugPlan<name(); /* int row = m_nodemap.values().indexOf( node ); if ( row != -1 ) { Q_ASSERT( m_nodemap.contains( node->wbsCode() ) ); Q_ASSERT( m_nodemap.keys().indexOf( node->wbsCode() ) == row ); beginRemoveRows( QModelIndex(), row, row ); m_nodemap.remove( node->wbsCode() ); endRemoveRows(); }*/ } void MilestoneItemModel::slotNodeRemoved( Node *node ) { Q_UNUSED(node); resetModel(); //endRemoveRows(); } void MilestoneItemModel::slotLayoutChanged() { //debugPlan<name(); emit layoutAboutToBeChanged(); emit layoutChanged(); } void MilestoneItemModel::slotNodeToBeMoved( Node *node, int pos, Node *newParent, int newPos ) { Q_UNUSED( node ); Q_UNUSED( pos ); Q_UNUSED( newParent ); Q_UNUSED( newPos ); } void MilestoneItemModel::slotNodeMoved( Node *node ) { Q_UNUSED( node ); resetModel(); } void MilestoneItemModel::setProject( Project *project ) { if ( m_project ) { disconnect(m_project, SIGNAL(aboutToBeDeleted()), this, SLOT(projectDeleted())); disconnect( m_project, SIGNAL(localeChanged()), this, SLOT(slotLayoutChanged()) ); disconnect( m_project, SIGNAL(wbsDefinitionChanged()), this, SLOT(slotWbsDefinitionChanged()) ); disconnect( m_project, SIGNAL(nodeChanged(Node*)), this, SLOT(slotNodeChanged(Node*)) ); disconnect( m_project, SIGNAL(nodeToBeAdded(Node*,int)), this, SLOT(slotNodeToBeInserted(Node*,int)) ); disconnect( m_project, SIGNAL(nodeToBeRemoved(Node*)), this, SLOT(slotNodeToBeRemoved(Node*)) ); disconnect(m_project, SIGNAL(nodeToBeMoved(Node*,int,Node*,int)), this, SLOT(slotNodeToBeMoved(Node*,int,Node*,int))); disconnect(m_project, SIGNAL(nodeMoved(Node*)), this, SLOT(slotNodeMoved(Node*))); disconnect( m_project, SIGNAL(nodeAdded(Node*)), this, SLOT(slotNodeInserted(Node*)) ); disconnect( m_project, SIGNAL(nodeRemoved(Node*)), this, SLOT(slotNodeRemoved(Node*)) ); } m_project = project; //debugPlan<"<allNodes() ) { m_nodemap.insert( n->wbsCode(true), n ); } } return cnt != m_nodemap.count(); } void MilestoneItemModel::resetModel() { resetData(); reset(); } Qt::ItemFlags MilestoneItemModel::flags( const QModelIndex &index ) const { Qt::ItemFlags flags = QAbstractItemModel::flags( index ); if ( !index.isValid() ) { if ( m_readWrite ) { flags |= Qt::ItemIsDropEnabled; } return flags; } if ( m_readWrite ) { flags |= Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled; switch ( index.column() ) { case NodeModel::NodeName: // name flags |= Qt::ItemIsEditable; break; case NodeModel::NodeType: break; // Node type case NodeModel::NodeResponsible: // Responsible flags |= Qt::ItemIsEditable; break; case NodeModel::NodeConstraint: // constraint type flags |= Qt::ItemIsEditable; break; case NodeModel::NodeConstraintStart: { // constraint start Node *n = node( index ); if ( n == 0 ) break; int c = n->constraint(); if ( c == Node::MustStartOn || c == Node::StartNotEarlier || c == Node::FixedInterval ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeConstraintEnd: { // constraint end Node *n = node( index ); if ( n == 0 ) break; int c = n->constraint(); if ( c == Node::MustFinishOn || c == Node::FinishNotLater || c == Node::FixedInterval ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeStartupAccount: // startup account case NodeModel::NodeStartupCost: // startup cost case NodeModel::NodeShutdownAccount: // shutdown account case NodeModel::NodeShutdownCost: { // shutdown cost Node *n = node( index ); if ( n && (n->type() == Node::Type_Task || n->type() == Node::Type_Milestone) ) { flags |= Qt::ItemIsEditable; } break; } case NodeModel::NodeDescription: // description break; default: flags &= ~Qt::ItemIsEditable; } } return flags; } QModelIndex MilestoneItemModel::parent( const QModelIndex &index ) const { Q_UNUSED(index); return QModelIndex(); } QModelIndex MilestoneItemModel::index( int row, int column, const QModelIndex &parent ) const { //debugPlan<= m_nodemap.count() ) { //debugPlan<<"No index for"<( node ) ), 0, const_cast(node) ); } QVariant MilestoneItemModel::data( const QModelIndex &index, int role ) const { QVariant result; if ( role == Qt::TextAlignmentRole ) { return headerData( index.column(), Qt::Horizontal, role ); } Node *n = node( index ); if ( n != 0 ) { if ( index.column() == NodeModel::NodeType && role == KGantt::ItemTypeRole ) { result = m_nodemodel.data( n, index.column(), Qt::EditRole ); switch ( result.toInt() ) { case Node::Type_Summarytask: return KGantt::TypeSummary; case Node::Type_Milestone: return KGantt::TypeEvent; default: return KGantt::TypeTask; } return result; } } result = m_nodemodel.data( n, index.column(), role ); return result; } bool MilestoneItemModel::setData( const QModelIndex &index, const QVariant &/*value*/, int role ) { if ( ( flags(index) &Qt::ItemIsEditable ) == 0 || role != Qt::EditRole ) { return false; } // Node *n = node( index ); switch (index.column()) { default: qWarning("data: invalid display value column %d", index.column()); return false; } return false; } QVariant MilestoneItemModel::headerData( int section, Qt::Orientation orientation, int role ) const { if ( orientation == Qt::Horizontal ) { if (role == Qt::DisplayRole || role == Qt::TextAlignmentRole || role == Qt::EditRole) { return m_nodemodel.headerData(section, role); } } if ( role == Qt::ToolTipRole ) { return NodeModel::headerData( section, role ); } return ItemModelBase::headerData(section, orientation, role); } QAbstractItemDelegate *MilestoneItemModel::createDelegate( int column, QWidget *parent ) const { switch ( column ) { case NodeModel::NodeEstimateType: return new EnumDelegate( parent ); case NodeModel::NodeEstimateCalendar: return new EnumDelegate( parent ); case NodeModel::NodeEstimate: return new DurationSpinBoxDelegate( parent ); case NodeModel::NodeOptimisticRatio: return new SpinBoxDelegate( parent ); case NodeModel::NodePessimisticRatio: return new SpinBoxDelegate( parent ); case NodeModel::NodeRisk: return new EnumDelegate( parent ); case NodeModel::NodeConstraint: return new EnumDelegate( parent ); case NodeModel::NodeRunningAccount: return new EnumDelegate( parent ); case NodeModel::NodeStartupAccount: return new EnumDelegate( parent ); case NodeModel::NodeStartupCost: return new MoneyDelegate( parent ); case NodeModel::NodeShutdownAccount: return new EnumDelegate( parent ); case NodeModel::NodeShutdownCost: return new MoneyDelegate( parent ); case NodeModel::NodeCompleted: return new TaskCompleteDelegate( parent ); case NodeModel::NodeRemainingEffort: return new DurationSpinBoxDelegate( parent ); case NodeModel::NodeActualEffort: return new DurationSpinBoxDelegate( parent ); default: return 0; } return 0; } int MilestoneItemModel::columnCount( const QModelIndex &/*parent*/ ) const { return m_nodemodel.propertyCount(); } int MilestoneItemModel::rowCount( const QModelIndex &parent ) const { //debugPlan< rows; foreach (const QModelIndex &index, indexes) { if ( index.isValid() && !rows.contains( index.row() ) ) { //debugPlan<id(); } } } m->setData("application/x-vnd.kde.plan.nodeitemmodel.internal", encodedData); return m; } bool MilestoneItemModel::dropAllowed( const QModelIndex &index, int dropIndicatorPosition, const QMimeData *data ) { //debugPlan; Node *dn = node( index ); if ( dn == 0 ) { errorPlan<<"no node to drop on!"; return false; // hmmm } switch ( dropIndicatorPosition ) { case ItemModelBase::AboveItem: case ItemModelBase::BelowItem: // dn == sibling return dropAllowed( dn->parentNode(), data ); case ItemModelBase::OnItem: // dn == new parent return dropAllowed( dn, data ); default: break; } return false; } bool MilestoneItemModel::dropAllowed( Node *on, const QMimeData *data ) { if ( !data->hasFormat("application/x-vnd.kde.plan.nodeitemmodel.internal") ) { return false; } if ( on == m_project ) { return true; } QByteArray encodedData = data->data( "application/x-vnd.kde.plan.nodeitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); QList lst = nodeList( stream ); foreach ( Node *n, lst ) { if ( on == n || on->isChildOf( n ) ) { return false; } } lst = removeChildNodes( lst ); foreach ( Node *n, lst ) { if ( ! m_project->canMoveTask( n, on ) ) { return false; } } return true; } QList MilestoneItemModel::nodeList( QDataStream &stream ) { QList lst; while (!stream.atEnd()) { QString id; stream >> id; Node *node = m_project->findNode( id ); if ( node ) { lst << node; } } return lst; } QList MilestoneItemModel::removeChildNodes( const QList &nodes ) { QList lst; foreach ( Node *node, nodes ) { bool ins = true; foreach ( Node *n, lst ) { if ( node->isChildOf( n ) ) { //debugPlan<name()<<" is child of"<name(); ins = false; break; } } if ( ins ) { //debugPlan<<" insert"<name(); lst << node; } } QList nl = lst; QList nlst = lst; foreach ( Node *node, nl ) { foreach ( Node *n, nlst ) { if ( n->isChildOf( node ) ) { //debugPlan<name()<<" is child of"<name(); int i = nodes.indexOf( n ); lst.removeAt( i ); } } } return lst; } bool MilestoneItemModel::dropMimeData( const QMimeData *data, Qt::DropAction action, int row, int /*column*/, const QModelIndex &parent ) { //debugPlan<hasFormat( "application/x-vnd.kde.plan.nodeitemmodel.internal" ) ) { return false; } if ( action == Qt::MoveAction ) { //debugPlan<<"MoveAction"; QByteArray encodedData = data->data( "application/x-vnd.kde.plan.nodeitemmodel.internal" ); QDataStream stream(&encodedData, QIODevice::ReadOnly); Node *par = 0; if ( parent.isValid() ) { par = node( parent ); } else { par = m_project; } QList lst = nodeList( stream ); QList nodes = removeChildNodes( lst ); // children goes with their parent foreach ( Node *n, nodes ) { if ( ! m_project->canMoveTask( n, par ) ) { //debugPlan<<"Can't move task:"<name(); return false; } } int offset = 0; MacroCommand *cmd = 0; foreach ( Node *n, nodes ) { if ( cmd == 0 ) cmd = new MacroCommand( kundo2_i18n( "Move tasks" ) ); // append nodes if dropped *on* another node, insert if dropped *after* int pos = row == -1 ? -1 : row + offset; cmd->addCommand( new NodeMoveCmd( m_project, n, par, pos ) ); offset++; } if ( cmd ) { emit executeCommand( cmd ); } //debugPlan<name(); return true; } return false; } Node *MilestoneItemModel::node( const QModelIndex &index ) const { Node *n = 0; if ( index.isValid() ) { //debugPlan<( index.internalPointer() ); } return n; } void MilestoneItemModel::slotNodeChanged( Node *node ) { //debugPlan<name(); if ( node == 0 ) { return; } // if ( ! m_nodemap.contains( node->wbsCode() ) || m_nodemap.value( node->wbsCode() ) != node ) { emit layoutAboutToBeChanged(); if ( resetData() ) { reset(); } else { emit layoutChanged(); } return; /* } int row = m_nodemap.values().indexOf( node ); debugPlan<name()<<": "<typeToString()<( sourceModel() ); } void NodeSortFilterProxyModel::setFilterUnscheduled( bool on ) { m_filterUnscheduled = on; invalidateFilter(); } bool NodeSortFilterProxyModel::filterAcceptsRow ( int row, const QModelIndex & parent ) const { //debugPlan<project() == 0 ) { //debugPlan<project(); return false; } if ( m_filterUnscheduled ) { QString s = sourceModel()->data( sourceModel()->index( row, NodeModel::NodeNotScheduled, parent ), Qt::EditRole ).toString(); if ( s == "true" ) { //debugPlan<<"Filtered unscheduled:"<index( row, 0, parent ); return false; } } bool accepted = QSortFilterProxyModel::filterAcceptsRow( row, parent ); //debugPlan<index( row, 0, parent )<<"accepted ="<sortRole(column)); QSortFilterProxyModel::sort(column, order); } //------------------ TaskModuleModel::TaskModuleModel( QObject *parent ) : QAbstractItemModel( parent ) { } void TaskModuleModel::addTaskModule( Project *project ) { beginInsertRows( QModelIndex(), m_modules.count(), m_modules.count() ); m_modules << project; endInsertRows(); } Qt::ItemFlags TaskModuleModel::flags( const QModelIndex &idx ) const { Qt::ItemFlags f = QAbstractItemModel::flags( idx ) | Qt::ItemIsDropEnabled; if ( idx.isValid() ) { f |= Qt::ItemIsDragEnabled; } return f; } int TaskModuleModel::columnCount (const QModelIndex &/*idx*/ ) const { return 1; } int TaskModuleModel::rowCount( const QModelIndex &idx ) const { return idx.isValid() ? 0 : m_modules.count(); } QVariant TaskModuleModel::data( const QModelIndex& idx, int role ) const { switch ( role ) { case Qt::DisplayRole: return m_modules.value( idx.row() )->name(); case Qt::ToolTipRole: return m_modules.value( idx.row() )->description(); case Qt::WhatsThisRole: return m_modules.value( idx.row() )->description(); default: break; } return QVariant(); } QVariant TaskModuleModel::headerData( int /*section*/, Qt::Orientation orientation , int role ) const { if ( orientation == Qt::Horizontal ) { switch ( role ) { case Qt::DisplayRole: return xi18nc( "@title:column", "Name" ); default: break; } } return QVariant(); } QModelIndex TaskModuleModel::parent( const QModelIndex& /*idx*/ ) const { return QModelIndex(); } QModelIndex TaskModuleModel::index( int row, int column, const QModelIndex &parent ) const { if ( parent.isValid() ) { return QModelIndex(); } return createIndex( row, column, m_modules.value( row ) ); } QStringList TaskModuleModel::mimeTypes() const { return QStringList() << "application/x-vnd.kde.plan" << "text/uri-list"; } bool TaskModuleModel::dropMimeData( const QMimeData *data, Qt::DropAction /*action*/, int /*row*/, int /*column*/, const QModelIndex &/*parent*/ ) { if ( data->hasUrls() ) { QList urls = data->urls(); debugPlan<bad() ) { // d->lastErrorMessage = i18n( "Not a valid Calligra file: %1", file ); debugPlan<<"bad store"<open( "root" ) ) { // maindoc.xml debugPlan<<"No root"<device() ); KoXmlElement element = doc.documentElement().namedItem( "project" ).toElement(); Project *project = new Project(); XMLLoaderObject status; status.setVersion( doc.documentElement().attribute( "version", PLAN_FILE_SYNTAX_VERSION ) ); status.setProject( project ); if ( project->load( element, status ) ) { stripProject( project ); addTaskModule( project ); if ( emitsignal ) { // FIXME: save destroys the project, so give it a copy (see kptview.cpp) Project p; status.setProject( &p ); p.load( element, status ); emit saveTaskModule( url, &p ); } } else { debugPlan<<"Failed to load project from:"<save( doc ); mime->setData( "application/x-vnd.kde.plan.project", document.toByteArray() ); } } return mime; } void TaskModuleModel::stripProject( Project *project ) const { foreach ( ScheduleManager *sm, project->scheduleManagers() ) { DeleteScheduleManagerCmd c( *project, sm ); } } void TaskModuleModel::loadTaskModules( const QStringList &files ) { debugPlan< * Copyright (C) 2017 Dag Andersen * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "kptcalendareditor.h" #include "kcalendar/kdatepicker.h" #include "kcalendar/kdatetable.h" //#include "kptcalendarpanel.h" #include "kptcommand.h" #include "kptcalendarmodel.h" #include "kptcalendar.h" #include "kptduration.h" #include "kptnode.h" #include "kptproject.h" #include "kpttask.h" #include "kptdatetime.h" #include "kptintervaledit.h" #include "kptdebug.h" #include #include #include #include #include #include #include #include #include #include #include namespace KPlato { //-------------------- CalendarTreeView::CalendarTreeView( QWidget *parent ) : TreeViewBase( parent ) { header()->setContextMenuPolicy( Qt::CustomContextMenu ); setModel( new CalendarItemModel() ); setSelectionBehavior( QAbstractItemView::SelectRows ); setSelectionMode( QAbstractItemView::SingleSelection ); setSelectionModel( new QItemSelectionModel( model() ) ); + setItemDelegateForColumn( CalendarItemModel::Scope, new EnumDelegate( this ) ); setItemDelegateForColumn( CalendarItemModel::TimeZone, new EnumDelegate( this ) ); // timezone #ifdef HAVE_KHOLIDAYS setItemDelegateForColumn( CalendarItemModel::HolidayRegion, new EnumDelegate( this ) ); #endif connect( header(), SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(headerContextMenuRequested(QPoint)) ); } void CalendarTreeView::headerContextMenuRequested( const QPoint &pos ) { debugPlan<logicalIndexAt(pos)<<" at"<pos()), event->globalPos() ); } void CalendarTreeView::focusInEvent ( QFocusEvent *event ) { //debugPlan; TreeViewBase::focusInEvent( event ); emit focusChanged(); } void CalendarTreeView::focusOutEvent ( QFocusEvent * event ) { //debugPlan; TreeViewBase::focusInEvent( event ); emit focusChanged(); } void CalendarTreeView::selectionChanged( const QItemSelection &sel, const QItemSelection &desel ) { //debugPlan<selectedIndexes() ) { debugPlan<selectedIndexes() ); } void CalendarTreeView::currentChanged( const QModelIndex & current, const QModelIndex & previous ) { //debugPlan; TreeViewBase::currentChanged( current, previous ); // possible bug in qt: in QAbstractItemView::SingleSelection you can select multiple items/rows selectionModel()->select( current, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows ); emit currentChanged( current ); } Calendar *CalendarTreeView::currentCalendar() const { return model()->calendar( currentIndex() ); } Calendar *CalendarTreeView::selectedCalendar() const { QModelIndexList lst = selectionModel()->selectedRows(); if ( lst.count() == 1 ) { return model()->calendar( lst.first() ); } return 0; } QList CalendarTreeView::selectedCalendars() const { QList lst; foreach ( const QModelIndex &i, selectionModel()->selectedRows() ) { Calendar *a = model()->calendar( i ); if ( a ) { lst << a; } } return lst; } void CalendarTreeView::dragMoveEvent(QDragMoveEvent *event) { if (dragDropMode() == InternalMove && (event->source() != this || !(event->possibleActions() & Qt::MoveAction))) { return; } TreeViewBase::dragMoveEvent( event ); if ( ! event->isAccepted() ) { return; } // QTreeView thinks it's ok to drop, but it might not be... event->ignore(); QModelIndex index = indexAt( event->pos() ); if ( ! index.isValid() ) { if ( model()->dropAllowed( 0, event->mimeData() ) ) { event->accept(); } return; } Calendar *c = model()->calendar( index ); if ( c == 0 ) { errorPlan<<"no calendar to drop on!"; return; // hmmm } switch ( dropIndicatorPosition() ) { case AboveItem: case BelowItem: // c == sibling // if siblings parent is me or child of me: illegal if ( model()->dropAllowed( c->parentCal(), event->mimeData() ) ) { event->accept(); } break; case OnItem: // c == new parent if ( model()->dropAllowed( c, event->mimeData() ) ) { event->accept(); } break; default: break; } } //-------------------- CalendarDayView::CalendarDayView( QWidget *parent ) : QTableView( parent ), m_readwrite( false ) { setTabKeyNavigation( false ); setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ); horizontalHeader()->setResizeMode( QHeaderView::Stretch ); m_model = new CalendarDayItemModel( this ); setModel(m_model); verticalHeader()->hide(); actionSetWork = new QAction( i18n( "Work..." ), this ); connect( actionSetWork, SIGNAL(triggered(bool)), SLOT(slotSetWork()) ); actionSetVacation = new QAction( i18n( "Non-working" ), this ); connect( actionSetVacation, SIGNAL(triggered(bool)), SLOT(slotSetVacation()) ); actionSetUndefined = new QAction( i18n( "Undefined" ), this ); connect( actionSetUndefined, SIGNAL(triggered(bool)), SLOT(slotSetUndefined()) ); } QSize CalendarDayView::sizeHint() const { QSize s = QTableView::sizeHint(); s.setHeight( horizontalHeader()->height() + rowHeight( 0 ) + frameWidth() * 2 ); return s; } void CalendarDayView::slotSetWork() { debugPlan; if ( receivers( SIGNAL(executeCommand(KUndo2Command*)) ) == 0 ) { return; } Calendar *cal = model()->calendar(); if ( cal == 0 ) { return; } QModelIndexList lst = selectionModel()->selectedIndexes(); if ( lst.isEmpty() ) { lst << currentIndex(); } if ( lst.isEmpty() ) { return; } QList days; foreach ( const QModelIndex &i, lst ) { CalendarDay *day = model()->day( i ); if ( day == 0 ) { continue; } days << day; } IntervalEditDialog *dlg = new IntervalEditDialog( cal, days, this ); connect(dlg, SIGNAL(finished(int)), SLOT(slotIntervalEditDialogFinished(int))); dlg->show(); dlg->raise(); dlg->activateWindow(); } void CalendarDayView::slotIntervalEditDialogFinished( int result ) { IntervalEditDialog *dlg = qobject_cast( sender() ); if ( dlg == 0 ) { return; } if ( result == QDialog::Accepted ) { MacroCommand *cmd = dlg->buildCommand(); if ( cmd ) { emit executeCommand( cmd ); } } dlg->deleteLater(); } void CalendarDayView::slotSetVacation() { debugPlan; if ( receivers( SIGNAL(executeCommand(KUndo2Command*)) ) == 0 ) { return; } QModelIndexList lst = selectionModel()->selectedIndexes(); if ( lst.isEmpty() ) { lst << currentIndex(); } if ( lst.isEmpty() ) { return; } bool mod = false; MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify Weekday State" ) ); foreach ( const QModelIndex &i, lst ) { CalendarDay *day = model()->day( i ); if ( day == 0 || day->state() == CalendarDay::NonWorking ) { continue; } mod = true; m->addCommand( new CalendarModifyStateCmd( model()->calendar(), day, CalendarDay::NonWorking ) ); } if ( mod ) { emit executeCommand( m ); } else { delete m; } } void CalendarDayView::slotSetUndefined() { debugPlan; if ( receivers( SIGNAL(executeCommand(KUndo2Command*)) ) == 0 ) { return; } QModelIndexList lst = selectionModel()->selectedIndexes(); if ( lst.isEmpty() ) { lst << currentIndex(); } if ( lst.isEmpty() ) { return; } bool mod = false; MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify Weekday State" ) ); foreach ( const QModelIndex &i, lst ) { CalendarDay *day = model()->day( i ); if ( day == 0 || day->state() == CalendarDay::Undefined ) { continue; } mod = true; m->addCommand( new CalendarModifyStateCmd( model()->calendar(), day, CalendarDay::Undefined ) ); } if ( mod ) { emit executeCommand( m ); } else { delete m; } } void CalendarDayView::setCurrentCalendar( Calendar *calendar ) { model()->setCalendar( calendar ); } void CalendarDayView::headerContextMenuRequested( const QPoint &/*pos*/ ) { // debugPlan<logicalIndexAt(pos)<<" at"<calendar() || model()->calendar()->isShared() ) { return; } QMenu menu; menu.addAction( actionSetWork ); menu.addAction( actionSetVacation ); menu.addAction( actionSetUndefined ); menu.exec( event->globalPos(), actionSetWork ); //emit contextMenuRequested( indexAt(event->pos()), event->globalPos() ); } void CalendarDayView::focusInEvent ( QFocusEvent *event ) { //debugPlan; QTableView::focusInEvent( event ); emit focusChanged(); } void CalendarDayView::focusOutEvent ( QFocusEvent * event ) { //debugPlan; QTableView::focusInEvent( event ); emit focusChanged(); } void CalendarDayView::selectionChanged( const QItemSelection &sel, const QItemSelection &desel ) { //debugPlan<selectedIndexes() ) { debugPlan<selectedIndexes() ); } void CalendarDayView::currentChanged( const QModelIndex & current, const QModelIndex & previous ) { //debugPlan; QTableView::currentChanged( current, previous ); // selectionModel()->select( current, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows ); emit currentChanged( current ); } CalendarDay *CalendarDayView::selectedDay() const { QModelIndexList lst = selectionModel()->selectedIndexes(); if ( lst.count() == 1 ) { return model()->day( lst.first() ); } return 0; } //----------------------------------- CalendarEditor::CalendarEditor(KoPart *part, KoDocument *doc, QWidget *parent ) : ViewBase(part, doc, parent ), m_model( new DateTableDataModel( this ) ) { setWhatsThis( xi18nc( "@info:whatsthis", "Work & Vacation Editor" "" "A calendar defines availability for resources or tasks of type Duration. " "A calendar can be specific to a resource or task, or shared by multiple resources or tasks. " "A day can be of type Undefined, Non-working day or Working day. " "A working day has one or more work intervals defined. " "" "A calendar can have sub calendars. If a day is undefined in a calendar, the parent calendar is checked. " "An Undefined day defaults to Non-working if used by a resource, or available all day if used by a task." "" "A calendar can be defined as the Default calendar. " "The default calendar is used by a working resource, when the resources calendar is not explicitly set." "" ) ); setupGui(); QVBoxLayout *l = new QVBoxLayout( this ); l->setMargin( 0 ); QSplitter *sp = new QSplitter( this ); l->addWidget( sp ); m_calendarview = new CalendarTreeView( sp ); QFrame *f = new QFrame( sp ); l = new QVBoxLayout( f ); l->setMargin( 0 ); m_dayview = new CalendarDayView( f ); l->addWidget( m_dayview ); sp = new QSplitter( f ); l->addWidget( sp ); m_datePicker = new KDatePicker( sp ); m_datePicker->setFrameStyle( QFrame::StyledPanel | QFrame::Sunken ); m_datePicker->dateTable()->setWeekNumbersEnabled( true ); m_datePicker->dateTable()->setGridEnabled( true ); m_datePicker->dateTable()->setSelectionMode( KDateTable::ExtendedSelection ); m_datePicker->dateTable()->setDateDelegate( new DateTableDateDelegate( m_datePicker->dateTable() ) ); m_datePicker->dateTable()->setModel( m_model ); m_datePicker->dateTable()->setPopupMenuEnabled( true ); m_calendarview->setDragDropMode( QAbstractItemView::InternalMove ); m_calendarview->setDropIndicatorShown( true ); m_calendarview->setDragEnabled ( true ); m_calendarview->setAcceptDrops( true ); m_calendarview->setAcceptDropsOnView( true ); connect( m_datePicker->dateTable(), SIGNAL(aboutToShowContextMenu(QMenu*,QDate)), SLOT(slotContextMenuDate(QMenu*,QDate)) ); connect( m_datePicker->dateTable(), SIGNAL(aboutToShowContextMenu(QMenu*,QList)), SLOT(slotContextMenuDate(QMenu*,QList)) ); /* const QDate date(2007,7,19); const QColor fgColor(Qt::darkGray); KDateTable::BackgroundMode bgMode = KDateTable::CircleMode; const QColor bgColor( Qt::lightGray); m_datePicker->dateTable()->setCustomDatePainting( date, fgColor, bgMode, bgColor );*/ m_calendarview->setEditTriggers( m_calendarview->editTriggers() | QAbstractItemView::EditKeyPressed ); m_dayview->setEditTriggers( m_dayview->editTriggers() | QAbstractItemView::EditKeyPressed ); m_calendarview->setDragDropMode( QAbstractItemView::InternalMove ); m_calendarview->setDropIndicatorShown ( true ); m_calendarview->setDragEnabled ( true ); m_calendarview->setAcceptDrops( true ); connect( m_calendarview->model(), SIGNAL(executeCommand(KUndo2Command*)), doc, SLOT(addCommand(KUndo2Command*)) ); connect( m_dayview->model(), SIGNAL(executeCommand(KUndo2Command*)), doc, SLOT(addCommand(KUndo2Command*)) ); connect( m_dayview, SIGNAL(executeCommand(KUndo2Command*)), doc, SLOT(addCommand(KUndo2Command*)) ); connect( m_calendarview, SIGNAL(currentChanged(QModelIndex)), this, SLOT(slotCurrentCalendarChanged(QModelIndex)) ); connect( m_calendarview, SIGNAL(selectionChanged(QModelIndexList)), this, SLOT(slotCalendarSelectionChanged(QModelIndexList)) ); connect( m_calendarview, SIGNAL(contextMenuRequested(QModelIndex,QPoint)), this, SLOT(slotContextMenuCalendar(QModelIndex,QPoint)) ); connect( m_dayview, SIGNAL(currentChanged(QModelIndex)), this, SLOT(slotCurrentDayChanged(QModelIndex)) ); connect( m_dayview, SIGNAL(selectionChanged(QModelIndexList)), this, SLOT(slotDaySelectionChanged(QModelIndexList)) ); connect( m_dayview, SIGNAL(contextMenuRequested(QModelIndex,QPoint)), this, SLOT(slotContextMenuDay(QModelIndex,QPoint)) ); connect( m_dayview->model(), SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(slotEnableActions()) ); connect( m_calendarview, SIGNAL(focusChanged()), this, SLOT(slotEnableActions()) ); connect( m_dayview, SIGNAL(focusChanged()), this, SLOT(slotEnableActions()) ); } void CalendarEditor::draw( Project &project ) { m_calendarview->setProject( &project ); m_dayview->setProject( &project ); } void CalendarEditor::draw() { } void CalendarEditor::setGuiActive( bool activate ) { //debugPlan<currentIndex().isValid() ) { m_calendarview->selectionModel()->setCurrentIndex(m_calendarview->model()->index( 0, 0 ), QItemSelectionModel::NoUpdate); } //slotSelectionChanged( m_calendarview->selectionModel()->selectedRows() ); } } void CalendarEditor::slotContextMenuDate( QMenu *menu, const QList &dates ) { if ( ! isReadWrite() ) { return; } if (!currentCalendar() || currentCalendar()->isShared()) { return; } if ( dates.isEmpty() ) { m_currentMenuDateList << m_datePicker->date(); } else { m_currentMenuDateList = dates; } menu->addAction( actionSetWork ); menu->addAction( actionSetVacation ); menu->addAction( actionSetUndefined ); } void CalendarEditor::slotContextMenuDate( QMenu *menu, const QDate &date ) { debugPlan<isShared()) { return; } m_currentMenuDateList << date; menu->addAction( actionSetWork ); menu->addAction( actionSetVacation ); menu->addAction( actionSetUndefined ); } void CalendarEditor::slotContextMenuCalendar( const QModelIndex &/*index*/, const QPoint& pos ) { if ( ! isReadWrite() || !currentCalendar() ) { return; } //debugPlan<model()->calendar( index ); if ( a ) { name = "calendareditor_calendar_popup"; } }*/ //debugPlan<model()->day( index ) ) { name = "calendareditor_day_popup"; } } debugPlan<currentCalendar(); } void CalendarEditor::slotCurrentCalendarChanged( const QModelIndex & ) { //debugPlan<setCurrentCalendar( currentCalendar() ); if ( m_model ) { m_model->setCalendar( currentCalendar() ); } } void CalendarEditor::slotCalendarSelectionChanged( const QModelIndexList& /*list */) { //debugPlan< lst = m_calendarview->selectedCalendars(); bool one = lst.count() == 1; bool more = lst.count() > 1; actionAddCalendar ->setEnabled( on && !more ); actionAddSubCalendar ->setEnabled( on && one ); actionDeleteSelection->setEnabled( on && ( one || more ) ); } void CalendarEditor::setupGui() { KActionCollection *coll = actionCollection(); QString name = "calendareditor_calendar_list"; actionAddCalendar = new QAction(koIcon("resource-calendar-insert"), i18n("Add Calendar"), this); coll->addAction("add_calendar", actionAddCalendar ); coll->setDefaultShortcut(actionAddCalendar, Qt::CTRL + Qt::Key_I); connect( actionAddCalendar , SIGNAL(triggered(bool)), SLOT(slotAddCalendar()) ); actionAddSubCalendar = new QAction(koIcon("resource-calendar-child-insert"), i18n("Add Subcalendar"), this); coll->addAction("add_subcalendar", actionAddSubCalendar ); coll->setDefaultShortcut(actionAddSubCalendar, Qt::SHIFT + Qt::CTRL + Qt::Key_I); connect( actionAddSubCalendar , SIGNAL(triggered(bool)), SLOT(slotAddSubCalendar()) ); actionDeleteSelection = new QAction(koIcon("edit-delete"), xi18nc("@action", "Delete"), this); coll->addAction("delete_calendar_selection", actionDeleteSelection ); coll->setDefaultShortcut(actionDeleteSelection, Qt::Key_Delete); connect( actionDeleteSelection, SIGNAL(triggered(bool)), SLOT(slotDeleteCalendar()) ); addAction( name, actionAddCalendar ); addAction( name, actionAddSubCalendar ); addAction( name, actionDeleteSelection ); actionSetWork = new QAction( i18n( "Work..." ), this ); connect( actionSetWork, SIGNAL(triggered(bool)), SLOT(slotSetWork()) ); actionSetVacation = new QAction( i18n( "Non-working" ), this ); connect( actionSetVacation, SIGNAL(triggered(bool)), SLOT(slotSetVacation()) ); actionSetUndefined = new QAction( i18n( "Undefined" ), this ); connect( actionSetUndefined, SIGNAL(triggered(bool)), SLOT(slotSetUndefined()) ); } void CalendarEditor::updateReadWrite( bool readwrite ) { m_calendarview->setReadWrite( readwrite ); m_dayview->setReadWrite( readwrite ); ViewBase::updateReadWrite( readwrite ); } void CalendarEditor::slotAddCalendar () { //debugPlan; // get parent through sibling Calendar *cal = m_calendarview->selectedCalendar(); Calendar *parent = cal ? cal->parentCal() : 0; int pos = parent ? parent->indexOf( cal ) : project()->indexOf( cal ); if ( pos >= 0 ) { ++pos; // after selected calendar } insertCalendar ( new Calendar(), parent, pos ); } void CalendarEditor::slotAddSubCalendar () { //debugPlan; insertCalendar ( new Calendar (), m_calendarview->selectedCalendar () ); } void CalendarEditor::insertCalendar ( Calendar *calendar, Calendar *parent, int pos ) { m_calendarview->closePersistentEditor( m_calendarview->selectionModel()->currentIndex() ); QModelIndex i = m_calendarview->model()->insertCalendar ( calendar, pos, parent ); if ( i.isValid() ) { QModelIndex p = m_calendarview->model()->parent( i ); //if (parent) debugPlan<<" parent="<name()<<":"<setExpanded( p, true ); m_calendarview->setCurrentIndex( i ); m_calendarview->edit( i ); } } void CalendarEditor::slotDeleteCalendar() { //debugPlan; m_calendarview->model()->removeCalendar( m_calendarview->selectedCalendar() ); } void CalendarEditor::slotAddInterval () { //debugPlan; /* CalendarDay *parent = m_dayview->selectedDay (); if ( parent == 0 ) { TimeInterval *ti = m_dayview->selectedInterval(); if ( ti == 0 ) { return; } parent = m_dayview->model()->parentDay( ti ); if ( parent == 0 ) { return; } } QModelIndex i = m_dayview->model()->insertInterval( new TimeInterval(), parent ); if ( i.isValid() ) { QModelIndex p = m_dayview->model()->index( parent ); m_dayview->setExpanded( p, true ); m_dayview->setCurrentIndex( i ); m_dayview->edit( i ); }*/ } void CalendarEditor::slotDeleteDaySelection() { //debugPlan; /* TimeInterval *ti = m_dayview->selectedInterval(); if ( ti != 0 ) { m_dayview->model()->removeInterval( ti ); return; } CalendarDay *day = m_dayview->selectedDay(); if ( day != 0 ) { m_dayview->model()->removeDay( day ); }*/ } void CalendarEditor::slotAddDay () { //debugPlan; /* Calendar *c = currentCalendar(); if ( c == 0 ) { return; } QDate date = QDate::currentDate(); while ( c->day( date ) ) { date = date.addDays( 1 ); } QModelIndex i = m_dayview->model()->insertDay( new CalendarDay(date, CalendarDay::NonWorking ) ); if ( i.isValid() ) { QModelIndex p = m_dayview->model()->parent( i ); m_dayview->setExpanded( p, true ); m_dayview->setCurrentIndex( i ); m_dayview->edit( i ); }*/ } void CalendarEditor::slotSetWork() { debugPlan<show(); dlg->raise(); dlg->activateWindow(); m_currentMenuDateList.clear(); } void CalendarEditor::slotIntervalEditDialogFinished( int result ) { IntervalEditDialog *dlg = qobject_cast( sender() ); if ( dlg == 0 ) { return; } if ( result == QDialog::Accepted ) { MacroCommand *cmd = dlg->buildCommand(); if ( cmd ) { part()->addCommand( cmd ); } } dlg->deleteLater(); } void CalendarEditor::slotSetVacation() { debugPlan<findDay( date ); if ( day == 0 ) { mod = true; day = new CalendarDay( date, CalendarDay::NonWorking ); m->addCommand( new CalendarAddDayCmd( currentCalendar(), day ) ); if ( m_currentMenuDateList.count() == 1 ) { m->setText( kundo2_i18n( "%1: Set to Non-Working", date.toString() ) ); } } else if ( day->state() != CalendarDay::NonWorking ) { mod = true; m->addCommand( new CalendarModifyStateCmd( currentCalendar(), day, CalendarDay::NonWorking ) ); if ( m_currentMenuDateList.count() == 1 ) { m->setText( kundo2_i18n( "%1: Set to Non-Working", date.toString() ) ); } } } if ( mod ) { part()->addCommand( m ); } else { delete m; } m_currentMenuDateList.clear(); } void CalendarEditor::slotSetUndefined() { debugPlan; if ( m_currentMenuDateList.isEmpty() || currentCalendar() == 0 ) { return; } bool mod = false; MacroCommand *m = new MacroCommand( kundo2_i18n( "Modify Calendar" ) ); foreach ( const QDate &date, m_currentMenuDateList ) { CalendarDay *day = currentCalendar()->findDay( date ); if ( day && day->state() != CalendarDay::Undefined ) { mod = true; m->addCommand( new CalendarRemoveDayCmd( currentCalendar(), day ) ); if ( m_currentMenuDateList.count() == 1 ) { m->setText( kundo2_i18n( "Set %1 to Undefined", date.toString() ) ); } } } if ( mod ) { part()->addCommand( m ); } else { delete m; } m_currentMenuDateList.clear(); } } // namespace KPlato diff --git a/plan/org.kde.calligraplan.desktop b/plan/org.kde.calligraplan.desktop index 4f0cd5eff7f..2488559f3b2 100644 --- a/plan/org.kde.calligraplan.desktop +++ b/plan/org.kde.calligraplan.desktop @@ -1,111 +1,111 @@ [Desktop Entry] Name=Plan Name[bs]=Plan Name[ca]=Plan Name[ca@valencia]=Plan Name[cs]=Plan Name[da]=Plan Name[de]=Plan Name[el]=Plan Name[en_GB]=Plan Name[es]=Plan Name[et]=Plan Name[eu]=Plan Name[fi]=Plan Name[fr]=Plan Name[gl]=Plan Name[hu]=Plan Name[ia]=Plan Name[it]=Plan Name[ja]=Plan Name[kk]=Plan Name[ko]=Plan Name[nb]=Plan Name[nds]=Plan Name[nl]=Plan Name[pl]=Plan Name[pt]=Plan Name[pt_BR]=Plan Name[ru]=Plan Name[sk]=Plan Name[sl]=Plan Name[sv]=Plan Name[tr]=Plan Name[ug]=Plan Name[uk]=Plan Name[x-test]=xxPlanxx Name[zh_CN]=Plan Name[zh_TW]=Plan GenericName=Project Management GenericName[bg]=Управление на проекти GenericName[br]=Mererezh raktresioù GenericName[bs]=Upravljanje projektima GenericName[ca]=Gestió de projectes GenericName[ca@valencia]=Gestió de projectes GenericName[cs]=Správa projektů GenericName[cy]=Rheoli Cywaith GenericName[da]=Projektstyring GenericName[de]=Projektmanagement GenericName[el]=Διαχείριση έργων GenericName[en_GB]=Project Management GenericName[eo]=Projektadministrado GenericName[es]=Gestión de proyectos GenericName[et]=Projektihaldus GenericName[eu]=Proiektu-kudeaketa GenericName[fa]=مدیریت پروژه GenericName[fi]=Projektinhallinta GenericName[fr]=Gestion de projets GenericName[fy]=Projektbehear GenericName[ga]=Bainisteoireacht Tionscadal GenericName[gl]=Xestión de proxectos GenericName[he]=ניהול פרוייקטים GenericName[hi]=परियोजना प्रबंधन GenericName[hne]=परियोजना प्रबंधन GenericName[hr]=Upravljanje projektima GenericName[hu]=Projektkezelő GenericName[ia]=Administration de projectos GenericName[is]=Verkefnisstjórn GenericName[it]=Gestione dei progetti GenericName[ja]=プロジェクト管理 GenericName[kk]=Жоба басқаруы GenericName[ko]=프로젝트 관리 GenericName[lt]=Projektų valdymas GenericName[lv]=Projektu pārvaldība GenericName[mai]=प्रोजेक्ट प्रबन्धन GenericName[mr]=परियोजना व्यवस्थापन GenericName[ms]=Pengurusan Projek GenericName[nb]=Prosjektstyring GenericName[nds]=Projektpleeg GenericName[ne]=परियोजना व्यवस्थापन GenericName[nl]=Projectbeheer GenericName[oc]=Gestion de projècte GenericName[pl]=Zarządzanie projektami GenericName[pt]=Gestão de Projectos GenericName[pt_BR]=Gerenciamento de Projetos GenericName[ru]=Проекты GenericName[sk]=Správa projektov GenericName[sl]=Projektno vodenje GenericName[sv]=Projekthantering GenericName[ta]=திட்ட மேலாண்மை GenericName[tg]=Идоракунии лоиҳа GenericName[tr]=Proje Yönetimi GenericName[ug]=قۇرۇلۇش باشقۇرۇش GenericName[uk]=Керування проектами GenericName[uz]=Loyiha boshqaruvi GenericName[uz@cyrillic]=Лойиҳа бошқаруви GenericName[wa]=Manaedjmint di pordjet GenericName[x-test]=xxProject Managementxx GenericName[zh_CN]=项目管理 GenericName[zh_TW]=專案管理 Exec=calligraplan %U MimeType=application/x-vnd.kde.plan; Type=Application Icon=calligraplan X-KDE-ServiceTypes=Calligra/Application X-Calligra-DefaultMimeTypes=application/x-vnd.kde.plan X-KDE-NativeMimeType=application/x-vnd.kde.plan X-DocPath=http://userbase.kde.org/Plan/Manual -Categories=Qt;KDE;Office; +Categories=Qt;KDE;Office;ProjectManagement; X-KDE-StartupNotify=true X-DBUS-StartupType=Multi X-DBUS-ServiceName=org.kde.calligraplan diff --git a/plan/plugins/schedulers/rcps/CMakeLists.txt b/plan/plugins/schedulers/rcps/CMakeLists.txt index 713cd4d3de0..8fd83c2b7d0 100644 --- a/plan/plugins/schedulers/rcps/CMakeLists.txt +++ b/plan/plugins/schedulers/rcps/CMakeLists.txt @@ -1,24 +1,26 @@ add_definitions(-DTRANSLATION_DOMAIN=\"calligraplan_scheduler_rcps\") set(LIBRCPS_INCLUDE_DIR 3rdparty/LibRCPS/src) include_directories( ${LIBRCPS_INCLUDE_DIR} ${KOODF_INCLUDES} ${CMAKE_SOURCE_DIR}/plan/libs/kernel ) add_subdirectory( 3rdparty ) -add_subdirectory( tests ) +if(BUILD_TESTING) + add_subdirectory( tests ) +endif() set ( RCPSScheduler_SRCS KPlatoRCPSPlugin.cpp KPlatoRCPSScheduler.cpp ) add_library(kplatorcpsscheduler MODULE ${RCPSScheduler_SRCS} ) calligraplan_scheduler_desktop_to_json(kplatorcpsscheduler planrcpsscheduler.desktop) target_link_libraries( kplatorcpsscheduler kplatokernel rcps_plan # ${LIBRCPS_LIBRARIES} ) install( TARGETS kplatorcpsscheduler DESTINATION ${PLUGIN_INSTALL_DIR}/calligraplan/schedulers ) diff --git a/plan/plugins/schedulers/tj/CMakeLists.txt b/plan/plugins/schedulers/tj/CMakeLists.txt index 60f042b212c..147155b82cb 100644 --- a/plan/plugins/schedulers/tj/CMakeLists.txt +++ b/plan/plugins/schedulers/tj/CMakeLists.txt @@ -1,51 +1,53 @@ add_definitions(-DTRANSLATION_DOMAIN=\"calligraplan_scheduler_tj\") set(LIBTJ_INCLUDE_DIR taskjuggler) include_directories( ${LIBTJ_INCLUDE_DIR} ${KOODF_INCLUDES} ${CMAKE_SOURCE_DIR}/plan/libs/kernel ) #add_subdirectory( taskjuggler ) -add_subdirectory( tests ) +if(BUILD_TESTING) + add_subdirectory( tests ) +endif() set ( TJScheduler_SRCS PlanTJPlugin.cpp PlanTJScheduler.cpp taskjuggler/Allocation.cpp taskjuggler/CoreAttributes.cpp taskjuggler/CoreAttributesList.cpp taskjuggler/Project.cpp taskjuggler/Task.cpp taskjuggler/TaskDependency.cpp taskjuggler/TaskList.cpp taskjuggler/TaskScenario.cpp taskjuggler/Resource.cpp taskjuggler/ResourceList.cpp taskjuggler/Scenario.cpp taskjuggler/ScenarioList.cpp taskjuggler/Shift.cpp taskjuggler/ShiftList.cpp taskjuggler/ShiftSelection.cpp taskjuggler/ShiftSelectionList.cpp taskjuggler/VacationList.cpp taskjuggler/TjMessageHandler.cpp taskjuggler/Utility.cpp # taskjuggler/XMLFile.cpp # taskjuggler/ParserElement.cpp # taskjuggler/ParserNode.cpp # taskjuggler/ParserTreeContext.cpp taskjuggler/Interval.cpp ) # TODO: plugin should not be SHARED, but MODULE. Needs to be SHARED because tests link to it -> fix with util lib/objects add_library(plantjscheduler SHARED ${TJScheduler_SRCS} ) calligraplan_scheduler_desktop_to_json(plantjscheduler plantjscheduler.desktop) # TODO: only export symbols for tests, not release generate_export_header(plantjscheduler BASE_NAME kplatotj) target_link_libraries( plantjscheduler kplatokernel ) set_target_properties( plantjscheduler PROPERTIES DEFINE_SYMBOL MAKE_KPLATOTJ_LIB ) install( TARGETS plantjscheduler DESTINATION ${PLUGIN_INSTALL_DIR}/calligraplan/schedulers ) diff --git a/plan/plugins/scripting/CMakeLists.txt b/plan/plugins/scripting/CMakeLists.txt index 69ac34b3bb4..e79cc24c3ea 100644 --- a/plan/plugins/scripting/CMakeLists.txt +++ b/plan/plugins/scripting/CMakeLists.txt @@ -1,47 +1,49 @@ add_definitions(-DTRANSLATION_DOMAIN=\"krossmoduleplan\") include_directories( ${CMAKE_SOURCE_DIR}/plan ${KPLATO_INCLUDES} ${KOKROSS_INCLUDES} ) add_subdirectory( scripts ) -add_subdirectory( tests ) +if(BUILD_TESTING) + add_subdirectory( tests ) +endif() set(krossmoduleplan_PART_SRCS Account.cpp Calendar.cpp Node.cpp Resource.cpp ResourceGroup.cpp Schedule.cpp Project.cpp Module.cpp ScriptingPart.cpp ScriptingWidgets.cpp ScriptingDebug.cpp ) ki18n_wrap_ui(krossmoduleplan_PART_SRCS ScriptingDataQueryView.ui ) # TODO: plugin should not be SHARED, but MODULE. Needs to be SHARED because tests link to it -> fix with util lib/objects add_library(krossmoduleplan SHARED ${krossmoduleplan_PART_SRCS}) generate_export_header(krossmoduleplan BASE_NAME kplatoscripting EXPORT_FILE_NAME kplatoscripting_generated_export.h ) calligraplan_viewplugin_desktop_to_json(krossmoduleplan planscripting.desktop) target_link_libraries(krossmoduleplan planprivate kplatokernel kplatomodels komain kokross ) install(TARGETS krossmoduleplan DESTINATION ${PLUGIN_INSTALL_DIR}/calligraplan/extensions) install(FILES scripting.rc DESTINATION ${DATA_INSTALL_DIR}/calligraplan/viewplugins) diff --git a/plan/workpackage/org.kde.calligraplanwork.desktop b/plan/workpackage/org.kde.calligraplanwork.desktop index de4239cb741..442cd93ed4a 100644 --- a/plan/workpackage/org.kde.calligraplanwork.desktop +++ b/plan/workpackage/org.kde.calligraplanwork.desktop @@ -1,102 +1,112 @@ [Desktop Entry] Name=Plan WorkPackage Handler Name[bs]=Upravljač planskih radnih paketa Name[ca]=Gestor de paquets de feina del Plan Name[ca@valencia]=Gestor de paquets de faena del Plan Name[da]=Håndtering af WorkPackage til Plan Name[de]=Plan-Arbeitspaketverwaltung Name[el]=Χειριστής πακέτου εργασίας Plan Name[en_GB]=Plan WorkPackage Handler Name[es]=Gestor de paquetes de trabajo de Plan Name[et]=Plani töölõikude käitleja Name[eu]=Plan-en lan-paketeen maneiatzailea Name[fi]=Plan-työpakettikäsittelijä Name[fr]=Paquet de travail de Plan Name[gl]=Xestor para Plan de paquetes de traballo Name[hu]=Plan munkacsomag-kezelő Name[it]=Gestore dei pacchetti di lavoro di Plan Name[ja]=Plan ワークパッケージハンドラ Name[kk]=Plan жұмыс дестесінің өңдеуіші Name[nb]=Plan arbeidspakkestyring Name[nl]=Plan werkpakketbeheerder Name[pl]=Program obsługujący pakiety pracy Plan Name[pt]=Tratamento de Pacotes de Trabalho do Plan Name[pt_BR]=Tratamento de pacotes de trabalho do Plan Name[ru]=Программа обработки Plan WorkPackage Name[sk]=Spracovač pracovných balíkov Plan Name[sv]=Hantering av arbetspaket i Plan Name[tr]=Plan Çalışma Paketi İşleyicisi Name[ug]=Plan خىزمەت بوغچا بىر تەرەپ قىلغۇچ Name[uk]=Програма опрацювання робочих пакунків Plan Name[x-test]=xxPlan WorkPackage Handlerxx Name[zh_CN]=Plan 工作包处理程序 Name[zh_TW]=Plan 工作套件處理器 GenericName=Project Management GenericName[bg]=Управление на проекти GenericName[br]=Mererezh raktresioù GenericName[bs]=Upravljanje projektima GenericName[ca]=Gestió de projectes GenericName[ca@valencia]=Gestió de projectes GenericName[cs]=Správa projektů GenericName[cy]=Rheoli Cywaith GenericName[da]=Projektstyring GenericName[de]=Projektmanagement GenericName[el]=Διαχείριση έργων GenericName[en_GB]=Project Management GenericName[eo]=Projektadministrado GenericName[es]=Gestión de proyectos GenericName[et]=Projektihaldus GenericName[eu]=Proiektu-kudeaketa GenericName[fa]=مدیریت پروژه GenericName[fi]=Projektinhallinta GenericName[fr]=Gestion de projets GenericName[fy]=Projektbehear GenericName[ga]=Bainisteoireacht Tionscadal GenericName[gl]=Xestión de proxectos GenericName[he]=ניהול פרוייקטים GenericName[hi]=परियोजना प्रबंधन GenericName[hne]=परियोजना प्रबंधन GenericName[hr]=Upravljanje projektima GenericName[hu]=Projektkezelő GenericName[ia]=Administration de projectos GenericName[is]=Verkefnisstjórn GenericName[it]=Gestione dei progetti GenericName[ja]=プロジェクト管理 GenericName[kk]=Жоба басқаруы GenericName[ko]=프로젝트 관리 GenericName[lt]=Projektų valdymas GenericName[lv]=Projektu pārvaldība GenericName[mai]=प्रोजेक्ट प्रबन्धन GenericName[mr]=परियोजना व्यवस्थापन GenericName[ms]=Pengurusan Projek GenericName[nb]=Prosjektstyring GenericName[nds]=Projektpleeg GenericName[ne]=परियोजना व्यवस्थापन GenericName[nl]=Projectbeheer GenericName[oc]=Gestion de projècte GenericName[pl]=Zarządzanie projektami GenericName[pt]=Gestão de Projectos GenericName[pt_BR]=Gerenciamento de Projetos GenericName[ru]=Проекты GenericName[sk]=Správa projektov GenericName[sl]=Projektno vodenje GenericName[sv]=Projekthantering GenericName[ta]=திட்ட மேலாண்மை GenericName[tg]=Идоракунии лоиҳа GenericName[tr]=Proje Yönetimi GenericName[ug]=قۇرۇلۇش باشقۇرۇش GenericName[uk]=Керування проектами GenericName[uz]=Loyiha boshqaruvi GenericName[uz@cyrillic]=Лойиҳа бошқаруви GenericName[wa]=Manaedjmint di pordjet GenericName[x-test]=xxProject Managementxx GenericName[zh_CN]=项目管理 GenericName[zh_TW]=專案管理 +Comment=Work Package handler for the Plan project planning tool +Comment[ca]=Gestor de paquets de feina per a l'eina de planificació de projectes Plan +Comment[ca@valencia]=Gestor de paquets de faena per a l'eina de planificació de projectes Plan +Comment[es]=Gestor de paquetes de trabajo para la herramienta de planificación de proyectos Plan +Comment[it]=Gestore dei pacchetti di lavoro dello strumento di pianificazione dei progetti Plan +Comment[nl]=Werkpakketbeheerder voor het Plan-projectplanninghulpmiddel +Comment[pt]=Tratamento de Pacotes de Trabalho para a ferramenta de gestão de projectos Plan +Comment[sv]=Hantering av arbetspaket för projektplaneringsverktyget Plan +Comment[uk]=Обробник робочих пакунків для засобу планування проектів Plan +Comment[x-test]=xxWork Package handler for the Plan project planning toolxx Exec=calligraplanwork %u MimeType=application/x-vnd.kde.plan.work;application/x-vnd.kde.kplato.work; Type=Application Icon=calligraplanwork X-KDE-ServiceTypes=Calligra/Application Categories=Qt;KDE;Office; X-KDE-StartupNotify=true X-DocPath=http://userbase.kde.org/PlanWork/Manual diff --git a/plugins/chartshape/CMakeLists.txt b/plugins/chartshape/CMakeLists.txt index 3d9a2b422ab..1179cf832ec 100644 --- a/plugins/chartshape/CMakeLists.txt +++ b/plugins/chartshape/CMakeLists.txt @@ -1,83 +1,85 @@ add_definitions(-DTRANSLATION_DOMAIN=\"calligra_shape_chart\") # To hide all the warnings from embedded 3rd party software like kdchart if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_GNUC) add_definitions(-Wno-switch) endif () -add_subdirectory( tests ) +if(BUILD_TESTING) + add_subdirectory( tests ) +endif() include_directories( ${CMAKE_SOURCE_DIR}/plugins/chartshape ${CMAKE_SOURCE_DIR}/plugins/chartshape/dialogs ${CMAKE_SOURCE_DIR}/interfaces ${KDCHART_INCLUDES} ${KOMAIN_INCLUDES} ${TEXTLAYOUT_INCLUDES} ) ########### Chart shape Plugin library ############### set (chartshape_LIB_SRCS ChartDebug.cpp kochart_global.cpp KChartConvertions.cpp Axis.cpp DataSet.cpp CellRegion.cpp CellRegionStringValidator.cpp ChartTableModel.cpp Legend.cpp TextLabelDummy.cpp PlotArea.cpp TableSource.cpp ChartProxyModel.cpp KChartModel.cpp Surface.cpp ChartDocument.cpp ChartPart.cpp ChartShape.cpp ChartTool.cpp ChartToolFactory.cpp ChartConfigWidget.cpp ChartTableView.cpp ScreenConversions.cpp ChartLayout.cpp SingleModelHelper.cpp OdfLoadingHelper.cpp dialogs/TableEditorDialog.cpp dialogs/NewAxisDialog.cpp dialogs/AxisScalingDialog.cpp dialogs/CellRegionDialog.cpp dialogs/FontEditorDialog.cpp dialogs/FormatErrorBarDialog.cpp commands/ChartTypeCommand.cpp commands/LegendCommand.cpp commands/AxisCommand.cpp commands/DatasetCommand.cpp commands/ChartTextShapeCommand.cpp commands/AddRemoveAxisCommand.cpp ) ki18n_wrap_ui(chartshape_LIB_SRCS ChartConfigWidget.ui dialogs/ChartTableEditor.ui dialogs/NewAxisDialog.ui dialogs/AxisScalingDialog.ui dialogs/CellRegionDialog.ui dialogs/FormatErrorBarDialog.ui ) add_library(chartshapecore STATIC ${chartshape_LIB_SRCS}) target_link_libraries(chartshapecore komain kotextlayout KChart) if(NOT MSVC AND NOT (WIN32 AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")) set_target_properties(chartshapecore PROPERTIES COMPILE_FLAGS "-fPIC") endif() add_library(calligra_shape_chart MODULE ChartShapeFactory.cpp ) calligra_shape_desktop_to_json(calligra_shape_chart calligra_shape_chart.desktop) target_link_libraries(calligra_shape_chart chartshapecore KChart flake KF5::IconThemes) install(TARGETS calligra_shape_chart DESTINATION ${PLUGIN_INSTALL_DIR}/calligra/shapes) diff --git a/plugins/colorengines/lcms2/CMakeLists.txt b/plugins/colorengines/lcms2/CMakeLists.txt index deff9f4797f..77807ff9702 100644 --- a/plugins/colorengines/lcms2/CMakeLists.txt +++ b/plugins/colorengines/lcms2/CMakeLists.txt @@ -1,99 +1,99 @@ project( lcmsengine ) -if(NOT WIN32) +if(BUILD_TESTING AND NOT WIN32) ## Only test if on non-Windows system - add_subdirectory(tests) + add_subdirectory(tests) endif() add_subdirectory(data) include_directories( ${PIGMENT_INCLUDES} ${LCMS2_INCLUDE_DIR} ) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/cmyk_u8 ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/cmyk_u16 ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/cmyk_f32 ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/gray_u8 ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/gray_u16 ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/gray_f32 ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/lab_u8 ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/lab_u16 ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/lab_f32 ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/rgb_u8 ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/rgb_u16 ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/rgb_f32 ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/xyz_u8 ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/xyz_u16 ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/xyz_f32 ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/ycbcr_u8 ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/ycbcr_u16 ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/ycbcr_f32 ${CMAKE_CURRENT_SOURCE_DIR}/colorprofiles ) if (HAVE_LCMS24 AND OPENEXR_FOUND) include_directories( ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/gray_f16 ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/rgb_f16 ${CMAKE_CURRENT_SOURCE_DIR}/colorspaces/xyz_f16 ) endif () set(FILE_OPENEXR_SOURCES) set(LINK_OPENEXR_LIB) if(OPENEXR_FOUND) include_directories(${OPENEXR_INCLUDE_DIR}) set(LINK_OPENEXR_LIB ${OPENEXR_LIBRARIES}) add_definitions(${OPENEXR_DEFINITIONS}) endif() set ( lcmsengine_SRCS colorspaces/cmyk_u8/CmykU8ColorSpace.cpp colorspaces/cmyk_u16/CmykU16ColorSpace.cpp colorspaces/cmyk_f32/CmykF32ColorSpace.cpp colorspaces/gray_u8/GrayU8ColorSpace.cpp colorspaces/gray_u16/GrayU16ColorSpace.cpp colorspaces/gray_f32/GrayF32ColorSpace.cpp colorspaces/lab_u8/LabU8ColorSpace.cpp colorspaces/lab_u16/LabColorSpace.cpp colorspaces/lab_f32/LabF32ColorSpace.cpp colorspaces/xyz_u8/XyzU8ColorSpace.cpp colorspaces/xyz_u16/XyzU16ColorSpace.cpp colorspaces/xyz_f32/XyzF32ColorSpace.cpp colorspaces/rgb_u8/RgbU8ColorSpace.cpp colorspaces/rgb_u16/RgbU16ColorSpace.cpp colorspaces/rgb_f32/RgbF32ColorSpace.cpp colorspaces/ycbcr_u8/YCbCrU8ColorSpace.cpp colorspaces/ycbcr_u16/YCbCrU16ColorSpace.cpp colorspaces/ycbcr_f32/YCbCrF32ColorSpace.cpp colorprofiles/LcmsColorProfileContainer.cpp colorprofiles/IccColorProfile.cpp IccColorSpaceEngine.cpp LcmsColorSpace.cpp LcmsEnginePlugin.cpp ) if (HAVE_LCMS24 AND OPENEXR_FOUND) set ( lcmsengine_SRCS ${lcmsengine_SRCS} colorspaces/gray_f16/GrayF16ColorSpace.cpp colorspaces/rgb_f16/RgbF16ColorSpace.cpp colorspaces/xyz_f16/XyzF16ColorSpace.cpp ) endif () add_library(kolcmsengine MODULE ${lcmsengine_SRCS}) calligra_colorspace_desktop_to_json(kolcmsengine kolcmsengine.desktop) target_link_libraries(kolcmsengine pigmentcms kowidgets KF5::I18n ${LCMS2_LIBRARIES} ${LINK_OPENEXR_LIB}) install(TARGETS kolcmsengine DESTINATION ${PLUGIN_INSTALL_DIR}/calligra/colorspaces) diff --git a/plugins/formulashape/CMakeLists.txt b/plugins/formulashape/CMakeLists.txt index c2e22a75d05..98d6ec1e78b 100644 --- a/plugins/formulashape/CMakeLists.txt +++ b/plugins/formulashape/CMakeLists.txt @@ -1,130 +1,132 @@ add_definitions(-DTRANSLATION_DOMAIN=\"calligra_shape_formula\") # To hide all the warnings from the generated itex2MML code if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_GNUC) add_definitions(-Wno-unused-label) add_definitions(-Wno-sign-compare) add_definitions(-Wno-unused-function) endif () set(ITEXTOMML_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/3rdparty/itexToMML") include_directories( ${CMAKE_SOURCE_DIR}/plugins/formulashape ${CMAKE_SOURCE_DIR}/plugins/formulashape/elements ${KOMAIN_INCLUDES} ) ########### KoFormula Library ############### set( koformula_LIB_SRCS FormulaDebug.cpp AttributeManager.cpp FormulaRenderer.cpp FormulaData.cpp FormulaCursor.cpp FormulaEditor.cpp FormulaCommand.cpp ElementFactory.cpp Dictionary.cpp elements/BasicElement.cpp elements/FixedElement.cpp elements/RowElement.cpp elements/FormulaElement.cpp elements/TextElement.cpp elements/FractionElement.cpp elements/SpaceElement.cpp elements/GlyphElement.cpp elements/IdentifierElement.cpp elements/OperatorElement.cpp elements/MultiscriptElement.cpp elements/SubSupElement.cpp elements/UnderOverElement.cpp elements/FencedElement.cpp elements/TableElement.cpp elements/TableRowElement.cpp elements/TableDataElement.cpp elements/RootElement.cpp elements/EncloseElement.cpp elements/ActionElement.cpp elements/PaddedElement.cpp elements/ErrorElement.cpp elements/StyleElement.cpp elements/TokenElement.cpp elements/NumberElement.cpp elements/StringElement.cpp elements/PhantomElement.cpp elements/SquareRootElement.cpp elements/AnnotationElement.cpp elements/UnknownElement.cpp ) add_library(koformula SHARED ${koformula_LIB_SRCS}) generate_export_header(koformula EXPORT_MACRO_NAME KOFORMULA_EXPORT) target_link_libraries(koformula PUBLIC komain) set_target_properties(koformula PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) install(TARGETS koformula ${INSTALL_TARGETS_DEFAULT_ARGS}) ########### FormulaShape Plugin ############### find_package(Cauchy) macro_optional_find_package(Cauchy) set_package_properties(Cauchy PROPERTIES DESCRIPTION "Cauchy's M2MML, a Matlab/Octave to MathML compiler" URL "https://bitbucket.org/cyrille/cauchy" PURPOSE "Required for the matlab/octave formula tool" TYPE OPTIONAL ) if(M2MML_FOUND) set(M2MML_TOOL_LIBRARIES ${M2MML_LIBRARY}) include_directories(${CAUCHY_INCLUDE_DIR}) add_definitions(-DHAVE_M2MML) endif() set( formulashape_PART_SRCS KoFormulaShapePlugin.cpp KoFormulaShape.cpp FormulaCommandUpdate.cpp KoFormulaShapeFactory.cpp KoFormulaTool.cpp FormulaCommand.cpp KoFormulaToolFactory.cpp FormulaToolWidget.cpp FormulaDocument.cpp FormulaPart.cpp ) if (NOT MSVC AND NOT (WIN32 AND "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel")) set (formulashape_PART_SRCS ${formulashape_PART_SRCS} KoM2MMLForumulaTool.cpp # itexToMML files ${ITEXTOMML_SOURCE_DIR}/lex.yy.c ${ITEXTOMML_SOURCE_DIR}/y.tab.c ) endif () ki18n_wrap_ui( formulashape_PART_SRCS FormulaToolWidget.ui ) add_library(calligra_shape_formula MODULE ${formulashape_PART_SRCS}) calligra_shape_desktop_to_json(calligra_shape_formula calligra_shape_formula.desktop) target_link_libraries(calligra_shape_formula koformula ${M2MML_TOOL_LIBRARIES}) #set_target_properties(koformula PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) install(TARGETS calligra_shape_formula DESTINATION ${PLUGIN_INSTALL_DIR}/calligra/shapes) add_subdirectory( pics ) # TODO: those font files seem unused currently, investigate if still needed # add_subdirectory( fonts ) -add_subdirectory( tests ) +if(BUILD_TESTING) + add_subdirectory( tests ) +endif() add_subdirectory( templates ) diff --git a/plugins/karbonplugins/tools/KarbonPatternEditStrategy.cpp b/plugins/karbonplugins/tools/KarbonPatternEditStrategy.cpp index 529a5db442f..bb5389732fb 100644 --- a/plugins/karbonplugins/tools/KarbonPatternEditStrategy.cpp +++ b/plugins/karbonplugins/tools/KarbonPatternEditStrategy.cpp @@ -1,371 +1,371 @@ /* This file is part of the KDE project * Copyright (C) 2007,2009 Jan Hambrecht * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KarbonPatternEditStrategy.h" #include #include #include #include #include #include uint KarbonPatternEditStrategyBase::m_handleRadius = 3; uint KarbonPatternEditStrategyBase::m_grabSensitivity = 3; KarbonPatternEditStrategyBase::KarbonPatternEditStrategyBase(KoShape * s, KoImageCollection * imageCollection) : m_selectedHandle(-1) , m_oldFill(new KoPatternBackground(imageCollection)), m_newFill(new KoPatternBackground(imageCollection)) , m_shape(s), m_imageCollection(imageCollection) , m_editing(false), m_modified(false) { Q_ASSERT(m_shape); Q_ASSERT(imageCollection); // cache the shapes transformation matrix m_matrix = shape()->absoluteTransformation(0); } KarbonPatternEditStrategyBase::~KarbonPatternEditStrategyBase() { } void KarbonPatternEditStrategyBase::setEditing(bool on) { m_editing = on; // if we are going into editing mode, save the old background // for use inside the command emitted when finished if (on) { m_modified = false; QSharedPointer fill = qSharedPointerDynamicCast(m_shape->background()); if (fill) m_oldFill = fill; } } void KarbonPatternEditStrategyBase::setModified() { m_modified = true; } bool KarbonPatternEditStrategyBase::isModified() const { return m_modified; } KUndo2Command * KarbonPatternEditStrategyBase::createCommand() { QSharedPointer fill = qSharedPointerDynamicCast(m_shape->background()); if (fill && isModified()) { fill = m_oldFill; QSharedPointer newFill(new KoPatternBackground(m_imageCollection)); newFill = m_newFill; return new KoShapeBackgroundCommand(m_shape, newFill, 0); } return 0; } void KarbonPatternEditStrategyBase::paintHandle(QPainter &painter, const KoViewConverter &converter, const QPointF &position) const { QRectF handleRect = converter.viewToDocument(QRectF(0, 0, 2 * m_handleRadius, 2 * m_handleRadius)); handleRect.moveCenter(position); painter.drawRect(handleRect); } bool KarbonPatternEditStrategyBase::mouseInsideHandle(const QPointF &mousePos, const QPointF &handlePos, const KoViewConverter &converter) const { qreal grabSensitivityInPt = converter.viewToDocumentX(m_grabSensitivity); if (mousePos.x() < handlePos.x() - grabSensitivityInPt) return false; if (mousePos.x() > handlePos.x() + grabSensitivityInPt) return false; if (mousePos.y() < handlePos.y() - grabSensitivityInPt) return false; if (mousePos.y() > handlePos.y() + grabSensitivityInPt) return false; return true; } void KarbonPatternEditStrategyBase::repaint() const { m_shape->update(); } KoShape * KarbonPatternEditStrategyBase::shape() const { return m_shape; } KoImageCollection * KarbonPatternEditStrategyBase::imageCollection() { return m_imageCollection; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// KarbonPatternEditStrategy::KarbonPatternEditStrategy(KoShape * s, KoImageCollection * imageCollection) : KarbonPatternEditStrategyBase(s, imageCollection) { // cache the shapes transformation matrix m_matrix = shape()->absoluteTransformation(0); QSizeF size = shape()->size(); // the fixed length of half the average shape dimension m_normalizedLength = 0.25 * (size.width() + size.height()); - // get the brush tranformation matrix + // get the brush transformation matrix QTransform brushMatrix; QSharedPointer fill = qSharedPointerDynamicCast(shape()->background()); if (fill) brushMatrix = fill->transform(); // the center handle at the center point of the shape //m_origin = QPointF( 0.5 * size.width(), 0.5 * size.height() ); m_handles.append(brushMatrix.map(QPointF())); // the direction handle with the length of half the average shape dimension QPointF dirVec = QPointF(m_normalizedLength, 0.0); m_handles.append(brushMatrix.map(dirVec)); } KarbonPatternEditStrategy::~KarbonPatternEditStrategy() { } void KarbonPatternEditStrategy::paint(QPainter &painter, const KoViewConverter &converter) const { QPointF centerPoint = m_matrix.map(m_origin + m_handles[center]); QPointF directionPoint = m_matrix.map(m_origin + m_handles[direction]); KoShape::applyConversion(painter, converter); painter.drawLine(centerPoint, directionPoint); paintHandle(painter, converter, centerPoint); paintHandle(painter, converter, directionPoint); } bool KarbonPatternEditStrategy::selectHandle(const QPointF &mousePos, const KoViewConverter &converter) { int handleIndex = 0; foreach(const QPointF & handle, m_handles) { if (mouseInsideHandle(mousePos, m_matrix.map(m_origin + handle), converter)) { m_selectedHandle = handleIndex; return true; } handleIndex++; } m_selectedHandle = -1; return false; } void KarbonPatternEditStrategy::handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers) if (m_selectedHandle == direction) { QPointF newPos = m_matrix.inverted().map(mouseLocation) - m_origin - m_handles[center]; // calculate the temporary length after handle movement qreal newLength = sqrt(newPos.x() * newPos.x() + newPos.y() * newPos.y()); // set the new direction vector with the new direction and normalized length m_handles[m_selectedHandle] = m_handles[center] + m_normalizedLength / newLength * newPos; } else if (m_selectedHandle == center) { QPointF diffPos = m_matrix.inverted().map(mouseLocation) - m_origin - m_handles[center]; m_handles[center] += diffPos; m_handles[direction] += diffPos; } else return; setModified(); QSharedPointer fill = qSharedPointerDynamicCast(shape()->background()); if (fill) { m_newFill = updatedBackground(); fill = m_newFill; } } QRectF KarbonPatternEditStrategy::boundingRect() const { // calculate the bounding rect of the handles QRectF bbox(m_matrix.map(m_origin + m_handles[0]), QSize(0, 0)); for (int i = 1; i < m_handles.count(); ++i) { QPointF handle = m_matrix.map(m_origin + m_handles[i]); bbox.setLeft(qMin(handle.x(), bbox.left())); bbox.setRight(qMax(handle.x(), bbox.right())); bbox.setTop(qMin(handle.y(), bbox.top())); bbox.setBottom(qMax(handle.y(), bbox.bottom())); } qreal hr = handleRadius(); return bbox.adjusted(-hr, -hr, hr, hr); } QSharedPointer KarbonPatternEditStrategy::updatedBackground() { // the direction vector controls the rotation of the pattern QPointF dirVec = m_handles[direction] - m_handles[center]; qreal angle = atan2(dirVec.y(), dirVec.x()) * 180.0 / M_PI; QTransform matrix; // the center handle controls the translation matrix.translate(m_handles[center].x(), m_handles[center].y()); matrix.rotate(angle); QSharedPointer newFill(new KoPatternBackground(imageCollection())); newFill->setTransform(matrix); return newFill; } /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// KarbonOdfPatternEditStrategy::KarbonOdfPatternEditStrategy(KoShape * s, KoImageCollection * imageCollection) : KarbonPatternEditStrategyBase(s, imageCollection) { m_handles.append(QPointF()); m_handles.append(QPointF()); updateHandles(qSharedPointerDynamicCast(shape()->background())); } KarbonOdfPatternEditStrategy::~KarbonOdfPatternEditStrategy() { } void KarbonOdfPatternEditStrategy::paint(QPainter &painter, const KoViewConverter &converter) const { KoShape::applyConversion(painter, converter); QSharedPointer fill = qSharedPointerDynamicCast(shape()->background()); if (! fill) return; painter.save(); painter.setTransform(m_matrix * painter.transform()); painter.setBrush(Qt::NoBrush); painter.drawRect(QRectF(m_handles[origin], m_handles[size])); painter.restore(); if (fill->repeat() == KoPatternBackground::Tiled) paintHandle(painter, converter, m_matrix.map(m_handles[origin])); if (fill->repeat() != KoPatternBackground::Stretched) paintHandle(painter, converter, m_matrix.map(m_handles[size])); } bool KarbonOdfPatternEditStrategy::selectHandle(const QPointF &mousePos, const KoViewConverter &converter) { QSharedPointer fill = qSharedPointerDynamicCast(shape()->background()); if (! fill) return false; if (fill->repeat() == KoPatternBackground::Stretched) return false; m_selectedHandle = -1; if (mouseInsideHandle(mousePos, m_matrix.map(m_handles[size]), converter)) { m_selectedHandle = size; return true; } if (fill->repeat() == KoPatternBackground::Original) return false; if (mouseInsideHandle(mousePos, m_matrix.map(m_handles[origin]), converter)) { m_selectedHandle = origin; return true; } return false; } void KarbonOdfPatternEditStrategy::handleMouseMove(const QPointF &mouseLocation, Qt::KeyboardModifiers modifiers) { Q_UNUSED(modifiers); QSharedPointer fill = qSharedPointerDynamicCast(shape()->background()); if (!fill) return; if (fill->repeat() == KoPatternBackground::Stretched) return; if (m_selectedHandle == origin) { if (fill->repeat() == KoPatternBackground::Original) return; QPointF diffPos = m_matrix.inverted().map(mouseLocation) - m_handles[origin]; m_handles[origin] += diffPos; m_handles[size] += diffPos; } else if (m_selectedHandle == size) { QPointF newPos = m_matrix.inverted().map(mouseLocation); newPos.setX(qMax(newPos.x(), m_handles[origin].x())); newPos.setY(qMax(newPos.y(), m_handles[origin].y())); if (fill->repeat() == KoPatternBackground::Original) { QPointF diffPos = newPos - m_handles[size]; m_handles[size] += 0.5 * diffPos; m_handles[origin] -= 0.5 * diffPos; } else { m_handles[size] = newPos; } } else return; setModified(); m_newFill = updatedBackground(); updateHandles(m_newFill); } QRectF KarbonOdfPatternEditStrategy::boundingRect() const { // calculate the bounding rect of the handles QRectF bbox(m_matrix.map(m_handles[origin]), m_matrix.map(m_handles[size])); qreal hr = handleRadius(); return bbox.adjusted(-hr, -hr, hr, hr); } QSharedPointer KarbonOdfPatternEditStrategy::updatedBackground() { QSizeF displaySize(m_handles[size].x() - m_handles[origin].x(), m_handles[size].y() - m_handles[origin].y()); qreal offsetX = 100.0 * (m_handles[origin].x() / displaySize.width()); qreal offsetY = 100.0 * (m_handles[origin].y() / displaySize.height()); QSharedPointer newFill(new KoPatternBackground(imageCollection())); newFill = m_oldFill; newFill->setReferencePoint(KoPatternBackground::TopLeft); newFill->setReferencePointOffset(QPointF(offsetX, offsetY)); newFill->setPatternDisplaySize(displaySize); return newFill; } void KarbonOdfPatternEditStrategy::updateHandles(QSharedPointer fill) { if (! fill) return; QRectF patternRect = fill->patternRectFromFillSize(shape()->size()); m_handles[origin] = patternRect.topLeft(); m_handles[size] = patternRect.bottomRight(); } void KarbonOdfPatternEditStrategy::updateHandles() { updateHandles(qSharedPointerDynamicCast(shape()->background())); } diff --git a/plugins/musicshape/CMakeLists.txt b/plugins/musicshape/CMakeLists.txt index 23181873e90..fce4b56f7bb 100644 --- a/plugins/musicshape/CMakeLists.txt +++ b/plugins/musicshape/CMakeLists.txt @@ -1,102 +1,104 @@ add_definitions(-DTRANSLATION_DOMAIN=\"calligra_shape_music\") include_directories( ${FLAKE_INCLUDES} ${kOWIDGETS_INCLUDES} ) add_subdirectory( fonts ) add_subdirectory( pics ) add_subdirectory( core ) -add_subdirectory( tests) +if(BUILD_TESTING) + add_subdirectory( tests) +endif() ########### Flake Plugin library ############### set( musiccore_SRCS core/Global.cpp core/Sheet.cpp core/Part.cpp core/PartGroup.cpp core/Staff.cpp core/Voice.cpp core/Bar.cpp core/VoiceBar.cpp core/VoiceElement.cpp core/StaffElement.cpp core/Chord.cpp core/Note.cpp core/Clef.cpp core/KeySignature.cpp core/TimeSignature.cpp core/StaffSystem.cpp core/MusicXmlWriter.cpp core/MusicXmlReader.cpp ) set (musicshape_LIB_SRCS MusicDebug.cpp MusicShape.cpp MusicShapeFactory.cpp MusicTool.cpp MusicToolFactory.cpp SimpleEntryTool.cpp SimpleEntryToolFactory.cpp MusicStyle.cpp Engraver.cpp Renderer.cpp MusicCursor.cpp dialogs/PartsWidget.cpp dialogs/PartDetailsDialog.cpp dialogs/SimpleEntryWidget.cpp dialogs/PartsListModel.cpp dialogs/KeySignatureDialog.cpp dialogs/StaffElementPreviewWidget.cpp actions/AbstractMusicAction.cpp actions/AbstractNoteMusicAction.cpp actions/NoteEntryAction.cpp actions/AccidentalAction.cpp actions/EraserAction.cpp actions/DotsAction.cpp actions/SetClefAction.cpp actions/TimeSignatureAction.cpp actions/KeySignatureAction.cpp actions/RemoveBarAction.cpp actions/TiedNoteAction.cpp actions/SelectionAction.cpp commands/AddPartCommand.cpp commands/RemovePartCommand.cpp commands/ChangePartDetailsCommand.cpp commands/CreateChordCommand.cpp commands/AddNoteCommand.cpp commands/SetAccidentalsCommand.cpp commands/AddBarsCommand.cpp commands/RemoveNoteCommand.cpp commands/RemoveChordCommand.cpp commands/AddDotCommand.cpp commands/SetClefCommand.cpp commands/RemoveStaffElementCommand.cpp commands/SetTimeSignatureCommand.cpp commands/SetKeySignatureCommand.cpp commands/RemoveBarCommand.cpp commands/MakeRestCommand.cpp commands/ToggleTiedNoteCommand.cpp ${musiccore_SRCS} ) ki18n_wrap_ui(musicshape_LIB_SRCS dialogs/PartsWidget.ui dialogs/PartDetailsDialog.ui dialogs/SimpleEntryWidget.ui dialogs/KeySignatureDialog.ui ) add_library(calligra_shape_music MODULE ${musicshape_LIB_SRCS}) calligra_shape_desktop_to_json(calligra_shape_music calligra_shape_music.desktop) target_link_libraries(calligra_shape_music flake kowidgets KF5::I18n Qt5::Svg) install(TARGETS calligra_shape_music DESTINATION ${PLUGIN_INSTALL_DIR}/calligra/shapes) diff --git a/plugins/musicshape/core/CMakeLists.txt b/plugins/musicshape/core/CMakeLists.txt index ef78810eb1d..16250635317 100644 --- a/plugins/musicshape/core/CMakeLists.txt +++ b/plugins/musicshape/core/CMakeLists.txt @@ -1,2 +1,4 @@ -add_subdirectory( tests ) +if(BUILD_TESTING) + add_subdirectory( tests ) +endif() diff --git a/plugins/textediting/autocorrection/calligra_textediting_autocorrect.desktop b/plugins/textediting/autocorrection/calligra_textediting_autocorrect.desktop index 4b79e270aa5..749c1b2554e 100644 --- a/plugins/textediting/autocorrection/calligra_textediting_autocorrect.desktop +++ b/plugins/textediting/autocorrection/calligra_textediting_autocorrect.desktop @@ -1,49 +1,49 @@ [Desktop Entry] Name=Autocorrection plugin Name[bg]=Приставка за автокорекция Name[bs]=Plugin za Automatske Popravke Name[ca]=Connector d'autocorrecció Name[ca@valencia]=Connector d'autocorrecció Name[cs]=Modul pro automatické opravy Name[da]=Plugin til autokorrektur Name[de]=Autokorrektur-Modul Name[el]=Πρόσθετο αυτόματης διόρθωσης Name[en_GB]=Autocorrection plugin Name[es]=Complemento de corrección automática Name[et]=Automaatkorrigeerimise plugin Name[eu]=Zuzenketa automatikoaren plugina Name[fa]=وصله اصلاح خودکار Name[fi]=Automaattikorjausliitännäinen Name[fr]=Module externe d'autocorrection Name[ga]=Breiseán uathcheartaithe -Name[gl]=Complemento de correción automática +Name[gl]=Complemento de corrección automática Name[hi]=स्वतःसुधार प्लगइन Name[hne]=अपन आपसुधार प्लगइन Name[hu]=Automatikus javítás bővítmény Name[it]=Estensione di correzione automatica Name[ja]=自動修正プラグイン Name[kk]=Автотүзету плагині Name[lv]=Autokorekcijas spraudnis Name[nb]=Programtillegg for autorettelse Name[nds]=Moduul "Automaatsche Korrektuur" Name[ne]=स्वत: सुधार गर्ने प्लगइन Name[nl]=Autocorrectieplug-in Name[pl]=Wtyczka samoczynnego poprawiania Name[pt]='Plugin' de correcção automática Name[pt_BR]=Plugin de autocorreção Name[ru]=Модуль автоматического исправления Name[sk]=Plugin automatických opráv Name[sl]=Vstavek za samodejno popravljanje Name[sv]=Insticksprogram för automatisk korrigering Name[tr]=Otomatik düzeltme eklentisi Name[uk]=Додаток автовиправлення Name[wa]=Tchôke-divins di coridjaedje otomatike Name[x-test]=xxAutocorrection pluginxx Name[zh_CN]=自动更正插件 Name[zh_TW]=自動修正外掛程式 X-KDE-ServiceTypes=Calligra/Text-EditingPlugin Type=Service X-KDE-PluginInfo-Name=autocorrect X-KoText-MinVersion=28 X-KoText-PluginVersion=28 X-KDE-Library=calligra_textediting_autocorrect diff --git a/plugins/textediting/spellcheck/CMakeLists.txt b/plugins/textediting/spellcheck/CMakeLists.txt index 490bda36dbe..2a715870893 100644 --- a/plugins/textediting/spellcheck/CMakeLists.txt +++ b/plugins/textediting/spellcheck/CMakeLists.txt @@ -1,28 +1,30 @@ project(spellcheck) add_definitions(-DTRANSLATION_DOMAIN=\"calligra_textediting_spellcheck\") -add_subdirectory( tests ) +if(BUILD_TESTING) + add_subdirectory( tests ) +endif() include_directories( ${TEXTLAYOUT_INCLUDES} ${FLAKE_INCLUDES} ) set(spellcheck_SRCS SpellCheckDebug.cpp SpellCheckPlugin.cpp SpellCheck.cpp SpellCheckFactory.cpp BgSpellCheck.cpp SpellCheckMenu.cpp ) add_library(calligra_textediting_spellcheck MODULE ${spellcheck_SRCS}) calligra_texteditingplugin_desktop_to_json(calligra_textediting_spellcheck calligra_textediting_spellcheck.desktop) target_link_libraries(calligra_textediting_spellcheck kotext kotextlayout KF5::SonnetCore KF5::SonnetUi ) install(TARGETS calligra_textediting_spellcheck DESTINATION ${PLUGIN_INSTALL_DIR}/calligra/textediting) diff --git a/plugins/textediting/spellcheck/SpellCheck.cpp b/plugins/textediting/spellcheck/SpellCheck.cpp index 6d7efa0b9ee..ed9df42d11a 100644 --- a/plugins/textediting/spellcheck/SpellCheck.cpp +++ b/plugins/textediting/spellcheck/SpellCheck.cpp @@ -1,359 +1,378 @@ /* This file is part of the KDE project * Copyright (C) 2007, 2008 Fredy Yanardi * Copyright (C) 2007,2009,2010 Thomas Zander * Copyright (C) 2010 Christoph Goerlich * Copyright (C) 2012 Shreya Pandit * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "SpellCheck.h" #include "BgSpellCheck.h" #include "SpellCheckMenu.h" #include "SpellCheckDebug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include SpellCheck::SpellCheck() : m_document(0) , m_bgSpellCheck(0) , m_enableSpellCheck(true) , m_documentIsLoading(false) , m_isChecking(false) , m_spellCheckMenu(0) , m_activeSection(0, 0, 0) , m_simpleEdit(false) + , m_cursorPosition(0) { /* setup actions for this plugin */ QAction *configureAction = new QAction(i18n("Configure &Spell Checking..."), this); connect(configureAction, SIGNAL(triggered()), this, SLOT(configureSpellCheck())); addAction("tool_configure_spellcheck", configureAction); KToggleAction *spellCheck = new KToggleAction(i18n("Auto Spell Check"), this); addAction("tool_auto_spellcheck", spellCheck); KConfigGroup spellConfig = KSharedConfig::openConfig()->group("Spelling"); m_enableSpellCheck = spellConfig.readEntry("autoSpellCheck", m_enableSpellCheck); spellCheck->setChecked(m_enableSpellCheck); m_speller = Sonnet::Speller(spellConfig.readEntry("defaultLanguage", "en_US")); m_bgSpellCheck = new BgSpellCheck(m_speller, this); m_spellCheckMenu = new SpellCheckMenu(m_speller, this); QPair pair = m_spellCheckMenu->menuAction(); addAction(pair.first, pair.second); connect(m_bgSpellCheck, SIGNAL(misspelledWord(QString,int,bool)), this, SLOT(highlightMisspelled(QString,int,bool))); connect(m_bgSpellCheck, SIGNAL(done()), this, SLOT(finishedRun())); connect(spellCheck, SIGNAL(toggled(bool)), this, SLOT(setBackgroundSpellChecking(bool))); } void SpellCheck::finishedWord(QTextDocument *document, int cursorPosition) { setDocument(document); if (!m_enableSpellCheck) return; QTextBlock block = document->findBlock(cursorPosition); if (!block.isValid()) return; KoTextBlockData blockData(block); blockData.setMarkupsLayoutValidity(KoTextBlockData::Misspell, false); checkSection(document, block.position(), block.position() + block.length() - 1); } void SpellCheck::finishedParagraph(QTextDocument *document, int cursorPosition) { setDocument(document); Q_UNUSED(document); Q_UNUSED(cursorPosition); } void SpellCheck::startingSimpleEdit(QTextDocument *document, int cursorPosition) { m_simpleEdit = true; setDocument(document); - Q_UNUSED(document); - Q_UNUSED(cursorPosition); + m_cursorPosition = cursorPosition; } void SpellCheck::checkSection(QTextDocument *document, int startPosition, int endPosition) { if (startPosition >= endPosition) { // no work return; } foreach (const SpellSections &ss, m_documentsQueue) { if (ss.from <= startPosition && ss.to >= endPosition) { runQueue(); m_spellCheckMenu->setVisible(true); return; } // TODO also check if we should replace an existing queued item with a longer span } SpellSections ss(document, startPosition, endPosition); m_documentsQueue.enqueue(ss); runQueue(); m_spellCheckMenu->setVisible(true); } void SpellCheck::setDocument(QTextDocument *document) { if (m_document == document) return; if (m_document) disconnect (document, SIGNAL(contentsChange(int,int,int)), this, SLOT(documentChanged(int,int,int))); m_document = document; connect (document, SIGNAL(contentsChange(int,int,int)), this, SLOT(documentChanged(int,int,int))); } QStringList SpellCheck::availableBackends() const { return m_speller.availableBackends(); } QStringList SpellCheck::availableLanguages() const { return m_speller.availableLanguages(); } void SpellCheck::setDefaultLanguage(const QString &language) { m_speller.setDefaultLanguage(language); m_bgSpellCheck->setDefaultLanguage(language); if (m_enableSpellCheck && m_document) { checkSection(m_document, 0, m_document->characterCount() - 1); } } void SpellCheck::setBackgroundSpellChecking(bool on) { if (m_enableSpellCheck == on) return; KConfigGroup spellConfig = KSharedConfig::openConfig()->group("Spelling"); m_enableSpellCheck = on; spellConfig.writeEntry("autoSpellCheck", m_enableSpellCheck); if (m_document) { if (!m_enableSpellCheck) { for (QTextBlock block = m_document->begin(); block != m_document->end(); block = block.next()) { KoTextBlockData blockData(block); blockData.clearMarkups(KoTextBlockData::Misspell); } m_spellCheckMenu->setEnabled(false); m_spellCheckMenu->setVisible(false); } else { //when re-enabling 'Auto Spell Check' we want spellchecking the whole document checkSection(m_document, 0, m_document->characterCount() - 1); m_spellCheckMenu->setVisible(true); } } } void SpellCheck::setSkipAllUppercaseWords(bool on) { m_speller.setAttribute(Speller::CheckUppercase, !on); } void SpellCheck::setSkipRunTogetherWords(bool on) { m_speller.setAttribute(Speller::SkipRunTogether, on); } bool SpellCheck::addWordToPersonal(const QString &word, int startPosition) { QTextBlock block = m_document->findBlock(startPosition); if (!block.isValid()) return false; KoTextBlockData blockData(block); blockData.setMarkupsLayoutValidity(KoTextBlockData::Misspell, false); checkSection(m_document, block.position(), block.position() + block.length() - 1); // TODO we should probably recheck the entire document so other occurrences are also removed, but then again we should recheck every document (footer,header etc) not sure how to do this return m_bgSpellCheck->addWordToPersonal(word); } QString SpellCheck::defaultLanguage() const { return m_speller.defaultLanguage(); } bool SpellCheck::backgroundSpellChecking() { return m_enableSpellCheck; } bool SpellCheck::skipAllUppercaseWords() { return m_speller.testAttribute(Speller::CheckUppercase); } bool SpellCheck::skipRunTogetherWords() { return m_speller.testAttribute(Speller::SkipRunTogether); } +// TODO: +// 1) When editing a misspelled word it should be spellchecked on the fly so the markup is removed when it is OK. +// 2) Deleting a character should be treated as a simple edit void SpellCheck::highlightMisspelled(const QString &word, int startPosition, bool misspelled) { if (!misspelled) return; #if 0 // DEBUG class MyThread : public QThread { public: static void mySleep(unsigned long msecs) { msleep(msecs); }}; static_cast(QThread::currentThread())->mySleep(400); #endif QTextBlock block = m_activeSection.document->findBlock(startPosition); KoTextBlockData blockData(block); blockData.appendMarkup(KoTextBlockData::Misspell, startPosition - block.position(), startPosition - block.position() + word.trimmed().length()); } -void SpellCheck::documentChanged(int from, int min, int plus) +void SpellCheck::documentChanged(int from, int charsRemoved, int charsAdded) { QTextDocument *document = qobject_cast(sender()); if (document == 0) return; - QTextBlock block = document->findBlock(from); + // If a simple edit, we use the cursor position to determine where + // the change occured. This makes it possible to handle cases + // where formatting of a block has changed, eg. when dropcaps is used. + // QTextDocument then reports the change as if the whole block has changed. + // Ex: Having a 10 char line and you add a char at pos 7: + // from = block->postion() + // charsRemoved = 10 + // charsAdded = 11 + // m_cursorPosition = 7 + int pos = m_simpleEdit ? m_cursorPosition : from; + QTextBlock block = document->findBlock(pos); if (!block.isValid()) return; do { KoTextBlockData blockData(block); if (m_enableSpellCheck) { + // This block and all blocks after this must be relayouted blockData.setMarkupsLayoutValidity(KoTextBlockData::Misspell, false); + // If it's a simple edit we will wait until finishedWord before spellchecking + // but we need to adjust all markups behind the added/removed character(s) if (m_simpleEdit) { - // if it's a simple edit we will wait until finishedWord - blockData.rebaseMarkups(KoTextBlockData::Misspell, from, plus - min); + // Since markups work on positions within each block only the edited block must be rebased + if (block.position() <= pos) { + blockData.rebaseMarkups(KoTextBlockData::Misspell, pos - block.position(), charsAdded - charsRemoved); + } } else { + // handle not so simple edits (like cut/paste etc) checkSection(document, block.position(), block.position() + block.length() - 1); } } else { blockData.clearMarkups(KoTextBlockData::Misspell); } block = block.next(); - } while(block.isValid() && block.position() <= from + plus); + } while(block.isValid() && block.position() <= from + charsAdded); m_simpleEdit = false; } void SpellCheck::runQueue() { Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread()); if (m_isChecking) return; while (!m_documentsQueue.isEmpty()) { m_activeSection = m_documentsQueue.dequeue(); if (m_activeSection.document.isNull()) continue; QTextBlock block = m_activeSection.document->findBlock(m_activeSection.from); if (!block.isValid()) continue; m_isChecking = true; do { KoTextBlockData blockData(block); blockData.clearMarkups(KoTextBlockData::Misspell); block = block.next(); } while(block.isValid() && block.position() < m_activeSection.to); m_bgSpellCheck->startRun(m_activeSection.document, m_activeSection.from, m_activeSection.to); break; } } void SpellCheck::configureSpellCheck() { Sonnet::ConfigDialog *dialog = new Sonnet::ConfigDialog(0); connect (dialog, SIGNAL(languageChanged(QString)), this, SLOT(setDefaultLanguage(QString))); dialog->exec(); delete dialog; } void SpellCheck::finishedRun() { Q_ASSERT(QThread::currentThread() == QApplication::instance()->thread()); m_isChecking = false; KoTextDocumentLayout *lay = qobject_cast(m_activeSection.document->documentLayout()); lay->provider()->updateAll(); QTimer::singleShot(0, this, SLOT(runQueue())); } void SpellCheck::setCurrentCursorPosition(QTextDocument *document, int cursorPosition) { setDocument(document); if (m_enableSpellCheck) { //check if word at cursor is misspelled QTextBlock block = m_document->findBlock(cursorPosition); if (block.isValid()) { KoTextBlockData blockData(block); KoTextBlockData::MarkupRange range = blockData.findMarkup(KoTextBlockData::Misspell, cursorPosition - block.position()); if (int length = range.lastChar - range.firstChar) { QString word = block.text().mid(range.firstChar, length); m_spellCheckMenu->setMisspelled(word, block.position() + range.firstChar, length); QString language = m_bgSpellCheck->currentLanguage(); if (!m_bgSpellCheck->currentLanguage().isEmpty() && !m_bgSpellCheck->currentCountry().isEmpty()) { language += '_'; } language += m_bgSpellCheck->currentCountry(); m_spellCheckMenu->setCurrentLanguage(language); m_spellCheckMenu->setVisible(true); m_spellCheckMenu->setEnabled(true); return; } m_spellCheckMenu->setEnabled(false); } else { m_spellCheckMenu->setEnabled(false); } } } void SpellCheck::replaceWordBySuggestion(const QString &word, int startPosition, int lengthOfWord) { if (!m_document) return; QTextBlock block = m_document->findBlock(startPosition); if (!block.isValid()) return; QTextCursor cursor(m_document); cursor.setPosition(startPosition); cursor.movePosition(QTextCursor::NextCharacter,QTextCursor::KeepAnchor, lengthOfWord); cursor.removeSelectedText(); cursor.insertText(word); } diff --git a/plugins/textediting/spellcheck/SpellCheck.h b/plugins/textediting/spellcheck/SpellCheck.h index 485fdbb07a2..93c789d7a19 100644 --- a/plugins/textediting/spellcheck/SpellCheck.h +++ b/plugins/textediting/spellcheck/SpellCheck.h @@ -1,117 +1,118 @@ /* This file is part of the KDE project * Copyright (C) 2007 Fredy Yanardi * Copyright (C) 2007,2010 Thomas Zander * Copyright (C) 2010 Christoph Goerlich * Copyright (C) 2012 Shreya Pandit * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef SPELLCHECK_H #define SPELLCHECK_H #include #include #include #include #include #include #include #include class QTextDocument; class QTextStream; class BgSpellCheck; class SpellCheckMenu; class SpellCheck : public KoTextEditingPlugin { Q_OBJECT public: SpellCheck(); /// reimplemented from superclass void finishedWord(QTextDocument *document, int cursorPosition); /// reimplemented from superclass void finishedParagraph(QTextDocument *document, int cursorPosition); /// reimplemented from superclass void startingSimpleEdit(QTextDocument *document, int cursorPosition); /// reimplemented from superclass void checkSection(QTextDocument *document, int startPosition, int endPosition); ///reimplemented from superclass void setCurrentCursorPosition(QTextDocument *document, int cursorPosition); QStringList availableBackends() const; QStringList availableLanguages() const; void setSkipAllUppercaseWords(bool b); void setSkipRunTogetherWords(bool b); QString defaultLanguage() const; bool backgroundSpellChecking(); bool skipAllUppercaseWords(); bool skipRunTogetherWords(); bool addWordToPersonal(const QString &word, int startPosition); //reimplemented from Calligra2.0, we disconnect and re- connect the 'documentChanged' signal only when the document has replaced void setDocument(QTextDocument *document); void replaceWordBySuggestion(const QString &word, int startPosition,int lengthOfWord); public Q_SLOTS: void setDefaultLanguage(const QString &lang); private Q_SLOTS: void highlightMisspelled(const QString &word, int startPosition, bool misspelled = true); void finishedRun(); void configureSpellCheck(); void runQueue(); void setBackgroundSpellChecking(bool b); - void documentChanged(int from, int min, int plus); + void documentChanged(int from, int charsRemoved, int charsAdded); private: Sonnet::Speller m_speller; QPointer m_document; QString m_word; BgSpellCheck *m_bgSpellCheck; struct SpellSections { SpellSections(QTextDocument *doc, int start, int end) : document(doc) { from = start; to = end; } QPointer document; int from; int to; }; QQueue m_documentsQueue; bool m_enableSpellCheck; bool m_documentIsLoading; bool m_isChecking; QTextStream stream; SpellCheckMenu *m_spellCheckMenu; SpellSections m_activeSection; // the section we are currently doing a run on; - bool m_simpleEdit; //set when user is doing a simple edit, meaning we should ignore documentCanged + bool m_simpleEdit; //set when user is doing a simple edit, meaning we should not start spellchecking + int m_cursorPosition; // simple edit cursor position }; #endif diff --git a/sheets/CMakeLists.txt b/sheets/CMakeLists.txt index 0256de5c6a8..58c0a5fd57b 100644 --- a/sheets/CMakeLists.txt +++ b/sheets/CMakeLists.txt @@ -1,484 +1,486 @@ project(calligra-sheets) # TEMPORARY: for Qt5/KF5 build porting phase deprecation warnings are only annoying noise if (CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_GNUC) add_definitions(-Wno-deprecated -Wno-deprecated-declarations) endif () include_directories( ${CMAKE_SOURCE_DIR}/interfaces ${KOMAIN_INCLUDES} ${KOTEXT_INCLUDES} ${TEXTLAYOUT_INCLUDES} ${Boost_INCLUDE_DIR} ${EIGEN3_INCLUDE_DIR} ) if (SHOULD_BUILD_PART_SHEETS) # have their own translation domain add_subdirectory( shape ) add_subdirectory( plugins ) add_definitions(-DTRANSLATION_DOMAIN=\"calligrasheets\") add_subdirectory( data ) -add_subdirectory( tests ) +if(BUILD_TESTING) + add_subdirectory( tests ) +endif() add_subdirectory( dtd ) add_subdirectory( functions ) #add_definitions(-DCALLIGRA_SHEETS_MT) if(NOT Qt5Sql_FOUND) add_definitions(-DQT_NO_SQL) endif() ########### next target ############### set (chart_DIR_SRCS chart/ChartDatabaseSelectorFactory.cpp chart/ChartDatabaseSelector.cpp chart/ChartDialog.cpp ) ki18n_wrap_ui(chart_DIR_SRCS chart/ChartDatabaseSelector.ui ) set (commands_DIR_SRCS commands/AbstractRegionCommand.cpp commands/ApplyFilterCommand.cpp commands/AutoFillCommand.cpp commands/AutoFilterCommand.cpp commands/AutoFormatCommand.cpp commands/BorderColorCommand.cpp commands/CommentCommand.cpp commands/ConditionCommand.cpp commands/CopyCommand.cpp commands/CSVDataCommand.cpp commands/DataManipulators.cpp commands/DeleteCommand.cpp commands/IndentationCommand.cpp commands/LinkCommand.cpp commands/MergeCommand.cpp commands/NamedAreaCommand.cpp commands/PageBreakCommand.cpp commands/PasteCommand.cpp commands/PrecisionCommand.cpp commands/RowColumnManipulators.cpp commands/SheetCommands.cpp commands/SortManipulator.cpp commands/SpellCheckCommand.cpp commands/StyleCommand.cpp commands/ValidityCommand.cpp ) set (database_DIR_SRCS #database/Database.cpp #database/DatabaseManager.cpp database/DatabaseSource.cpp database/DatabaseSourceQuery.cpp database/DatabaseSourceSql.cpp database/DatabaseSourceTable.cpp #database/Filter.cpp database/FilterPopup.cpp ) set (dialogs_DIR_SRCS dialogs/AddNamedAreaDialog.cpp dialogs/AngleDialog.cpp dialogs/AutoFormatDialog.cpp dialogs/CharacterSelectDialog.cpp dialogs/CommentDialog.cpp dialogs/ConditionalDialog.cpp dialogs/ConsolidateDialog.cpp dialogs/CSVDialog.cpp dialogs/DatabaseDialog.cpp dialogs/DocumentSettingsDialog.cpp dialogs/FindDialog.cpp dialogs/FormulaDialog.cpp dialogs/GoalSeekDialog.cpp dialogs/GotoDialog.cpp dialogs/InsertDialog.cpp dialogs/LayoutDialog.cpp dialogs/LinkDialog.cpp dialogs/ListDialog.cpp dialogs/NamedAreaDialog.cpp dialogs/PasteInsertDialog.cpp dialogs/Resize2Dialog.cpp dialogs/SeriesDialog.cpp dialogs/ShowDialog.cpp dialogs/ShowColRowDialog.cpp dialogs/SortDialog.cpp dialogs/SpecialPasteDialog.cpp dialogs/StyleManagerDialog.cpp dialogs/SubtotalDialog.cpp dialogs/ValidityDialog.cpp dialogs/pivot.cpp dialogs/pivotfilters.cpp dialogs/pivotoptions.cpp dialogs/pivotmain.cpp ) ki18n_wrap_ui(dialogs_DIR_SRCS dialogs/ConsolidateWidget.ui dialogs/ConsolidateDetailsWidget.ui dialogs/FontWidget.ui dialogs/GoalSeekWidget.ui dialogs/PositionWidget.ui dialogs/ProtectionWidget.ui dialogs/SpecialPasteWidget.ui dialogs/SortWidget.ui dialogs/SortDetailsWidget.ui dialogs/SubtotalWidget.ui dialogs/SubtotalsDetailsWidget.ui dialogs/pivot.ui dialogs/pivotfilters.ui dialogs/pivotoptions.ui dialogs/pivotmain.ui ) set (functions_DIR_SRCS functions/helper.cpp ) if(Qt5DBus_FOUND) set (interfaces_DIR_SRCS interfaces/MapAdaptor.cpp interfaces/SheetAdaptor.cpp interfaces/ViewAdaptor.cpp ) endif() set (odf_DIR_SRCS odf/SheetsOdfDoc.cpp odf/SheetsOdfMap.cpp odf/SheetsOdfSheet.cpp odf/SheetsOdfCell.cpp odf/SheetsOdfStyle.cpp odf/SheetsOdfRegion.cpp odf/SheetsOdfCondition.cpp odf/SheetsOdfValidity.cpp odf/GenValidationStyle.cpp ) set (part_DIR_SRCS part/CanvasBase.cpp part/Canvas.cpp part/CanvasItem.cpp part/CellTool.cpp part/CellToolFactory.cpp #part/Digest.cpp part/Doc.cpp part/Part.cpp part/Factory.cpp part/Find.cpp part/Headers.cpp part/HeaderWidgets.cpp part/HeaderItems.cpp part/PrintJob.cpp part/ToolRegistry.cpp part/TabBar.cpp part/View.cpp part/commands/DefinePrintRangeCommand.cpp part/commands/PageLayoutCommand.cpp part/dialogs/PageLayoutDialog.cpp part/dialogs/PreferenceDialog.cpp part/dialogs/SheetPropertiesDialog.cpp part/dialogs/SheetSelectPage.cpp ) ki18n_wrap_ui(part_DIR_SRCS part/dialogs/FileOptionsWidget.ui part/dialogs/InterfaceOptionsWidget.ui part/dialogs/PageLayoutSheetPage.ui part/dialogs/SheetPropertiesWidget.ui part/dialogs/SheetSelectWidget.ui ) set (ui_DIR_SRCS ui/AbstractSelectionStrategy.cpp ui/ActionOptionWidget.cpp ui/AutoFillStrategy.cpp ui/CellEditorBase.cpp ui/CellEditor.cpp ui/CellEditorDocker.cpp ui/CellToolBase.cpp ui/CellToolBase_p.cpp ui/CellView.cpp ui/DragAndDropStrategy.cpp ui/FormulaEditorHighlighter.cpp ui/FunctionCompletion.cpp ui/ExternalEditor.cpp ui/HyperlinkStrategy.cpp ui/LocationComboBox.cpp ui/MapViewModel.cpp ui/MergeStrategy.cpp ui/PasteStrategy.cpp ui/PixmapCachingSheetView.cpp ui/RegionSelector.cpp ui/RightToLeftPaintingStrategy.cpp ui/Selection.cpp ui/SelectionStrategy.cpp ui/SheetView.cpp ) set (calligrasheetscommon_LIB_SRCS MapModel.cpp PageManager.cpp RegionModel.cpp tests/inspector.cpp ${chart_DIR_SRCS} ${commands_DIR_SRCS} ${database_DIR_SRCS} ${dialogs_DIR_SRCS} ${functions_DIR_SRCS} ${part_DIR_SRCS} ${ui_DIR_SRCS} ) if(Qt5DBus_FOUND) set (calligrasheetscommon_LIB_SRCS ${calligrasheetscommon_LIB_SRCS} ${interfaces_DIR_SRCS} ) endif() set (calligrasheetsodf_LIB_SRCS SheetsDebug.cpp part/Digest.cpp ApplicationSettings.cpp Binding.cpp BindingManager.cpp BindingModel.cpp BindingStorage.cpp CalculationSettings.cpp Cell.cpp CellStorage.cpp Cluster.cpp Condition.cpp ConditionsStorage.cpp Currency.cpp Damages.cpp DependencyManager.cpp DocBase.cpp Format.cpp Formula.cpp HeaderFooter.cpp Localization.cpp Map.cpp NamedAreaManager.cpp Number.cpp PrintSettings.cpp ProtectableObject.cpp RecalcManager.cpp RectStorage.cpp Region.cpp RowColumnFormat.cpp RowFormatStorage.cpp RowRepeatStorage.cpp ShapeApplicationData.cpp Sheet.cpp SheetAccessModel.cpp SheetModel.cpp Style.cpp StyleManager.cpp StyleStorage.cpp Util.cpp Validity.cpp ValidityStorage.cpp Value.cpp ValueCalc.cpp ValueConverter.cpp ValueFormatter.cpp ValueParser.cpp database/Database.cpp database/DatabaseManager.cpp database/DatabaseStorage.cpp database/Filter.cpp ${odf_DIR_SRCS} # TODO: move the formula evaluation out of Formula.cpp so these files can move out of libcalligrasheetsodf Function.cpp FunctionDescription.cpp FunctionModule.cpp FunctionModuleRegistry.cpp FunctionRepository.cpp # TODO: move HeaderFooter from SheetPrint to PrintSettings, and replace SheetPrint with PrintSettings in Sheet to get rid of this dependency SheetPrint.cpp SheetPrint_p.cpp ) add_library(calligrasheetsodf SHARED ${calligrasheetsodf_LIB_SRCS}) generate_export_header(calligrasheetsodf EXPORT_FILE_NAME sheets_odf_generated_export.h BASE_NAME CALLIGRA_SHEETS_ODF ) target_link_libraries(calligrasheetsodf PUBLIC komain KF5::KDELibs4Support PRIVATE koplugin ) set_target_properties(calligrasheetsodf PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) install(TARGETS calligrasheetsodf ${INSTALL_TARGETS_DEFAULT_ARGS}) add_library(calligrasheetscommon SHARED ${calligrasheetscommon_LIB_SRCS}) generate_export_header(calligrasheetscommon EXPORT_FILE_NAME sheets_common_generated_export.h BASE_NAME CALLIGRA_SHEETS_COMMON ) target_link_libraries(calligrasheetscommon PUBLIC komain calligrasheetsodf PRIVATE koplugin KF5::SonnetCore KF5::SonnetUi KF5::NotifyConfig KF5::KCMUtils ) if(Qt5Sql_FOUND) target_link_libraries(calligrasheetscommon PRIVATE Qt5::Sql) endif() set_target_properties(calligrasheetscommon PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) install(TARGETS calligrasheetscommon ${INSTALL_TARGETS_DEFAULT_ARGS}) ########### next target ############### set (calligrasheetspart_PART_SRCS part/Factory_init.cpp ) add_library(calligrasheetspart MODULE ${calligrasheetspart_PART_SRCS}) calligra_part_desktop_to_json(calligrasheetspart sheetspart.desktop) target_link_libraries(calligrasheetspart calligrasheetscommon ) install(TARGETS calligrasheetspart DESTINATION ${PLUGIN_INSTALL_DIR}/calligra/parts) ########### install files ############### install( FILES calligrasheets.rc calligrasheets_readonly.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/calligrasheets) install( FILES ui/CellToolOptionWidgets.xml DESTINATION ${DATA_INSTALL_DIR}/calligrasheets) install( FILES calligrasheets.kcfg DESTINATION ${KCFG_INSTALL_DIR} ) install( FILES calligrasheetsrc DESTINATION ${CONFIG_INSTALL_DIR}) # TODO: with the new embedded JSON data for plugins there is no schema ATM to define extended # propertiessheets_plugin.desktop, sheets_viewplugin.desktop if (SHOULD_BUILD_DEVEL_HEADERS) install( FILES sheets_odf_export.h ${CMAKE_CURRENT_BINARY_DIR}/sheets_odf_generated_export.h sheets_common_export.h ${CMAKE_CURRENT_BINARY_DIR}/sheets_common_generated_export.h calligra_sheets_limits.h Cell.h CellStorage.h Condition.h Currency.h DocBase.h Format.h Global.h Map.h Number.h odf/OdfLoadingContext.h PointStorage.h PrintSettings.h ProtectableObject.h RectStorage.h Region.h RowColumnFormat.h RowFormatStorage.h RTree.h Sheet.h Style.h Value.h ValueCalc.h ValueConverter.h ValueStorage.h DESTINATION ${INCLUDE_INSTALL_DIR}/sheets COMPONENT Devel) install( FILES part/CanvasBase.h part/CanvasItem.h part/CellTool.h part/Doc.h part/Part.h part/Find.h part/HeaderItems.h part/Headers.h part/ToolRegistry.h part/View.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligrasheets/part COMPONENT Devel) install( FILES ui/CellToolBase.h ui/CellEditorBase.h ui/Selection.h ui/SheetView.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligrasheets/ui COMPONENT Devel) install( FILES database/Database.h database/Filter.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligrasheets/database COMPONENT Devel) install( FILES commands/AbstractRegionCommand.h commands/DataManipulators.h commands/SortManipulator.h dialogs/pivot.h dialogs/pivotfilters.h dialogs/pivotoptions.h dialogs/pivotmain.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligrasheets/commands COMPONENT Devel) endif() endif () ########### APP ############### if (SHOULD_BUILD_APP_SHEETS) set (calligrasheets_KDEINIT_SRCS part/Main.cpp ) file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/data/pics/*-apps-calligrasheets.png") ecm_add_app_icon(calligrasheets_KDEINIT_SRCS ICONS ${ICONS_SRCS}) kf5_add_kdeinit_executable( calligrasheets ${calligrasheets_KDEINIT_SRCS}) if (APPLE) set_target_properties(calligrasheets PROPERTIES MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.template) set_target_properties(calligrasheets PROPERTIES MACOSX_BUNDLE_GUI_IDENTIFIER "org.calligra.sheets") set_target_properties(calligrasheets PROPERTIES MACOSX_BUNDLE_BUNDLE_NAME "Calligra Sheets 2") install( FILES ${CMAKE_CURRENT_BINARY_DIR}/calligrasheets_KDEINIT_SRCS.icns DESTINATION ${BUNDLE_INSTALL_DIR}/calligrasheets.app/Contents/Resources) endif () target_link_libraries(kdeinit_calligrasheets komain KF5::KDELibs4Support) install(TARGETS kdeinit_calligrasheets ${INSTALL_TARGETS_DEFAULT_ARGS}) target_link_libraries( calligrasheets kdeinit_calligrasheets ) install(TARGETS calligrasheets ${INSTALL_TARGETS_DEFAULT_ARGS}) install( PROGRAMS org.kde.calligrasheets.desktop DESTINATION ${XDG_APPS_INSTALL_DIR}) install( FILES org.kde.calligrasheets.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR}) endif () diff --git a/sheets/data/templates/HomeFamily/VacationChecklist.desktop b/sheets/data/templates/HomeFamily/VacationChecklist.desktop index f4089e068e3..70274342782 100644 --- a/sheets/data/templates/HomeFamily/VacationChecklist.desktop +++ b/sheets/data/templates/HomeFamily/VacationChecklist.desktop @@ -1,58 +1,58 @@ [Desktop Entry] Type=Link URL=.source/VacationChecklist.kst Name=Vacation Checklist Name[bg]=Ваканция Name[bs]=Spisak Odmora Name[ca]=Llista de comprovació de vacances Name[ca@valencia]=Llista de comprovació de vacances Name[cs]=Seznam na dovolenou Name[cy]=Rhestr Wirio Gwyliau Name[da]=Ferie-tjekliste Name[de]=Urlaubs-Checkliste Name[el]=Λίστα ελέγχου διακοπών Name[en_GB]=Vacation Checklist Name[eo]=Marklisto por Feriado Name[es]=Lista de comprobación de vacaciones Name[et]=Puhkuseplaan Name[eu]=Oporretako egiaztapen-zerrenda Name[fa]=فهرست بررسی تعطیلی Name[fi]=Loman tarkistuslista Name[fr]=Checklist des vacances Name[fy]=Fakânsje-kontrôlelist -Name[gl]=Lista para viaxes +Name[gl]=Lista para marcar das vacacións Name[he]=בדיקת חופשות Name[hi]=छुट्टी हेतु जांचसूची Name[hne]=छुट्टी बर जांचसूची Name[hu]=Utazási tennivalók listája Name[is]=Ferðalags gáttlisti­ Name[it]=Lista per le vacanze Name[ja]=休暇チェックリスト Name[kk]=Демалысқа шығу тізімі Name[ko]=휴가 점검표 Name[lv]=Atvaļinājuma lietu saraksts Name[mr]=सुट्टीची यादी Name[ms]=Senarai Semak Percutian Name[nb]=Sjekkliste for ferie Name[nds]=Oorlööf-Pröövlist Name[ne]=बिदा जाँच सूची Name[nl]=Vakantie-controlelijst Name[pl]=Wakacyjny spis kontrolny Name[pt]=Lista para Férias Name[pt_BR]=Lista para férias Name[ru]=Подготовка к отпуску Name[se]=Luopmodárkkistanlistu Name[sk]=Zoznam pre dovolenku Name[sl]=Seznam za dopust Name[sv]=Semesterchecklista Name[ta]=விடுமுறை பட்டியலை சரிபார்த்தல் Name[tg]=Раҳонидани Рӯйхати Тафтиш Name[tr]=İzin Takibi Name[uk]=Список для підготовки до відпустки Name[uz]=Dam olishga tayyorgarlik Name[uz@cyrillic]=Дам олишга тайёргарлик Name[wa]=Djivêye des vancances Name[x-test]=xxVacation Checklistxx Name[zh_CN]=度假核对表 Name[zh_TW]=渡假查核表 Icon=template_vacationchecklist diff --git a/sheets/org.kde.calligrasheets.desktop b/sheets/org.kde.calligrasheets.desktop index a2e70f7557d..6a0c1d0183a 100644 --- a/sheets/org.kde.calligrasheets.desktop +++ b/sheets/org.kde.calligrasheets.desktop @@ -1,133 +1,133 @@ [Desktop Entry] Name=Calligra Sheets Name[bs]=Calligra Tablice Name[ca]=Sheets del Calligra Name[ca@valencia]=Sheets del Calligra Name[cs]=Calligra Sheets Name[da]=Calligra Sheets Name[de]=Calligra Sheets Name[el]=Calligra Sheets Name[en_GB]=Calligra Sheets Name[es]=Calligra Sheets Name[et]=Calligra Sheets Name[eu]=Calligra Sheets Name[fi]=Calligra Sheets Name[fr]=Calligra Sheets Name[gl]=Calligra Sheets Name[hu]=Calligra munkalapok Name[it]=Calligra Sheets Name[ja]=Calligra Sheets Name[kk]=Calligra Sheets Name[mr]=कॅलिग्रा शीट्स Name[nb]=Calligra Sheets Name[nl]=Calligra Sheets Name[pl]=Arkusze Calligra Name[pt]=Calligra Sheets Name[pt_BR]=Calligra Sheets Name[sk]=Calligra Sheets Name[sl]=Calligra Sheets Name[sv]=Calligra Sheets Name[tr]=Calligra Sheets Name[ug]=Calligra ۋاراقلار Name[uk]=Calligra Sheets Name[x-test]=xxCalligra Sheetsxx Name[zh_CN]=Calligra Sheets Exec=calligrasheets %U GenericName=Spreadsheet GenericName[bg]=Електронни таблици GenericName[bs]=Tablica GenericName[ca]=Full de càlcul GenericName[ca@valencia]=Full de càlcul GenericName[cs]=Sešit GenericName[da]=Regneark GenericName[de]=Tabellenkalkulation GenericName[el]=Φύλλο εργασίας GenericName[en_GB]=Spreadsheet GenericName[es]=Hoja de cálculo GenericName[et]=Tabelitöötlus GenericName[eu]=Kalkulu-orria GenericName[fi]=Taulukkolaskenta GenericName[fr]=Tableur GenericName[fy]=Spreadsheet GenericName[ga]=Scarbhileog GenericName[gl]=Follas de cálculo GenericName[he]=גיליון אלקטרוני GenericName[hi]=स्प्रेडशीट GenericName[hne]=स्प्रेडसीट GenericName[hu]=Munkafüzet GenericName[ia]=Folio de calculo electronic GenericName[it]=Foglio di calcolo GenericName[ja]=表計算 GenericName[kk]=Электрондық кесте GenericName[ko]=스프레드시트 GenericName[lv]=Tabullapa GenericName[mai]=स्प्रेडशीट GenericName[mr]=स्प्रेडशीट GenericName[nb]=Regneark GenericName[nds]=Tabellenutreken GenericName[nl]=Spreadsheet GenericName[oc]=Fuèlha de calcul GenericName[pl]=Arkusz kalkulacyjny GenericName[pt]=Folha de cálculo GenericName[pt_BR]=Planilha GenericName[ru]=Электронные таблицы GenericName[sk]=Tabuľkový procesor GenericName[sl]=Preglednica GenericName[sv]=Kalkylark GenericName[tr]=Hesap Tablosu GenericName[ug]=ئېلېكترونلۇق جەدۋەل GenericName[uk]=Електронні таблиці GenericName[wa]=Tåvleu GenericName[x-test]=xxSpreadsheetxx GenericName[zh_CN]=电子表格 GenericName[zh_TW]=工作表 Comment=Write spreadsheet documents Comment[bg]=Писане на електронни таблици Comment[bs]=Napiši Dokumente Tablice Comment[ca]=Escriu documents de full de càlcul Comment[ca@valencia]=Escriu documents de full de càlcul Comment[da]=Skriv regnearksdokumenter Comment[de]=Tabellendokumente verfassen Comment[el]=Γραφή εγγράφων φύλλου εργασίας Comment[en_GB]=Write spreadsheet documents Comment[es]=Escribir documentos de hoja de cálculo Comment[et]=Tabelidokumentide kirjutamine Comment[eu]=Kalkulu-orriak idatzi Comment[fi]=Kirjoita taulukkolaskentatiedostoja Comment[fr]=Documents tableurs vides Comment[ga]=Scríobh cáipéisí scarbhileoige Comment[gl]=Escriba documentos de follas de cálculo. Comment[hu]=Munkafüzet dokumentumok írása Comment[it]=Scrivi fogli di testo Comment[ja]=スプレッドシート文書を作成 Comment[kk]=Эл.кесте құжаттарды жазу Comment[ko]=스프레드시트 문서 만들기 Comment[mr]=स्प्रेडशीट दस्तऐवज लिहा Comment[nb]=Skriv regneark-dokumenter Comment[nds]=Tabellenutreken opstellen Comment[nl]=Rekenbladdocumenten schrijven Comment[pl]=Pisz dokumenty arkuszy kalkulacyjnych Comment[pt]=Escrever documentos de folhas de cálculo Comment[pt_BR]=Escrever documentos de planilha Comment[ru]=Редактирование таблиц Comment[sk]=Písanie tabuľkových dokumentov Comment[sl]=Pišite dokumente s preglednicami Comment[sv]=Skriv kalkylarksdokument Comment[tr]=Çizelge belgeleri oluşturun Comment[ug]=جەدۋەل پۈتۈكلىرى(Excel غا ئوخشاش) نى يېزىش پروگراممىسى Comment[uk]=Створення електронних таблиць Comment[wa]=Sicrire des documints tåvleas Comment[x-test]=xxWrite spreadsheet documentsxx Comment[zh_CN]=编写电子表格文档 Comment[zh_TW]=撰寫工作表文件 MimeType=application/vnd.oasis.opendocument.spreadsheet;application/x-kspread;application/vnd.ms-excel;text/csv;application/x-quattropro;application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;application/vnd.openxmlformats-officedocument.spreadsheetml.template; Type=Application Icon=calligrasheets X-KDE-ServiceTypes=Calligra/Application X-Calligra-DefaultMimeTypes=application/vnd.oasis.opendocument.spreadsheet,application/x-kspread,application/vnd.ms-excel,text/csv,application/x-quattropro,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.openxmlformats-officedocument.spreadsheetml.template X-KDE-NativeMimeType=application/vnd.oasis.opendocument.spreadsheet X-DBUS-StartupType=multi X-DBUS-ServiceName=org.kde.calligrasheets X-DocPath=sheets/index.html X-KDE-StartupNotify=true -Categories=Qt;KDE;Office; +Categories=Qt;KDE;Office;Spreadsheet; diff --git a/stage/app/org.kde.calligrastage.desktop b/stage/app/org.kde.calligrastage.desktop index 63b606dbb8e..dca742ced50 100644 --- a/stage/app/org.kde.calligrastage.desktop +++ b/stage/app/org.kde.calligrastage.desktop @@ -1,138 +1,138 @@ [Desktop Entry] Type=Application Name=Calligra Stage Name[bg]=Calligra Stage Name[bs]=Calligra scena Name[ca]=Stage del Calligra Name[ca@valencia]=Stage del Calligra Name[cs]=Calligra Stage Name[da]=Calligra Stage Name[de]=Calligra Stage Name[el]=Calligra Stage Name[en_GB]=Calligra Stage Name[es]=Calligra Stage Name[et]=Calligra Stage Name[eu]=Calligra Stage Name[fi]=Calligra Stage Name[fr]=Calligra Stage Name[gl]=Calligra Stage Name[hu]=Calligra Stage Name[it]=Calligra Stage Name[ja]=Calligra Stage Name[kk]=Calligra Stage Name[mr]=कॅलिग्रा स्टेज Name[nb]=Calligra Stage Name[nds]=Calligra-Stage Name[nl]=Calligra Stage Name[pl]=Scena Calligra Name[pt]=Calligra Stage Name[pt_BR]=Calligra Stage Name[ru]=Calligra Stage Name[sk]=Calligra Stage Name[sl]=Calligra Stage Name[sv]=Calligra Stage Name[tr]=Calligra Stage Name[ug]=Calligra Stage Name[uk]=Calligra Stage Name[x-test]=xxCalligra Stagexx Name[zh_CN]=Calligra Stage Name[zh_TW]=Calligra Stage Exec=calligrastage %U GenericName=Presentation GenericName[bg]=Екранни презентации GenericName[bs]=Prezentacija GenericName[ca]=Presentació GenericName[ca@valencia]=Presentació GenericName[cs]=Prezentace GenericName[da]=Præsentation GenericName[de]=Präsentation GenericName[el]=Παρουσίαση GenericName[en_GB]=Presentation GenericName[es]=Presentación GenericName[et]=Esitlus GenericName[eu]=Aurkezpena GenericName[fi]=Esitysgrafiikka GenericName[fr]=Présentation GenericName[fy]=Presintaasje GenericName[ga]=Láithreoireacht GenericName[gl]=Edición e reprodución de presentacións GenericName[he]=מצגת GenericName[hi]=प्रस्तुतीकरण GenericName[hne]=प्रस्तुतीकरन GenericName[hr]=Prezentacija GenericName[hu]=Bemutató GenericName[ia]=Presentation GenericName[it]=Presentazione GenericName[ja]=プレゼンテーション GenericName[kk]=Презентация GenericName[ko]=프레젠테이션 GenericName[lv]=Prezentācija GenericName[mai]=प्रस्तुति GenericName[mr]=प्रेझेंटेशन GenericName[nb]=Presentasjon GenericName[nds]=Presentatschoon GenericName[nl]=Presentatie GenericName[oc]=Presentacion GenericName[pl]=Prezentacja GenericName[pt]=Apresentação GenericName[pt_BR]=Apresentação GenericName[ru]=Презентации GenericName[sk]=Prezentácia GenericName[sl]=Predstavitev GenericName[sv]=Presentation GenericName[tg]=Тақдим GenericName[tr]=Sunum GenericName[ug]=سۇنۇلما GenericName[uk]=Презентація GenericName[wa]=Prezintaedje GenericName[x-test]=xxPresentationxx GenericName[zh_CN]=幻灯片演示 GenericName[zh_TW]=簡報 Comment=Write presentation documents Comment[bg]=Създаване на документи за презентации Comment[bs]=Napišite prezentacijske dokumente Comment[ca]=Escriu documents de presentació Comment[ca@valencia]=Escriu documents de presentació Comment[da]=Skriv præsentationsdokumenter Comment[de]=Präsentationsdokumente verfassen Comment[el]=Γράψτε έγγραφα παρουσιάσεων Comment[en_GB]=Write presentation documents Comment[es]=Escribir documentos de presentación Comment[et]=Esitlusdokumentide kirjutamine Comment[eu]=Aurkezpen-dokumentuak idatzi Comment[fi]=Kirjoita esitystiedostoja Comment[fr]=Écrire des documents de présentation Comment[gl]=Escriba e reproduza documentos de presentación. Comment[hu]=Prezentáció dokumentumok írása Comment[it]=Scrivi presentazioni Comment[ja]=プレゼンテーション文書を作成 Comment[kk]=Презентация құжатын жазу Comment[ko]=프레젠테이션 문서 만들기 Comment[mr]=प्रेझेंटेशन दस्तऐवज लिहा Comment[nb]=Skriv presentasjonsdokumenter Comment[nds]=Presentatschonen opstellen Comment[nl]=Presentatiedocumenten schrijven Comment[pl]=Pisz dokumenty prezentacji Comment[pt]=Escrever documentos de apresentações Comment[pt_BR]=Escrever documentos de apresentações Comment[ru]=Редактирование презентаций Comment[sk]=Písanie dokumentov prezentácií Comment[sl]=Pišite predstavitvene dokumente Comment[sv]=Skriv presentationsdokument Comment[tr]=Sunum belgeleri oluşturun Comment[ug]=سۇنۇلما پۈتۈكلىرىنى يېزىش پروگراممىسى Comment[uk]=Створення документів презентацій Comment[wa]=Sicrire documint di prezintaedje Comment[x-test]=xxWrite presentation documentsxx Comment[zh_CN]=编写演示文稿 Comment[zh_TW]=撰寫簡報文件 MimeType=application/vnd.oasis.opendocument.presentation;application/vnd.oasis.opendocument.presentation-template;application/x-kpresenter;application/vnd.ms-powerpoint;application/vnd.openxmlformats-officedocument.presentationml.presentation;application/vnd.openxmlformats-officedocument.presentationml.template; X-KDE-ServiceTypes=Calligra/Application Icon=calligrastage X-KDE-NativeMimeType=application/vnd.oasis.opendocument.presentation X-Calligra-DefaultMimeTypes=application/vnd.oasis.opendocument.presentation,application/vnd.oasis.opendocument.presentation-template,application/x-kpresenter,application/vnd.ms-powerpoint,application/vnd.openxmlformats-officedocument.presentationml.presentation,application/vnd.openxmlformats-officedocument.presentationml.template X-DBUS-StartupType=multi X-DBUS-ServiceName=org.kde.calligrastage X-DocPath=stage/index.html X-KDE-StartupNotify=true -Categories=Qt;KDE;Office; +Categories=Qt;KDE;Office;Presentation; diff --git a/stage/part/CMakeLists.txt b/stage/part/CMakeLists.txt index f37562bacb7..b678457c9c2 100644 --- a/stage/part/CMakeLists.txt +++ b/stage/part/CMakeLists.txt @@ -1,227 +1,227 @@ project(stagepart) include_directories( ${KOMAIN_INCLUDES} ${KOPAGEAPP_INCLUDES} ${Boost_INCLUDE_DIR}) -if (NOT WIN32) ## Disable tests in Windows whilst they break builds +if (BUILD_TESTING AND NOT WIN32) ## Disable tests in Windows whilst they break builds add_subdirectory(tests) endif() add_subdirectory(tools) ### calligrastageprivate ### set( calligrastageprivate_LIB_SRCS StageDebug.cpp KPrFactory.cpp KPrDocument.cpp KPrDeclarations.cpp KPrPart.cpp KPrView.cpp KPrViewModePresentation.cpp KPrViewModeNotes.cpp KPrViewModeSlidesSorter.cpp KPrViewModePreviewPageEffect.cpp KPrViewModePreviewShapeAnimations.cpp KPrPresentationTool.cpp KPrAnimationDirector.cpp KPrShapeAnimations.cpp KPrShapeManagerAnimationStrategy.cpp KPrShapeManagerDisplayMasterStrategy.cpp KPrPageData.cpp KPrPage.cpp KPrMasterPage.cpp KPrNotes.cpp KPrSoundData.cpp KPrSoundCollection.cpp KPrEventActionData.cpp KPrEventActionWidget.cpp KPrPageApplicationData.cpp KPrShapeApplicationData.cpp KPrCustomSlideShows.cpp KPrPresenterViewBaseInterface.cpp KPrPresenterViewInterface.cpp KPrPresenterViewSlidesInterface.cpp KPrPresenterViewToolWidget.cpp KPrPresenterViewWidget.cpp KPrEndOfSlideShowPage.cpp KPrPlaceholderShape.cpp KPrPlaceholderShapeFactory.cpp KPrPlaceholderStrategy.cpp KPrPlaceholderPictureStrategy.cpp KPrPlaceholderTextStrategy.cpp KPrPresentationHighlightWidget.cpp KPrPresentationDrawWidget.cpp KPrPresentationBlackWidget.cpp KPrPresentationStrategy.cpp KPrPresentationHighlightStrategy.cpp KPrPresentationBlackStrategy.cpp KPrPresentationStrategyBase.cpp KPrPresentationToolEventForwarder.cpp KPrPresentationDrawStrategy.cpp KPrPageSelectStrategyBase.cpp KPrPageSelectStrategyFixed.cpp KPrPageSelectStrategyActive.cpp KPrDurationParser.cpp KPrHtmlExport.cpp KPrHtmlExportUiDelegate.cpp KPrPicturesImport.cpp KPrPdfPrintJob.cpp KPrSlidesSorterDocumentModel.cpp KPrSlidesManagerView.cpp KPrCustomSlideShowsModel.cpp KPrSlidesSorterItemDelegate.cpp KPrPageLayoutWidget.cpp commands/KPrAnimationCreateCommand.cpp commands/KPrAnimationRemoveCommand.cpp commands/KPrPageEffectSetCommand.cpp commands/KPrPageLayoutCommand.cpp commands/KPrEditCustomSlideShowsCommand.cpp commands/KPrAddCustomSlideShowCommand.cpp commands/KPrDelCustomSlideShowCommand.cpp commands/KPrRenameCustomSlideShowCommand.cpp commands/KPrDeleteSlidesCommand.cpp commands/KPrEditAnimationTimeLineCommand.cpp commands/KPrAnimationEditNodeTypeCommand.cpp commands/KPrReorderAnimationCommand.cpp commands/KPrReplaceAnimationCommand.cpp dockers/KPrPreviewWidget.cpp pageeffects/KPrPageEffectRunner.cpp pageeffects/KPrPageEffect.cpp pageeffects/KPrPageEffectStrategy.cpp pageeffects/KPrPageEffectFactory.cpp pageeffects/KPrPageEffectRegistry.cpp animations/KPrAnimationBase.cpp animations/KPrAnimSet.cpp animations/KPrAnimate.cpp animations/KPrAnimateColor.cpp animations/KPrAnimateMotion.cpp animations/KPrAnimateTransform.cpp animations/KPrAnimTransitionFilter.cpp animations/KPrAnimationFactory.cpp animations/KPrAnimationCache.cpp animations/KPrTextBlockPaintStrategy.cpp animations/KPrShapeAnimation.cpp animations/KPrAnimationStep.cpp animations/KPrAnimationSubStep.cpp animations/KPrAnimationLoader.cpp animations/KPrAnimationData.cpp animations/strategy/KPrAnimationValue.cpp animations/strategy/KPrFormulaParser.cpp animations/strategy/KPrAnimationAttribute.cpp animations/strategy/KPrSmilValues.cpp animations/strategy/KPrAttributeX.cpp animations/strategy/KPrAttributeY.cpp animations/strategy/KPrAttributeWidth.cpp animations/strategy/KPrAttributeHeight.cpp animations/strategy/KPrAttributeRotate.cpp pagelayout/KPrPlaceholder.cpp pagelayout/KPrPageLayout.cpp pagelayout/KPrPageLayouts.cpp pagelayout/KPrPageLayoutSharedSavingData.cpp pagelayout/KPrPlaceholders.cpp ui/KPrConfigureSlideShowDialog.cpp ui/KPrConfigurePresenterViewDialog.cpp ui/KPrPresentationToolWidget.cpp ui/KPrHtmlExportDialog.cpp tools/KPrPlaceholderTool.cpp tools/KPrPlaceholderToolFactory.cpp ) if(Qt5DBus_FOUND) set( calligrastageprivate_LIB_SRCS ${calligrastageprivate_LIB_SRCS} KPrViewAdaptor.cpp KPrPresentationToolAdaptor.cpp ) endif() ki18n_wrap_ui(calligrastageprivate_LIB_SRCS ui/KPrConfigureSlideShow.ui ui/KPrConfigurePresenterView.ui ui/KPrPresentationTool.ui ui/KPrHtmlExport.ui ) add_library(calligrastageprivate SHARED ${calligrastageprivate_LIB_SRCS}) generate_export_header(calligrastageprivate BASE_NAME stage EXPORT_FILE_NAME stage_generated_export.h ) target_link_libraries(calligrastageprivate PUBLIC kopageapp PRIVATE kowidgets kotextlayout koplugin KF5::Archive KF5::IconThemes Qt5::Svg # Qt5::WebKitWidgets ) if(HAVE_OPENGL) target_link_libraries(calligrastageprivate PRIVATE Qt5::OpenGL) endif() set_target_properties(calligrastageprivate PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) install(TARGETS calligrastageprivate ${INSTALL_TARGETS_DEFAULT_ARGS}) ### calligrastagepart ### set(calligrastagepart_PART_SRCS KPrFactoryInit.cpp ) add_library(calligrastagepart MODULE ${calligrastagepart_PART_SRCS}) calligra_part_desktop_to_json(calligrastagepart stagepart.desktop) target_link_libraries(calligrastagepart calligrastageprivate ) install(TARGETS calligrastagepart DESTINATION ${PLUGIN_INSTALL_DIR}/calligra/parts) ### GUI files ### install( FILES calligrastage.rc calligrastage_readonly.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/calligrastage) install( FILES calligrastagerc DESTINATION ${CONFIG_INSTALL_DIR} ) ### Predefined Animations ### install(FILES animations/animations.xml DESTINATION ${DATA_INSTALL_DIR}/calligrastage/animations) ### Include files ### if (SHOULD_BUILD_DEVEL_HEADERS) install( FILES stage_export.h KPrAnimationDirector.h KPrCustomSlideShows.h KPrDocument.h KPrPage.h KPrPageData.h KPrDeclarations.h KPrPresentationTool.h KPrNotes.h KPrShapeAnimations.h KPrView.h KPrViewModePresentation.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligrastage/part COMPONENT Devel) install( FILES animations/KPrAnimationData.h animations/KPrAnimationStep.h animations/KPrShapeAnimation.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligrastage/part/animations COMPONENT Devel) install( FILES pagelayout/KPrPlaceholders.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligrastage/part/pagelayout COMPONENT Devel) endif() diff --git a/stage/part/KPrShapeAnimations.h b/stage/part/KPrShapeAnimations.h index f13c5984d9a..86f6e0c9b4b 100644 --- a/stage/part/KPrShapeAnimations.h +++ b/stage/part/KPrShapeAnimations.h @@ -1,313 +1,313 @@ /* This file is part of the KDE project * Copyright ( C ) 2007 Thorsten Zachmann * Copyright ( C ) 2010 Benjamin Port * Copyright ( C ) 2012 Paul Mendez * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or ( at your option ) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPRSHAPEANIMATIONS_H #define KPRSHAPEANIMATIONS_H #include #include #include "animations/KPrShapeAnimation.h" #include "animations/KPrAnimationStep.h" #include "stage_export.h" class KPrDocument; /** * Model for Animations data of each KPrPage */ class STAGE_EXPORT KPrShapeAnimations : public QAbstractTableModel { Q_OBJECT public: /// Time to be updated enum TimeUpdated { BeginTime, DurationTime, BothTimes }; /// column names enum ColumnNames { Group, StepCount, TriggerEvent, Name, ShapeThumbnail, AnimationIcon, StartTime, Duration, AnimationClass, NodeType }; explicit KPrShapeAnimations(KPrDocument *document, QObject *parent = 0); ~KPrShapeAnimations(); /// Model Methods Qt::ItemFlags flags(const QModelIndex &index) const; QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const; QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const; int rowCount(const QModelIndex &parent=QModelIndex()) const; int columnCount(const QModelIndex &parent=QModelIndex()) const; bool setHeaderData(int, Qt::Orientation, const QVariant&, int=Qt::EditRole) {return false;} bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole); void init(const QList &animations); /** * Add animation to the animations * * It the animation for this shape/step already exists it is replaced by the given one * * @parama animation the animation to insert */ void add(KPrShapeAnimation *animation); /** * Remove animation to the animations * * @parama animation the animation to remove */ void remove(KPrShapeAnimation *animation); /** * @brief Insert a new step on the steps list * * @param i position in which the step will be inserted * @param step step to be inserted */ void insertStep(const int i, KPrAnimationStep *step); /** * @brief Remove a step on the steps list * * @param step step to be removed */ void removeStep(KPrAnimationStep *step); /** * @brief Swap steps in positions i and j * * @param i position of the first step * @param j position of the second step */ void swapSteps(int i, int j); void swapAnimations(KPrShapeAnimation *oldAnimation, KPrShapeAnimation *newAnimation); /** * @brief Replace old animation with new animation * * @param oldAnimation animation to be replaced * @param newAnimation */ void replaceAnimation(KPrShapeAnimation *oldAnimation, KPrShapeAnimation *newAnimation); /** * Get the animations for the given step * * @param step the step for which the animations should be returned * @return A map of the shape -> animation if the animation is 0 the shape * is not visible */ /* QMap animations( int step ) const; */ /** * Get all steps */ QList steps() const; /// Save a edit command void endTimeLineEdition(); /** * @brief Set animation begin and duration * * @param index index of the animation - * @param begin time in miliseconds - * @param duration time in miliseconds + * @param begin time in milliseconds + * @param duration time in milliseconds */ void setTimeRange(KPrShapeAnimation *item, const int begin, const int duration); /** * Get the end time for the given animation * * @param index of the animation - * @return the time in miliseconds of the animation end + * @return the time in milliseconds of the animation end */ int animationEnd(const QModelIndex &index) const; /** * Get the scale begin time for the given animation * * @param index of the animation - * @return the time in miliseconds where the scale begin (relative to the parent onclik animation) + * @return the time in milliseconds where the scale begin (relative to the parent onclik animation) */ int animationStart(const QModelIndex &index) const; /** * @brief Replace animation in the given index * * @param index index of the animation * @param newAnimation animation to be used to replace */ QModelIndex replaceAnimation(const QModelIndex &index, KPrShapeAnimation *newAnimation); /** * @brief Create command to change trigger event of the animation on index * * @param index index of the animation * @param type new Node Type for the animation */ bool setTriggerEvent(const QModelIndex &index, const KPrShapeAnimation::NodeType type); /** * @brief Change trigger event of the animation * * @param animation * @param type new Node Type for the animation */ bool setNodeType(KPrShapeAnimation *animation, const KPrShapeAnimation::NodeType type); /** * @brief Redefine start of the animation if is moved below the minimum limit * of its animation trigger event scale. * * @param mIndex index of the animation */ void recalculateStart(const QModelIndex &mIndex); /** * @brief Move animation up in the animation list * Redefine trigger event if it's necessary * * @param index of the animation */ QModelIndex moveUp(const QModelIndex &index); /** * @brief Move animation down in the animation list * Redefine trigger event if it's necessary * * @param index of the animation */ QModelIndex moveDown(const QModelIndex &index); /** * @brief Move animation from oldRow to newRow * Redefine trigger event if it's necessary * * @param index of the animation */ QModelIndex moveAnimation(int oldRow, int newRow); /** * @brief remove animation on index * * @param index of the animation to be removed */ QModelIndex removeAnimationByIndex(const QModelIndex &index); /** * @brief Return the shape of the animation on given index * * @param index of the animation */ KoShape *shapeByIndex(const QModelIndex &index) const; /// Return the first animation index for the given shape QModelIndex indexByShape(KoShape* shape) const; /** * @brief Set begin time for the animation on index * * @param index of the animation */ void setBeginTime(const QModelIndex &index, const int begin); /** * @brief Set duration for the animation on index * * @param index of the animation */ void setDuration(const QModelIndex &index, const int duration); KPrShapeAnimation *animationByRow(int row, int *pGroup = 0, KPrShapeAnimation::NodeType *pNodeType = 0) const; /** * @brief add new animation after index * * @param newAnimation animation to be inserted * @param previousAnimation index of the previous animation */ void insertNewAnimation(KPrShapeAnimation *newAnimation, const QModelIndex &previousAnimation); QModelIndex indexByAnimation(KPrShapeAnimation *animation) const; void resyncStepsWithAnimations(); KPrShapeAnimation::NodeType triggerEventByIndex(const QModelIndex &index); public Q_SLOTS: /// Notify a external edition of begin or end time void notifyAnimationEdited(); /// Notify an external edition of an animation void notifyAnimationChanged(KPrShapeAnimation *animation); /// Notify if an animation set as OnClick has changed of trigger event void notifyOnClickEventChanged(); Q_SIGNALS: void timeScaleModified(); void onClickEventChanged(); private: void dump() const; QString getAnimationName(KPrShapeAnimation *animation, bool omitSubType = false) const; QPixmap getAnimationShapeThumbnail(KPrShapeAnimation *animation) const; QPixmap getAnimationIcon(KPrShapeAnimation *animation) const; QImage createThumbnail(KoShape *shape, const QSize &thumbSize) const; void setTimeRangeIncrementalChange(KPrShapeAnimation *item, const int begin, const int duration, TimeUpdated updatedTimes); QList getWithPreviousSiblings(KPrShapeAnimation *animation) const; QList getSubSteps(int start, int end, KPrAnimationStep *step) const; bool createTriggerEventEditCmd(KPrShapeAnimation *animation, KPrShapeAnimation::NodeType oldType, KPrShapeAnimation::NodeType newType); QList m_shapeAnimations; KPrShapeAnimation *m_currentEditedAnimation; bool m_firstEdition; int m_oldBegin; int m_oldDuration; KPrDocument *m_document; }; #endif /* KPRSHAPEANIMATIONS_H */ diff --git a/stage/part/animations/KPrShapeAnimation.h b/stage/part/animations/KPrShapeAnimation.h index 29d6e3fd446..6fa8897cab7 100644 --- a/stage/part/animations/KPrShapeAnimation.h +++ b/stage/part/animations/KPrShapeAnimation.h @@ -1,215 +1,215 @@ /* This file is part of the KDE project * Copyright (C) 2010 Thorsten Zachmann * Copyright (C) 2010 Benjamin Port * Copyright (C) 2012 Paul Mendez * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or ( at your option ) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KPRSHAPEANIMATION_H #define KPRSHAPEANIMATION_H #include #include "KPrAnimationData.h" #include #include "stage_export.h" class KoShape; class QTextBlockUserData; class KoXmlElement; class KoShapeLoadingContext; class KoPASavingContext; class KPrAnimationCache; class KPrAnimationStep; class KPrAnimationSubStep; class STAGE_EXPORT KPrShapeAnimation : public QParallelAnimationGroup, KPrAnimationData { Q_OBJECT public: /// Node Type (Trigger Event of the animation) enum NodeType { OnClick, AfterPrevious, WithPrevious }; /// Animation class enum PresetClass { None, Entrance, Exit, Emphasis, Custom, MotionPath, OleAction, MediaCall }; KPrShapeAnimation(KoShape *shape, QTextBlockUserData *textBlockData); virtual ~KPrShapeAnimation(); bool loadOdf(const KoXmlElement &element, KoShapeLoadingContext &context); virtual bool saveOdf(KoPASavingContext &paContext, bool startStep, bool startSubStep) const; KoShape *shape() const; QTextBlockUserData *textBlockUserData() const; virtual void init(KPrAnimationCache *animationCache, int step); virtual void deactivate(); /** * Read the value from the first KPrAnimationBase object */ //QPair animationShape() const; /** * Return the begin and end time of the animation as a QPair */ virtual QPair timeRange() const; /** * Return global duration of the shape animation */ virtual int globalDuration() const; /** * @brief Set the begin time for the animation * - * @param timeMS time in miliseconds + * @param timeMS time in milliseconds */ virtual void setBeginTime(int timeMS); /** * @brief Set duration time for the animation * - * @param timeMS time in miliseconds + * @param timeMS time in milliseconds */ virtual void setGlobalDuration(int timeMS); /** * @brief asign text block data * * @param textBlockData */ void setTextBlockUserData(QTextBlockUserData *textBlockUserData); /// The following data are just for quick access /// to different params of the animation /// Use set methods to mantain them in sync with the /// real animation data /** * @brief Set class of the animation * * @param presetClass */ void setPresetClass(PresetClass presetClass); /** * @brief Set the id (name) of the animation * * @param id QString with the animation name */ void setId(const QString &id); void setPresetSubType(const QString &subType); /** * @brief Returns stored class of the animation * * @return Preset_Class */ PresetClass presetClass() const; /** * @brief Returns the id (name) of the animation * * @return animation id */ QString id() const; /** * @brief Returns the class of the animation as String * * @return animation class text */ QString presetClassText() const; QString presetSubType() const; /** * @brief Set a pointer to the parent step of the animation * * @param step pointer to the step that holds the substep of the * animation */ void setStep(KPrAnimationStep *step); /** * @brief Set a pointer to the parent substep of the animation * * @param step pointer to the substep that holds the animation */ void setSubStep(KPrAnimationSubStep *subStep); /// The following pointers are used to reduce the necessity to /// look for animation in all the step-substep structure /// But they must be maintain in sync manually /** * @brief Returns the stored pointer of the animation step * * @return animation step */ KPrAnimationStep *step() const; /** * @brief Returns the stored pointer of the animation substep * * @return animation substep */ KPrAnimationSubStep *subStep() const; /// Store index for undo redo commands /// The indexes are used and updated with in the remove/move commands /// during other times they could be ou of sync void setStepIndex(int index); void setSubStepIndex(int index); void setAnimIndex(int index); int stepIndex() const; int subStepIndex() const; int animIndex() const; Q_SIGNALS: /// Notify if an animation stored property has been changed void timeChanged(int begin, int end); private: KoShape *m_shape; QTextBlockUserData *m_textBlockData; PresetClass m_class; QString m_id; QString m_presetSubType; KPrAnimationStep *m_step; KPrAnimationSubStep *m_subStep; int m_stepIndex; int m_subStepIndex; int m_animIndex; }; #endif /* KPRSHAPEANIMATION_H */ diff --git a/stage/part/tests/MockShapeAnimation.h b/stage/part/tests/MockShapeAnimation.h index 4269e82b1b7..a4418e48446 100644 --- a/stage/part/tests/MockShapeAnimation.h +++ b/stage/part/tests/MockShapeAnimation.h @@ -1,63 +1,63 @@ /* This file is part of the KDE project * Copyright ( C ) 2012 Paul Mendez * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or ( at your option ) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef MOCKSHAPEANIMATION_H #define MOCKSHAPEANIMATION_H #include class MockShapeAnimation : public KPrShapeAnimation { public: MockShapeAnimation(KoShape *shape, QTextBlockUserData *textBlockUserData) : KPrShapeAnimation(shape, textBlockUserData) , m_beginTime(0) , m_duration(1) {} /** * Return the begin and end time of the animation as a QPair */ QPair timeRange() const {return QPair(m_beginTime, m_beginTime + m_duration);} /** * Return global duration of the shape animation */ int globalDuration() const {return m_duration;} /** * @brief Set the begin time for the animation * - * @param timeMS time in miliseconds + * @param timeMS time in milliseconds */ void setBeginTime(int timeMS) {m_beginTime = timeMS;} /** * @brief Set duration time for the animation * - * @param timeMS time in miliseconds + * @param timeMS time in milliseconds */ void setGlobalDuration(int timeMS) {m_duration = timeMS;} private: int m_beginTime; int m_duration; }; #endif // MOCKSHAPEANIMATION_H diff --git a/words/app/org.kde.calligrawords.desktop b/words/app/org.kde.calligrawords.desktop index db7f345d76b..33938cab821 100644 --- a/words/app/org.kde.calligrawords.desktop +++ b/words/app/org.kde.calligrawords.desktop @@ -1,141 +1,141 @@ [Desktop Entry] Type=Application Name=Calligra Words Name[bg]=Calligra Words Name[bs]=Calligra riječi Name[ca]=Words del Calligra Name[ca@valencia]=Words del Calligra Name[cs]=Calligra Words Name[da]=Calligra Words Name[de]=Calligra Words Name[el]=Calligra Words Name[en_GB]=Calligra Words Name[es]=Calligra Words Name[et]=Calligra Words Name[eu]=Calligra Words Name[fi]=Calligra Words Name[fr]=Calligra Words Name[gl]=Calligra Words Name[hu]=Calligra Words Name[ia]=Calligra Words Name[it]=Calligra Words Name[ja]=Calligra Words Name[kk]=Calligra Words Name[ko]=Calligra Words Name[lt]=Calligra Words Name[mr]=कॅलिग्रा वर्डस् Name[nb]=Calligra Words Name[nl]=Calligra Words Name[pl]=Słowa Calligra Name[pt]=Words do Calligra Name[pt_BR]=Calligra Words Name[ru]=Calligra Words Name[sk]=Calligra Words Name[sl]=Calligra Words Name[sv]=Calligra Words Name[tr]=Calligra Words Name[ug]=Calligra Words Name[uk]=Calligra Words Name[x-test]=xxCalligra Wordsxx Name[zh_CN]=Calligra Words Name[zh_TW]=Calligra Words Exec=calligrawords %U GenericName=Word Processor GenericName[bg]=Текстообработваща програма GenericName[bs]=Obrađivač teksta GenericName[ca]=Processador de textos GenericName[ca@valencia]=Processador de textos GenericName[cs]=Textový procesor GenericName[da]=Tekstbehandling GenericName[de]=Textverarbeitung GenericName[el]=Επεξεργαστής κειμένου GenericName[en_GB]=Word Processor GenericName[es]=Procesador de texto GenericName[et]=Tekstitöötlus GenericName[eu]=Testu-tratamenduko programa GenericName[fi]=Tekstinkäsittely GenericName[fr]=Traitement de texte GenericName[fy]=Tekstferwurker GenericName[ga]=Próiseálaí Focal GenericName[gl]=Procesador de texto GenericName[he]=מעבד תמלילים GenericName[hi]=वर्ड प्रोसेसर GenericName[hne]=वर्ड प्रोसेसर GenericName[hu]=Szövegszerkesztő GenericName[ia]=Word Processor (Processor de Parola) GenericName[it]=Elaboratore di testi GenericName[ja]=ワープロ GenericName[kk]=Мәтін өңдеуші GenericName[ko]=워드 프로세서 GenericName[lt]=Tekstų rengyklė GenericName[lv]=Tekstapstrādes programma GenericName[mai]=वर्ड प्रोसेसर GenericName[mr]=वर्ड प्रोसेसर GenericName[ms]=Pemproses Kata GenericName[nb]=Tekstbehandler GenericName[nds]=Textprogramm GenericName[nl]=Tekstverwerker GenericName[oc]=Tractament de tèxt GenericName[pl]=Edytor tekstu GenericName[pt]=Processador de Texto GenericName[pt_BR]=Processador de Textos GenericName[ru]=Текстовый процессор GenericName[sk]=Textový procesor GenericName[sl]=Urejevalnik besedil GenericName[sv]=Ordbehandling GenericName[tr]=Kelime İşlemci GenericName[ug]=يېزىق بىر تەرەپ قىلىش GenericName[uk]=Текстовий процесор GenericName[wa]=Aspougneu d' documints GenericName[x-test]=xxWord Processorxx GenericName[zh_CN]=字处理 GenericName[zh_TW]=文書處理器 Comment=Write documents Comment[bg]=Писане на документи Comment[bs]=Pisanje dokumenata Comment[ca]=Escriviu documents Comment[ca@valencia]=Escriviu documents Comment[cs]=Zapsat dokumenty Comment[da]=Skriv dokumenter Comment[de]=Dokumente verfassen Comment[el]=Γραφή εγγράφων Comment[en_GB]=Write documents Comment[es]=Escribir documentos Comment[et]=Dokumentide kirjutamine Comment[eu]=Dokumentuak idatzi Comment[fi]=Kirjoita tekstitiedostoja Comment[fr]=Documents texte Comment[gl]=Escriba e edite documentos. Comment[hu]=Dokumentumok írása Comment[it]=Scrivi documenti Comment[ja]=文書を作成 Comment[kk]=Құжаттарды жазу Comment[ko]=문서 쓰기 Comment[mr]=दस्तऐवज लिहा Comment[nb]=Skriv tekstdokumenter Comment[nds]=Dokmenten opstellen Comment[nl]=Documenten wegschrijven Comment[pl]=Pisz dokumenty Comment[pt]=Escrever documentos Comment[pt_BR]=Escrever documentos Comment[ru]=Текстовые документы Comment[sk]=Písanie dokumentov Comment[sl]=Pišite dokumente Comment[sv]=Skriv dokument Comment[tr]=Belgeler oluşturun Comment[ug]=پۈتۈك يېزىش Comment[uk]=Створення документів Comment[x-test]=xxWrite documentsxx Comment[zh_CN]=编写文档 Comment[zh_TW]=寫文件 MimeType=application/vnd.oasis.opendocument.text-master;application/vnd.oasis.opendocument.text;application/vnd.oasis.opendocument.text-template;application/msword;application/rtf;application/x-mswrite;application/vnd.openxmlformats-officedocument.wordprocessingml.document;application/vnd.openxmlformats-officedocument.wordprocessingml.template;application/vnd.ms-works;application/vnd.wordperfect; X-KDE-ServiceTypes=Calligra/Application Icon=calligrawords X-KDE-NativeMimeType=application/vnd.oasis.opendocument.text X-Calligra-DefaultMimeTypes=application/vnd.oasis.opendocument.text-master,application/vnd.oasis.opendocument.text,application/vnd.oasis.opendocument.text-template,application/msword,application/rtf,application/x-mswrite,application/vnd.openxmlformats-officedocument.wordprocessingml.document,application/vnd.openxmlformats-officedocument.wordprocessingml.template,application/vnd.ms-works,application/vnd.wordperfect X-DocPath=http://userbase.kde.org/Special:MyLanguage/Words X-KDE-StartupNotify=true X-DBUS-StartupType=Multi X-DBUS-ServiceName=org.kde.calligrawords -Categories=Qt;KDE;Office; +Categories=Qt;KDE;Office;WordProcessor; InitialPreference=4 diff --git a/words/part/CMakeLists.txt b/words/part/CMakeLists.txt index 41c9187918a..86dce02880f 100644 --- a/words/part/CMakeLists.txt +++ b/words/part/CMakeLists.txt @@ -1,182 +1,184 @@ project(wordspart) #TODO port to textlayout-rework -add_subdirectory( tests ) +if(BUILD_TESTING) + add_subdirectory( tests ) +endif() include_directories( ${KORDF_INCLUDES} ${KOTEXT_INCLUDES} ${TEXTLAYOUT_INCLUDES}) ########### WordsPrivate library ############### set(wordsprivate_LIB_SRCS KWFactory.cpp WordsDebug.cpp Words.cpp KWApplicationConfig.cpp KWCanvasBase.cpp KWCanvas.cpp KWCanvasItem.cpp KWDocument.cpp KWGui.cpp KWView.cpp KWPart.cpp KWPage.cpp KWPageCacheManager.cpp KWPageManager.cpp KWPageStyle.cpp KWViewMode.cpp KWViewModeNormal.cpp KWViewModePreview.cpp KWStatusBar.cpp KWOdfLoader.cpp KWOdfWriter.cpp KWOdfSharedLoadingData.cpp KWRootAreaProviderBase.cpp KWRootAreaProviderTextBox.cpp KWRootAreaProvider.cpp pagetool/KWPageTool.cpp pagetool/KWPageToolFactory.cpp pagetool/SimpleSetupWidget.cpp pagetool/SimpleHeaderFooterWidget.cpp frames/KWFrameLayout.cpp frames/KWFrame.cpp frames/KWFrameSet.cpp frames/KWTextFrameSet.cpp frames/KWCopyShape.cpp widgets/KoFindToolbar.cpp dialogs/KWFrameConnectSelector.cpp dialogs/KWRunAroundProperties.cpp dialogs/KWAnchoringProperties.cpp dialogs/KWFrameDialog.cpp dialogs/KWShapeConfigFactory.cpp dialogs/KWDocumentColumns.cpp dialogs/KWStartupWidget.cpp dialogs/KWPageSettingsDialog.cpp dialogs/KWPrintingDialog.cpp dialogs/KWCreateBookmarkDialog.cpp dialogs/KWSelectBookmarkDialog.cpp dialogs/KWConfigureDialog.cpp dockers/KWStatisticsDocker.cpp dockers/KWStatisticsWidget.cpp dockers/StatisticsPreferencesPopup.cpp dockers/KWNavigationDocker.cpp dockers/KWNavigationDockerFactory.cpp dockers/KWNavigationWidget.cpp commands/KWPageStylePropertiesCommand.cpp commands/KWNewPageStyleCommand.cpp commands/KWChangePageStyleCommand.cpp commands/KWShapeCreateCommand.cpp ) set(wordsprivate_LIB_SRCS ${wordsprivate_LIB_SRCS} dockers/KWDebugDocker.cpp dockers/KWDebugDockerFactory.cpp dockers/KWDebugWidget.cpp ) if( SHOULD_BUILD_FEATURE_RDF ) set(wordsprivate_LIB_SRCS ${wordsprivate_LIB_SRCS} dockers/KWRdfDocker.cpp dockers/KWRdfDockerFactory.cpp dockers/KWRdfDockerTree.cpp ) endif() ki18n_wrap_ui(wordsprivate_LIB_SRCS dialogs/KWFrameConnectSelector.ui dialogs/KWRunAroundProperties.ui dialogs/KWAnchoringProperties.ui dialogs/KWDocumentColumns.ui dialogs/KWStartupWidget.ui dialogs/KWCreateBookmark.ui dialogs/KWSelectBookmark.ui dialogs/KWInsertImage.ui dockers/StatisticsPreferencesPopup.ui pagetool/SimpleSetupWidget.ui pagetool/SimpleHeaderFooterWidget.ui ) if( SHOULD_BUILD_FEATURE_RDF ) ki18n_wrap_ui(wordsprivate_LIB_SRCS dockers/KWRdfDocker.ui ) endif() add_library(wordsprivate SHARED ${wordsprivate_LIB_SRCS}) generate_export_header(wordsprivate BASE_NAME words EXPORT_FILE_NAME words_generated_export.h ) target_link_libraries(wordsprivate PUBLIC komain PRIVATE kotextlayout KF5::IconThemes ) if( SHOULD_BUILD_FEATURE_RDF ) target_link_libraries(wordsprivate PRIVATE kordf ) endif() set_target_properties(wordsprivate PROPERTIES VERSION ${GENERIC_CALLIGRA_LIB_VERSION} SOVERSION ${GENERIC_CALLIGRA_LIB_SOVERSION} ) install(TARGETS wordsprivate ${INSTALL_TARGETS_DEFAULT_ARGS}) ########### WordsPart Library ############### set(wordspart_PART_SRCS KWFactoryInit.cpp ) add_library(calligrawordspart MODULE ${wordspart_PART_SRCS}) calligra_part_desktop_to_json(calligrawordspart wordspart.desktop) target_link_libraries(calligrawordspart wordsprivate ) install(TARGETS calligrawordspart DESTINATION ${PLUGIN_INSTALL_DIR}/calligra/parts) ########### install files ############### install( FILES calligrawords.rc calligrawords_readonly.rc DESTINATION ${KXMLGUI_INSTALL_DIR}/calligrawords) install( FILES calligrawordsrc DESTINATION ${CONFIG_INSTALL_DIR} ) if (SHOULD_BUILD_DEVEL_HEADERS) install( FILES words_export.h KWPage.h KWPageManager.h KWPageStyle.h Words.h KWCanvasBase.h KWCanvas.h KWCanvasItem.h KWDocument.h KWApplicationConfig.h KWViewMode.h KWView.h KWViewModeNormal.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligrawords/part COMPONENT Devel) install( FILES frames/KWFrame.h frames/KWFrameLayout.h frames/KWFrameSet.h frames/KWTextFrameSet.h DESTINATION ${INCLUDE_INSTALL_DIR}/calligrawords/part/frames COMPONENT Devel) endif()