diff --git a/.appveyor.yml b/.appveyor.yml new file mode 100644 index 00000000..719dedc5 --- /dev/null +++ b/.appveyor.yml @@ -0,0 +1,58 @@ +# Build glabels windows installer. +# +# Currently, only builds snapshots and is triggered by the travis CI build. + + +version: 3.99.0.{build} + +branches: + # blacklist + except: + - /.*/ + + # whitelist + only: + - master + +skip_non_tags: true + +image: Visual Studio 2017 + +init: + - git config --global core.autocrlf true + +install: + # Locate Qt installation + - set QTDIR=c:\Qt\5.11\msvc2017_64 + - set PATH=%PATH%;%QTDIR%\bin + # Zlib and QREncode + - vcpkg install zlib:x64-windows + - vcpkg install libqrencode:x64-windows + - vcpkg integrate install + +build_script: + - git checkout master # re-attach to master to satisfy auto version tooling + - mkdir build + - cd build + - cmake -G "Visual Studio 15 2017 Win64" .. -DCMAKE_PREFIX_PATH=%QTDIR% -DCMAKE_TOOLCHAIN_FILE=C:/Tools/vcpkg/scripts/buildsystems/vcpkg.cmake + - cmake --build . --config Release + - ctest -C Release + - cpack -C Release -G NSIS64 + - set /P VERSION= /usr" | cut -d " " -f 2-3 | sort | uniq - - ls -lh ./gLabels*.AppImage - - mv ./gLabels*.AppImage glabels-continuous-x86_64.AppImage - - wget -c https://github.com/jimevins/uploadtool/raw/master/upload.sh - - bash ./upload.sh glabels-continuous-x86_64.AppImage +# blocklist branches: except: - - /.*/ - - # Do not build tags that we create when we upload to GitHub Releases - - /^(?i:continuous)$/ + - /.*/ # Everything +# safelist branches: only: - master + +matrix: + include: + + ##################### + # + # Linux build + # + ##################### + - name: "Linux" + os: linux + dist: bionic + sudo: require + env: + - QMAKE_PATH=/usr/lib/x86_64-linux-gnu/qt5/bin/qmake + install: + - sudo apt-get -y install qtbase5-dev libqt5svg5-dev qttools5-dev + - sudo apt-get -y install xvfb + - sudo apt-get -y install pkgconf libqrencode-dev + - sudo apt-get -y install barcode + # Install zint from source + - wget https://downloads.sourceforge.net/project/zint/zint/2.6.5/zint-2.6.5.tar.gz && tar xzf zint-2.6.5.tar.gz && ( cd zint-2.6.5 && mkdir build && cd build && cmake .. && make && sudo make install ) + - export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH + + before_script: + - git fetch --unshallow # restore repository depth to properly count commits in auto versioning + - git checkout master # re-attach to master to satisfy auto versioning + + script: + - mkdir build + - cd build + - cmake .. -DCMAKE_INSTALL_PREFIX=/usr + - make -j4 + - xvfb-run ctest + - VERSION=$(cat VERSION) + + after_success: + # Download AppImage QT deployment tool (local snapshot -- original at github/probono) + - wget -c "https://github.com/jimevins/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" + - chmod a+x linuxdeployqt*.AppImage + # + # Create AppImage + # + - make DESTDIR=appdir install + - unset QTDIR; unset QT_PLUGIN_PATH + - ./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -qmake=${QMAKE_PATH} -bundle-non-qt-libs + - ./linuxdeployqt*.AppImage ./appdir/usr/share/applications/*.desktop -qmake=${QMAKE_PATH} -appimage + - mv ./gLabels*.AppImage glabels-${VERSION}-x86_64.AppImage + + before_deploy: + - git config --local user.name "Jim Evins (Travis CI)" + - git config --local user.email evins@snaught.com + - git tag -f -a glabels-${VERSION} -m "glabels-${VERSION} CI" + + deploy: + provider: releases + api_key: ${GITHUB_TOKEN} + file: + - glabels-${VERSION}-x86_64.AppImage + draft: false + prerelease: true + skip_cleanup: true + + +# ##################### +# # +# # MacOS build +# # +# ##################### +# - name: "MacOS" +# os: osx +# +# install: +# - brew install qt +# +# before_script: +# - git fetch --unshallow # restore repository depth to properly count commits in auto versioning +# - git checkout master # re-attach to master to satisfy auto versioning +# +# script: +# - mkdir build +# - cd build +# - cmake .. -DCMAKE_PREFIX_PATH=/usr/local/opt/qt +# - make -j4 +# #- ctest --verbose +# - VERSION=$(cat VERSION) + + +# ##################### +# # +# # Windows build +# # +# ##################### +# - name: "Windows" +# os: windows +# +# before_script: +# - git fetch --unshallow # restore repository depth to properly count commits in auto versioning +# - git checkout master # re-attach to master to satisfy auto versioning +# +# script: +# - mkdir build +# - cd build +# #- cmake .. -G "Visual Studio 15 2017 Win64" +# #- cmake --build . --config Release +# #- ctest -C Release +# #- VERSION=$(cat VERSION) +# +# after_success: +# # +# # Create Windows Installer +# # +# #- cpack -C Release -G NSIS64 diff --git a/.version.in b/.version.in new file mode 100644 index 00000000..f3e763db --- /dev/null +++ b/.version.in @@ -0,0 +1 @@ +@VERSION_STRING@ diff --git a/CMakeLists.txt b/CMakeLists.txt index 14b5bee7..c70fcc9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 3.2) +cmake_minimum_required (VERSION 3.9) ############################################################################### # gLabels Label Designer Project @@ -12,27 +12,83 @@ set (CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/" #======================================= -# Packaging and Version Information +# Version Information #======================================= -set (Website "glabels.org") -set (CPACK_PACKAGE_VENDOR ${Website}) +set (WEBSITE "glabels.org") +set (BUG_WEBSITE "https://github.com/jimevins/glabels-qt/issues") + +execute_process( + COMMAND git symbolic-ref --short HEAD + RESULT_VARIABLE BRANCH_VALID + OUTPUT_VARIABLE BRANCH + OUTPUT_STRIP_TRAILING_WHITESPACE +) +if (NOT ${BRANCH_VALID} STREQUAL "0") + set (BRANCH "Unkonwn") +endif () + +execute_process( + COMMAND git rev-list --count ${BRANCH} + RESULT_VARIABLE COMMIT_COUNT_VALID + OUTPUT_VARIABLE COMMIT_COUNT + OUTPUT_STRIP_TRAILING_WHITESPACE +) +if (NOT ${COMMIT_COUNT_VALID} STREQUAL "0") + set (COMMIT_COUNT "?") +endif () + +execute_process( + COMMAND git log -1 --format=%h + RESULT_VARIABLE COMMIT_HASH_VALID + OUTPUT_VARIABLE COMMIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE +) +if (NOT ${COMMIT_HASH_VALID} STREQUAL "0") + set (COMMIT_HASH "exported") +endif () + +execute_process( + COMMAND git log -1 --format=%cd --date=short + RESULT_VARIABLE COMMIT_DATE_VALID + OUTPUT_VARIABLE COMMIT_DATE + OUTPUT_STRIP_TRAILING_WHITESPACE +) +if (NOT ${COMMIT_DATE_VALID} STREQUAL "0") + string (TIMESTAMP COMMIT_DATE %Y-%m-%d) +endif () + +# Uncomment for snapshots, comment for releases +set(VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}-${BRANCH}${COMMIT_COUNT}") + +# Uncomment for releases, comment for snapshots +#set(VERSION_STRING "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}") + +set(LONG_VERSION_STRING "${VERSION_STRING} (${COMMIT_HASH} ${COMMIT_DATE})") + +# Auto-generate version file +configure_file (.version.in VERSION @ONLY) + + +#======================================= +# Packaging Information +#======================================= +set (CPACK_PACKAGE_VENDOR ${WEBSITE}) set (CPACK_PACKAGE_NAME glabels) -string (TIMESTAMP _date "%Y%m%d") set (CPACK_PACKAGE_VERSION_MAJOR ${PROJECT_VERSION_MAJOR}) set (CPACK_PACKAGE_VERSION_MINOR ${PROJECT_VERSION_MINOR}) set (CPACK_PACKAGE_VERSION_PATCH ${PROJECT_VERSION_PATCH}) -set (CPACK_PACKAGE_VERSION_TWEAK ${_date}) +set (CPACK_PACKAGE_VERSION_TWEAK ${COMMIT_HASH}) set (CPACK_PACKAGE_DESCRIPTION_SUMMARY "gLabels Label Designer") -set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/COPYING") +set (CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE") set (CPACK_PACKAGE_EXECUTABLES "glabels-qt;gLabels") set (CPACK_CREATE_DESKTOP_LINKS "glabels-qt") set (CPACK_NSIS_DISPLAY_NAME "gLabels") -set (CPACK_NSIS_URL_INFO_ABOUT "http://${Website}") +set (CPACK_NSIS_URL_INFO_ABOUT "http://${WEBSITE}") set (CPACK_NSIS_MUI_ICON "${CMAKE_CURRENT_SOURCE_DIR}/glabels/windows-icon/glabels.ico") set (CPACK_NSIS_MODIFY_PATH "ON") @@ -51,11 +107,11 @@ if (MINGW) set (CMAKE_PREFIX_PATH ${MINGW_BASE_DIR} ) endif () -find_package (Qt5Core 5.4 REQUIRED) -find_package (Qt5Widgets 5.4 REQUIRED) -find_package (Qt5PrintSupport 5.4 REQUIRED) -find_package (Qt5Xml 5.4 REQUIRED) -find_package (Qt5Svg 5.4 REQUIRED) +find_package (Qt5Core 5.6 REQUIRED) +find_package (Qt5Widgets 5.6 REQUIRED) +find_package (Qt5PrintSupport 5.6 REQUIRED) +find_package (Qt5Xml 5.6 REQUIRED) +find_package (Qt5Svg 5.6 REQUIRED) find_package (Qt5LinguistTools) if (WIN32) @@ -70,20 +126,24 @@ endif () find_package (ZLIB 1.2 QUIET) find_package (GnuBarcode 0.98 QUIET) find_package (LibQrencode 3.4 QUIET) -find_package (LibZint 2.6 QUIET) +find_package (LibZint 2.6 EXACT QUIET) # Unit testing support -find_package (Qt5Test 5.4 QUIET) +find_package (Qt5Test 5.6 QUIET) #======================================= # Global compiler options #======================================= -# -# Uncomment to compile everything with aggressively pedantic options -# (not recommended -- only for testing -- also not portable) -# -#add_compile_options("-Wall" "-Werror" "-Wpedantic") -add_compile_options("-g") +if (${CMAKE_CXX_COMPILER_ID} STREQUAL "GNU") + # + # Uncomment to compile everything with aggressively pedantic options + # (not recommended -- only for testing -- also not portable) + # + #add_compile_options("-Wall" "-Werror" "-Wpedantic") + + # Uncomment to always compile with debug symbols + add_compile_options("-g") +endif () #======================================= @@ -102,6 +162,7 @@ add_subdirectory (model) add_subdirectory (glabels) add_subdirectory (glabels-batch) add_subdirectory (templates) +add_subdirectory (user-docs) add_subdirectory (translations) add_subdirectory (data) @@ -111,9 +172,10 @@ add_subdirectory (data) #======================================= message (STATUS "") message (STATUS "Project name ............ " ${CMAKE_PROJECT_NAME}) -message (STATUS "Project version ......... " ${Full_Version}) +message (STATUS "Project version ......... " ${LONG_VERSION_STRING}) message (STATUS "Installation prefix ..... " ${CMAKE_INSTALL_PREFIX}) message (STATUS "Source code location .... " ${glabels_SOURCE_DIR}) +message (STATUS "CMake version ........... " ${CMAKE_VERSION}) message (STATUS "C++ Compiler ............ " ${CMAKE_CXX_COMPILER_ID} " " ${CMAKE_CXX_COMPILER} " " ${CMAKE_CXX_COMPILER_VERSION}) message (STATUS "Qt version .............. " ${Qt5Core_VERSION}) diff --git a/COPYING-DOCS b/COPYING-DOCS deleted file mode 100644 index 23fba304..00000000 --- a/COPYING-DOCS +++ /dev/null @@ -1,61 +0,0 @@ -Attribution-ShareAlike 3.0 Unported - -CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM ITS USE. -License - -THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. - -BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND CONDITIONS. - -1. Definitions - -"Adaptation" means a work based upon the Work, or upon the Work and other pre-existing works, such as a translation, adaptation, derivative work, arrangement of music or other alterations of a literary or artistic work, or phonogram or performance and includes cinematographic adaptations or any other form in which the Work may be recast, transformed, or adapted including in any form recognizably derived from the original, except that a work that constitutes a Collection will not be considered an Adaptation for the purpose of this License. For the avoidance of doubt, where the Work is a musical work, performance or phonogram, the synchronization of the Work in timed-relation with a moving image ("synching") will be considered an Adaptation for the purpose of this License. -"Collection" means a collection of literary or artistic works, such as encyclopedias and anthologies, or performances, phonograms or broadcasts, or other works or subject matter other than works listed in Section 1(f) below, which, by reason of the selection and arrangement of their contents, constitute intellectual creations, in which the Work is included in its entirety in unmodified form along with one or more other contributions, each constituting separate and independent works in themselves, which together are assembled into a collective whole. A work that constitutes a Collection will not be considered an Adaptation (as defined below) for the purposes of this License. -"Creative Commons Compatible License" means a license that is listed at http://creativecommons.org/compatiblelicenses that has been approved by Creative Commons as being essentially equivalent to this License, including, at a minimum, because that license: (i) contains terms that have the same purpose, meaning and effect as the License Elements of this License; and, (ii) explicitly permits the relicensing of adaptations of works made available under that license under this License or a Creative Commons jurisdiction license with the same License Elements as this License. -"Distribute" means to make available to the public the original and copies of the Work or Adaptation, as appropriate, through sale or other transfer of ownership. -"License Elements" means the following high-level license attributes as selected by Licensor and indicated in the title of this License: Attribution, ShareAlike. -"Licensor" means the individual, individuals, entity or entities that offer(s) the Work under the terms of this License. -"Original Author" means, in the case of a literary or artistic work, the individual, individuals, entity or entities who created the Work or if no individual or entity can be identified, the publisher; and in addition (i) in the case of a performance the actors, singers, musicians, dancers, and other persons who act, sing, deliver, declaim, play in, interpret or otherwise perform literary or artistic works or expressions of folklore; (ii) in the case of a phonogram the producer being the person or legal entity who first fixes the sounds of a performance or other sounds; and, (iii) in the case of broadcasts, the organization that transmits the broadcast. -"Work" means the literary and/or artistic work offered under the terms of this License including without limitation any production in the literary, scientific and artistic domain, whatever may be the mode or form of its expression including digital form, such as a book, pamphlet and other writing; a lecture, address, sermon or other work of the same nature; a dramatic or dramatico-musical work; a choreographic work or entertainment in dumb show; a musical composition with or without words; a cinematographic work to which are assimilated works expressed by a process analogous to cinematography; a work of drawing, painting, architecture, sculpture, engraving or lithography; a photographic work to which are assimilated works expressed by a process analogous to photography; a work of applied art; an illustration, map, plan, sketch or three-dimensional work relative to geography, topography, architecture or science; a performance; a broadcast; a phonogram; a compilation of data to the extent it is protected as a copyrightable work; or a work performed by a variety or circus performer to the extent it is not otherwise considered a literary or artistic work. -"You" means an individual or entity exercising rights under this License who has not previously violated the terms of this License with respect to the Work, or who has received express permission from the Licensor to exercise rights under this License despite a previous violation. -"Publicly Perform" means to perform public recitations of the Work and to communicate to the public those public recitations, by any means or process, including by wire or wireless means or public digital performances; to make available to the public Works in such a way that members of the public may access these Works from a place and at a place individually chosen by them; to perform the Work to the public by any means or process and the communication to the public of the performances of the Work, including by public digital performance; to broadcast and rebroadcast the Work by any means including signs, sounds or images. -"Reproduce" means to make copies of the Work by any means including without limitation by sound or visual recordings and the right of fixation and reproducing fixations of the Work, including storage of a protected performance or phonogram in digital form or other electronic medium. -2. Fair Dealing Rights. Nothing in this License is intended to reduce, limit, or restrict any uses free from copyright or rights arising from limitations or exceptions that are provided for in connection with the copyright protection under copyright law or other applicable laws. - -3. License Grant. Subject to the terms and conditions of this License, Licensor hereby grants You a worldwide, royalty-free, non-exclusive, perpetual (for the duration of the applicable copyright) license to exercise the rights in the Work as stated below: - -to Reproduce the Work, to incorporate the Work into one or more Collections, and to Reproduce the Work as incorporated in the Collections; -to create and Reproduce Adaptations provided that any such Adaptation, including any translation in any medium, takes reasonable steps to clearly label, demarcate or otherwise identify that changes were made to the original Work. For example, a translation could be marked "The original work was translated from English to Spanish," or a modification could indicate "The original work has been modified."; -to Distribute and Publicly Perform the Work including as incorporated in Collections; and, -to Distribute and Publicly Perform Adaptations. -For the avoidance of doubt: - -Non-waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme cannot be waived, the Licensor reserves the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; -Waivable Compulsory License Schemes. In those jurisdictions in which the right to collect royalties through any statutory or compulsory licensing scheme can be waived, the Licensor waives the exclusive right to collect such royalties for any exercise by You of the rights granted under this License; and, -Voluntary License Schemes. The Licensor waives the right to collect royalties, whether individually or, in the event that the Licensor is a member of a collecting society that administers voluntary licensing schemes, via that society, from any exercise by You of the rights granted under this License. -The above rights may be exercised in all media and formats whether now known or hereafter devised. The above rights include the right to make such modifications as are technically necessary to exercise the rights in other media and formats. Subject to Section 8(f), all rights not expressly granted by Licensor are hereby reserved. - -4. Restrictions. The license granted in Section 3 above is expressly made subject to and limited by the following restrictions: - -You may Distribute or Publicly Perform the Work only under the terms of this License. You must include a copy of, or the Uniform Resource Identifier (URI) for, this License with every copy of the Work You Distribute or Publicly Perform. You may not offer or impose any terms on the Work that restrict the terms of this License or the ability of the recipient of the Work to exercise the rights granted to that recipient under the terms of the License. You may not sublicense the Work. You must keep intact all notices that refer to this License and to the disclaimer of warranties with every copy of the Work You Distribute or Publicly Perform. When You Distribute or Publicly Perform the Work, You may not impose any effective technological measures on the Work that restrict the ability of a recipient of the Work from You to exercise the rights granted to that recipient under the terms of the License. This Section 4(a) applies to the Work as incorporated in a Collection, but this does not require the Collection apart from the Work itself to be made subject to the terms of this License. If You create a Collection, upon notice from any Licensor You must, to the extent practicable, remove from the Collection any credit as required by Section 4(c), as requested. If You create an Adaptation, upon notice from any Licensor You must, to the extent practicable, remove from the Adaptation any credit as required by Section 4(c), as requested. -You may Distribute or Publicly Perform an Adaptation only under the terms of: (i) this License; (ii) a later version of this License with the same License Elements as this License; (iii) a Creative Commons jurisdiction license (either this or a later license version) that contains the same License Elements as this License (e.g., Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons Compatible License. If you license the Adaptation under one of the licenses mentioned in (iv), you must comply with the terms of that license. If you license the Adaptation under the terms of any of the licenses mentioned in (i), (ii) or (iii) (the "Applicable License"), you must comply with the terms of the Applicable License generally and the following provisions: (I) You must include a copy of, or the URI for, the Applicable License with every copy of each Adaptation You Distribute or Publicly Perform; (II) You may not offer or impose any terms on the Adaptation that restrict the terms of the Applicable License or the ability of the recipient of the Adaptation to exercise the rights granted to that recipient under the terms of the Applicable License; (III) You must keep intact all notices that refer to the Applicable License and to the disclaimer of warranties with every copy of the Work as included in the Adaptation You Distribute or Publicly Perform; (IV) when You Distribute or Publicly Perform the Adaptation, You may not impose any effective technological measures on the Adaptation that restrict the ability of a recipient of the Adaptation from You to exercise the rights granted to that recipient under the terms of the Applicable License. This Section 4(b) applies to the Adaptation as incorporated in a Collection, but this does not require the Collection apart from the Adaptation itself to be made subject to the terms of the Applicable License. -If You Distribute, or Publicly Perform the Work or any Adaptations or Collections, You must, unless a request has been made pursuant to Section 4(a), keep intact all copyright notices for the Work and provide, reasonable to the medium or means You are utilizing: (i) the name of the Original Author (or pseudonym, if applicable) if supplied, and/or if the Original Author and/or Licensor designate another party or parties (e.g., a sponsor institute, publishing entity, journal) for attribution ("Attribution Parties") in Licensor's copyright notice, terms of service or by other reasonable means, the name of such party or parties; (ii) the title of the Work if supplied; (iii) to the extent reasonably practicable, the URI, if any, that Licensor specifies to be associated with the Work, unless such URI does not refer to the copyright notice or licensing information for the Work; and (iv) , consistent with Ssection 3(b), in the case of an Adaptation, a credit identifying the use of the Work in the Adaptation (e.g., "French translation of the Work by Original Author," or "Screenplay based on original Work by Original Author"). The credit required by this Section 4(c) may be implemented in any reasonable manner; provided, however, that in the case of a Adaptation or Collection, at a minimum such credit will appear, if a credit for all contributing authors of the Adaptation or Collection appears, then as part of these credits and in a manner at least as prominent as the credits for the other contributing authors. For the avoidance of doubt, You may only use the credit required by this Section for the purpose of attribution in the manner set out above and, by exercising Your rights under this License, You may not implicitly or explicitly assert or imply any connection with, sponsorship or endorsement by the Original Author, Licensor and/or Attribution Parties, as appropriate, of You or Your use of the Work, without the separate, express prior written permission of the Original Author, Licensor and/or Attribution Parties. -Except as otherwise agreed in writing by the Licensor or as may be otherwise permitted by applicable law, if You Reproduce, Distribute or Publicly Perform the Work either by itself or as part of any Adaptations or Collections, You must not distort, mutilate, modify or take other derogatory action in relation to the Work which would be prejudicial to the Original Author's honor or reputation. Licensor agrees that in those jurisdictions (e.g. Japan), in which any exercise of the right granted in Section 3(b) of this License (the right to make Adaptations) would be deemed to be a distortion, mutilation, modification or other derogatory action prejudicial to the Original Author's honor and reputation, the Licensor will waive or not assert, as appropriate, this Section, to the fullest extent permitted by the applicable national law, to enable You to reasonably exercise Your right under Section 3(b) of this License (right to make Adaptations) but not otherwise. -5. Representations, Warranties and Disclaimer - -UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. - -6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -7. Termination - -This License and the rights granted hereunder will terminate automatically upon any breach by You of the terms of this License. Individuals or entities who have received Adaptations or Collections from You under this License, however, will not have their licenses terminated provided such individuals or entities remain in full compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will survive any termination of this License. -Subject to the above terms and conditions, the license granted here is perpetual (for the duration of the applicable copyright in the Work). Notwithstanding the above, Licensor reserves the right to release the Work under different license terms or to stop distributing the Work at any time; provided, however that any such election will not serve to withdraw this License (or any other license that has been, or is required to be, granted under the terms of this License), and this License will continue in full force and effect unless terminated as stated above. -8. Miscellaneous - -Each time You Distribute or Publicly Perform the Work or a Collection, the Licensor offers to the recipient a license to the Work on the same terms and conditions as the license granted to You under this License. -Each time You Distribute or Publicly Perform an Adaptation, Licensor offers to the recipient a license to the original Work on the same terms and conditions as the license granted to You under this License. -If any provision of this License is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this License, and without further action by the parties to this agreement, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. -No term or provision of this License shall be deemed waived and no breach consented to unless such waiver or consent shall be in writing and signed by the party to be charged with such waiver or consent. -This License constitutes the entire agreement between the parties with respect to the Work licensed here. There are no understandings, agreements or representations with respect to the Work not specified here. Licensor shall not be bound by any additional provisions that may appear in any communication from You. This License may not be modified without the mutual written agreement of the Licensor and You. -The rights granted under, and the subject matter referenced, in this License were drafted utilizing the terminology of the Berne Convention for the Protection of Literary and Artistic Works (as amended on September 28, 1979), the Rome Convention of 1961, the WIPO Copyright Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 and the Universal Copyright Convention (as revised on July 24, 1971). These rights and subject matter take effect in the relevant jurisdiction in which the License terms are sought to be enforced according to the corresponding provisions of the implementation of those treaty provisions in the applicable national law. If the standard suite of rights granted under applicable copyright law includes additional rights not granted under this License, such additional rights are deemed to be included in the License; this License is not intended to restrict the license of any rights under applicable law. diff --git a/COPYING.README_FIRST b/COPYING.README_FIRST deleted file mode 100644 index 1f702e30..00000000 --- a/COPYING.README_FIRST +++ /dev/null @@ -1,37 +0,0 @@ -The gLabels official distribution is an aggregate of several components. -Each component is licensed as follows - - -GLABELS: - - The gLabels application, and other portions of the official gLabels - distribution not explicitly licensed otherwise, are licensed under the - GNU GENERAL PUBLIC LICENSE (GPL); either version 3 of the License, or - (at your option) any later version -- see the 'COPYING' file in this - directory for details. - - -GLBARCODE: - - gLabels currently includes a version of the glbarcode++ library, located in - the "glbarcode/" subdirectory. It is licensed under the GNU LESSER GENERAL - PUBLIC LICENSE (LGPL); either version 3 of the License, or (at your option) - any later version -- see the 'COPYING-LIB - - -DOCUMENTATION: - - The gLabels documentation, located in the "docs/" and "help/" subdirectories, - is licensed under the CreativeCommons Attribution-Share Alike 3.0 Unported - license. See 'COPYING-DOCS' in this directory for details, or visit - 'http://creativecommons.org/licenses/by-sa/3.0/'. - - -TEMPLATE DATABASE: - - The XML files in the "templates/" subdirectory constitute the glabels - label database. No copyright is claimed on the facts contained within - the database and can be used for any purpose. However, to clear up any - ambiguity, the DTD file that defines the format of these files is - licensed using the MIT/X license, see 'COPYING-TEMPLATES' in this - directory for details. diff --git a/CREDITS.md b/CREDITS.md index 4b00a582..3dd30015 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -1,7 +1,7 @@ gLabels Author ============== -Jim Evins +[Jim Evins ](https://github.com/jimevins) Acknowledgments @@ -9,145 +9,20 @@ Acknowledgments gLabels includes contributions from +* [Mario Blättermann](https://github.com/mariobl) * [probonopd](https://github.com/probonopd) - travis-ci setup, appImage +* [gitlost](https://github.com/gitlost) +* [Yuri Chornoivan](https://github.com/yurchor) +* [nblock](https://github.com/nblock) +* [hochwasser](https://github.com/hochwasser) +* [Chris Morgan](https://github.com/chmorgan) +* [Brian K. White](https://github.com/aljex) Translations ------------ -The following people have contributed translations to previous Glabels versions before 4.x: - -* Arabic: - - red sea - -* Bosnian: - - Ademir Havic - -* Catalan and Catalan (Valencia): - - David Planella , Pere Orga - -* Czech: - - David Makovský (Yakeen) , Zbynek Mrkvicka , Marek Černocký - -* Danish: - - Joe Hansen - -* German: - - Marcus Bauer , Christian Neumair , Mario Blättermann - -* Greek: - - Efstathios Iosifidis , Dimitris Spingos (Δημήτρης Σπίγγος) , Tom Tryfonidis - -* British English: - - Bruce Cowan - -* Esperanto: - - Antonio C. Codazzi "la Filozofo" , Kristjan Schmidt - -* Spanish: - - Jorge González , Daniel Mustieles - -* Finnish: - - Jiri Grönroos - -* French: - - Olivier Berger , Florent Morel , Claude Paroz , Bruno Brouard , Gérard Baylard , Pierre Henry , Mathieu Stumpf - -* Galician: - - Leandro Regueiro - -* Hungarian: - - Balázs Úr , Gabor Kelemen , Meskó Balázs - -* Indonesian: - - Andika Triwidada - -* Italian: - - Andrea , Daniele Medri - -* Japanese: - - Takeshi Aihana , Jiro Matsuzawa - -* Korean: - - YunSeok Choi - -* Latvian: - - Rūdolfs Mazurs - -* Norwegian bokmål: - - Kjartan Maraas - -* Dutch: - - Mario Blättermann , Hannie Dumoleyn - -* Occitan: - - Cédric Valmary - -* Portuguese: - - Filipe Roque , Tiago Santos , Pedro Albuquerque - -* Brazilian Portuguese: - - Paulo R. Ormenese , Michel Recondo , Henrique P. Machado , Antonio Fernandes C. Neto , Enrico Nicoletto , Rafael Fontenelle - -* Romanian: - - Daniel Șerbănescu - -* Russian: - - Vitaly Lipatov , Александр Прокудин - -* Slovak: - - Dušan Kazik - -* Slovenian: - - Andrej Žnidaršič , Matej Urbančič - -* Serbian (both cyrillic and latin): - - Miroslav Nikolić - -* Swedish: - - Daniel Nylander , Åke Engelbrektson , Josef Andersson , Anders Jonsson - -* Turkish: - - Yusuf Bolu , Muhammet Kara , Necdet Yücel , İşbaran Akçayır - -* Chinese (China): - - Careon , Du Baodao , YunQiang Su - -* Chinese (Hongkong and Taiwan): - - Shell Hung , Walter Cheuk - +* German + - [Mario Blättermann](https://github.com/mariobl) Product Templates @@ -291,6 +166,7 @@ particular products (I apologize if I have missed anybody): * Thomas Steinert * Windel Bouwman + Previous Versions ----------------- Glabels 4.0 is a complete rewrite. The following people have made contributions to previous versions, @@ -319,8 +195,114 @@ which certainly influenced the current version. * Robin Stuart +Translations from Previous Versions +----------------------------------- +The following people have contributed translations to previous Glabels versions before 4.x. These translations have been imported to seed the current +version, however only a small subset of strings are still valid so the coverage from these translations is very sparse. + +* Arabic: + red sea + +* Bosnian: + Ademir Havic + +* Catalan and Catalan (Valencia): + David Planella , Pere Orga + +* Czech: + David Makovský (Yakeen) , Zbynek Mrkvicka , Marek Černocký + +* Danish: + Joe Hansen + +* German: + Marcus Bauer , Christian Neumair , Mario Blättermann + +* Greek: + Efstathios Iosifidis , Dimitris Spingos (Δημήτρης Σπίγγος) , Tom Tryfonidis + +* British English: + Bruce Cowan + +* Esperanto: + Antonio C. Codazzi "la Filozofo" , Kristjan Schmidt + +* Spanish: + Jorge González , Daniel Mustieles + +* Finnish: + Jiri Grönroos + +* French: + Olivier Berger , Florent Morel , Claude Paroz , Bruno Brouard , Gérard Baylard , Pierre Henry , Mathieu Stumpf + +* Galician: + Leandro Regueiro + +* Hungarian: + Balázs Úr , Gabor Kelemen , Meskó Balázs + +* Indonesian: + Andika Triwidada + +* Italian: + Andrea , Daniele Medri + +* Japanese: + Takeshi Aihana , Jiro Matsuzawa + +* Korean: + YunSeok Choi + +* Latvian: + Rūdolfs Mazurs + +* Norwegian bokmål: + Kjartan Maraas + +* Dutch: + Mario Blättermann , Hannie Dumoleyn + +* Occitan: + Cédric Valmary + +* Portuguese: + Filipe Roque , Tiago Santos , Pedro Albuquerque + +* Brazilian Portuguese: + Paulo R. Ormenese , Michel Recondo , Henrique P. Machado , Antonio Fernandes C. Neto , Enrico Nicoletto , Rafael Fontenelle + +* Romanian: + Daniel Șerbănescu + +* Russian: + Vitaly Lipatov , Александр Прокудин + +* Slovak: + Dušan Kazik + +* Slovenian: + Andrej Žnidaršič , Matej Urbančič + +* Serbian (both cyrillic and latin): + Miroslav Nikolić + +* Swedish: + Daniel Nylander , Åke Engelbrektson , Josef Andersson , Anders Jonsson + +* Turkish: + Yusuf Bolu , Muhammet Kara , Necdet Yücel , İşbaran Akçayır + +* Chinese (China): + Careon , Du Baodao , YunQiang Su + +* Chinese (Hongkong and Taiwan): + Shell Hung , Walter Cheuk + + + Others ------ -And many others for their many helpful suggestions and bug reports -- thanks. +And many others for their many helpful suggestions, reviews, and bug reports -- thanks. diff --git a/COPYING b/LICENSE similarity index 100% rename from COPYING rename to LICENSE diff --git a/README.md b/README.md index fc269ccd..efc01574 100644 --- a/README.md +++ b/README.md @@ -2,30 +2,24 @@ ![Cover Image](docs/images/cover-image.png) -[![Build Status](https://travis-ci.org/jimevins/glabels-qt.svg?branch=master)](https://travis-ci.org/jimevins/glabels-qt) +[![Travis Build Status](https://travis-ci.org/jimevins/glabels-qt.svg?branch=master)](https://travis-ci.org/jimevins/glabels-qt) +[![Appveyor Build Status](https://ci.appveyor.com/api/projects/status/github/jimevins/glabels-qt?branch=master&svg=true)](https://ci.appveyor.com/project/jimevins/glabels-qt) ******************************************************************************* ## What is gLabels-qt? -gLabels-qt is the development version of the next major version of gLabels (4.0). +gLabels-qt is the development version of the next major version of gLabels (a.k.a. glabels-4). ## What's new in gLabels 4? -- Based on the Qt5 framework. -- Updated UI based on typical workflows. -- Intended to be a cross-platform application. - * So far, it has been built and tested under Linux, Windows 7, and Windows 10. (Windows testing is still very intermittent.) - - -## Status - -gLabels-qt has been under off-and-on development for several years. -It is still missing several features to bring it in parity with glabels-3.4. These include - -- Compatability with older glabels project files -- An online manual +- A complete rewrite, based on the Qt5 framework. +- A new UI layout based on common activities. +- Cross-platform support +- User-defined variables +- Support for continuous-roll labels +- Many new product templates ## Download @@ -37,39 +31,61 @@ There are currently no official releases of gLabels 4. ### Continuous Integration Snapshots Continuous integration snapshots are not official releases. These snapshots represent the latest -bleading-edge development (unstable) code. Please, DO NOT use it in a production environment. Do not -expect compatability or consistency of features between snapshots. +bleeding-edge development (unstable) code. Please, DO NOT use it in a production environment. Do not +expect compatibility or consistency of features between snapshots. + +Pre-release Linux [AppImage](http:appimage.org) and Windows installer binaries are available in +**[Releases](https://github.com/jimevins/glabels-qt/releases)**. + +Some third-party packages are also available: + + +| Platform | Files | Notes | +|:----------|:-------------------------------------------------------------------------------------|:--------------------------------------------------------------| +| Archlinux | [Archlinux User Repository Page](https://aur.archlinux.org/packages/glabels-qt-git/) | Maintained by [Mario Blättermann](https://github.com/mariobl) | +| Ubuntu | [PPA Page](https://code.launchpad.net/~krisives/+archive/ubuntu/glabels-qt) | Maintained by [Kristopher Ives](https://github.com/krisives) | -| Platform | Files | -|:---------|:------| -| Linux x86_64 [AppImage](http:appimage.org): *Download, make it executable, and run!* | [glabels-continuous-x86_64.AppImage](https://github.com/jimevins/glabels-qt/releases/download/continuous/glabels-continuous-x86_64.AppImage) | -| Source Code TAR.GZ | [continuous.tar.gz](https://github.com/jimevins/glabels-qt/archive/continuous.tar.gz) | -| Source Code ZIP | [continuous.zip](https://github.com/jimevins/glabels-qt/archive/continuous.tar.gz) | ## Build Instructions - [Linux Build Instructions](docs/BUILD-INSTRUCTIONS-LINUX.md) - [Windows Build Instructions](docs/BUILD-INSTRUCTIONS-WINDOWS.md) -- Mac Build Instructions (TBD) +- [Mac Build Instructions](docs/BUILD-INSTRUCTIONS-MACOS.md) ## Help Needed -* Help is needed to add support to build and package glabels for various platforms and packaging systems. -These include +Please see [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md). + + +## License + +gLabels-qt 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 3 of the License, or +(at your option) any later version. + +gLabels-qt 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. - - Windows (VisualStudio) - - Windows (MINGW) - - Mac - - Linux (flatpak) - - Linux (snap) +See [LICENSE](LICENSE) in this directory. -* Help is needed writing online documentation. +The following sub-components are also made available under less +restrictive licensing: -* Help is needed writing translations. +### Glbarcode -* Suggestions. + gLabels-qt currently includes a version of the glbarcode++ library, located in + the "glbarcode/" subdirectory. It is licensed under the GNU LESSER GENERAL + PUBLIC LICENSE (LGPL); either version 3 of the License, or (at your option) + any later version. See [glbarcode/LICENSE](glbarcode/LICENSE). -* For code contributions, see [docs/CONTRIBUTING.md](docs/CONTRIBUTING.md). +### Template Database + The XML files in the "templates/" subdirectory constitute the glabels + label database. No copyright is claimed on the facts contained within + the database and can be used for any purpose. The files themselves are + licensed using the MIT/X license. See [templates/LICENSE](templates/LICENSE). diff --git a/backends/barcode/Backends.cpp b/backends/barcode/Backends.cpp index f2525ba6..2e4307a9 100644 --- a/backends/barcode/Backends.cpp +++ b/backends/barcode/Backends.cpp @@ -50,10 +50,10 @@ namespace glabels true, true, true, true, "1234567890", true, 10 ); registerStyle( "upc-a", "", tr("UPC-A"), - true, false, true, false, "12345678901", false, 11 ); + true, true, true, false, "12345678901", false, 11 ); registerStyle( "ean-13", "", tr("EAN-13"), - true, false, true, false, "123456789012", false, 12 ); + true, true, true, false, "123456789012", false, 12 ); registerStyle( "postnet", "", tr("POSTNET (any)"), false, false, true, false, "12345-6789-12", false, 11 ); @@ -205,7 +205,7 @@ namespace glabels glbarcode::Factory::registerType( "zint::kix", Zint::Kix::create ); glbarcode::Factory::registerType( "zint::ean", Zint::Ean::create ); glbarcode::Factory::registerType( "zint::gmtx", Zint::Gmtx::create ); - glbarcode::Factory::registerType( "zint::gs1128", Zint::Gs1128::create ); + glbarcode::Factory::registerType( "zint::gs1-128", Zint::Gs1128::create ); glbarcode::Factory::registerType( "zint::rss14", Zint::Rss14::create ); glbarcode::Factory::registerType( "zint::rssltd", Zint::Rssltd::create ); glbarcode::Factory::registerType( "zint::rssexp", Zint::Rssexp::create ); @@ -246,7 +246,7 @@ namespace glabels glbarcode::Factory::registerType( "zint::usps", Zint::Usps::create ); glbarcode::Factory::registerType( "zint::pls", Zint::Pls::create ); - registerStyle( "ausp", "zint", tr("Austraila Post Standard"), + registerStyle( "ausp", "zint", tr("Australia Post Standard"), false, false, true, false, "12345678901234567890123", true, 23 ); registerStyle( "ausrp", "zint", tr("Australia Post Reply Paid"), @@ -303,7 +303,7 @@ namespace glabels registerStyle( "code128", "zint", tr("Code 128"), true, true, true, false, "0000000000", true, 10 ); - registerStyle( "code128b", "zint", tr("Code 128 (Mode C supression)"), + registerStyle( "code128b", "zint", tr("Code 128 (Mode C suppression)"), true, true, true, false, "0000000000", true, 10 ); registerStyle( "daft", "zint", tr("DAFT Code"), diff --git a/backends/barcode/QrEncode.cpp b/backends/barcode/QrEncode.cpp index 27726600..2fd8bcf8 100644 --- a/backends/barcode/QrEncode.cpp +++ b/backends/barcode/QrEncode.cpp @@ -80,7 +80,6 @@ namespace glabels QRcode_free( qrcode ); - QRcode_clearCache(); return true; } diff --git a/backends/barcode/Style.cpp b/backends/barcode/Style.cpp index 59e5ff7e..2e453dd4 100644 --- a/backends/barcode/Style.cpp +++ b/backends/barcode/Style.cpp @@ -20,6 +20,8 @@ #include "Style.h" +#include "Backends.h" + namespace glabels { @@ -116,6 +118,22 @@ namespace glabels } + /// + /// Full Name Property Getter + /// + QString Style::fullName() const + { + if ( mBackendId == "" ) + { + return mName; + } + else + { + return Backends::backendName(mBackendId) + " / " + mName; + } + } + + /// /// Can Text Property Getter /// @@ -171,7 +189,7 @@ namespace glabels /// - /// Prefered N Property Getter + /// Preferred N Property Getter /// int Style::preferedN() const { @@ -200,7 +218,7 @@ namespace glabels /// bool Style::operator!=( const Style& other ) const { - return mId != other.mId; + return (mBackendId != other.mBackendId) || (mId != other.mId); } } // namespace barcode diff --git a/backends/barcode/Style.h b/backends/barcode/Style.h index db9e0379..2661e1b1 100644 --- a/backends/barcode/Style.h +++ b/backends/barcode/Style.h @@ -65,6 +65,8 @@ namespace glabels const QString& name() const; + QString fullName() const; + bool canText() const; bool textOptional() const; diff --git a/backends/merge/Merge.cpp b/backends/merge/Merge.cpp index 993c0d1c..8277ac5e 100644 --- a/backends/merge/Merge.cpp +++ b/backends/merge/Merge.cpp @@ -31,7 +31,7 @@ namespace glabels /// /// Constructor /// - Merge::Merge( const Merge* merge ) : mSource(merge->mSource) + Merge::Merge( const Merge* merge ) : mId(merge->mId), mSource(merge->mSource) { foreach ( Record* record, merge->mRecordList ) { diff --git a/backends/merge/Text.cpp b/backends/merge/Text.cpp index be4765b5..93d5350b 100644 --- a/backends/merge/Text.cpp +++ b/backends/merge/Text.cpp @@ -45,7 +45,7 @@ namespace glabels Text::Text( const Text* merge ) : Merge( merge ), mDelimeter(merge->mDelimeter), mLine1HasKeys(merge->mLine1HasKeys), - mNFieldsMax(merge->mNFieldsMax) + mKeys(merge->mKeys), mNFieldsMax(merge->mNFieldsMax) { } @@ -217,7 +217,7 @@ namespace glabels } else { - /* begining of a simple field. */ + /* beginning of a simple field. */ field.append( c ); state = SIMPLE; } diff --git a/cmake/Modules/FindGnuBarcode.cmake b/cmake/Modules/FindGnuBarcode.cmake index b8703a20..01ca2308 100644 --- a/cmake/Modules/FindGnuBarcode.cmake +++ b/cmake/Modules/FindGnuBarcode.cmake @@ -35,7 +35,7 @@ endif () if (GNUBARCODE_INCLUDE_DIR AND EXISTS "${GNUBARCODE_INCLUDE_DIR}/barcode.h") file (STRINGS "${GNUBARCODE_INCLUDE_DIR}/barcode.h" BARCODE_H REGEX "^#define BARCODE_VERSION *\"[^\"]*\"") - string (REGEX REPLACE "^.*VERSION *\"([^\"]*)\"" "\\1" GNUBARCODE_VERSION_STRING ${BARCODE_H}) + string (REGEX REPLACE "^.*VERSION *\"([^\"]*)\"" "\\1" GNUBARCODE_VERSION_STRING "${BARCODE_H}") endif() # handle the QUIETLY and REQUIRED arguments and set GNUBARCODE_FOUND to TRUE if diff --git a/cmake/Modules/FindLibZint.cmake b/cmake/Modules/FindLibZint.cmake index b44d80aa..9f1a7545 100644 --- a/cmake/Modules/FindLibZint.cmake +++ b/cmake/Modules/FindLibZint.cmake @@ -11,7 +11,7 @@ set (LIBZINT_DEFINITIONS "") find_path (LIBZINT_INCLUDE_DIR NAMES zint.h) -find_library (LIBZINT_LIBRARIES NAMES zint ) +find_library (LIBZINT_LIBRARY NAMES zint ) if (LIBZINT_LIBRARY AND LIBZINT_INCLUDE_DIR) diff --git a/docs/BUILD-INSTRUCTIONS-LINUX.md b/docs/BUILD-INSTRUCTIONS-LINUX.md index 6571feee..dbc40a0d 100644 --- a/docs/BUILD-INSTRUCTIONS-LINUX.md +++ b/docs/BUILD-INSTRUCTIONS-LINUX.md @@ -1,18 +1,19 @@ gLabels Linux Build Instructions ================================ -## Prerequisites +## General +### Prerequisites - g++ - CMake 2.8.12+ -- Qt5 5.4+ Development Packages ( Qt5Core, Qt5Widgets, Qt5PrintSupport, Qt5Xml, Qt5Svg ) +- Qt5 5.6+ Development Packages ( Qt5Core, Qt5Widgets, Qt5PrintSupport, Qt5Xml, Qt5Svg ) - zlib 1.2+ Development Package > Even if the above library packages are installed, their corresponding development packages > may also need to be installed. Development packages are usually named something like > libraryName-dev or libraryName-devel. -## Compile and Install +### Compile and Install gLabels uses the CMake meta build system. Use the following commands to build and install gLabels: @@ -24,3 +25,45 @@ $ cmake .. $ make $ sudo make install + +## Example: Ubuntu 19.04 + +### Installing Prerequisites +``` +sudo apt install cmake +sudo apt install qtbase5-dev libqt5svg5-dev qttools5-dev zlib1g-dev +``` +_QREncode (Optional)_ +``` +sudo apt install pkgconf libqrencode-dev +``` +_Zint (Optional)_ + +Install zint from source: +``` +wget https://downloads.sourceforge.net/project/zint/zint/2.6.3/zint-2.6.3_final.tar.gz +tar xzf zint-2.6.3_final.tar.gz +cd zint-2.6.3.src/ +mkdir build && cd build && cmake .. && make +sudo make install +``` +_GNU Barcode (Optional)_ + +As of version 0.99, GNU Barcode no longer installs its library. So install 0.98 from source: +``` +wget https://ftp.gnu.org/gnu/barcode/barcode-0.98.tar.gz +tar xzf barcode-0.98.tar.gz +cd barcode-0.98/ +./configure && make +sudo make install +``` +### Compile and Install gLabels + +``` +$ cd glabels-qt +$ mkdir build +$ cd build +$ cmake .. +$ make +$ sudo make install +``` diff --git a/docs/BUILD-INSTRUCTIONS-MACOS.md b/docs/BUILD-INSTRUCTIONS-MACOS.md new file mode 100644 index 00000000..5f0b440f --- /dev/null +++ b/docs/BUILD-INSTRUCTIONS-MACOS.md @@ -0,0 +1,20 @@ +gLabels MacOS Build Instructions +================================ + +## Prerequisites + +``` +brew install cmake +brew install qt +``` + +## Compile and Install + +
+cd glabels_source_directory
+mkdir build
+cd build
+cmake -D CMAKE_PREFIX_PATH=/usr/local/opt/qt  ..
+make
+sudo make install
+
diff --git a/docs/BUILD-INSTRUCTIONS-WINDOWS.md b/docs/BUILD-INSTRUCTIONS-WINDOWS.md index a710e1cb..83f8921a 100644 --- a/docs/BUILD-INSTRUCTIONS-WINDOWS.md +++ b/docs/BUILD-INSTRUCTIONS-WINDOWS.md @@ -3,86 +3,99 @@ gLabels Windows Build Instructions gLabels for Windows can be built using one of the following toolchains: -- [MSYS/MINGW](#msysmingw) - [Visual Studio](#visual-studio) +- [MSYS/MINGW](#msysmingw) -MSYS/MINGW ----------- +Visual Studio +------------- ### Prerequisites -- MSYS/MINGW, including the following packages - + mingw32-gcc-g++ - + mingw32-libz +- Visual Studio (these instructions are for _Visual Studio 15 2017 Win64_) - CMake 3.2+ -- Qt5 5.9+ for MINGW +- Qt5 5.9+ for your version of Visual Studio +- NSIS 3.03+ (optional -- for creating an installer) -Make sure that Qt tools and CMake are in your executable search path. For example, add something like this to your .profile file: +Make sure that CMake and the Qt tools are in your executable search path. For example, you may need to add something like the following to your PATH environment variable: ``` -export PATH=/c/Qt/5.9.3/mingw53_32/bin:${PATH} -export PATH="/c/Program Files/CMake/bin":${PATH} +c:\Program Files\CMake\bin +c:\Qt\5.9.3\msvc2017_64\bin ``` The exact paths will depend on the version of Qt and CMake you have installed. +### Compile -### Compile and Install +From a Windows Power Shell, use CMake to create and build a Visual Studio Solution: -From an MSYS shell, type the following commands: +
+> cd glabels_source_directory
+> mkdir build
+> cd build
+> cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_PREFIX_PATH="c:\qt\5.9.3\msvc2017_64" ..
+> cmake --build . --config Release 
+
+ +### Install + +To install, run a Windows Power Shell `as administrator`, and type the following commands:
-$ cd glabels_source_directory
-$ mkdir build
-$ cd build
-$ cmake -G "MSYS Makefiles" ..
-$ make
+> cd glabels_source_directory/build
+> cmake --build . --config Release --target INSTALL
 
-To install glabels, run an MSYS shell `as administrator`, and type the following commands: +### Create installer + +To create a Windows installer, run the following commands:
-$ cd glabels_source_directory/build
-$ make install
+> cd glabels_source_directory/build
+> cpack -C Release -G NSIS
 
-Visual Studio -------------- + +MSYS/MINGW +---------- ### Prerequisites -- Visual Studio (these instructions are for _Visual Studio 15 2017 Win64_) +- MSYS/MINGW, including the following packages + + mingw32-gcc-g++ + + mingw32-libz - CMake 3.2+ -- Qt5 5.9+ for your version of Visual Studio +- Qt5 5.9+ for MINGW -Make sure that CMake and the Qt tools are in your executable search path. For example, you may need to add something like the following to your PATH environment variable: +Make sure that Qt tools and CMake are in your executable search path. For example, add something like this to your .profile file: ``` -c:\Program Files\CMake\bin -c:\Qt\5.9.3\msvc2017_64\bin +export PATH=/c/Qt/5.9.3/mingw53_32/bin:${PATH} +export PATH="/c/Program Files/CMake/bin":${PATH} ``` The exact paths will depend on the version of Qt and CMake you have installed. + ### Compile and Install -From a Windows Power Shell, use CMake to create and build a Visual Studio Solution: +From an MSYS shell, type the following commands:
-> cd glabels_source_directory
-> mkdir build
-> cd build
-> cmake -G "Visual Studio 15 2017 Win64" -DCMAKE_PREFIX_PATH="c:\qt\5.9.3\msvc2017_64" ..
-> cmake --build . --config Release 
+$ cd glabels_source_directory
+$ mkdir build
+$ cd build
+$ cmake -G "MSYS Makefiles" ..
+$ make
 
-To install, run a Windows Power Shell `as administrator`, and type the following commands: +To install glabels, run an MSYS shell `as administrator`, and type the following commands:
-> cd glabels_source_directory/build
-> cmake --build . --config Release --target INSTALL
+$ cd glabels_source_directory/build
+$ make install
 
diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md index 67a5fdee..0cbe5066 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -33,7 +33,7 @@ How To Contribute to gLabels ### Would you like to provide translations? -* Guidelines TBD +* Please read [TRANSLATIONS.md](TRANSLATIONS.md) located in this directory. ### Would you like to write documentation? @@ -54,3 +54,10 @@ How To Contribute to gLabels * Ideally, such builds can be integrated into the current CMake/CPack build system and automated using a CI service. +### Would you like to donate money? + +I would prefer not to take donations at this time. Please donate to another worthwhile organization. + +gLabels is a personal project which I work on intermittently, when the itch drives me. I like to walk away occasionally +for extended periods of time. Accepting donations would turn this into an obligation and it would no longer be fun -- at +least that is how I would feel, even if it wasn't necessarily true. diff --git a/docs/PRODUCT-TEMPLATES.md b/docs/PRODUCT-TEMPLATES.md index 1be42f49..9c2a904a 100644 --- a/docs/PRODUCT-TEMPLATES.md +++ b/docs/PRODUCT-TEMPLATES.md @@ -20,10 +20,6 @@ Assumptions/caveats A sheet contains only one size of label or card (if a sheet contains more than one size of item, it can be split into multiple templates for multiple pass printing). -Distances can be expressed in units of *pt*, *in1*, *mm*, *cm*, or *pc*. For example: -`"1.0in"` or `"2.54cm"`. If no units are specified, computer points (*pt*) will -be assumed (1 *pt* = 1/72 *in* = 0.352778 *mm*). - Template Files -------------- @@ -57,22 +53,37 @@ An example template file containing a single *Template* node: ``` +*Distance* Attributes +--------------------- + +A distance attribute is expressed as a number followed by a unit identifier. +Valid unit identifiers are *pt*, *in*, *mm*, *cm*, or *pc*. These are a subset of +the SVG absolute length units. Examples: + +```xml + + +``` + +If no units are specified, computer points (*pt*) will be assumed (1 *pt* = 1/72 *in* = 0.352778 *mm*). + + *Template* Node --------------- A *Template* node describes a single stationery product. It must contain exactly one instance of a label node, either *Label-rectangle*, *Label-round*, *Label-ellipse* or *Label-cd*. -Property | Description ----------------|------------ -*brand* | Brand or manufacturer of stationery product. E.g. "Avery". -*part* | Part number or name of stationery product. E.g. "8160". -*size* | Paper size. Must match an ID defined in *paper-sizes.xml* or "Other". E.g. "A4". -*description* | Description of stationery product. E.g, "Mailing Labels". -*_description* | Translatable description of stationery product. Used in predefined labels instead of description. -*width* | Page width. Only valid if `size="Other"`. -*height* | Page height. Only valid if `size="Other"`. -*equiv* | Equivalent part number. If this property is present, the template is a clone of another template of the same brand. The template will inherit all properties, except brand and name from the other template. This equiv property must refer to a previously defined template - *gLabels* does not currently support forward references. +Property | Type | Description +---------------|----------|------------ +*brand* | string | Brand or manufacturer of stationery product. E.g. "Avery". +*part* | string | Part number or name of stationery product. E.g. "8160". +*size* | string | Paper size. Must match an ID defined in *paper-sizes.xml*, "roll" or "other". E.g. "A4". +*description* | string | Description of stationery product. E.g, "Mailing Labels". +*_description* | string | Translatable description of stationery product. Used in predefined labels instead of description. +*width* | distance | Page width. Only valid if `size="other"` or `size="roll"`. +*height* | distance | Page height. Only valid if `size="other"`. Value is ignored if `size="roll"`. +*equiv* | string | Equivalent part number. If this property is present, the template is a clone of another template of the same brand. The template will inherit all properties, except brand and name from the other template. This equiv property must refer to a previously defined template - *gLabels* does not currently support forward references. ### Guidelines for Creating Product Descriptions @@ -90,10 +101,10 @@ If creating templates to be distributed with *gLabels*, please use the following A *Meta* node contains some additional information about the template. A *Template* node may contain zero or more *Meta* nodes. Only one property should be defined per *Meta* node. -Property | Description ---------------|------------ -*category* | A category for the template. A template can belong to multiple categories by simply adding multiple *Meta* nodes to the parent *Template* node. The category must match an existing ID defined in categories.xml. E.g. `category="media"` -*product_url* | A URL pointing to the vendor's webpage for the specific product, if available. +Property | Type | Description +---------------|----------|------------ +*category* | string | A category for the template. A template can belong to multiple categories by simply adding multiple *Meta* nodes to the parent *Template* node. The category must match an existing ID defined in categories.xml. E.g. `category="media"` +*product_url* | string | A URL pointing to the vendor's webpage for the specific product, if available. *Label-rectangle* Node @@ -102,14 +113,14 @@ Property | Description A *Label-rectangle* node describes the dimensions of a single label or business card that is rectangular in shape (may have rounded edges). -Property | Description ---------------|------------ -*id* | Reserved for future use. Should always be 0. -*width* | Width of label or card. E.g. `width="29mm"` -*height* | Height of label or card. E.g. `height="100mm"` -*round* | Radius of corners. For items with square edges (business cards), the radius should be 0. -*x_waste* | Amount of horizontal waste (over-print) to allow. This is useful for minimizing alignment problems when using non-white backgrounds (e.g. images). -*y_waste* | Amount of vertical waste (over-print) to allow. +Property | Type | Description +---------------|----------|------------ +*id* | integer | Reserved for future use. Should always be 0. +*width* | distance | Width of label or card. E.g. `width="29mm"` +*height* | distance | Height of label or card. E.g. `height="100mm"` +*round* | distance | Radius of corners. For items with square edges (business cards), the radius should be 0. +*x_waste* | distance | Amount of horizontal waste (over-print) to allow. This is useful for minimizing alignment problems when using non-white backgrounds (e.g. images). +*y_waste* | distance | Amount of vertical waste (over-print) to allow. ![Label-rectangle properties](images/glabels-template-rect-label.png) @@ -120,12 +131,12 @@ Property | Description A *Label-ellipse* node describes the dimensions of a single label or business card that is elliptical in shape. -Property | Description ---------------|------------ -*id* | Reserved for future use. Should always be 0. -*width* | Width of label or card. E.g. `width="29mm"` -*height* | Height of label or card. E.g. `height="100mm"` -*waste* | Amount of waste (over-print) to allow. This is useful for minimizing alignment problems when using non-white backgrounds (e.g. images). +Property | Type | Description +---------------|----------|------------ +*id* | integer | Reserved for future use. Should always be 0. +*width* | distance | Width of label or card. E.g. `width="29mm"` +*height* | distance | Height of label or card. E.g. `height="100mm"` +*waste* | distance | Amount of waste (over-print) to allow. This is useful for minimizing alignment problems when using non-white backgrounds (e.g. images). ![Label-ellipse properties](images/glabels-template-ellipse-label.png) @@ -135,11 +146,11 @@ Property | Description A *Label-round* node describes the dimensions of a simple round label (not a CD). -Property | Description ---------------|------------ -*id* | Reserved for future use. Should always be 0. -*radius* | Radius (1/2 diameter) of label or card. E.g. `radius="14.5mm"` -*waste* | Amount of waste (over-print) to allow. This is useful for minimizing alignment problems when using non-white backgrounds (e.g. images). +Property | Type | Description +---------------|----------|------------ +*id* | integer | Reserved for future use. Should always be 0. +*radius* | distance | Radius (1/2 diameter) of label or card. E.g. `radius="14.5mm"` +*waste* | distance | Amount of waste (over-print) to allow. This is useful for minimizing alignment problems when using non-white backgrounds (e.g. images). ![Label-round properties](images/glabels-template-round-label.png) @@ -149,17 +160,44 @@ Property | Description A *Label-cd* node describes the dimensions of a CD, DVD, or business card CD. -Property | Description ---------------|------------ -*id* | Reserved for future use. Should always be 0. -*radius* | Outer radius (1/2 diameter) of label. E.g. `radius="58.5mm"` -*hole* | Radius (1/2 diameter) of concentric hole. E.g. `hole="18mm"` -*width* | If present, the label is clipped to the given Width. (For use with business card CDs.) -*height* | If present, the label is clipped to the given height. (For use with business card CDs.) -*waste* | Amount of waste (over-print) to allow. This is useful for minimizing alignment problems when using non-white backgrounds (e.g. images). +Property | Type | Description +---------------|----------|------------ +*id* | integer | Reserved for future use. Should always be 0. +*radius* | distance | Outer radius (1/2 diameter) of label. E.g. `radius="58.5mm"` +*hole* | distance | Radius (1/2 diameter) of concentric hole. E.g. `hole="18mm"` +*width* | distance | If present, the label is clipped to the given Width. (For use with business card CDs.) +*height* | distance | If present, the label is clipped to the given height. (For use with business card CDs.) +*waste* | distance | Amount of waste (over-print) to allow. This is useful for minimizing alignment problems when using non-white backgrounds (e.g. images). ![Label-cd properties](images/glabels-template-cd-label.png) +*Label-continuous* Node +----------------------- + +A *Label-continuous* node describes the dimensions of a single section of a continuous label tape. + +Property | Type | Description +-----------------|----------|------------ +*id* | integer | Reserved for future use. Should always be 0. +*width* | distance | Width of label or card. E.g. `width="29mm"` +*min_height* | distance | Minimum user defined height or length of label. +*max_height* | distance | Maximum user defined height or length of label. +*default_height* | distance | Default user defined height or length of label. + + +*Label-path* Node +----------------- + +A *Label-path* node describes the dimensions of a label with an outline defined by an arbitrary path. + +Property | Type | Description +---------------|----------|------------ +*id* | integer | Reserved for future use. Should always be 0. +*d_units* | string | Units used in path definition. Must be a supported distance unit identifier. (default = `"pt"`) +*d* | string | Path definition. This is a subset of of the SVG path "d" attribute. Commands include "M/m", "L/l", "H/h", "V/v", and "Z/z". Commands and data must be delimited by white space." +*x_waste* | distance | Amount of horizontal waste (over-print) to allow. This is useful for minimizing alignment problems when using non-white backgrounds (e.g. images). +*y_waste* | distance | Amount of vertical waste (over-print) to allow. + *Markup* Nodes -------------- @@ -174,57 +212,59 @@ special areas, or other helpful hints to the user of a template. A *Markup-margin* describes a margin along all edges of a label. -Property | Description ---------------|------------ -*size* | Size of the margin. I.e. the distance of the margin line from the edge of the card/label. +Property | Type | Description +---------------|----------|------------ +*size* | distance | Size of the margin. I.e. the distance of the margin line from the edge of the card/label. +*x_size* | distance | Size of the margin in x dimension. I.e. the distance of the margin line from the edge of the card/label. +*y_size* | distance | Size of the margin in y dimension. I.e. the distance of the margin line from the edge of the card/label. ### *Markup-line* Node A *Markup-line* node describes a markup line. -Property | Description ---------------|------------ -*x1* | X coordinate of 1st endpoint of the line segment. -*y1* | Y coordinate of 1st endpoint of the line segment. -*x2* | X coordinate of 2nd endpoint of the line segment. -*y2* | Y coordinate of 2nd endpoint of the line segment. +Property | Type | Description +---------------|----------|------------ +*x1* | distance | X coordinate of 1st endpoint of the line segment. +*y1* | distance | Y coordinate of 1st endpoint of the line segment. +*x2* | distance | X coordinate of 2nd endpoint of the line segment. +*y2* | distance | Y coordinate of 2nd endpoint of the line segment. ### *Markup-circle* Node A *Markup-circle* describes a markup circle. -Property | Description ---------------|------------ -*x0* | X coordinate of circle origin (center). -*y0* | Y coordinate of circle origin (center). -*radius* | Radius of circle. +Property | Type | Description +---------------|----------|------------ +*x0* | distance | X coordinate of circle origin (center). +*y0* | distance | Y coordinate of circle origin (center). +*radius* | distance | Radius of circle. ### *Markup-rect* Node A *Markup-rect* describes a markup rectangle. -Property | Description ---------------|------------ -*x1* | X coordinate of upper left corner of rectangle. -*y1* | Y coordinate of upper left corner of rectangle. -*w* | Width of rectangle. -*h* | Height of rectangle. -*r* | Radius of rounded corners of rectangle. +Property | Type | Description +---------------|----------|------------ +*x1* | distance | X coordinate of upper left corner of rectangle. +*y1* | distance | Y coordinate of upper left corner of rectangle. +*w* | distance | Width of rectangle. +*h* | distance | Height of rectangle. +*r* | distance | Radius of rounded corners of rectangle. ### *Markup-ellipse* Node A *Markup-ellipse* describes a markup ellipse. -Property | Description ---------------|------------ -*x1* | X coordinate of upper left corner of ellipse bounding box. -*y1* | Y coordinate of upper left corner of ellipse bounding box. -*w* | Width of ellipse. -*h* | Height of ellipse. +Property | Type | Description +---------------|----------|------------ +*x1* | distance | X coordinate of upper left corner of ellipse bounding box. +*y1* | distance | Y coordinate of upper left corner of ellipse bounding box. +*w* | distance | Width of ellipse. +*h* | distance | Height of ellipse. *Layout* Node @@ -253,13 +293,13 @@ with unique coordinates for the top left corner of that label. > A single label can always be treated as a grid of one. -Property | Description ---------------|------------ -*nx* | Number of labels/cards in the grid in the X direction (horizontal). -*ny* | Number of labels/cards in the grid in the Y direction (vertical). -*x0* | Distance from left edge of sheet to the left edge of the left column of cards/labels in the layout. -*y0* | Distance from the top edge of sheet to the top edge of the top row of labels/cards in the layout. -*dx* | Horizontal pitch of grid. -*dy* | Vertical pitch of grid. +Property | Type | Description +---------------|----------|------------ +*nx* | integer | Number of labels/cards in the grid in the X direction (horizontal). +*ny* | integer | Number of labels/cards in the grid in the Y direction (vertical). +*x0* | distance | Distance from left edge of sheet to the left edge of the left column of cards/labels in the layout. +*y0* | distance | Distance from the top edge of sheet to the top edge of the top row of labels/cards in the layout. +*dx* | distance | Horizontal pitch of grid. +*dy* | distance | Vertical pitch of grid. ![Layout example](images/glabels-template-layout.png) diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 00000000..988d6673 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,5 @@ +Developer Documentation Directory +================================= + +This directory contains developer and installation documentation primarily for developers, packagers and other contributors. +For user documentation, see [user-docs/](../user-docs/_build). diff --git a/docs/SUBSTITUTION-FIELD-SPEC.md b/docs/SUBSTITUTION-FIELD-SPEC.md index b69ff38e..ddfc2ef2 100644 --- a/docs/SUBSTITUTION-FIELD-SPEC.md +++ b/docs/SUBSTITUTION-FIELD-SPEC.md @@ -17,6 +17,28 @@ modifiers = modifier [ ":" modifiers ] ; modifier = format-modifier | default-value-modifier | new-line-modifier; ``` + +Field Names +----------- +Field names can refer to either [Document Merge Fields](#document-merge-fields) or [User Variables](#user-variables). If a document merge field and a user variable share the same name, the document merge field takes precidence. Its syntax is simply: + +```ebnf +field-name = merge-field-name | user-variable-name ; +``` + +### Document Merge Fields +Document merge fields are the primary source of substitution fields. A document merge field represents a field from an external data source, such as a CSV file. The valid syntax for a document merge field name is determined by the merge source, with the following exception. Merge field names cannot contain either a colon (":") or closing curly bracket ("}"). + +### User Variables +Substitution fields can also refer to user variables. The syntax for valid user variable names is + +```ebnf +letter = "a" | "b" | ... | "z" | "A" | ... | "Z"; +digit = "0" | "1" | "2" | ... | "9"; +user-variable-name = ( letter | "_" ) , { letter | digit | "_" } ; +``` + + Modifiers --------- ### Format-Modifier (`%`) @@ -89,20 +111,3 @@ ${CITY} ${STATE} ${ZIP} `${ADDR2}` would be printed on its own line, only if it is set and non-empty. -Document Merge Fields ---------------------- -Document merge fields are the primary source of substitution fields. A document merge field represents a field from an external data source, such as a CSV file. - -User Defined Variables ----------------------- -Alternatively, merge fields can refer to user defined variables. - -Built-In Variables ------------------- -Potentially, merge fields may also refer to built-in variables. Candidates include: - - LABEL_NUMBER - - PAGE_NUMBER - - DATE - - TIME - - FILE_NAME - diff --git a/docs/TODO.md b/docs/TODO.md index 6eb3cb87..175aa41f 100644 --- a/docs/TODO.md +++ b/docs/TODO.md @@ -1,31 +1,74 @@ -To Do List for gLabels 4.0 -- 2018-04-15 +To Do List for gLabels 4.0 -- 2019-10-07 ======================================== -Add "User Variables" feature ----------------------------- +- [x] Expose user variables to glabels-batch -* Add page to MainWindow to - - Define variables - - Set their initial values - - Control how they might increment (per item instance, per item copy, per page) + There should be a way to set initial values of user variables from the glabels-batch + command line. Possibly even create them on the fly. -* Save as part of glabels project file +- [x] Improve print copies UI + As with previous versions of glabels, there should be a way of selecting + the number of sheets to print. Merge printing should be able to be collated + or not, and if printing multiple copies, start on the same first label. -Add support for arbitrary label shapes --------------------------------------- +- [ ] Write help documentation -* I.e. similar to an SVG path. +- [x] Add "User Variables" feature + Add page to MainWindow to -Add support for "Continuous Roll" labels ----------------------------------------- + - Define variables, + - Set their initial values + - Control how they might increment (per item instance, per item copy, per page) -* If product template is for a continuous roll, define a mininum and default length - - This determines h and dy + Save as part of glabels project file -* Add option to Properties page to adjust this value + +- [x] Add support for arbitrary label shapes + + I.e. similar to an SVG path. + + +- [x] Add support for "Continuous Roll" labels + + If product template is for a continuous roll, define a mininum and default length. + This determines h and dy + + Add option to Properties page to adjust this value + + + +To Do List for post gLabels 4.0 -- 2019-03-17 +============================================= + +- [ ] Create a "built-in" merge source + + As an alternative to external merge sources, let the user edit the merge source + in situ. The user can add fields. The user can add records using those fields. + The user created database will become part of the glabels project file. + For simple databases, such as a small address list, this would be much easier + to deal with than creating it externally. + +- [ ] Printer calibration tool + + There desperately needs to be a printer calibration tool. At a minimum + the tool should allow adjusting horizontal and vertical offsets. The + adjustments should be unique to each printer. + + I am thinking of a test page with a cross in the center. It would have a + grid around this cross, indicating how much to adjust x & y. The user + would fold the paper in quaters and use this grid to guestimate the offsets. + + There should be warnings about how printers may drift from calibration or + even not have repeatable results. + + A more sophisticated tool, could adjust for scaling and skew, but it would + be very difficult to interact with the user on these. + +- [ ] Add programmable margin to text objects + + The current built-in fixed margin seems to confuse people when dealing with + different horizontal and vertical alignments. -Write help documentation ------------------------- diff --git a/docs/TRANSLATIONS.md b/docs/TRANSLATIONS.md new file mode 100644 index 00000000..e9767aa4 --- /dev/null +++ b/docs/TRANSLATIONS.md @@ -0,0 +1,4 @@ +## Translation management + +We manage all translations within a [transifex](https://www.transifex.com/glabels/public/). Please don't send your files via Github, use only transifex. + diff --git a/docs/images/glabels-template-circle-label.png b/docs/images/glabels-template-round-label.png similarity index 100% rename from docs/images/glabels-template-circle-label.png rename to docs/images/glabels-template-round-label.png diff --git a/glabels-batch/CMakeLists.txt b/glabels-batch/CMakeLists.txt index 672720cb..c2abedc6 100644 --- a/glabels-batch/CMakeLists.txt +++ b/glabels-batch/CMakeLists.txt @@ -14,10 +14,9 @@ add_executable (glabels-batch-qt WIN32 ${glabels-batch_sources} ) -#target_compile_features (glabels-batch-qt -# PUBLIC cxx_std_11 -#) -set_property (TARGET glabels-batch-qt PROPERTY CXX_STANDARD 11) +target_compile_features (glabels-batch-qt + PUBLIC cxx_std_11 +) target_link_libraries (glabels-batch-qt Model diff --git a/glabels-batch/main.cpp b/glabels-batch/main.cpp index 3a8b4e01..274b4fc2 100644 --- a/glabels-batch/main.cpp +++ b/glabels-batch/main.cpp @@ -31,12 +31,29 @@ #include #include +#include #include #include #include #include #include -#include + + +namespace +{ + +#if defined(Q_OS_WIN) + const QString STDOUT_FILENAME = "CON:"; + const QString STDIN_FILENAME = "CON:"; +#elif defined(Q_OS_LINUX) + const QString STDOUT_FILENAME = "/dev/stdout"; + const QString STDIN_FILENAME = "/dev/stdin"; +#else + const QString STDOUT_FILENAME = "/dev/stdout"; + const QString STDIN_FILENAME = "/dev/stdin"; +#endif + +} int main( int argc, char **argv ) @@ -46,7 +63,7 @@ int main( int argc, char **argv ) QCoreApplication::setOrganizationName( "glabels.org" ); QCoreApplication::setOrganizationDomain( "glabels.org" ); QCoreApplication::setApplicationName( "glabels-batch-qt" ); - QCoreApplication::setApplicationVersion( glabels::model::Version::STRING ); + QCoreApplication::setApplicationVersion( glabels::model::Version::LONG_STRING ); // // Setup translators @@ -84,7 +101,7 @@ int main( int argc, char **argv ) QPrinterInfo::defaultPrinterName() }, {{"o","output"}, - QCoreApplication::translate( "main", "Set output filename to . (Default=\"output.pdf\")" ), + QCoreApplication::translate( "main", "Set output filename to . Set to \"-\" for stdout. (Default=\"output.pdf\")" ), QCoreApplication::translate( "main", "filename" ), "output.pdf" }, @@ -96,8 +113,14 @@ int main( int argc, char **argv ) QCoreApplication::translate( "main", "Set number of copies to . (Default=1)" ), "n", "1" }, + {{"a","collate"}, + QCoreApplication::translate( "main", "Collate merge copies." ) }, + + {{"g","group"}, + QCoreApplication::translate( "main", "Start each merge group on a new page." ) }, + {{"f","first"}, - QCoreApplication::translate( "main", "Set starting label on 1st page to . (Default=1)" ), + QCoreApplication::translate( "main", "Set starting position to . (Default=1)" ), "n", "1" }, {{"l","outlines"}, @@ -107,7 +130,11 @@ int main( int argc, char **argv ) QCoreApplication::translate( "main", "Print crop marks." ) }, {{"r","reverse"}, - QCoreApplication::translate( "main", "Print in reverse (mirror image)." ) } + QCoreApplication::translate( "main", "Print in reverse (mirror image)." ) }, + + {{"D","define"}, + QCoreApplication::translate( "main", "Set user variable to " ), + QCoreApplication::translate( "main", "var>= variableDefinitions; + for ( QString definition : parser.values("define") ) + { + QStringList parts = definition.split( '=' ); + if ( parts.size() != 2 ) + { + qWarning() << "Error: bad variable definition: " << definition; + return -1; + } + + variableDefinitions[ parts[0] ] = parts[1]; + } // // Initialize subsystems @@ -133,22 +176,36 @@ int main( int argc, char **argv ) if ( parser.positionalArguments().size() == 1 ) { + qDebug() << "Batch mode."; + QString filename = parser.positionalArguments().constFirst(); + if ( filename == "-" ) + { + filename = STDIN_FILENAME; + } + qDebug() << "Project file =" << filename; glabels::model::Model *model = glabels::model::XmlLabelParser::readFile( filename ); if ( model ) { + model->variables()->setVariables( variableDefinitions ); + QPrinter printer( QPrinter::HighResolution ); printer.setColorMode( QPrinter::Color ); if ( parser.isSet("printer") ) { - qDebug() << "Batch mode. printer =" << parser.value("printer"); + qDebug() << "Printer =" << parser.value("printer"); printer.setPrinterName( parser.value("printer") ); } else if ( parser.isSet("output") ) { - qDebug() << "Batch mode. output =" << parser.value("output"); - printer.setOutputFileName( parser.value("output") ); + QString outputFilename = parser.value("output"); + if ( outputFilename == "-" ) + { + outputFilename = STDOUT_FILENAME; + } + qDebug() << "Output =" << outputFilename; + printer.setOutputFileName( outputFilename ); } else { @@ -156,11 +213,58 @@ int main( int argc, char **argv ) } glabels::model::PageRenderer renderer( model ); - renderer.setNCopies( 1 ); - renderer.setStartLabel( parser.value( "first" ).toInt() - 1 ); + if ( model->merge()->keys().empty() ) + { + // Simple project (no merge) + if ( parser.isSet( "sheets" ) ) + { + // Full sheets of simple items + renderer.setNCopies( parser.value( "sheets" ).toInt() * model->frame()->nLabels() ); + renderer.setStartItem( 0 ); + } + else if ( parser.isSet( "copies" ) ) + { + // Partial sheet(s) of simple items + renderer.setNCopies( parser.value( "copies" ).toInt() ); + renderer.setStartItem( parser.value( "first" ).toInt() - 1 ); + } + else + { + // One full sheet of simple items + renderer.setNCopies( model->frame()->nLabels() ); + renderer.setStartItem( 0 ); + } + } + else + { + // Project with merge + renderer.setNCopies( parser.value( "copies" ).toInt() ); + renderer.setStartItem( parser.value( "first" ).toInt() - 1 ); + renderer.setIsCollated( parser.isSet( "collate" ) ); + renderer.setAreGroupsContiguous( !parser.isSet( "group" ) ); + } renderer.setPrintOutlines( parser.isSet( "outlines" ) ); renderer.setPrintCropMarks( parser.isSet( "crop-marks" ) ); renderer.setPrintReverse( parser.isSet( "reverse" ) ); + + // Item and page count summary + if ( renderer.nPages() == 1 ) + { + if ( renderer.nItems() == 1 ) + { + qDebug() << "Printing 1 item on 1 page."; + } + else + { + qDebug() << "Printing" << renderer.nItems() << "items on 1 page."; + } + } + else + { + qDebug() << "Printing" << renderer.nItems() << "items on" << renderer.nPages() << "pages."; + } + + // Do it! renderer.print( &printer ); } } diff --git a/glabels/AboutDialog.cpp b/glabels/AboutDialog.cpp index d72db397..acfb4233 100644 --- a/glabels/AboutDialog.cpp +++ b/glabels/AboutDialog.cpp @@ -38,11 +38,11 @@ namespace glabels { setupUi( this ); - QString version = tr("Version") + " " + model::Version::STRING; + QString version = tr("Version") + " " + model::Version::LONG_STRING; QString description = tr("A program to create labels and business cards."); - QString copyright = "Copyright © 2017 Jim Evins "; + QString copyright = "Copyright © 2018 Jim Evins "; QString licenseParagraph1 = tr( "gLabels is free software: you can redistribute it and/or modify " diff --git a/glabels/BarcodeMenuButton.cpp b/glabels/BarcodeMenuButton.cpp index 8a2b13ee..c708a5ca 100644 --- a/glabels/BarcodeMenuButton.cpp +++ b/glabels/BarcodeMenuButton.cpp @@ -40,7 +40,7 @@ namespace glabels setMenu( mMenu ); mBcStyle = barcode::Backends::defaultStyle(); - setText( mBcStyle.name() ); + setText( mBcStyle.fullName() ); connect( mMenu, SIGNAL(selectionChanged()), this, SLOT(onMenuSelectionChanged()) ); } @@ -61,7 +61,7 @@ namespace glabels void BarcodeMenuButton::setBcStyle( const barcode::Style& bcStyle ) { mBcStyle = bcStyle; - setText( mBcStyle.name() ); + setText( mBcStyle.fullName() ); } @@ -71,7 +71,7 @@ namespace glabels void BarcodeMenuButton::onMenuSelectionChanged() { mBcStyle = mMenu->bcStyle(); - setText( mBcStyle.name() ); + setText( mBcStyle.fullName() ); emit selectionChanged(); } diff --git a/glabels/CMakeLists.txt b/glabels/CMakeLists.txt index 65c54b51..f8ba83f7 100644 --- a/glabels/CMakeLists.txt +++ b/glabels/CMakeLists.txt @@ -13,9 +13,9 @@ set (glabels_sources ColorHistory.cpp ColorPaletteDialog.cpp ColorPaletteItem.cpp - ColorPaletteButtonItem.cpp ColorSwatch.cpp Cursors.cpp + EditVariableDialog.cpp FieldButton.cpp File.cpp Help.cpp @@ -31,6 +31,8 @@ set (glabels_sources PropertiesView.cpp Preview.cpp PreviewOverlayItem.cpp + ReportBugDialog.cpp + RollTemplatePath.cpp SelectProductDialog.cpp SimplePreview.cpp StartupView.cpp @@ -38,6 +40,7 @@ set (glabels_sources TemplatePicker.cpp TemplatePickerItem.cpp UndoRedoModel.cpp + VariablesView.cpp ) set (glabels_qobject_headers @@ -49,7 +52,7 @@ set (glabels_qobject_headers ColorHistory.h ColorPaletteDialog.h ColorPaletteItem.h - ColorPaletteButtonItem.h + EditVariableDialog.h FieldButton.h File.h LabelEditor.h @@ -60,21 +63,25 @@ set (glabels_qobject_headers PrintView.h PropertiesView.h Preview.h + ReportBugDialog.h SelectProductDialog.h SimplePreview.h StartupView.h TemplateDesigner.h TemplatePicker.h UndoRedoModel.h + VariablesView.h ) set (glabels_forms ui/AboutDialog.ui + ui/EditVariableDialog.ui ui/MergeView.ui ui/ObjectEditor.ui ui/PreferencesDialog.ui ui/PrintView.ui ui/PropertiesView.ui + ui/ReportBugDialog.ui ui/SelectProductDialog.ui ui/StartupView.ui ui/TemplateDesignerIntroPage.ui @@ -85,10 +92,13 @@ set (glabels_forms ui/TemplateDesignerRoundPage.ui ui/TemplateDesignerEllipsePage.ui ui/TemplateDesignerCdPage.ui + ui/TemplateDesignerPathPage.ui + ui/TemplateDesignerContinuousPage.ui ui/TemplateDesignerNLayoutsPage.ui ui/TemplateDesignerOneLayoutPage.ui ui/TemplateDesignerTwoLayoutPage.ui ui/TemplateDesignerApplyPage.ui + ui/VariablesView.ui ) set (glabels_resource_files @@ -117,10 +127,9 @@ add_executable (glabels-qt WIN32 ${glabels_win_rc} ) -#target_compile_features (glabels-qt -# PUBLIC cxx_std_11 -#) -set_property (TARGET glabels-qt PROPERTY CXX_STANDARD 11) +target_compile_features (glabels-qt + PUBLIC cxx_std_11 +) target_include_directories (glabels-qt PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} @@ -136,16 +145,16 @@ target_link_libraries (glabels-qt #======================================= install (TARGETS glabels-qt RUNTIME DESTINATION bin) -install (FILES icons/scalable/apps/glabels.svg DESTINATION share/icons/hicolor/scalable/apps) -install (FILES icons/16x16/apps/glabels.svg DESTINATION share/icons/hicolor/16x16/apps) -install (FILES icons/22x22/apps/glabels.svg DESTINATION share/icons/hicolor/22x22/apps) -install (FILES icons/32x32/apps/glabels.svg DESTINATION share/icons/hicolor/32x32/apps) -install (FILES icons/48x48/apps/glabels.svg DESTINATION share/icons/hicolor/48x48/apps) +install (FILES icons/apps/scalable/glabels.svg DESTINATION share/icons/hicolor/scalable/apps) +install (FILES icons/apps/16x16/glabels.svg DESTINATION share/icons/hicolor/16x16/apps) +install (FILES icons/apps/22x22/glabels.svg DESTINATION share/icons/hicolor/22x22/apps) +install (FILES icons/apps/32x32/glabels.svg DESTINATION share/icons/hicolor/32x32/apps) +install (FILES icons/apps/48x48/glabels.svg DESTINATION share/icons/hicolor/48x48/apps) -install (FILES icons/scalable/mimetypes/x-glabels-project.svg DESTINATION share/icons/hicolor/scalable/mimetypes) -install (FILES icons/16x16/mimetypes/x-glabels-project.svg DESTINATION share/icons/hicolor/16x16/mimetypes) -install (FILES icons/22x22/mimetypes/x-glabels-project.svg DESTINATION share/icons/hicolor/22x22/mimetypes) -install (FILES icons/24x24/mimetypes/x-glabels-project.svg DESTINATION share/icons/hicolor/24x24/mimetypes) +install (FILES icons/mimetypes/scalable/x-glabels-project.svg DESTINATION share/icons/hicolor/scalable/mimetypes) +install (FILES icons/mimetypes/16x16/x-glabels-project.svg DESTINATION share/icons/hicolor/16x16/mimetypes) +install (FILES icons/mimetypes/22x22/x-glabels-project.svg DESTINATION share/icons/hicolor/22x22/mimetypes) +install (FILES icons/mimetypes/24x24/x-glabels-project.svg DESTINATION share/icons/hicolor/24x24/mimetypes) # # Windows Runtime diff --git a/glabels/ColorButton.cpp b/glabels/ColorButton.cpp index 1abb9a03..28ea4c88 100644 --- a/glabels/ColorButton.cpp +++ b/glabels/ColorButton.cpp @@ -48,8 +48,9 @@ namespace glabels void ColorButton::init( const QString& defaultLabel, - const QColor& defaultColor, - const QColor& color ) + const QColor& defaultColor, + const QColor& color, + bool showUseFieldButton ) { mDefaultColor = defaultColor; mColorNode = model::ColorNode( color ); @@ -61,7 +62,10 @@ namespace glabels setText( "" ); setCheckable( true ); - mDialog = new ColorPaletteDialog( defaultLabel, defaultColor, color ); + mDialog = new ColorPaletteDialog( defaultLabel, + defaultColor, + color, + showUseFieldButton ); connect( this, SIGNAL(toggled(bool)), this, SLOT(onButtonToggled(bool)) ); connect( mDialog, SIGNAL(colorChanged(model::ColorNode,bool)), @@ -124,15 +128,10 @@ namespace glabels } - void ColorButton::setKeys( const QList keyList ) + void ColorButton::setKeys( const merge::Merge* merge, + const model::Variables* variables ) { - mDialog->setKeys( keyList ); - } - - - void ColorButton::clearKeys() - { - mDialog->clearKeys(); + mDialog->setKeys( merge, variables ); } diff --git a/glabels/ColorButton.h b/glabels/ColorButton.h index eb16611e..81ba63d0 100644 --- a/glabels/ColorButton.h +++ b/glabels/ColorButton.h @@ -58,13 +58,18 @@ namespace glabels // Public Methods ///////////////////////////////// public: - void init( const QString& defaultLabel, const QColor& defaultColor, const QColor& color ); + void init( const QString& defaultLabel, + const QColor& defaultColor, + const QColor& color, + bool showUseFieldButton = true ); + void setColorNode( model::ColorNode colorNode ); void setColor( QColor color ); void setToDefault(); model::ColorNode colorNode(); - void setKeys( const QList keyList ); - void clearKeys(); + + void setKeys( const merge::Merge* merge, + const model::Variables* variables ); ///////////////////////////////// diff --git a/glabels/ColorHistory.cpp b/glabels/ColorHistory.cpp index e6c6d70d..ba564dee 100644 --- a/glabels/ColorHistory.cpp +++ b/glabels/ColorHistory.cpp @@ -46,23 +46,25 @@ namespace glabels } - void ColorHistory::addColor( const QColor &color ) + void ColorHistory::addColor( const QColor &color, const QString& name ) { - QList colorList = readColorList(); + QString nameColor = name + ":" + color.name(); - // Remove any occurances of this color already in list - colorList.removeAll( color ); + QStringList nameColorList = readNameColorList(); + + // Remove any occurrences of this color already in list + nameColorList.removeAll( nameColor ); // Now add to list - colorList.append( color ); + nameColorList.append( nameColor ); // Remove oldest colors, if size exceeds current max - while ( colorList.size() > MAX_COLORS ) + while ( nameColorList.size() > MAX_COLORS ) { - colorList.removeFirst(); + nameColorList.removeFirst(); } - writeColorList( colorList ); + writeNameColorList( nameColorList ); emit changed(); } @@ -70,55 +72,83 @@ namespace glabels QList ColorHistory::getColors() { - return readColorList(); + QList colorList; + + for ( QString& nameColor : readNameColorList() ) + { + QStringList v = nameColor.split( ':' ); + if ( v.size() == 2 ) + { + colorList << QColor( v[1] ); + } + else if ( v.size() == 1 ) + { + // Old-style, no name + colorList << QColor( v[0] ); + } + else + { + // Should not happen + qWarning() << "Invalid color history."; + } + } + + return colorList; } - QColor ColorHistory::getColor( int id ) + QStringList ColorHistory::getNames() { - QList colors = readColorList(); - return colors[id]; + QStringList nameList; + + for ( QString& nameColor : readNameColorList() ) + { + QStringList v = nameColor.split( ':' ); + if ( v.size() == 2 ) + { + nameList << v[0]; + } + else if ( v.size() == 1 ) + { + // Old-style, no name + nameList << QString(tr("color %1")).arg( v[0] ); + } + else + { + // Should not happen + qWarning() << "Invalid color history."; + } + } + + return nameList; } - QList ColorHistory::readColorList() + QStringList ColorHistory::readNameColorList() { QStringList defaultList; QSettings settings; settings.beginGroup( "ColorHistory" ); - QStringList colorNameList = settings.value( "colors", defaultList ).toStringList(); + QStringList nameColorList = settings.value( "colors", defaultList ).toStringList(); settings.endGroup(); - QList colorList; - foreach ( QString colorName, colorNameList ) - { - colorList << QColor( colorName ); - } - // Remove oldest colors, if size exceeds current max - while ( colorList.size() > MAX_COLORS ) + while ( nameColorList.size() > MAX_COLORS ) { - colorList.removeFirst(); + nameColorList.removeFirst(); } - return colorList; + return nameColorList; } - void ColorHistory::writeColorList( const QList& colorList ) + void ColorHistory::writeNameColorList( const QStringList& nameColorList ) { - // Build name list - QStringList colorNameList; - foreach ( QColor color, colorList ) - { - colorNameList << color.name(); - } - // Save QSettings settings; settings.beginGroup( "ColorHistory" ); - settings.setValue( "colors", colorNameList ); + settings.setValue( "colors", nameColorList ); settings.endGroup(); } diff --git a/glabels/ColorHistory.h b/glabels/ColorHistory.h index b74b3b40..eb148128 100644 --- a/glabels/ColorHistory.h +++ b/glabels/ColorHistory.h @@ -60,17 +60,17 @@ namespace glabels // Public Methods ///////////////////////////////// public: - void addColor( const QColor &color ); + void addColor( const QColor& color, const QString& name ); QList getColors(); - QColor getColor( int id ); + QStringList getNames(); ///////////////////////////////// // Private Methods ///////////////////////////////// private: - QList readColorList(); - void writeColorList( const QList& colorList ); + QStringList readNameColorList(); + void writeNameColorList( const QStringList& nameColorList ); ///////////////////////////////// diff --git a/glabels/ColorPaletteButtonItem.cpp b/glabels/ColorPaletteButtonItem.cpp deleted file mode 100644 index ce8d5bf3..00000000 --- a/glabels/ColorPaletteButtonItem.cpp +++ /dev/null @@ -1,124 +0,0 @@ -/* ColorPaletteButtonItem.cpp - * - * Copyright (C) 2014 Jim Evins - * - * This file is part of gLabels-qt. - * - * gLabels-qt 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 3 of the License, or - * (at your option) any later version. - * - * gLabels-qt 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 gLabels-qt. If not, see . - */ - -#include "ColorPaletteButtonItem.h" - -#include -#include - - -namespace glabels -{ - - // - // Private - // - namespace - { - const int border = 4; - const int hBox = 25; - const int outlineWidthPixels = 1; - } - - - /// - /// Constructor From Data - /// - ColorPaletteButtonItem::ColorPaletteButtonItem( const QString& text, QWidget* parent ) - : QWidget(parent), mText(text), mHover(false) - { - setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) ); - setMinimumSize( hBox+2*border+1, hBox+2*border+1 ); - } - - - /// - /// Paint Event - /// - void ColorPaletteButtonItem::paintEvent( QPaintEvent* event ) - { - QPainter painter(this); - - // - // Draw background - // - if ( isEnabled() && mHover ) - { - QLinearGradient gradient( 0, 0, 0, height() ); - gradient.setColorAt( 0, palette().color( QPalette::Highlight ).lighter() ); - gradient.setColorAt( 1, palette().color( QPalette::Highlight ) ); - painter.setBrush( QBrush( gradient ) ); - - QPen pen( palette().color( QPalette::Text ) ); - pen.setWidth( outlineWidthPixels ); - painter.setPen( pen ); - - painter.drawRect( 0, 0, width()-1, height()-1 ); - } - - // - // Draw text - // - painter.setBrush( QBrush( Qt::NoBrush ) ); - - if ( isEnabled() && mHover ) - { - painter.setPen( QPen( palette().color( QPalette::HighlightedText ) ) ); - } - else - { - painter.setPen( QPen( palette().color( QPalette::Text ) ) ); - } - - QRect textRect( border, border, width()-2*border, hBox ); - - painter.drawText( textRect, Qt::AlignLeft|Qt::AlignVCenter, mText ); - } - - - /// - /// Enter Event - /// - void ColorPaletteButtonItem::enterEvent( QEvent* event ) - { - mHover = true; - update(); - } - - - /// - /// Leave Event - /// - void ColorPaletteButtonItem::leaveEvent( QEvent* event ) - { - mHover = false; - update(); - } - - - /// - /// Mouse Press Event - /// - void ColorPaletteButtonItem::mousePressEvent( QMouseEvent* event ) - { - emit activated(); - } - -} // namespace glabels diff --git a/glabels/ColorPaletteDialog.cpp b/glabels/ColorPaletteDialog.cpp index 71feb32c..9ad496a4 100644 --- a/glabels/ColorPaletteDialog.cpp +++ b/glabels/ColorPaletteDialog.cpp @@ -18,14 +18,16 @@ * along with gLabels-qt. If not, see . */ + #include "ColorPaletteDialog.h" #include -#include -#include -#include #include +#include +#include +#include #include +#include #include @@ -83,6 +85,7 @@ namespace glabels ColorPaletteDialog::ColorPaletteDialog( const QString& defaultLabel, const QColor& defaultColor, const QColor& color, + bool showUseFieldButton, QWidget* parent ) : QDialog( parent ) { @@ -99,14 +102,12 @@ namespace glabels vLayout->setContentsMargins( 0, 0, 0, 0 ); vLayout->setSpacing( 0 ); - auto* defaultButton = new ColorPaletteButtonItem( defaultLabel ); - connect( defaultButton, SIGNAL(activated()), this, SLOT(onDefaultItemActivated()) ); - vLayout->addWidget( defaultButton ); - - QFrame* hline1 = new QFrame; - hline1->setFrameStyle( QFrame::HLine | QFrame::Plain ); - hline1->setLineWidth( 1 ); - vLayout->addWidget( hline1 ); + // + // Construct Standard Colors Grid + // + auto* standardColorsGroup = new QGroupBox( tr("Standard Colors") ); + standardColorsGroup->setAlignment( Qt::AlignHCenter ); + vLayout->addWidget( standardColorsGroup ); auto* mainPaletteLayout = new QGridLayout(); mainPaletteLayout->setSpacing( 0 ); @@ -119,17 +120,20 @@ namespace glabels ColorPaletteItem* item = new ColorPaletteItem( i, QColor( mColorTable[i].colorSpec ), tr(mColorTable[i].trname) ); - connect( item, SIGNAL(activated(int)), this, SLOT(onPaletteItemActivated(int)) ); + connect( item, SIGNAL(activated(int)), + this, SLOT(onPaletteItemActivated(int)) ); mainPaletteLayout->addWidget( item, iRow, iCol ); } } - vLayout->addLayout( mainPaletteLayout ); + standardColorsGroup->setLayout( mainPaletteLayout ); - QFrame* hline2 = new QFrame; - hline2->setFrameStyle( QFrame::HLine | QFrame::Plain ); - hline2->setLineWidth( 1 ); - vLayout->addWidget( hline2 ); + // + // Construct Recent Colors Grid + // + auto* recentColorsGroup = new QGroupBox( tr("Recent Colors") ); + recentColorsGroup->setAlignment( Qt::AlignHCenter ); + vLayout->addWidget( recentColorsGroup ); auto* customPaletteLayout = new QHBoxLayout(); customPaletteLayout->setSpacing( 0 ); @@ -137,40 +141,49 @@ namespace glabels { mHistoryItem[iCol] = new ColorPaletteItem( iCol, QColor(0,0,0,0), "" ); mHistoryItem[iCol]->setEnabled( false ); - connect( mHistoryItem[iCol], SIGNAL(activated(int)), this, SLOT(onHistoryItemActivated(int)) ); + connect( mHistoryItem[iCol], SIGNAL(activated(int)), + this, SLOT(onHistoryItemActivated(int)) ); customPaletteLayout->addWidget( mHistoryItem[iCol] ); } - vLayout->addLayout( customPaletteLayout ); - + recentColorsGroup->setLayout( customPaletteLayout ); - QFrame* hline3 = new QFrame; - hline3->setFrameStyle( QFrame::HLine | QFrame::Plain ); - hline3->setLineWidth( 1 ); - vLayout->addWidget( hline3 ); - ColorPaletteButtonItem* customColorButton = new ColorPaletteButtonItem( tr("Custom color...") ); - connect( customColorButton, SIGNAL(activated()), this, SLOT(onCustomColorItemActivated()) ); + // + // Construct Default (e.g. "No Fill") Button + // + auto* defaultColorButton = new QPushButton( defaultLabel ); + defaultColorButton->setAutoDefault( false ); + defaultColorButton->setDefault( false ); + connect( defaultColorButton, SIGNAL(clicked()), this, SLOT(onDefaultButtonClicked()) ); + vLayout->addWidget( defaultColorButton ); + + // + // Construct Custom Color Button + // + auto* customColorButton = new QPushButton( tr("Custom color...") ); + customColorButton->setAutoDefault( false ); + customColorButton->setDefault( false ); + connect( customColorButton, SIGNAL(clicked()), this, SLOT(onCustomColorButtonClicked()) ); vLayout->addWidget( customColorButton ); - QFrame* hline4 = new QFrame; - hline4->setFrameStyle( QFrame::HLine | QFrame::Plain ); - hline4->setLineWidth( 1 ); - vLayout->addWidget( hline4 ); - - mMergeFieldCombo = new QComboBox(); - mMergeFieldCombo->addItem( tr("Merge key...") ); - mMergeFieldCombo->setMinimumSize( 34, 34 ); - mMergeFieldCombo->setFrame( false ); - mMergeFieldCombo->setEnabled( false ); - connect( mMergeFieldCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(onComboIndexChanged(int)) ); - vLayout->addWidget( mMergeFieldCombo ); - - // Item 0 is the ComboBox title, not an item intended for selection. So disable it. - const auto* model = qobject_cast(mMergeFieldCombo->model()); - QStandardItem* item = model->item(0); - item->setFlags( item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled) ); - + // + // Construct "Use field" Button + // + if ( showUseFieldButton ) + { + mFieldButton = new FieldButton(); + mFieldButton->setText( tr("Use substitution field") ); + mFieldButton->setAutoDefault( false ); + mFieldButton->setDefault( false ); + connect( mFieldButton, SIGNAL(keySelected(QString)), this, SLOT(onKeySelected(QString)) ); + vLayout->addWidget( mFieldButton ); + } + else + { + mFieldButton = nullptr; + } + setLayout( vLayout ); loadCustomColorHistory(); @@ -183,55 +196,39 @@ namespace glabels } - void ColorPaletteDialog::setKeys( const QStringList& keyList ) + void ColorPaletteDialog::setKeys( const merge::Merge* merge, + const model::Variables* variables ) { - mKeys = keyList; - - // Clear old keys, (all entries, except item 0) - for ( int index = mMergeFieldCombo->count()-1; index > 0; index-- ) - { - mMergeFieldCombo->removeItem( index ); - } - - // Add new keys - if ( keyList.size() > 0 ) - { - mMergeFieldCombo->addItems( keyList ); - mMergeFieldCombo->setEnabled( true ); - } - else + if (mFieldButton) { - mMergeFieldCombo->setEnabled( false ); + mFieldButton->setKeys( merge, variables ); } } - void ColorPaletteDialog::clearKeys() + void ColorPaletteDialog::onPaletteItemActivated( int id ) { - - for ( int index = mMergeFieldCombo->count()-1; index > 0; index-- ) - { - mMergeFieldCombo->removeItem( index ); - } - mMergeFieldCombo->setEnabled( false ); - } - + model::ColorNode newColorNode; + newColorNode.setField( false ); + newColorNode.setColor( QColor( mColorTable[id].colorSpec ) ); + newColorNode.setKey( "" ); - void ColorPaletteDialog::onDefaultItemActivated() - { - mColorNode.setField( false ); - mColorNode.setColor( mDefaultColor ); - mColorNode.setKey( "" ); + if ( newColorNode != mColorNode ) + { + mColorNode = newColorNode; + + mColorHistory->addColor( mColorNode.color(), mColorTable[id].trname ); - emit colorChanged( mColorNode, true ); - accept(); + emit colorChanged( mColorNode, false ); + accept(); + } } - void ColorPaletteDialog::onPaletteItemActivated( int id ) + void ColorPaletteDialog::onHistoryItemActivated( int id ) { mColorNode.setField( false ); - mColorNode.setColor( QColor( mColorTable[id].colorSpec ) ); + mColorNode.setColor( mColorHistory->getColors()[id] ); mColorNode.setKey( "" ); emit colorChanged( mColorNode, false ); @@ -239,18 +236,18 @@ namespace glabels } - void ColorPaletteDialog::onHistoryItemActivated( int id ) + void ColorPaletteDialog::onDefaultButtonClicked() { mColorNode.setField( false ); - mColorNode.setColor( mColorHistory->getColor(id) ); + mColorNode.setColor( mDefaultColor ); mColorNode.setKey( "" ); - emit colorChanged( mColorNode, false ); + emit colorChanged( mColorNode, true ); accept(); } - void ColorPaletteDialog::onCustomColorItemActivated() + void ColorPaletteDialog::onCustomColorButtonClicked() { QColorDialog dlg( mColorNode.color(), this ); dlg.setWindowTitle( tr("Custom Color") ); @@ -267,7 +264,10 @@ namespace glabels { mColorNode = newColorNode; - mColorHistory->addColor( mColorNode.color() ); + // TRANSLATORS + //: %1 = color specification in hex. String must not contain a colon (:). + mColorHistory->addColor( mColorNode.color(), + QString(tr("Custom Color %1")).arg(mColorNode.color().name()) ); emit colorChanged( mColorNode, false ); accept(); @@ -284,12 +284,13 @@ namespace glabels void ColorPaletteDialog::loadCustomColorHistory() { + QStringList nameList = mColorHistory->getNames(); QList colorList = mColorHistory->getColors(); int id = 0; foreach ( QColor color, colorList ) { - mHistoryItem[id]->setColor( id, color, QString(tr("Custom color #%1").arg(id+1) ) ); + mHistoryItem[id]->setColor( id, color, nameList[id] ); mHistoryItem[id]->setEnabled( true ); id++; } @@ -302,25 +303,14 @@ namespace glabels } - void ColorPaletteDialog::onComboIndexChanged( int index ) - { - if ( index != 0 ) - { - mColorNode.setField( true ); - mColorNode.setColor( QColor( 0xee, 0xee, 0xec ) ); - mColorNode.setKey( mKeys[index-1] ); - - emit colorChanged( mColorNode, false ); - accept(); - } - } - - - void ColorPaletteDialog::showEvent( QShowEvent* event ) + void ColorPaletteDialog::onKeySelected( QString key ) { - mMergeFieldCombo->setCurrentIndex( 0 ); + mColorNode.setField( true ); + mColorNode.setColor( QColor( 0xee, 0xee, 0xec ) ); + mColorNode.setKey( key ); - QDialog::showEvent( event ); + emit colorChanged( mColorNode, false ); + accept(); } } // namespace glabels diff --git a/glabels/ColorPaletteDialog.h b/glabels/ColorPaletteDialog.h index db5f7e18..6e1a22c1 100644 --- a/glabels/ColorPaletteDialog.h +++ b/glabels/ColorPaletteDialog.h @@ -24,11 +24,10 @@ #include "ColorHistory.h" #include "ColorPaletteItem.h" -#include "ColorPaletteButtonItem.h" +#include "FieldButton.h" #include "model/ColorNode.h" -#include #include @@ -50,6 +49,7 @@ namespace glabels ColorPaletteDialog( const QString& defaultLabel, const QColor& defaultColor, const QColor& color, + bool showUseFieldButton = true, QWidget* parent = nullptr ); @@ -64,25 +64,23 @@ namespace glabels // Public Methods ///////////////////////////////// public: - void setColorNode( const model::ColorNode& colorNode ); - void setKeys( const QStringList& keyList ); - void clearKeys(); + void setColorNode( const model::ColorNode& colorNode ); + + void setKeys( const merge::Merge* merge, + const model::Variables* variables ); ///////////////////////////////// // Slots ///////////////////////////////// private slots: - void onDefaultItemActivated(); void onPaletteItemActivated( int id ); void onHistoryItemActivated( int id ); - void onCustomColorItemActivated(); + void onDefaultButtonClicked(); + void onCustomColorButtonClicked(); + void onKeySelected( QString key ); void onColorHistoryChanged(); - void onComboIndexChanged( int index ); - protected: - void showEvent( QShowEvent* event ) override; - ///////////////////////////////// // Private Methods @@ -111,8 +109,7 @@ namespace glabels ColorHistory* mColorHistory; ColorPaletteItem* mHistoryItem[PALETTE_COLS]; - QComboBox* mMergeFieldCombo; - QStringList mKeys; + FieldButton* mFieldButton; }; diff --git a/glabels/EditVariableDialog.cpp b/glabels/EditVariableDialog.cpp new file mode 100644 index 00000000..a685e73a --- /dev/null +++ b/glabels/EditVariableDialog.cpp @@ -0,0 +1,220 @@ +/* EditVariableDialog.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "EditVariableDialog.h" + +#include "model/Settings.h" + +#include + + +namespace +{ + // All variable types. (must be in sorted order) + const QVector allTypes = { + glabels::model::Variable::Type::STRING, + glabels::model::Variable::Type::INTEGER, + glabels::model::Variable::Type::FLOATING_POINT, + glabels::model::Variable::Type::COLOR + }; + + // All variable increments. (must be in sorted order) + const QVector allIncrements = { + glabels::model::Variable::Increment::NEVER, + glabels::model::Variable::Increment::PER_ITEM, + glabels::model::Variable::Increment::PER_COPY, + glabels::model::Variable::Increment::PER_PAGE + }; +} + + +namespace glabels +{ + + /// + /// Constructor + /// + EditVariableDialog::EditVariableDialog( QWidget *parent ) + : QDialog(parent) + { + setupUi( this ); + + QRegularExpression reIdentifier( "[a-zA-Z_][a-zA-Z_0-9]*" ); + nameEdit->setValidator( new QRegularExpressionValidator( reIdentifier ) ); + + colorValueButton->init( tr("Default"), + QColor(0,0,0,255), + QColor(0,0,0,255), + false ); + + for ( auto type : allTypes ) + { + typeCombo->addItem( model::Variable::typeToI18nString( type ) ); + } + + for ( auto type : allIncrements ) + { + incrementCombo->addItem( model::Variable::incrementToI18nString( type ) ); + } + + stepSizeEdit->setText( "1" ); + } + + + /// + /// Set variable + /// + void EditVariableDialog::setVariable( const model::Variable& variable ) + { + typeCombo->setCurrentIndex( static_cast(variable.type()) ); + nameEdit->setText( variable.name() ); + valueEdit->setText( variable.initialValue() ); + colorValueButton->setColor( QColor( variable.initialValue() ) ); + incrementCombo->setCurrentIndex( static_cast(variable.increment()) ); + stepSizeEdit->setText( variable.stepSize() ); + + updateControls(); + } + + + /// + /// Get variable + /// + model::Variable EditVariableDialog::variable() const + { + return model::Variable( static_cast(typeCombo->currentIndex()), + nameEdit->text(), + valueEdit->text(), + static_cast(incrementCombo->currentIndex()), + stepSizeEdit->text() ); + } + + + /// + /// nameEdit Changed + /// + void EditVariableDialog::onNameEditChanged() + { + validateCurrentInputs(); + } + + + /// + /// typeCombo Changed + /// + void EditVariableDialog::onTypeComboChanged() + { + updateControls(); + } + + + /// + /// valueEdit Changed + /// + void EditVariableDialog::onValueEditChanged() + { + validateCurrentInputs(); + } + + + /// + /// colorValueButton Changed + /// + void EditVariableDialog::onColorValueButtonChanged() + { + valueEdit->setText( colorValueButton->colorNode().color().name() ); + validateCurrentInputs(); + } + + + /// + /// incrementCombo Changed + /// + void EditVariableDialog::onIncrementComboChanged() + { + updateControls(); + } + + + /// + /// stepSizeEdit Changed + /// + void EditVariableDialog::onStepSizeEditChanged() + { + validateCurrentInputs(); + } + + + /// + /// update controls + /// + void EditVariableDialog::updateControls() + { + auto type = static_cast(typeCombo->currentIndex()); + auto increment = static_cast(incrementCombo->currentIndex()); + + switch (type) + { + + case model::Variable::Type::INTEGER: + valueEdit->setValidator( new QIntValidator() ); + stepSizeEdit->setValidator( new QIntValidator() ); + break; + + case model::Variable::Type::FLOATING_POINT: + valueEdit->setValidator( new QDoubleValidator() ); + stepSizeEdit->setValidator( new QDoubleValidator() ); + break; + + default: + valueEdit->setValidator( nullptr ); + stepSizeEdit->setValidator( nullptr ); + break; + + } + + colorValueButton->setVisible( type == model::Variable::Type::COLOR ); + + bool isNumeric = ( type == model::Variable::Type::INTEGER ) || + ( type == model::Variable::Type::FLOATING_POINT ); + + incrementGroup->setVisible( isNumeric ); + stepSizeLabel->setEnabled( isNumeric && (increment != model::Variable::Increment::NEVER) ); + stepSizeEdit->setEnabled( isNumeric && (increment != model::Variable::Increment::NEVER) ); + + validateCurrentInputs(); + } + + + /// + /// validate current inputs + /// + void EditVariableDialog::validateCurrentInputs() + { + bool hasValidIdentifier = nameEdit->hasAcceptableInput(); + bool hasValidValue = valueEdit->hasAcceptableInput(); + bool hasValidStepSize = stepSizeEdit->hasAcceptableInput(); + + bool isValid = hasValidIdentifier && hasValidValue && hasValidStepSize; + buttonBox->button(QDialogButtonBox::Ok)->setEnabled( isValid ); + } + + +} // namespace glabels diff --git a/glabels/ColorPaletteButtonItem.h b/glabels/EditVariableDialog.h similarity index 59% rename from glabels/ColorPaletteButtonItem.h rename to glabels/EditVariableDialog.h index 0ffc6272..6c71d613 100644 --- a/glabels/ColorPaletteButtonItem.h +++ b/glabels/EditVariableDialog.h @@ -1,6 +1,6 @@ -/* ColorPaletteButtonItem.h +/* EditVariableDialog.h * - * Copyright (C) 2014 Jim Evins + * Copyright (C) 2019 Jim Evins * * This file is part of gLabels-qt. * @@ -18,21 +18,21 @@ * along with gLabels-qt. If not, see . */ -#ifndef ColorPaletteButtonItem_h -#define ColorPaletteButtonItem_h +#ifndef EditVariableDialog_h +#define EditVariableDialog_h -#include -#include +#include "ui_EditVariableDialog.h" +#include "model/Variable.h" namespace glabels { /// - /// Color Palette Item + /// New Label Dialog Widget /// - class ColorPaletteButtonItem : public QWidget + class EditVariableDialog : public QDialog, public Ui_EditVariableDialog { Q_OBJECT @@ -40,36 +40,37 @@ namespace glabels // Life Cycle ///////////////////////////////// public: - ColorPaletteButtonItem( const QString& text, QWidget* parent = nullptr ); + EditVariableDialog( QWidget *parent = nullptr ); ///////////////////////////////// - // Signals + // Public methods ///////////////////////////////// - signals: - void activated(); - + void setVariable( const model::Variable& variable ); + model::Variable variable() const; + ///////////////////////////////// - // Event handlers + // Slots ///////////////////////////////// - protected: - void paintEvent( QPaintEvent* event ) override; - void enterEvent( QEvent* event ) override; - void leaveEvent( QEvent* event ) override; - void mousePressEvent( QMouseEvent* event ) override; - - + private slots: + void onNameEditChanged(); + void onTypeComboChanged(); + void onValueEditChanged(); + void onColorValueButtonChanged(); + void onIncrementComboChanged(); + void onStepSizeEditChanged(); + + ///////////////////////////////// - // Private Data + // Private methods ///////////////////////////////// - private: - QString mText; + void updateControls(); + void validateCurrentInputs(); - bool mHover; }; } -#endif // ColorPaletteButtonItem_h +#endif // EditVariableDialog_h diff --git a/glabels/FieldButton.cpp b/glabels/FieldButton.cpp index f6c25b67..483e7705 100644 --- a/glabels/FieldButton.cpp +++ b/glabels/FieldButton.cpp @@ -1,6 +1,6 @@ /* FieldButton.cpp * - * Copyright (C) 2014-2016 Jim Evins + * Copyright (C) 2019 Jim Evins * * This file is part of gLabels-qt. * @@ -30,79 +30,61 @@ namespace glabels /// /// Constructor /// - FieldButton::FieldButton( QWidget* parent ) - : QComboBox(parent) + FieldButton::FieldButton( QWidget* parent ) : QPushButton(parent) { setEnabled( false ); - - connect( this, SIGNAL(currentIndexChanged(int)), this, SLOT(onIndexChanged(int)) ); + setMenu( &mMenu ); + + connect( &mMenu, SIGNAL(triggered(QAction*)), + this, SLOT(onMenuActionTriggered(QAction*)) ); } - void FieldButton::setName( const QString& name ) + /// + /// Set Keys + /// + void FieldButton::setKeys( const merge::Merge* merge, + const model::Variables* variables ) { - mName = name; - if ( count() == 0 ) + // Clear old keys + mMenu.clear(); + + // Add merge keys, if any + mMenu.addSection( tr("Merge fields") ); + for ( auto& key : merge->keys() ) { - addItem( mName ); + auto* action = mMenu.addAction( QString( "${%1}" ).arg( key ) ); + action->setData( key ); } - else + if ( merge->keys().empty() ) { - setItemText( 0, mName ); + auto* action = mMenu.addAction( "None" ); + action->setEnabled( false ); } - // Item 0 is the ComboBox title, not an item intended for selection. So disable it. - const auto* itemModel = qobject_cast(model()); - QStandardItem* item = itemModel->item(0); - item->setFlags( item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled) ); - } - - - void FieldButton::setKeys( const QStringList& keyList ) - { - // Clear old keys - clear(); - addItem( mName ); - - // Item 0 is the ComboBox title, not an item intended for selection. So disable it. - const auto* itemModel = qobject_cast(model()); - QStandardItem* item = itemModel->item(0); - item->setFlags( item->flags() & ~(Qt::ItemIsSelectable|Qt::ItemIsEnabled) ); - - // Add new keys - if ( keyList.size() > 0 ) + // Add variable keys, if any + mMenu.addSection( tr("Variables") ); + for ( auto& key : variables->keys() ) { - addItems( keyList ); - setEnabled( true ); + auto* action = mMenu.addAction( QString( "${%1}" ).arg( key ) ); + action->setData( key ); } - else + if ( variables->keys().empty() ) { - setEnabled( false ); + auto* action = mMenu.addAction( "None" ); + action->setEnabled( false ); } - } - - void FieldButton::clearKeys() - { - clear(); - addItem( mName ); - - setEnabled( false ); + setEnabled( !merge->keys().empty() || !variables->keys().empty() ); } - /// - /// onMenuKeySelected slot + /// onMenuActionTriggered slot /// - void FieldButton::onIndexChanged( int index ) + void FieldButton::onMenuActionTriggered( QAction* action ) { - if ( index > 0 ) - { - emit keySelected( itemText(index) ); - - setCurrentIndex( 0 ); - } + emit keySelected( action->data().toString() ); } } // namespace glabels diff --git a/glabels/FieldButton.h b/glabels/FieldButton.h index 1da2d576..230a86dd 100644 --- a/glabels/FieldButton.h +++ b/glabels/FieldButton.h @@ -1,6 +1,6 @@ /* FieldButton.h * - * Copyright (C) 2014-2016 Jim Evins + * Copyright (C) 2019 Jim Evins * * This file is part of gLabels-qt. * @@ -22,8 +22,13 @@ #define FieldButton_h -#include -#include +#include "model/Variables.h" +#include "merge/Merge.h" + +#include +#include +#include +#include namespace glabels @@ -32,7 +37,7 @@ namespace glabels /// /// Field Button /// - class FieldButton : public QComboBox + class FieldButton : public QPushButton { Q_OBJECT @@ -54,23 +59,22 @@ namespace glabels // Public Methods ///////////////////////////////// public: - void setName( const QString& name = "" ); - void setKeys( const QStringList& keyList ); - void clearKeys(); + void setKeys( const merge::Merge* merge, + const model::Variables* variables ); ///////////////////////////////// // Slots ///////////////////////////////// private slots: - void onIndexChanged( int index ); + void onMenuActionTriggered( QAction* action ); ///////////////////////////////// // Private Data ///////////////////////////////// private: - QString mName; + QMenu mMenu; }; diff --git a/glabels/File.cpp b/glabels/File.cpp index f87a4c8f..5a354738 100644 --- a/glabels/File.cpp +++ b/glabels/File.cpp @@ -26,6 +26,7 @@ #include "model/FileUtil.h" #include "model/Model.h" +#include "model/Settings.h" #include "model/XmlLabelParser.h" #include "model/XmlLabelCreator.h" @@ -56,12 +57,13 @@ namespace glabels { auto* model = new model::Model(); model->setTmplate( tmplate ); - model->clearModified(); - + // Intelligently decide to rotate label by default const model::Frame* frame = tmplate->frames().first(); model->setRotate( frame->h() > frame->w() ); + model->clearModified(); + // Either apply to current window or open a new one if ( window->isEmpty() ) { @@ -110,8 +112,6 @@ namespace glabels model::Model *model = model::XmlLabelParser::readFile( fileName ); if ( model ) { - model->setFileName( fileName ); - // Either apply to current window or open a new one if ( window->isEmpty() ) { @@ -123,6 +123,45 @@ namespace glabels newWindow->setModel( model ); newWindow->show(); } + model::Settings::addToRecentFileList( fileName ); + + // Save CWD + mCwd = QFileInfo( fileName ).absolutePath(); + } + else + { + QMessageBox msgBox; + msgBox.setText( tr("Unable to open \"") + fileName + tr("\".") ); + msgBox.setStandardButtons( QMessageBox::Ok ); + msgBox.setDefaultButton( QMessageBox::Ok ); + msgBox.exec(); + } + } + } + + + /// + /// Open file + /// + void File::open( const QString& fileName, MainWindow *window ) + { + if ( !fileName.isEmpty() ) + { + model::Model *model = model::XmlLabelParser::readFile( fileName ); + if ( model ) + { + // Either apply to current window or open a new one + if ( window->isEmpty() ) + { + window->setModel( model ); + } + else + { + auto *newWindow = new MainWindow(); + newWindow->setModel( model ); + newWindow->show(); + } + model::Settings::addToRecentFileList( fileName ); // Save CWD mCwd = QFileInfo( fileName ).absolutePath(); @@ -156,6 +195,7 @@ namespace glabels model::XmlLabelCreator::writeFile( window->model(), window->model()->fileName() ); window->model()->clearModified(); + model::Settings::addToRecentFileList( window->model()->fileName() ); // Save CWD mCwd = QFileInfo( window->model()->fileName() ).absolutePath(); @@ -169,7 +209,8 @@ namespace glabels /// bool File::saveAs( MainWindow *window ) { - // Either use the saved CWD from a previous open/save or grab it from the path of the current file + // Either use the saved CWD from a previous open/save or grab it from the path + // of the current file. QString cwd = mCwd; if ( window->model() && !window->model()->fileName().isEmpty() ) { @@ -211,6 +252,7 @@ namespace glabels model::XmlLabelCreator::writeFile( window->model(), fileName ); window->model()->setFileName( fileName ); window->model()->clearModified(); + model::Settings::addToRecentFileList( fileName ); // Save CWD mCwd = QFileInfo( fileName ).absolutePath(); diff --git a/glabels/File.h b/glabels/File.h index f151b04c..0dddf526 100644 --- a/glabels/File.h +++ b/glabels/File.h @@ -44,6 +44,7 @@ namespace glabels public: static bool newLabel( MainWindow *window = nullptr ); static void open( MainWindow *window ); + static void open( const QString& fileName, MainWindow *window ); static bool save( MainWindow *window ); static bool saveAs( MainWindow *window ); static void templateDesigner( MainWindow *window ); diff --git a/glabels/Help.cpp b/glabels/Help.cpp index c41ee015..4859e250 100644 --- a/glabels/Help.cpp +++ b/glabels/Help.cpp @@ -21,6 +21,7 @@ #include "Help.h" #include "AboutDialog.h" +#include "ReportBugDialog.h" #include @@ -37,6 +38,16 @@ namespace glabels } + /// + /// Display Help->"Report Bug" Dialog + /// + void Help::displayReportBug( QWidget *parent ) + { + ReportBugDialog dialog( parent ); + dialog.exec(); + } + + /// /// Display Help->About Dialog /// diff --git a/glabels/Help.h b/glabels/Help.h index 21779ad2..8a542d33 100644 --- a/glabels/Help.h +++ b/glabels/Help.h @@ -35,6 +35,7 @@ namespace glabels { void displayContents( QWidget *parent ); + void displayReportBug( QWidget *parent ); void displayAbout( QWidget *parent ); } diff --git a/glabels/Icons.h b/glabels/Icons.h index 7d5acf1a..7141bedb 100644 --- a/glabels/Icons.h +++ b/glabels/Icons.h @@ -39,9 +39,9 @@ namespace glabels public: Arrow() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-arrow.svg" ) ); - addPixmap( QPixmap( ":icons/22x22/actions/glabels-arrow.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/glabels-arrow.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-arrow.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-arrow.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-arrow.svg" ) ); } }; @@ -51,9 +51,9 @@ namespace glabels public: Barcode() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-barcode.svg" ) ); - addPixmap( QPixmap( ":icons/22x22/actions/glabels-barcode.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/glabels-barcode.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-barcode.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-barcode.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-barcode.svg" ) ); } }; @@ -63,9 +63,9 @@ namespace glabels public: Box() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-box.svg" ) ); - addPixmap( QPixmap( ":icons/22x22/actions/glabels-box.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/glabels-box.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-box.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-box.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-box.svg" ) ); } }; @@ -75,9 +75,9 @@ namespace glabels public: Ellipse() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-ellipse.svg" ) ); - addPixmap( QPixmap( ":icons/22x22/actions/glabels-ellipse.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/glabels-ellipse.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-ellipse.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-ellipse.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-ellipse.svg" ) ); } }; @@ -87,9 +87,9 @@ namespace glabels public: Image() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-image.svg" ) ); - addPixmap( QPixmap( ":icons/22x22/actions/glabels-image.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/glabels-image.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-image.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-image.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-image.svg" ) ); } }; @@ -99,9 +99,9 @@ namespace glabels public: Line() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-line.svg" ) ); - addPixmap( QPixmap( ":icons/22x22/actions/glabels-line.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/glabels-line.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-line.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-line.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-line.svg" ) ); } }; @@ -111,9 +111,9 @@ namespace glabels public: Text() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-text.svg" ) ); - addPixmap( QPixmap( ":icons/22x22/actions/glabels-text.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/glabels-text.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-text.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-text.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-text.svg" ) ); } }; @@ -123,7 +123,7 @@ namespace glabels public: ObjectProperties() { - addPixmap( QPixmap( ":icons/24x24/actions/glabels-object-properties.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-object-properties.svg" ) ); } }; @@ -133,7 +133,7 @@ namespace glabels public: AlignLeft() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-align-left.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-align-left.svg" ) ); } }; @@ -143,7 +143,7 @@ namespace glabels public: AlignHCenter() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-align-hcenter.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-align-hcenter.svg" ) ); } }; @@ -153,7 +153,7 @@ namespace glabels public: AlignRight() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-align-right.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-align-right.svg" ) ); } }; @@ -163,7 +163,7 @@ namespace glabels public: AlignBottom() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-align-bottom.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-align-bottom.svg" ) ); } }; @@ -173,7 +173,7 @@ namespace glabels public: AlignVCenter() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-align-vcenter.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-align-vcenter.svg" ) ); } }; @@ -183,7 +183,7 @@ namespace glabels public: AlignTop() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-align-top.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-align-top.svg" ) ); } }; @@ -193,7 +193,7 @@ namespace glabels public: CenterHoriz() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-center-horiz.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-center-horiz.svg" ) ); } }; @@ -203,7 +203,7 @@ namespace glabels public: CenterVert() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-center-vert.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-center-vert.svg" ) ); } }; @@ -213,7 +213,7 @@ namespace glabels public: FlipHoriz() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-flip-horiz.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-flip-horiz.svg" ) ); } }; @@ -223,7 +223,7 @@ namespace glabels public: FlipVert() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-flip-vert.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-flip-vert.svg" ) ); } }; @@ -233,7 +233,7 @@ namespace glabels public: RotateLeft() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-rotate-left.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-rotate-left.svg" ) ); } }; @@ -243,7 +243,7 @@ namespace glabels public: RotateRight() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-rotate-right.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-rotate-right.svg" ) ); } }; @@ -253,7 +253,7 @@ namespace glabels public: OrderBottom() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-order-bottom.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-order-bottom.svg" ) ); } }; @@ -263,7 +263,7 @@ namespace glabels public: OrderTop() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-order-top.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-order-top.svg" ) ); } }; @@ -273,8 +273,8 @@ namespace glabels public: AlignTextLeft() { - addPixmap( QPixmap( ":icons/22x22/actions/glabels-align-text-left.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/glabels-align-text-left.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-align-text-left.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-align-text-left.svg" ) ); } }; @@ -284,8 +284,8 @@ namespace glabels public: AlignTextCenter() { - addPixmap( QPixmap( ":icons/22x22/actions/glabels-align-text-center.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/glabels-align-text-center.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-align-text-center.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-align-text-center.svg" ) ); } }; @@ -295,8 +295,8 @@ namespace glabels public: AlignTextRight() { - addPixmap( QPixmap( ":icons/22x22/actions/glabels-align-text-right.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/glabels-align-text-right.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-align-text-right.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-align-text-right.svg" ) ); } }; @@ -306,8 +306,8 @@ namespace glabels public: AlignTextBottom() { - addPixmap( QPixmap( ":icons/22x22/actions/glabels-valign-text-bottom.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/glabels-valign-text-bottom.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-valign-text-bottom.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-valign-text-bottom.svg" ) ); } }; @@ -317,8 +317,8 @@ namespace glabels public: AlignTextMiddle() { - addPixmap( QPixmap( ":icons/22x22/actions/glabels-valign-text-middle.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/glabels-valign-text-middle.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-valign-text-middle.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-valign-text-middle.svg" ) ); } }; @@ -328,8 +328,8 @@ namespace glabels public: AlignTextTop() { - addPixmap( QPixmap( ":icons/22x22/actions/glabels-valign-text-top.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/glabels-valign-text-top.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-valign-text-top.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-valign-text-top.svg" ) ); } }; @@ -339,11 +339,21 @@ namespace glabels public: Glabels() { - addFile( ":icons/16x16/apps/glabels.svg" ); - addFile( ":icons/22x22/apps/glabels.svg" ); - addFile( ":icons/32x32/apps/glabels.svg" ); - addFile( ":icons/48x48/apps/glabels.svg" ); - addFile( ":icons/scalable/apps/glabels.svg" ); + addFile( ":icons/apps/16x16/glabels.svg" ); + addFile( ":icons/apps/22x22/glabels.svg" ); + addFile( ":icons/apps/32x32/glabels.svg" ); + addFile( ":icons/apps/48x48/glabels.svg" ); + addFile( ":icons/apps/scalable/glabels.svg" ); + } + }; + + + class Edit : public QIcon + { + public: + Edit() + { + addPixmap( QPixmap( ":icons/flat/48x48/glabels-edit.svg" ) ); } }; @@ -353,9 +363,9 @@ namespace glabels public: EditCopy() { - addPixmap( QPixmap( ":icons/16x16/actions/edit-copy.svg" ) ); - addPixmap( QPixmap( ":icons/22x22/actions/edit-copy.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/edit-copy.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-edit-copy.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-edit-copy.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-edit-copy.svg" ) ); } }; @@ -365,9 +375,9 @@ namespace glabels public: EditCut() { - addPixmap( QPixmap( ":icons/16x16/actions/edit-cut.svg" ) ); - addPixmap( QPixmap( ":icons/22x22/actions/edit-cut.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/edit-cut.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-edit-cut.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-edit-cut.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-edit-cut.svg" ) ); } }; @@ -377,9 +387,9 @@ namespace glabels public: EditPaste() { - addPixmap( QPixmap( ":icons/16x16/actions/edit-paste.svg" ) ); - addPixmap( QPixmap( ":icons/22x22/actions/edit-paste.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/edit-paste.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-edit-paste.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-edit-paste.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-edit-paste.svg" ) ); } }; @@ -389,9 +399,10 @@ namespace glabels public: FileNew() { - addPixmap( QPixmap( ":icons/16x16/actions/file-new.svg" ) ); - addPixmap( QPixmap( ":icons/22x22/actions/file-new.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/file-new.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-file-new.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-file-new.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-file-new.svg" ) ); + addPixmap( QPixmap( ":icons/flat/32x32/glabels-file-new.svg" ) ); } }; @@ -401,9 +412,23 @@ namespace glabels public: FileOpen() { - addPixmap( QPixmap( ":icons/16x16/actions/file-open.svg" ) ); - addPixmap( QPixmap( ":icons/22x22/actions/file-open.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/file-open.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-file-open.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-file-open.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-file-open.svg" ) ); + addPixmap( QPixmap( ":icons/flat/32x32/glabels-file-open.svg" ) ); + } + }; + + + class FileRecent : public QIcon + { + public: + FileRecent() + { + addPixmap( QPixmap( ":icons/flat/16x16/glabels-file-recent.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-file-recent.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-file-recent.svg" ) ); + addPixmap( QPixmap( ":icons/flat/32x32/glabels-file-recent.svg" ) ); } }; @@ -413,7 +438,8 @@ namespace glabels public: FilePrint() { - addPixmap( QPixmap( ":icons/32x32/actions/print.svg" ) ); + addPixmap( QPixmap( ":icons/flat/32x32/glabels-print.svg" ) ); + addPixmap( QPixmap( ":icons/flat/48x48/glabels-print.svg" ) ); } }; @@ -423,9 +449,9 @@ namespace glabels public: FileSave() { - addPixmap( QPixmap( ":icons/16x16/actions/file-save.svg" ) ); - addPixmap( QPixmap( ":icons/22x22/actions/file-save.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/file-save.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-file-save.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-file-save.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-file-save.svg" ) ); } }; @@ -435,9 +461,39 @@ namespace glabels public: FileSaveAs() { - addPixmap( QPixmap( ":icons/16x16/actions/file-save-as.svg" ) ); - addPixmap( QPixmap( ":icons/22x22/actions/file-save-as.svg" ) ); - addPixmap( QPixmap( ":icons/24x24/actions/file-save-as.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-file-save-as.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-file-save-as.svg" ) ); + addPixmap( QPixmap( ":icons/flat/24x24/glabels-file-save-as.svg" ) ); + } + }; + + + class Merge : public QIcon + { + public: + Merge() + { + addPixmap( QPixmap( ":icons/flat/48x48/glabels-merge.svg" ) ); + } + }; + + + class Properties : public QIcon + { + public: + Properties() + { + addPixmap( QPixmap( ":icons/flat/48x48/glabels-properties.svg" ) ); + } + }; + + + class Variables : public QIcon + { + public: + Variables() + { + addPixmap( QPixmap( ":icons/flat/48x48/glabels-variables.svg" ) ); } }; @@ -447,8 +503,8 @@ namespace glabels public: ZoomBestFit() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-zoom-to-fit.svg" ) ); - addPixmap( QPixmap( ":icons/22x22/actions/glabels-zoom-to-fit.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-zoom-to-fit.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-zoom-to-fit.svg" ) ); } }; @@ -458,8 +514,8 @@ namespace glabels public: ZoomIn() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-zoom-in.svg" ) ); - addPixmap( QPixmap( ":icons/22x22/actions/glabels-zoom-in.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-zoom-in.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-zoom-in.svg" ) ); } }; @@ -469,8 +525,8 @@ namespace glabels public: ZoomOriginal() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-zoom-one-to-one.svg" ) ); - addPixmap( QPixmap( ":icons/22x22/actions/glabels-zoom-one-to-one.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-zoom-one-to-one.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-zoom-one-to-one.svg" ) ); } }; @@ -480,8 +536,8 @@ namespace glabels public: ZoomOut() { - addPixmap( QPixmap( ":icons/16x16/actions/glabels-zoom-out.svg" ) ); - addPixmap( QPixmap( ":icons/22x22/actions/glabels-zoom-out.svg" ) ); + addPixmap( QPixmap( ":icons/flat/16x16/glabels-zoom-out.svg" ) ); + addPixmap( QPixmap( ":icons/flat/22x22/glabels-zoom-out.svg" ) ); } }; diff --git a/glabels/LabelEditor.cpp b/glabels/LabelEditor.cpp index 574fe386..85ede544 100644 --- a/glabels/LabelEditor.cpp +++ b/glabels/LabelEditor.cpp @@ -469,6 +469,10 @@ namespace glabels mResizeObject = handle->owner(); mResizeHandle = handle; mResizeHonorAspect = event->modifiers() & Qt::ControlModifier; + if ( mResizeObject->lockAspectRatio() ) + { + mResizeHonorAspect = !mResizeHonorAspect; + } mState = ArrowResize; } @@ -657,6 +661,7 @@ namespace glabels break; case ArrowResize: + mUndoRedoModel->checkpoint( tr("Resize") ); handleResizeMotion( xWorld, yWorld ); break; @@ -1143,7 +1148,7 @@ namespace glabels foreach( model::Markup* markup, mModel->frame()->markups() ) { - painter->drawPath( markup->path() ); + painter->drawPath( markup->path( mModel->frame() ) ); } painter->restore(); @@ -1157,7 +1162,7 @@ namespace glabels void LabelEditor::drawObjectsLayer( QPainter* painter ) { - mModel->draw( painter ); + mModel->draw( painter, true, nullptr, nullptr ); } diff --git a/glabels/MainWindow.cpp b/glabels/MainWindow.cpp index beb402f4..55f23832 100644 --- a/glabels/MainWindow.cpp +++ b/glabels/MainWindow.cpp @@ -31,6 +31,7 @@ #include "PropertiesView.h" #include "StartupView.h" #include "UndoRedoModel.h" +#include "VariablesView.h" #include "model/Db.h" #include "model/Model.h" @@ -43,14 +44,27 @@ #include +namespace +{ + enum PageIndex + { + WELCOME_PAGE_INDEX = 0, + EDITOR_PAGE_INDEX = 1, + PROPERTIES_PAGE_INDEX = 2, + MERGE_PAGE_INDEX = 3, + VARIABLES_PAGE_INDEX = 4, + PRINT_PAGE_INDEX = 5, + }; +} + + namespace glabels { /// /// Constructor /// - MainWindow::MainWindow() - : mModel(nullptr) + MainWindow::MainWindow() : mModel(nullptr), mUndoRedoModel(nullptr) { setWindowIcon( Icons::Glabels() ); @@ -64,60 +78,99 @@ namespace glabels QWidget* editorPage = createEditorPage(); QWidget* propertiesPage = createPropertiesPage(); QWidget* mergePage = createMergePage(); + QWidget* variablesPage = createVariablesPage(); QWidget* printPage = createPrintPage(); // Table of contents widget - mContents = new QListWidget(); - mContents->setViewMode(QListView::ListMode); - mContents->setMovement(QListView::Static); - mContents->setSpacing(6); - + mContents = new QToolBar(); + mContents->setOrientation( Qt::Vertical ); + mContents->setIconSize( QSize(48,48) ); + mContents->setSizePolicy( QSizePolicy::MinimumExpanding, + QSizePolicy::Preferred ); + mContents->setStyleSheet( "* { background: #CCCCCC }" ); + + // Table of contents button group + auto group = new QButtonGroup( this ); + group->setExclusive( true ); + // Pages widget mPages = new QStackedWidget(); // Add "Welcome" page mPages->addWidget( welcomePage ); - mWelcomeButton = new QListWidgetItem(mContents); - mWelcomeButton->setText(tr("Welcome")); - mWelcomeButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - + mWelcomeButton = new QToolButton( this ); + mWelcomeButton->setIcon( Icons::Glabels() ); + mWelcomeButton->setText( tr("Welcome") ); + mWelcomeButton->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + mWelcomeButton->setCheckable( true ); + mWelcomeButton->setSizePolicy( QSizePolicy::MinimumExpanding, + QSizePolicy::Preferred ); + mWelcomeAction = mContents->addWidget( mWelcomeButton ); + group->addButton( mWelcomeButton ); + // Add "Editor" page mPages->addWidget( editorPage ); - mEditorButton = new QListWidgetItem(mContents); - mEditorButton->setText(tr("Edit")); - mEditorButton->setToolTip( tr("Select Edit mode") ); - mEditorButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + mEditorButton = new QToolButton( this ); + mEditorButton->setIcon( Icons::Edit() ); + mEditorButton->setText( tr("Edit") ); + mEditorButton->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + mEditorButton->setCheckable( true ); + mEditorButton->setSizePolicy( QSizePolicy::MinimumExpanding, + QSizePolicy::Preferred ); + mEditorAction = mContents->addWidget( mEditorButton ); + group->addButton( mEditorButton ); // Add "Properties" page mPages->addWidget( propertiesPage ); - mPropertiesButton = new QListWidgetItem(mContents); - mPropertiesButton->setText(tr("Properties")); - mPropertiesButton->setToolTip( tr("Select Properties mode") ); - mPropertiesButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + mPropertiesButton = new QToolButton( this ); + mPropertiesButton->setIcon( Icons::Properties() ); + mPropertiesButton->setText( tr("Properties") ); + mPropertiesButton->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + mPropertiesButton->setCheckable( true ); + mPropertiesButton->setSizePolicy( QSizePolicy::MinimumExpanding, + QSizePolicy::Preferred ); + mPropertiesAction = mContents->addWidget( mPropertiesButton ); + group->addButton( mPropertiesButton ); // Add "Merge" page mPages->addWidget( mergePage ); - mMergeButton = new QListWidgetItem(mContents); - mMergeButton->setText(tr("Merge")); - mMergeButton->setToolTip( tr("Select Merge mode") ); - mMergeButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); + mMergeButton = new QToolButton( this ); + mMergeButton->setIcon( Icons::Merge() ); + mMergeButton->setText( tr("Merge") ); + mMergeButton->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + mMergeButton->setCheckable( true ); + mMergeButton->setSizePolicy( QSizePolicy::MinimumExpanding, + QSizePolicy::Preferred ); + mMergeAction = mContents->addWidget( mMergeButton ); + group->addButton( mMergeButton ); + + // Add "Variables" page + mPages->addWidget( variablesPage ); + mVariablesButton = new QToolButton( this ); + mVariablesButton->setIcon( Icons::Variables() ); + mVariablesButton->setText( tr("Variables") ); + mVariablesButton->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + mVariablesButton->setCheckable( true ); + mVariablesButton->setSizePolicy( QSizePolicy::MinimumExpanding, + QSizePolicy::Preferred ); + mVariablesAction = mContents->addWidget( mVariablesButton ); + group->addButton( mVariablesButton ); // Add "Print" page mPages->addWidget( printPage ); - mPrintButton = new QListWidgetItem(mContents); - mPrintButton->setText(tr("Print")); - mPrintButton->setToolTip( tr("Select Print mode") ); - mPrintButton->setFlags(Qt::ItemIsSelectable | Qt::ItemIsEnabled); - - // Adjust width of list view based on its contents - mContents->setHorizontalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); - mContents->setVerticalScrollBarPolicy( Qt::ScrollBarAlwaysOff ); - mContents->setMinimumWidth( mContents->sizeHintForColumn(0) + 24 ); - mContents->setMaximumWidth( mContents->sizeHintForColumn(0) + 24 ); + mPrintButton = new QToolButton( this ); + mPrintButton->setIcon( Icons::FilePrint() ); + mPrintButton->setText( tr("Print") ); + mPrintButton->setToolButtonStyle( Qt::ToolButtonTextUnderIcon ); + mPrintButton->setCheckable( true ); + mPrintButton->setSizePolicy( QSizePolicy::MinimumExpanding, + QSizePolicy::Preferred ); + mPrintAction = mContents->addWidget( mPrintButton ); + group->addButton( mPrintButton ); // Set initial page selection - mWelcomeButton->setSelected( true ); - mPages->setCurrentIndex(mContents->row(mWelcomeButton)); + mWelcomeButton->setChecked( true ); + mPages->setCurrentIndex( WELCOME_PAGE_INDEX ); // Create central widget QWidget *centralWidget = new QWidget(); @@ -125,18 +178,22 @@ namespace glabels hLayout->setContentsMargins( 0, 0, 0, 0 ); hLayout->addWidget( mContents ); hLayout->addWidget( mPages ); + hLayout->setStretch( 0, 0 ); + hLayout->setStretch( 1, 1 ); centralWidget->setLayout( hLayout ); setCentralWidget( centralWidget ); - setDocVerbsEnabled( false ); - setPasteVerbsEnabled( false ); - setWelcomeMode( true ); + manageActions(); setTitle(); // Connect - connect( mContents, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), - this, SLOT(changePage(QListWidgetItem*,QListWidgetItem*))); + connect( mEditorButton, SIGNAL(toggled(bool)), this, SLOT(changePage(bool))); + connect( mPropertiesButton, SIGNAL(toggled(bool)), this, SLOT(changePage(bool))); + connect( mMergeButton, SIGNAL(toggled(bool)), this, SLOT(changePage(bool))); + connect( mVariablesButton, SIGNAL(toggled(bool)), this, SLOT(changePage(bool))); + connect( mPrintButton, SIGNAL(toggled(bool)), this, SLOT(changePage(bool))); connect( mLabelEditor, SIGNAL(zoomChanged()), this, SLOT(onZoomChanged()) ); + connect( model::Settings::instance(), SIGNAL(changed()), this, SLOT(onSettingsChanged()) ); connect( QApplication::clipboard(), SIGNAL(dataChanged()), this, SLOT(clipboardChanged()) ); #if 0 connect( mLabelEditor, SIGNAL(pointerMoved(double, double)), @@ -153,7 +210,16 @@ namespace glabels /// MainWindow::~MainWindow() { - // empty + if ( mUndoRedoModel ) + { + delete mUndoRedoModel; + } + if ( mModel ) + { + delete mModel->merge(); // Ownership of final Merge instance is ours + delete mModel->variables(); // Ownership of Variables instance is ours + delete mModel; + } } @@ -171,22 +237,20 @@ namespace glabels /// void MainWindow::setModel( model::Model* model ) { - mModel = model; + mModel = model; // Ownership passes to us mUndoRedoModel = new UndoRedoModel( mModel ); mPropertiesView->setModel( mModel, mUndoRedoModel ); mLabelEditor->setModel( mModel, mUndoRedoModel ); mObjectEditor->setModel( mModel, mUndoRedoModel ); - mMergeView->setModel( mModel , mUndoRedoModel ); + mMergeView->setModel( mModel, mUndoRedoModel ); + mVariablesView->setModel( mModel, mUndoRedoModel ); mPrintView->setModel( mModel ); - mContents->setCurrentItem( mEditorButton ); - mPages->setCurrentIndex(mContents->row(mEditorButton)); + mEditorButton->setChecked( true ); + mPages->setCurrentIndex( EDITOR_PAGE_INDEX ); - setDocVerbsEnabled( true ); - setSelectionVerbsEnabled( false ); - setMultiSelectionVerbsEnabled( false ); - setWelcomeMode( false ); + manageActions(); setTitle(); connect( mLabelEditor, SIGNAL(contextMenuActivate()), this, SLOT(onContextMenuActivate()) ); @@ -242,6 +306,14 @@ namespace glabels fileOpenAction->setStatusTip( tr("Open an existing gLabels project") ); connect( fileOpenAction, SIGNAL(triggered()), this, SLOT(fileOpen()) ); + for ( int i = 0; i < model::Settings::maxRecentFiles(); i++ ) + { + auto* action = new QAction( this ); + action->setVisible( false ); + fileRecentActionList.append( action ); + connect( action, SIGNAL(triggered()), this, SLOT(fileOpenRecent()) ); + } + fileSaveAction = new QAction( tr("&Save"), this ); fileSaveAction->setIcon( Icons::FileSave() ); fileSaveAction->setShortcut( QKeySequence::Save ); @@ -269,6 +341,11 @@ namespace glabels fileShowMergePageAction->setStatusTip( tr("Select project Merge mode") ); connect( fileShowMergePageAction, SIGNAL(triggered()), this, SLOT(fileShowMergePage()) ); + fileShowVariablesPageAction = new QAction( tr("&Variables") , this ); + fileShowVariablesPageAction->setShortcut( QKeySequence( Qt::CTRL + Qt::Key_4 ) ); + fileShowVariablesPageAction->setStatusTip( tr("Select project Variables mode") ); + connect( fileShowVariablesPageAction, SIGNAL(triggered()), this, SLOT(fileShowVariablesPage()) ); + fileShowPrintPageAction = new QAction( tr("&Print") , this ); fileShowPrintPageAction->setShortcut( QKeySequence::Print ); fileShowPrintPageAction->setStatusTip( tr("Select project Print mode") ); @@ -347,14 +424,14 @@ namespace glabels /* View actions */ - viewFileToolBarAction = new QAction( tr("File"), this ); + viewFileToolBarAction = new QAction( tr("Quick Access"), this ); viewFileToolBarAction->setCheckable( true ); - viewFileToolBarAction->setStatusTip( tr("Change visibility of file toolbar in current window") ); + viewFileToolBarAction->setStatusTip( tr("Change visibility of the \"Quick Access\" toolbar in current window") ); connect( viewFileToolBarAction, SIGNAL(toggled(bool)), this, SLOT(viewFileToolBar(bool)) ); viewEditorToolBarAction = new QAction( tr("Editor"), this ); viewEditorToolBarAction->setCheckable( true ); - viewEditorToolBarAction->setStatusTip( tr("Change visibility of editor toolbar in current window") ); + viewEditorToolBarAction->setStatusTip( tr("Change visibility of the \"Editor\" toolbar in current window") ); connect( viewEditorToolBarAction, SIGNAL(toggled(bool)), this, SLOT(viewEditorToolBar(bool)) ); @@ -499,12 +576,16 @@ namespace glabels /* Help actions */ - helpContentsAction = new QAction( tr("&Contents..."), this ); + helpContentsAction = new QAction( tr("&User Manual..."), this ); helpContentsAction->setIcon( QIcon::fromTheme( "help-contents" ) ); helpContentsAction->setShortcut( QKeySequence::HelpContents ); helpContentsAction->setStatusTip( tr("Open gLabels manual") ); connect( helpContentsAction, SIGNAL(triggered()), this, SLOT(helpContents()) ); + helpReportBugAction = new QAction( tr("&Report Bug..."), this ); + helpReportBugAction->setStatusTip( tr("Report a bug to the developers") ); + connect( helpReportBugAction, SIGNAL(triggered()), this, SLOT(helpReportBug()) ); + helpAboutAction = new QAction( tr("&About..."), this ); helpAboutAction->setIcon( QIcon::fromTheme( "help-about" ) ); helpAboutAction->setStatusTip( tr("About gLabels") ); @@ -542,12 +623,18 @@ namespace glabels fileMenu = menuBar()->addMenu( tr("&File") ); fileMenu->addAction( fileNewAction ); fileMenu->addAction( fileOpenAction ); + fileRecentMenu = fileMenu->addMenu( Icons::FileRecent(), tr("Open Recent") ); + for ( auto* action : fileRecentActionList ) + { + fileRecentMenu->addAction( action ); + } fileMenu->addAction( fileSaveAction ); fileMenu->addAction( fileSaveAsAction ); fileMenu->addSeparator(); fileMenu->addAction( fileShowEditorPageAction ); fileMenu->addAction( fileShowPropertiesPageAction ); fileMenu->addAction( fileShowMergePageAction ); + fileMenu->addAction( fileShowVariablesPageAction ); fileMenu->addAction( fileShowPrintPageAction ); fileMenu->addSeparator(); fileMenu->addAction( fileTemplateDesignerAction ); @@ -614,6 +701,7 @@ namespace glabels helpMenu = menuBar()->addMenu( tr("&Help") ); helpMenu->addAction( helpContentsAction ); + helpMenu->addAction( helpReportBugAction ); helpMenu->addAction( helpAboutAction ); contextMenu = new QMenu(); @@ -652,7 +740,8 @@ namespace glabels /// void MainWindow::createToolBars() { - fileToolBar = addToolBar( tr("&File") ); + fileToolBar = addToolBar( tr("Quick Access Toolbar") ); + connect( fileToolBar, SIGNAL(visibilityChanged(bool)), viewFileToolBarAction, SLOT(setChecked(bool)) ); fileToolBar->addAction( fileNewAction ); fileToolBar->addAction( fileOpenAction ); fileToolBar->addAction( fileSaveAction ); @@ -759,136 +848,174 @@ namespace glabels /// - /// Create Print Page - /// - QWidget* MainWindow::createPrintPage() - { - mPrintView = new PrintView(); - - return mPrintView; - } - - - /// - /// Set enabled state of TOC buttons based on Welcome mode + /// Create Variables Page /// - void MainWindow::setWelcomeMode( bool enabled ) + QWidget* MainWindow::createVariablesPage() { - mWelcomeButton->setHidden( !enabled ); - mPropertiesButton->setHidden( enabled ); - mEditorButton->setHidden( enabled ); - mMergeButton->setHidden( enabled ); - mPrintButton->setHidden( enabled ); - } - + mVariablesView = new VariablesView(); - /// - /// Set enabled state of actions associated with a document. - /// - void MainWindow::setDocVerbsEnabled( bool enabled ) - { - fileSaveAction->setEnabled( mModel && mModel->isModified() ); - fileSaveAsAction->setEnabled( mModel ); - editUndoAction->setEnabled( enabled && mUndoRedoModel->canUndo() ); - editRedoAction->setEnabled( enabled && mUndoRedoModel->canRedo() ); - editDeleteAction->setEnabled( enabled ); - editSelectAllAction->setEnabled( enabled ); - editUnSelectAllAction->setEnabled( enabled ); - viewZoomInAction->setEnabled( enabled ); - viewZoomOutAction->setEnabled( enabled ); - viewZoom1To1Action->setEnabled( enabled ); - viewZoomToFitAction->setEnabled( enabled ); - viewGridAction->setEnabled( enabled ); - viewMarkupAction->setEnabled( enabled ); - objectsArrowModeAction->setEnabled( enabled ); - objectsCreateMenu->setEnabled( enabled ); - objectsCreateTextAction->setEnabled( enabled ); - objectsCreateLineAction->setEnabled( enabled ); - objectsCreateBoxAction->setEnabled( enabled ); - objectsCreateEllipseAction->setEnabled( enabled ); - objectsCreateImageAction->setEnabled( enabled ); - objectsCreateBarcodeAction->setEnabled( enabled ); - objectsOrderMenu->setEnabled( enabled ); - objectsOrderRaiseAction->setEnabled( enabled ); - objectsOrderLowerAction->setEnabled( enabled ); - objectsXformMenu->setEnabled( enabled ); - objectsXformRotateLeftAction->setEnabled( enabled ); - objectsXformRotateRightAction->setEnabled( enabled ); - objectsXformFlipHorizAction->setEnabled( enabled ); - objectsXformFlipVertAction->setEnabled( enabled ); - objectsAlignMenu->setEnabled( enabled ); - objectsAlignLeftAction->setEnabled( enabled ); - objectsAlignRightAction->setEnabled( enabled ); - objectsAlignHCenterAction->setEnabled( enabled ); - objectsAlignTopAction->setEnabled( enabled ); - objectsAlignBottomAction->setEnabled( enabled ); - objectsAlignVCenterAction->setEnabled( enabled ); - objectsCenterMenu->setEnabled( enabled ); - objectsCenterHorizAction->setEnabled( enabled ); - objectsCenterVertAction->setEnabled( enabled ); + return mVariablesView; } /// - /// Set enabled state of actions associated with a document being modified since last save. + /// Create Print Page /// - void MainWindow::setDocModifiedVerbsEnabled( bool enabled ) + QWidget* MainWindow::createPrintPage() { - fileSaveAction->setEnabled( enabled ); - } - + mPrintView = new PrintView(); - /// - /// Set enabled state of actions associated with data being available on clipboard. - /// - void MainWindow::setPasteVerbsEnabled( bool enabled ) - { - editPasteAction->setEnabled( enabled ); - contextPasteAction->setEnabled( enabled ); + return mPrintView; } /// - /// Set enabled state of actions associated with a non-empty selection. + /// Manage enabled/visibility state of actions /// - void MainWindow::setSelectionVerbsEnabled( bool enabled ) + void MainWindow::manageActions() { - editCutAction->setEnabled( enabled ); - editCopyAction->setEnabled( enabled ); - editDeleteAction->setEnabled( enabled ); - editUnSelectAllAction->setEnabled( enabled ); - objectsOrderMenu->setEnabled( enabled ); - objectsOrderRaiseAction->setEnabled( enabled ); - objectsOrderLowerAction->setEnabled( enabled ); - objectsXformMenu->setEnabled( enabled ); - objectsXformRotateLeftAction->setEnabled( enabled ); - objectsXformRotateRightAction->setEnabled( enabled ); - objectsXformFlipHorizAction->setEnabled( enabled ); - objectsXformFlipVertAction->setEnabled( enabled ); - objectsCenterMenu->setEnabled( enabled ); - objectsCenterHorizAction->setEnabled( enabled ); - objectsCenterVertAction->setEnabled( enabled ); + // Do we have a model? + bool hasModel = mModel != nullptr; - contextOrderMenu->setEnabled( enabled ); - contextXformMenu->setEnabled( enabled ); - contextCenterMenu->setEnabled( enabled ); - } + // Which page is currently active? + bool isWelcomePage = mWelcomeButton->isChecked(); + bool isEditorPage = mEditorButton->isChecked(); + bool isPropertiesPage = mPropertiesButton->isChecked(); + bool isMergePage = mMergeButton->isChecked(); + bool isVariablesPage = mVariablesButton->isChecked(); + bool isPrintPage = mPrintButton->isChecked(); + // What is the current selection state? + bool hasSelection = hasModel && !mModel->isSelectionEmpty(); + bool hasMultiSelection = hasSelection && !mModel->isSelectionAtomic(); + bool canPaste = hasModel && mModel->canPaste(); + + // Toggle visibility of TOC buttons based on welcome mode + mWelcomeAction->setVisible( isWelcomePage ); + mEditorAction->setVisible( !isWelcomePage ); + mPropertiesAction->setVisible( !isWelcomePage ); + mMergeAction->setVisible( !isWelcomePage ); + mVariablesAction->setVisible( !isWelcomePage ); + mPrintAction->setVisible( !isWelcomePage ); - /// - /// Set enabled state of actions associated with a non-atomic selection. - /// - void MainWindow::setMultiSelectionVerbsEnabled( bool enabled ) - { - objectsAlignMenu->setEnabled( enabled ); - objectsAlignLeftAction->setEnabled( enabled ); - objectsAlignRightAction->setEnabled( enabled ); - objectsAlignHCenterAction->setEnabled( enabled ); - objectsAlignTopAction->setEnabled( enabled ); - objectsAlignBottomAction->setEnabled( enabled ); - objectsAlignVCenterAction->setEnabled( enabled ); + // Recent file actions + QStringList recentFileList = model::Settings::recentFileList(); + for ( int i = 0; i < recentFileList.size(); i++ ) + { + QString baseName = QFileInfo( recentFileList.at(i) ).completeBaseName(); + fileRecentActionList.at(i)->setText(baseName); + fileRecentActionList.at(i)->setData(recentFileList.at(i)); + fileRecentActionList.at(i)->setVisible( true ); + } + for ( int i = recentFileList.size(); i < model::Settings::maxRecentFiles(); i++ ) + { + fileRecentActionList.at(i)->setVisible( false ); + } - contextAlignMenu->setEnabled( enabled ); + // File actions + fileNewAction->setEnabled( true ); + fileOpenAction->setEnabled( true ); + fileRecentMenu->setEnabled( !recentFileList.isEmpty() ); + fileSaveAction->setEnabled( hasModel && mModel->isModified() ); + fileSaveAsAction->setEnabled( hasModel ); + fileShowEditorPageAction->setEnabled( !isWelcomePage && !isEditorPage ); + fileShowPropertiesPageAction->setEnabled( !isWelcomePage && !isPropertiesPage ); + fileShowMergePageAction->setEnabled( !isWelcomePage && !isMergePage ); + fileShowVariablesPageAction->setEnabled( !isWelcomePage && !isVariablesPage ); + fileShowPrintPageAction->setEnabled( !isWelcomePage && !isPrintPage ); + fileTemplateDesignerAction->setEnabled( true ); + fileCloseAction->setEnabled( true ); + fileExitAction->setEnabled( true ); + + // Edit actions + if ( hasModel ) + { + if ( mUndoRedoModel->canUndo() ) + { + editUndoAction->setEnabled( true ); + /* Translators: %1 is the action description to undo. */ + editUndoAction->setText( QString( tr("Undo %1") ).arg( mUndoRedoModel->undoDescription() ) ); + } + else + { + editUndoAction->setEnabled( false ); + editUndoAction->setText( tr("Undo") ); + } + if ( mUndoRedoModel->canRedo() ) + { + editRedoAction->setEnabled( true ); + /* Translators: %1 is the action description to redo. */ + editRedoAction->setText( QString( tr("Redo %1") ).arg( mUndoRedoModel->redoDescription() ) ); + } + else + { + editRedoAction->setEnabled( false ); + editRedoAction->setText( tr("Redo") ); + } + } + else + { + editUndoAction->setEnabled( false ); + editUndoAction->setText( tr("Undo") ); + editRedoAction->setEnabled( false ); + editRedoAction->setText( tr("Redo") ); + } + editCutAction->setEnabled( isEditorPage && hasSelection ); + editCopyAction->setEnabled( isEditorPage && hasSelection ); + editPasteAction->setEnabled( isEditorPage && canPaste ); + editDeleteAction->setEnabled( isEditorPage && hasSelection ); + editSelectAllAction->setEnabled( isEditorPage ); + editUnSelectAllAction->setEnabled( isEditorPage && hasSelection ); + editPreferencesAction->setEnabled( true ); + + // View actions + viewFileToolBarAction->setEnabled( true ); + viewEditorToolBarAction->setEnabled( true ); + viewGridAction->setEnabled( isEditorPage ); + viewMarkupAction->setEnabled( isEditorPage ); + viewZoomInAction->setEnabled( isEditorPage ); + viewZoomOutAction->setEnabled( isEditorPage ); + viewZoom1To1Action->setEnabled( isEditorPage ); + viewZoomToFitAction->setEnabled( isEditorPage ); + + // Object actions + objectsArrowModeAction->setEnabled( isEditorPage ); + objectsCreateMenu->setEnabled( isEditorPage ); + objectsCreateTextAction->setEnabled( isEditorPage ); + objectsCreateLineAction->setEnabled( isEditorPage ); + objectsCreateBoxAction->setEnabled( isEditorPage ); + objectsCreateEllipseAction->setEnabled( isEditorPage ); + objectsCreateImageAction->setEnabled( isEditorPage ); + objectsCreateBarcodeAction->setEnabled( isEditorPage ); + objectsOrderMenu->setEnabled( isEditorPage && hasSelection ); + objectsOrderRaiseAction->setEnabled( isEditorPage && hasSelection ); + objectsOrderLowerAction->setEnabled( isEditorPage && hasSelection ); + objectsXformMenu->setEnabled( isEditorPage && hasSelection ); + objectsXformRotateLeftAction->setEnabled( isEditorPage && hasSelection ); + objectsXformRotateRightAction->setEnabled( isEditorPage && hasSelection ); + objectsXformFlipHorizAction->setEnabled( isEditorPage && hasSelection ); + objectsXformFlipVertAction->setEnabled( isEditorPage && hasSelection ); + objectsAlignMenu->setEnabled( isEditorPage && hasMultiSelection ); + objectsAlignLeftAction->setEnabled( isEditorPage && hasMultiSelection ); + objectsAlignRightAction->setEnabled( isEditorPage && hasMultiSelection ); + objectsAlignHCenterAction->setEnabled( isEditorPage && hasMultiSelection ); + objectsAlignTopAction->setEnabled( isEditorPage && hasMultiSelection ); + objectsAlignBottomAction->setEnabled( isEditorPage && hasMultiSelection ); + objectsAlignVCenterAction->setEnabled( isEditorPage && hasMultiSelection ); + objectsCenterMenu->setEnabled( isEditorPage && hasSelection ); + objectsCenterHorizAction->setEnabled( isEditorPage && hasSelection ); + objectsCenterVertAction->setEnabled( isEditorPage && hasSelection ); + + // Help actions + helpContentsAction->setEnabled( true ); + helpReportBugAction->setEnabled( true ); + helpAboutAction->setEnabled( true ); + + // Special context actions + contextCutAction->setEnabled( isEditorPage && hasSelection ); + contextCopyAction->setEnabled( isEditorPage && hasSelection ); + contextPasteAction->setEnabled( isEditorPage && canPaste ); + contextDeleteAction->setEnabled( isEditorPage && hasSelection ); } @@ -1005,28 +1132,33 @@ namespace glabels /// /// Change page /// - void MainWindow::changePage(QListWidgetItem *current, QListWidgetItem *previous) + void MainWindow::changePage( bool checked ) { - if (!current) + if ( checked ) { - current = previous; - } - - int row = mContents->row(current); - - mPages->setCurrentIndex(row); - bool isEditorPage = ( row == mContents->row(mEditorButton) ); - - setDocVerbsEnabled( isEditorPage ); - setSelectionVerbsEnabled( isEditorPage && !mModel->isSelectionEmpty() ); - setMultiSelectionVerbsEnabled( isEditorPage && !mModel->isSelectionEmpty() && !mModel->isSelectionAtomic() ); - setPasteVerbsEnabled( isEditorPage && mModel->canPaste() ); + if ( mEditorButton->isChecked() ) + { + mPages->setCurrentIndex( EDITOR_PAGE_INDEX ); + } + else if ( mPropertiesButton->isChecked() ) + { + mPages->setCurrentIndex( PROPERTIES_PAGE_INDEX ); + } + else if ( mMergeButton->isChecked() ) + { + mPages->setCurrentIndex( MERGE_PAGE_INDEX ); + } + else if ( mVariablesButton->isChecked() ) + { + mPages->setCurrentIndex( VARIABLES_PAGE_INDEX ); + } + else if ( mPrintButton->isChecked() ) + { + mPages->setCurrentIndex( PRINT_PAGE_INDEX ); + } - bool isWelcome = ( current == mWelcomeButton ); - fileShowEditorPageAction->setEnabled( !isWelcome && (current != mEditorButton) ); - fileShowPropertiesPageAction->setEnabled( !isWelcome && (current != mPropertiesButton) ); - fileShowMergePageAction->setEnabled( !isWelcome && (current != mMergeButton) ); - fileShowPrintPageAction->setEnabled( !isWelcome && (current != mPrintButton) ); + manageActions(); + } } @@ -1035,7 +1167,7 @@ namespace glabels /// void MainWindow::clipboardChanged() { - setPasteVerbsEnabled( mModel->canPaste() ); + manageActions(); } @@ -1057,6 +1189,20 @@ namespace glabels } + /// + /// File->OpenRecent Action + /// + void MainWindow::fileOpenRecent() + { + QAction* action = qobject_cast( sender() ); + if ( action ) + { + QString fileName = action->data().toString(); + File::open( fileName, this ); + } + } + + /// /// File->Save Action /// @@ -1080,7 +1226,7 @@ namespace glabels /// void MainWindow::fileShowEditorPage() { - mContents->setCurrentItem( mEditorButton ); + mEditorButton->setChecked( true ); } @@ -1089,7 +1235,7 @@ namespace glabels /// void MainWindow::fileShowPropertiesPage() { - mContents->setCurrentItem( mPropertiesButton ); + mPropertiesButton->setChecked( true ); } @@ -1098,7 +1244,16 @@ namespace glabels /// void MainWindow::fileShowMergePage() { - mContents->setCurrentItem( mMergeButton ); + mMergeButton->setChecked( true ); + } + + + /// + /// File->Show Variables Page + /// + void MainWindow::fileShowVariablesPage() + { + mVariablesButton->setChecked( true ); } @@ -1107,7 +1262,7 @@ namespace glabels /// void MainWindow::fileShowPrintPage() { - mContents->setCurrentItem( mPrintButton ); + mPrintButton->setChecked( true ); } @@ -1514,6 +1669,15 @@ namespace glabels } + /// + /// Help->Report Bug Action + /// + void MainWindow::helpReportBug() + { + Help::displayReportBug( this ); + } + + /// /// Help->About Action /// @@ -1584,8 +1748,8 @@ namespace glabels /// void MainWindow::onModifiedChanged() { + manageActions(); setTitle(); - setDocModifiedVerbsEnabled( mModel->isModified() ); } @@ -1594,8 +1758,7 @@ namespace glabels /// void MainWindow::onSelectionChanged() { - setSelectionVerbsEnabled( !mModel->isSelectionEmpty() ); - setMultiSelectionVerbsEnabled( !mModel->isSelectionEmpty() && !mModel->isSelectionAtomic() ); + manageActions(); } @@ -1613,8 +1776,16 @@ namespace glabels /// void MainWindow::onUndoRedoChanged() { - editUndoAction->setEnabled( mUndoRedoModel->canUndo() ); - editRedoAction->setEnabled( mUndoRedoModel->canRedo() ); + manageActions(); + } + + + /// + /// Settings changed handler + /// + void MainWindow::onSettingsChanged() + { + manageActions(); } } // namespace glabels diff --git a/glabels/MainWindow.h b/glabels/MainWindow.h index 1c934b76..56e4fab5 100644 --- a/glabels/MainWindow.h +++ b/glabels/MainWindow.h @@ -33,6 +33,7 @@ #include #include #include +#include namespace glabels @@ -46,6 +47,7 @@ namespace glabels class PropertiesView; class StartupView; class UndoRedoModel; + class VariablesView; /// @@ -84,17 +86,19 @@ namespace glabels // Slots ///////////////////////////////////// private slots: - void changePage(QListWidgetItem *current, QListWidgetItem *previous); + void changePage( bool checked ); void clipboardChanged(); void fileNew(); void fileOpen(); + void fileOpenRecent(); void fileSave(); void fileSaveAs(); void fileShowEditorPage(); void fileShowPropertiesPage(); void fileShowMergePage(); + void fileShowVariablesPage(); void fileShowPrintPage(); void fileTemplateDesigner(); void fileClose(); @@ -142,6 +146,7 @@ namespace glabels void objectsCenterVert(); void helpContents(); + void helpReportBug(); void helpAbout(); void onContextMenuActivate(); @@ -156,6 +161,8 @@ namespace glabels void onLabelChanged(); void onUndoRedoChanged(); + void onSettingsChanged(); + ///////////////////////////////////// // Internal Private Methods @@ -167,17 +174,13 @@ namespace glabels void createStatusBar(); QWidget* createWelcomePage(); - QWidget* createPropertiesPage(); QWidget* createEditorPage(); + QWidget* createPropertiesPage(); QWidget* createMergePage(); + QWidget* createVariablesPage(); QWidget* createPrintPage(); - void setWelcomeMode( bool ); - void setDocVerbsEnabled( bool ); - void setDocModifiedVerbsEnabled( bool ); - void setPasteVerbsEnabled( bool ); - void setSelectionVerbsEnabled( bool ); - void setMultiSelectionVerbsEnabled( bool ); + void manageActions(); void setTitle(); @@ -185,13 +188,14 @@ namespace glabels void writeSettings(); bool isOkToClose(); - + ///////////////////////////////////// // Private Data ///////////////////////////////////// private: QMenu* fileMenu; + QMenu* fileRecentMenu; QMenu* editMenu; QMenu* viewMenu; QMenu* viewToolBarsMenu; @@ -216,20 +220,29 @@ namespace glabels model::Model* mModel; UndoRedoModel* mUndoRedoModel; - QListWidget* mContents; - QListWidgetItem* mWelcomeButton; - QListWidgetItem* mPropertiesButton; - QListWidgetItem* mEditorButton; - QListWidgetItem* mMergeButton; - QListWidgetItem* mPrintButton; + QToolBar* mContents; + QToolButton* mWelcomeButton; + QToolButton* mEditorButton; + QToolButton* mPropertiesButton; + QToolButton* mMergeButton; + QToolButton* mVariablesButton; + QToolButton* mPrintButton; + + QAction* mWelcomeAction; + QAction* mEditorAction; + QAction* mPropertiesAction; + QAction* mMergeAction; + QAction* mVariablesAction; + QAction* mPrintAction; QStackedWidget* mPages; StartupView* mWelcomeView; - PropertiesView* mPropertiesView; QScrollArea* mLabelEditorScrollArea; LabelEditor* mLabelEditor; ObjectEditor* mObjectEditor; + PropertiesView* mPropertiesView; MergeView* mMergeView; + VariablesView* mVariablesView; PrintView* mPrintView; QLabel* zoomInfoLabel; @@ -242,11 +255,14 @@ namespace glabels QAction* fileShowEditorPageAction; QAction* fileShowPropertiesPageAction; QAction* fileShowMergePageAction; + QAction* fileShowVariablesPageAction; QAction* fileShowPrintPageAction; QAction* fileTemplateDesignerAction; QAction* fileCloseAction; QAction* fileExitAction; + QList fileRecentActionList; + QAction* editUndoAction; QAction* editRedoAction; QAction* editCutAction; @@ -289,6 +305,7 @@ namespace glabels QAction* objectsCenterVertAction; QAction* helpContentsAction; + QAction* helpReportBugAction; QAction* helpAboutAction; QAction* contextCutAction; diff --git a/glabels/MergeView.cpp b/glabels/MergeView.cpp index 9ff31431..c9323246 100644 --- a/glabels/MergeView.cpp +++ b/glabels/MergeView.cpp @@ -22,6 +22,8 @@ #include "merge/Factory.h" +#include "model/FileUtil.h" + #include #include #include @@ -63,14 +65,7 @@ namespace glabels mUndoRedoModel = undoRedoModel; // Initialize CWD - if ( model->fileName().isEmpty() ) - { - mCwd = "."; - } - else - { - mCwd = QFileInfo( model->fileName() ).absolutePath(); - } + mCwd = mModel->dirPath(); onMergeChanged(); connect( mModel, SIGNAL(mergeChanged()), this, SLOT(onMergeChanged()) ); @@ -87,26 +82,22 @@ namespace glabels mOldFormatComboIndex = index; formatCombo->setCurrentIndex( index ); + QString fn; + switch ( merge::Factory::idToType( mModel->merge()->id() ) ) { case merge::Factory::NONE: case merge::Factory::FIXED: locationLabel->setEnabled( false ); - locationButton->setEnabled( false ); - locationButton->setText( "" ); + locationLineEdit->setText( "" ); + locationBrowseButton->setVisible( false ); break; case merge::Factory::FILE: locationLabel->setEnabled( true ); - locationButton->setEnabled( true ); - if ( mModel->merge()->source().isEmpty() ) - { - locationButton->setText( "Select file..." ); - } - else - { - locationButton->setText( mModel->merge()->source() ); - } + fn = model::FileUtil::makeRelativeIfInDir( mModel->dir(), mModel->merge()->source() ); + locationLineEdit->setText( fn ); + locationBrowseButton->setVisible( true ); break; default: @@ -135,7 +126,8 @@ namespace glabels /// void MergeView::onMergeSourceChanged() { - locationButton->setText( mModel->merge()->source() ); + QString fn = model::FileUtil::makeRelativeIfInDir( mModel->dir(), mModel->merge()->source() ); + locationLineEdit->setText( fn ); recordsTable->clear(); recordsTable->setColumnCount( 0 ); @@ -185,7 +177,7 @@ namespace glabels /// /// Location button clicked handler /// - void MergeView::onLocationButtonClicked() + void MergeView::onLocationBrowseButtonClicked() { QString fileName = QFileDialog::getOpenFileName( this, @@ -245,7 +237,7 @@ namespace glabels { recordsTable->setColumnCount( mKeys.size() + 1 ); // Include extra column - // First column = primay Key + // First column = primary Key auto* item = new QTableWidgetItem( mPrimaryKey ); item->setFlags( Qt::ItemIsEnabled ); recordsTable->setHorizontalHeaderItem( 0, item ); @@ -287,7 +279,7 @@ namespace glabels int iRow = 0; foreach ( merge::Record* record, records ) { - // First column for primay field + // First column for primary field auto* item = new QTableWidgetItem(); if ( record->contains( mPrimaryKey ) ) { diff --git a/glabels/MergeView.h b/glabels/MergeView.h index 87349945..e0ddead8 100644 --- a/glabels/MergeView.h +++ b/glabels/MergeView.h @@ -67,7 +67,7 @@ namespace glabels void onMergeSelectionChanged(); void onFormatComboActivated(); - void onLocationButtonClicked(); + void onLocationBrowseButtonClicked(); void onSelectAllButtonClicked(); void onUnselectAllButtonClicked(); void onCellChanged( int iRow, int iCol ); diff --git a/glabels/MiniPreviewPixmap.cpp b/glabels/MiniPreviewPixmap.cpp index 4486eedd..15a7d8e3 100644 --- a/glabels/MiniPreviewPixmap.cpp +++ b/glabels/MiniPreviewPixmap.cpp @@ -20,6 +20,8 @@ #include "MiniPreviewPixmap.h" + +#include "RollTemplatePath.h" #include "model/Template.h" @@ -63,16 +65,25 @@ namespace glabels painter.setBackgroundMode( Qt::TransparentMode ); painter.setRenderHint( QPainter::Antialiasing, true ); + // For "Roll" templates, allow extra room for tape width and continuation break lines + model::Distance drawWidth = tmplate->pageWidth(); + model::Distance drawHeight = tmplate->pageHeight(); + if ( tmplate->isRoll() ) + { + drawWidth = tmplate->rollWidth(); + drawHeight *= 1.2; + } + double w = width - 1; double h = height - 1; double scale; - if ( (w/tmplate->pageWidth().pt()) > (h/tmplate->pageHeight().pt()) ) + if ( (w/drawWidth.pt()) > (h/drawHeight.pt()) ) { - scale = h / tmplate->pageHeight().pt(); + scale = h / drawHeight.pt(); } else { - scale = w / tmplate->pageWidth().pt(); + scale = w / drawWidth.pt(); } painter.scale( scale, scale ); @@ -95,7 +106,15 @@ namespace glabels painter.setBrush( brush ); painter.setPen( pen ); - painter.drawRect( 0, 0, tmplate->pageWidth().pt(), tmplate->pageHeight().pt() ); + + if ( !tmplate->isRoll() ) + { + painter.drawRect( 0, 0, tmplate->pageWidth().pt(), tmplate->pageHeight().pt() ); + } + else + { + painter.drawPath( RollTemplatePath( tmplate ) ); + } painter.restore(); } diff --git a/glabels/ObjectEditor.cpp b/glabels/ObjectEditor.cpp index fd40e439..0ddefde4 100644 --- a/glabels/ObjectEditor.cpp +++ b/glabels/ObjectEditor.cpp @@ -30,12 +30,14 @@ #include "model/ModelImageObject.h" #include "model/ModelLineObject.h" #include "model/ModelTextObject.h" +#include "model/FileUtil.h" #include "model/Settings.h" #include "model/Size.h" #include "merge/Merge.h" #include +#include #include #include @@ -67,9 +69,9 @@ namespace glabels barcodeColorButton->init( tr("Default"), QColor(0,0,0,255), QColor(0,0,0,255) ); shadowColorButton->init( tr("Default"), QColor(0,0,0,255), QColor(0,0,0,255) ); - textInsertFieldCombo->setName( tr("Insert Field") ); - barcodeInsertFieldCombo->setName( tr("Insert Field") ); - imageFieldCombo->setName( tr("Key") ); + textInsertFieldButton->setText( tr("Insert substitution field") ); + barcodeInsertFieldButton->setText( tr("Insert substitution field") ); + imageFieldButton->setText( tr("Use substitution field") ); setEnabled( false ); hidePages(); @@ -93,11 +95,14 @@ namespace glabels this, SLOT(onSelectionChanged()) ); connect( mModel, SIGNAL(mergeSourceChanged()), - this, SLOT(onMergeSourceChanged()) ); + this, SLOT(onFieldsAvailableChanged()) ); + + connect( mModel, SIGNAL(variablesChanged()), + this, SLOT(onFieldsAvailableChanged()) ); onLabelSizeChanged(); onSelectionChanged(); - onMergeSourceChanged(); + onFieldsAvailableChanged(); } @@ -122,12 +127,12 @@ namespace glabels if ( filenameNode.isField() ) { - QString field = QString("${%1}").arg( filenameNode.data() ); - imageFilenameLineEdit->setText( field ); + imageFilenameLineEdit->setText( QString("${%1}").arg(filenameNode.data()) ); } else { - imageFilenameLineEdit->setText( filenameNode.data() ); + QString fn = model::FileUtil::makeRelativeIfInDir( mModel->dir(), filenameNode.data() ); + imageFilenameLineEdit->setText( fn ); } mBlocked = false; @@ -188,6 +193,7 @@ namespace glabels sizeWSpin->setValue( mObject->w().inUnits(mUnits) ); sizeHSpin->setValue( mObject->h().inUnits(mUnits) ); + sizeAspectCheck->setChecked( mObject->lockAspectRatio() ); model::Size originalSize = mObject->naturalSize(); QString originalSizeString = QString( "%1: %2 x %3 %4" ) @@ -358,7 +364,7 @@ namespace glabels if ( dynamic_cast(mObject) ) { - titleImageLabel->setPixmap( QPixmap(":icons/24x24/actions/glabels-box.svg") ); + titleImageLabel->setPixmap( QPixmap(":icons/flat/24x24/glabels-box.svg") ); titleLabel->setText( tr("Box object properties") ); notebook->addTab( lineFillPage, tr("line/fill") ); @@ -379,7 +385,7 @@ namespace glabels } else if ( dynamic_cast(mObject) ) { - titleImageLabel->setPixmap( QPixmap(":icons/24x24/actions/glabels-ellipse.svg") ); + titleImageLabel->setPixmap( QPixmap(":icons/flat/24x24/glabels-ellipse.svg") ); titleLabel->setText( tr("Ellipse object properties") ); notebook->addTab( lineFillPage, tr("line/fill") ); @@ -400,7 +406,7 @@ namespace glabels } else if ( dynamic_cast(mObject) ) { - titleImageLabel->setPixmap( QPixmap(":icons/24x24/actions/glabels-image.svg") ); + titleImageLabel->setPixmap( QPixmap(":icons/flat/24x24/glabels-image.svg") ); titleLabel->setText( tr("Image object properties") ); notebook->addTab( imagePage, tr("image") ); @@ -420,7 +426,7 @@ namespace glabels } else if ( dynamic_cast(mObject) ) { - titleImageLabel->setPixmap( QPixmap(":icons/24x24/actions/glabels-line.svg") ); + titleImageLabel->setPixmap( QPixmap(":icons/flat/24x24/glabels-line.svg") ); titleLabel->setText( tr("Line object properties") ); notebook->addTab( lineFillPage, tr("line/fill") ); @@ -441,7 +447,7 @@ namespace glabels } else if ( dynamic_cast(mObject) ) { - titleImageLabel->setPixmap( QPixmap(":icons/24x24/actions/glabels-text.svg") ); + titleImageLabel->setPixmap( QPixmap(":icons/flat/24x24/glabels-text.svg") ); titleLabel->setText( tr("Text object properties") ); notebook->addTab( textPage, tr("text") ); @@ -454,13 +460,14 @@ namespace glabels loadTextPage(); loadPositionPage(); + loadRectSizePage(); loadShadowPage(); setEnabled( true ); } else if ( dynamic_cast(mObject) ) { - titleImageLabel->setPixmap( QPixmap(":icons/24x24/actions/glabels-barcode.svg") ); + titleImageLabel->setPixmap( QPixmap(":icons/flat/24x24/glabels-barcode.svg") ); titleLabel->setText( tr("Barcode object properties") ); notebook->addTab( barcodePage, tr("barcode") ); @@ -472,6 +479,7 @@ namespace glabels loadBarcodePage(); loadPositionPage(); + loadRectSizePage(); setEnabled( true ); } @@ -488,7 +496,7 @@ namespace glabels { mObject = nullptr; - titleImageLabel->setPixmap( QPixmap(":icons/24x24/actions/glabels-object-properties.svg") ); + titleImageLabel->setPixmap( QPixmap(":icons/flat/24x24/glabels-object-properties.svg") ); titleLabel->setText( tr("Object properties") ); setEnabled( false ); } @@ -496,17 +504,19 @@ namespace glabels } - void ObjectEditor::onMergeSourceChanged() + void ObjectEditor::onFieldsAvailableChanged() { if ( !mBlocked ) { - QStringList keys = mModel->merge()->keys(); - lineColorButton->setKeys( keys ); - fillColorButton->setKeys( keys ); - textInsertFieldCombo->setKeys( keys ); - barcodeInsertFieldCombo->setKeys( keys ); - imageFieldCombo->setKeys( keys ); - shadowColorButton->setKeys( keys ); + lineColorButton->setKeys( mModel->merge(), mModel->variables() ); + fillColorButton->setKeys( mModel->merge(), mModel->variables() ); + textColorButton->setKeys( mModel->merge(), mModel->variables() ); + barcodeColorButton->setKeys( mModel->merge(), mModel->variables() ); + shadowColorButton->setKeys( mModel->merge(), mModel->variables() ); + + textInsertFieldButton->setKeys( mModel->merge(), mModel->variables() ); + barcodeInsertFieldButton->setKeys( mModel->merge(), mModel->variables() ); + imageFieldButton->setKeys( mModel->merge(), mModel->variables() ); } } @@ -617,8 +627,11 @@ namespace glabels void ObjectEditor::onImageKeySelected( QString key ) { - mUndoRedoModel->checkpoint( tr("Set image") ); - mObject->setFilenameNode( model::TextNode( true, key ) ); + if ( mObject ) + { + mUndoRedoModel->checkpoint( tr("Set image") ); + mObject->setFilenameNode( model::TextNode( true, key ) ); + } } @@ -628,7 +641,7 @@ namespace glabels { mBlocked = true; - mUndoRedoModel->checkpoint( tr("Move") ); + mUndoRedoModel->checkpoint( tr("Position") ); model::Distance x = model::Distance(posXSpin->value(), mUnits); model::Distance y = model::Distance(posYSpin->value(), mUnits); @@ -653,6 +666,7 @@ namespace glabels if ( sizeAspectCheck->isChecked() ) { + mObject->setLockAspectRatio( true ); if ( fabs(spinW - mObject->w()) > fabs(spinH - mObject->h()) ) { mObject->setWHonorAspect( spinW ); @@ -666,6 +680,7 @@ namespace glabels } else { + mObject->setLockAspectRatio( false ); mObject->setSize( spinW, spinH ); } @@ -739,6 +754,8 @@ namespace glabels { mBlocked = true; + mUndoRedoModel->checkpoint( tr("Barcode") ); + barcode::Style bcStyle = barcodeStyleButton->bcStyle(); barcodeShowTextCheck->setEnabled( bcStyle.textOptional() ); @@ -773,6 +790,8 @@ namespace glabels void ObjectEditor::onResetImageSize() { + mUndoRedoModel->checkpoint( tr("Reset") ); + mObject->setSize( mObject->naturalSize() ); } diff --git a/glabels/ObjectEditor.h b/glabels/ObjectEditor.h index 6e84687a..57b380f1 100644 --- a/glabels/ObjectEditor.h +++ b/glabels/ObjectEditor.h @@ -80,7 +80,7 @@ namespace glabels void onSettingsChanged(); void onLabelSizeChanged(); void onSelectionChanged(); - void onMergeSourceChanged(); + void onFieldsAvailableChanged(); void onObjectChanged(); void onObjectMoved(); void onObjectDestroyed(); diff --git a/glabels/Preview.cpp b/glabels/Preview.cpp index 259732a3..413132d7 100644 --- a/glabels/Preview.cpp +++ b/glabels/Preview.cpp @@ -21,6 +21,7 @@ #include "Preview.h" #include "PreviewOverlayItem.h" +#include "RollTemplatePath.h" #include #include @@ -35,7 +36,7 @@ namespace glabels // namespace { - const QColor paperColor( 255, 255, 255 ); + const QColor paperColor( 242, 242, 242 ); const QColor paperOutlineColor( 0, 0, 0 ); const double paperOutlineWidthPixels = 1; @@ -44,8 +45,12 @@ namespace glabels const double shadowRadiusPixels = 12; const QColor labelColor( 255, 255, 255 ); - const QColor labelOutlineColor( 215, 215, 215 ); + const QColor labelOutlineColor( 192, 192, 192 ); const double labelOutlineWidthPixels = 1; + + const QColor labelNumberColor( 192, 192, 255, 128 ); + const QString labelNumberFontFamily( "Sans" ); + const double labelNumberScale = 0.5; } @@ -85,52 +90,84 @@ namespace glabels { mModel = mRenderer->model(); - clearScene(); + mScene->clear(); if ( mModel != nullptr ) { + auto tmplate = mModel->tmplate(); + + // For "Roll" templates, allow extra room to draw continuation break lines. + model::Distance drawHeight = mModel->tmplate()->pageHeight(); + model::Distance drawOffset = 0; + if ( tmplate->isRoll() ) + { + drawHeight = 1.2 * tmplate->pageHeight(); + drawOffset = 0.1 * tmplate->pageHeight(); + } + // Set scene up with a 5% margin around paper - model::Distance x = -0.05 * mModel->tmplate()->pageWidth(); - model::Distance y = -0.05 * mModel->tmplate()->pageHeight(); - model::Distance w = 1.10 * mModel->tmplate()->pageWidth(); - model::Distance h = 1.10 * mModel->tmplate()->pageHeight(); + model::Distance x = -0.05 * tmplate->pageWidth(); + model::Distance y = -0.05 * drawHeight - drawOffset; + model::Distance w = 1.10 * tmplate->pageWidth(); + model::Distance h = 1.10 * drawHeight; mScene->setSceneRect( x.pt(), y.pt(), w.pt(), h.pt() ); fitInView( mScene->sceneRect(), Qt::KeepAspectRatio ); - drawPaper( mModel->tmplate()->pageWidth(), mModel->tmplate()->pageHeight() ); + drawPaper(); drawLabels(); drawPreviewOverlay(); + drawLabelNumberOverlay(); } } - - /// - /// Resize Event Handler - /// - void Preview::resizeEvent( QResizeEvent* event ) + void Preview::drawLabelNumberOverlaySingle(const model::Distance& x, const model::Distance& y, const QPainterPath& path, uint32_t labelInstance) { - fitInView( mScene->sceneRect(), Qt::KeepAspectRatio ); + QBrush brush( labelNumberColor ); + + model::Frame *frame = mModel->tmplate()->frames().first(); + + model::Distance w = frame->w(); + model::Distance h = frame->h(); + + model::Distance minWH = min( w, h ); + + auto labelText = QString::number(labelInstance); + QGraphicsSimpleTextItem *labelNumberItem = new QGraphicsSimpleTextItem( labelText ); + labelNumberItem->setBrush( brush ); + labelNumberItem->setFont( QFont( labelNumberFontFamily, minWH.pt()*labelNumberScale, QFont::Bold ) ); + labelNumberItem->setPos( (x+w/2).pt(), (y+h/2).pt() ); + QRectF rect = labelNumberItem->boundingRect(); + labelNumberItem->setPos(labelNumberItem->x() - (rect.width() / 2), labelNumberItem->y() - (rect.height() / 2)); + + mScene->addItem( labelNumberItem ); } + void Preview::drawLabelNumberOverlay() + { + model::Frame *frame = mModel->tmplate()->frames().first(); + auto i = 0; + + foreach (model::Point origin, frame->getOrigins() ) + { + i++; + drawLabelNumberOverlaySingle( origin.x(), origin.y(), frame->path(), i); + } + } /// - /// Clear View + /// Resize Event Handler /// - void Preview::clearScene() + void Preview::resizeEvent( QResizeEvent* event ) { - foreach ( QGraphicsItem *item, mScene->items() ) - { - mScene->removeItem( item ); - delete item; - } + fitInView( mScene->sceneRect(), Qt::KeepAspectRatio ); } /// /// Draw Paper /// - void Preview::drawPaper( const model::Distance& pw, const model::Distance& ph ) + void Preview::drawPaper() { auto *shadowEffect = new QGraphicsDropShadowEffect(); shadowEffect->setColor( shadowColor ); @@ -142,7 +179,16 @@ namespace glabels pen.setCosmetic( true ); pen.setWidthF( paperOutlineWidthPixels ); - auto *pageItem = new QGraphicsRectItem( 0, 0, pw.pt(), ph.pt() ); + QAbstractGraphicsShapeItem* pageItem; + auto tmplate = mModel->tmplate(); + if ( !tmplate->isRoll() ) + { + pageItem = new QGraphicsRectItem( 0, 0, tmplate->pageWidth().pt(), tmplate->pageHeight().pt() ); + } + else + { + pageItem = new QGraphicsPathItem( RollTemplatePath( tmplate ) ); + } pageItem->setBrush( brush ); pageItem->setPen( pen ); pageItem->setGraphicsEffect( shadowEffect ); @@ -170,9 +216,8 @@ namespace glabels /// void Preview::drawLabel( const model::Distance& x, const model::Distance& y, const QPainterPath& path ) { - QBrush brush( Qt::NoBrush ); + QBrush brush( labelColor ); QPen pen( labelOutlineColor ); - pen.setStyle( Qt::DotLine ); pen.setCosmetic( true ); pen.setWidthF( labelOutlineWidthPixels ); diff --git a/glabels/Preview.h b/glabels/Preview.h index 3ba79817..8d43de8d 100644 --- a/glabels/Preview.h +++ b/glabels/Preview.h @@ -68,13 +68,15 @@ namespace glabels // Internal Methods ///////////////////////////////// private: - void clearScene(); - void drawPaper( const model::Distance& pw, const model::Distance& ph ); + void drawPaper(); void drawLabels(); void drawLabel( const model::Distance& x, const model::Distance& y, const QPainterPath& path ); void drawPreviewOverlay(); + void drawLabelNumberOverlaySingle(const model::Distance& x, const model::Distance& y, const QPainterPath& path, uint32_t labelInstance); + void drawLabelNumberOverlay(); + ///////////////////////////////// // Private Data diff --git a/glabels/PrintView.cpp b/glabels/PrintView.cpp index 13384e9c..a2a3e435 100644 --- a/glabels/PrintView.cpp +++ b/glabels/PrintView.cpp @@ -40,6 +40,14 @@ namespace glabels preview->setRenderer( &mRenderer ); mPrinter = new QPrinter( QPrinter::HighResolution ); mPrinter->setColorMode( QPrinter::Color ); + + mPrintDialog = new QPrintDialog( mPrinter, this ); + mPrintDialog->setOption( QAbstractPrintDialog::PrintToFile, true ); + mPrintDialog->setOption( QAbstractPrintDialog::PrintSelection, false ); + mPrintDialog->setOption( QAbstractPrintDialog::PrintPageRange, false ); + mPrintDialog->setOption( QAbstractPrintDialog::PrintShowPageSize, true ); + mPrintDialog->setOption( QAbstractPrintDialog::PrintCollateCopies, false ); + mPrintDialog->setOption( QAbstractPrintDialog::PrintCurrentPage, false ); } @@ -62,7 +70,7 @@ namespace glabels connect( mModel, SIGNAL(changed()), this, SLOT(onModelChanged()) ); - onFormChanged(); + onModelChanged(); } @@ -71,7 +79,15 @@ namespace glabels /// void PrintView::onModelChanged() { - updateView(); + printRangeStartPositionSpin->setRange( 1, mModel->frame()->nLabels() ); + printRangeLastPositionSpin->setRange( 1, mModel->frame()->nLabels() ); + mergeStartPositionSpin->setRange( 1, mModel->frame()->nLabels() ); + + printRangeBox->setVisible( mModel->merge()->keys().empty() ); + mergeBox->setVisible( !mModel->merge()->keys().empty() ); + mergeOnlyOptions->setVisible( !mModel->merge()->keys().empty() ); + + onFormChanged(); } @@ -80,13 +96,28 @@ namespace glabels /// void PrintView::updateView() { - copiesStartSpin->setRange( 1, mModel->frame()->nLabels() ); - - copiesDescriptionLabel->setText( tr("(Will print a total of %1 items on %2 pages.)") - .arg(mRenderer.nItems()).arg(mRenderer.nPages()) ); + if ( mRenderer.nPages() == 1 ) + { + if ( mRenderer.nItems() == 1 ) + { + printDescriptionLabel->setText( tr("(Will print a total of 1 item on 1 page.)") ); + } + else + { + printDescriptionLabel->setText( tr("(Will print a total of %1 items on 1 page.)") + .arg(mRenderer.nItems()) ); + } + } + else + { + printDescriptionLabel->setText( tr("(Will print a total of %1 items on %2 pages.)") + .arg(mRenderer.nItems()).arg(mRenderer.nPages()) ); + } pageSpin->setRange( 1, mRenderer.nPages() ); nPagesLabel->setText( QString::number( mRenderer.nPages() ) ); + + mRenderer.setIPage( pageSpin->value() - 1 ); // Update preview } @@ -99,15 +130,64 @@ namespace glabels { mBlocked = true; - mRenderer.setNCopies( copiesSpin->value() ); - mRenderer.setStartLabel( copiesStartSpin->value() - 1 ); + if ( mModel->merge()->keys().empty() ) + { + // Simple project (no merge) + if ( printRangePagesRadio->isChecked() ) + { + // Print full sheets of labels + int nItemsPerPage = mModel->frame()->nLabels(); + + printRangePagesSpin->setEnabled( true ); + + printRangeStartPositionSpin->setEnabled( false ); + printRangeLastPositionSpin->setEnabled( false ); + + printRangeStartPositionSpin->setMaximum( nItemsPerPage ); + printRangeLastPositionSpin->setMinimum( 1 ); + + printRangeStartPositionSpin->setValue( 1 ); + printRangeLastPositionSpin->setValue( nItemsPerPage ); + + mRenderer.setNCopies( printRangePagesSpin->value()*nItemsPerPage ); + mRenderer.setStartItem( 0 ); + } + else + { + // Print a partial sheet of labels + int iStart = printRangeStartPositionSpin->value(); + int iLast = printRangeLastPositionSpin->value(); + + printRangePagesSpin->setEnabled( false ); + + printRangeStartPositionSpin->setEnabled( true ); + printRangeLastPositionSpin->setEnabled( true ); + + printRangeStartPositionSpin->setMaximum( iLast ); + printRangeLastPositionSpin->setMinimum( iStart ); + + mRenderer.setNCopies( iLast - iStart + 1 ); + mRenderer.setStartItem( iStart - 1 ); + } + } + else + { + // Project with merge + bool isMultipleCopies = mergeCopiesSpin->value() > 1; + + mergeCollateCombo->setEnabled ( isMultipleCopies ); + mergeGroupCombo->setEnabled ( isMultipleCopies ); + + mRenderer.setNCopies( mergeCopiesSpin->value() ); + mRenderer.setIsCollated( mergeCollateCombo->currentIndex() == 1 ); + mRenderer.setAreGroupsContiguous( mergeGroupCombo->currentIndex() == 0 ); + mRenderer.setStartItem( mergeStartPositionSpin->value() - 1 ); + } mRenderer.setPrintOutlines( printOutlinesCheck->isChecked() ); mRenderer.setPrintCropMarks( printCropMarksCheck->isChecked() ); mRenderer.setPrintReverse( printReverseCheck->isChecked() ); - mRenderer.setIPage( pageSpin->value() - 1 ); - updateView(); mBlocked = false; @@ -120,16 +200,7 @@ namespace glabels /// void PrintView::onPrintButtonClicked() { - QPrintDialog printDialog( mPrinter, this ); - - printDialog.setOption( QAbstractPrintDialog::PrintToFile, true ); - printDialog.setOption( QAbstractPrintDialog::PrintSelection, false ); - printDialog.setOption( QAbstractPrintDialog::PrintPageRange, false ); - printDialog.setOption( QAbstractPrintDialog::PrintShowPageSize, true ); - printDialog.setOption( QAbstractPrintDialog::PrintCollateCopies, false ); - printDialog.setOption( QAbstractPrintDialog::PrintCurrentPage, false ); - - if ( printDialog.exec() == QDialog::Accepted ) + if ( mPrintDialog->exec() == QDialog::Accepted ) { mRenderer.print( mPrinter ); } diff --git a/glabels/PrintView.h b/glabels/PrintView.h index 72408449..1902e613 100644 --- a/glabels/PrintView.h +++ b/glabels/PrintView.h @@ -28,6 +28,7 @@ #include "model/PageRenderer.h" #include +#include namespace glabels @@ -73,7 +74,9 @@ namespace glabels QPrinter* mPrinter; model::PageRenderer mRenderer; - bool mBlocked; + QPrintDialog* mPrintDialog; + + bool mBlocked; }; diff --git a/glabels/PropertiesView.cpp b/glabels/PropertiesView.cpp index 63eaff2a..83897d42 100644 --- a/glabels/PropertiesView.cpp +++ b/glabels/PropertiesView.cpp @@ -24,6 +24,7 @@ #include "UndoRedoModel.h" #include "model/Db.h" +#include "model/FrameContinuous.h" #include "model/Settings.h" #include @@ -171,6 +172,38 @@ namespace glabels orientationCombo->setCurrentIndex( isRotated ? 0 : 1 ); } mOldOrientationIndex = orientationCombo->currentIndex(); + + if ( auto* continuousFrame = dynamic_cast( frame ) ) + { + if (!mInLengthSpinChanged) + { + const QSignalBlocker blocker( lengthSpin ); + + lengthSpin->setSuffix( " " + mUnits.toTrName() ); + lengthSpin->setDecimals( mUnits.resolutionDigits() ); + lengthSpin->setSingleStep( mUnits.resolution() ); + lengthSpin->setMinimum( continuousFrame->hMin().inUnits( mUnits ) ); + lengthSpin->setMaximum( continuousFrame->hMax().inUnits( mUnits ) ); + lengthSpin->setValue( continuousFrame->h().inUnits( mUnits ) ); + } + + parametersGroupBox->setVisible( true ); + } + else + { + parametersGroupBox->setVisible( false ); + } + } + + + /// + /// Length spin changed handler + /// + void PropertiesView::onLengthSpinChanged() + { + mInLengthSpinChanged = true; + mModel->setH( model::Distance( lengthSpin->value(), mUnits ) ); + mInLengthSpinChanged = false; } diff --git a/glabels/PropertiesView.h b/glabels/PropertiesView.h index e2beaf95..798d5ee0 100644 --- a/glabels/PropertiesView.h +++ b/glabels/PropertiesView.h @@ -63,6 +63,7 @@ namespace glabels private slots: void onSettingsChanged(); void onLabelSizeChanged(); + void onLengthSpinChanged(); void onOrientationActivated(); void onChangeProductButtonClicked(); @@ -71,6 +72,8 @@ namespace glabels // Private Data ///////////////////////////////// private: + bool mInLengthSpinChanged{ false }; + model::Model* mModel; UndoRedoModel* mUndoRedoModel; model::Units mUnits; diff --git a/glabels/ReportBugDialog.cpp b/glabels/ReportBugDialog.cpp new file mode 100644 index 00000000..1c418c9b --- /dev/null +++ b/glabels/ReportBugDialog.cpp @@ -0,0 +1,103 @@ +/* ReportBugDialog.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "ReportBugDialog.h" + +#include "model/Version.h" + +#include +#include +#include +#include +#include +#include + + +namespace glabels +{ + + /// + /// Constructor + /// + ReportBugDialog::ReportBugDialog( QWidget *parent ) + : QDialog(parent) + { + setupUi( this ); + + QString title = tr("How to Report a Bug"); + titleLabel->setText( QString( "%1" ).arg( title ) ); + + QString directionsP1 = + tr( "To submit a bug report, click on the button below. This will open a " + "web browser to the gLabels github issue tracking page." ); + + p1Label->setText( QString( "

%1

" ).arg( directionsP1 ) ); + + QString directionsP2 = + tr( "Before submitting a report, look through the existing issues for similar " + "or related bugs. If the issue has already been reported, please consider " + "contributing to its report instead. Otherwise, create a new issue report. " + "Please paste the following information into the issue description." ); + + p2Label->setText( QString( "

%1

" ).arg( directionsP2 ) ); + + infoText->append( "> GLABELS" ); + infoText->append( "> Version: " + model::Version::STRING ); + infoText->append( "> " ); + + infoText->append( "> SYSTEM INFO" ); + infoText->append( "> OS: " + QSysInfo::prettyProductName() ); + infoText->append( "> Kernel: " + QSysInfo::kernelType() + " " + QSysInfo::kernelVersion() ); + infoText->append( "> Build CPU Architecture: " + QSysInfo::buildCpuArchitecture() ); + infoText->append( "> Current CPU Architecture: " + QSysInfo::currentCpuArchitecture() ); + infoText->append( "> " ); + + infoText->append( "> LOCALE" ); + infoText->append( "> Name: " + QLocale::system().name() ); + + QString directionsP3 = + tr( "Be sure to include a detailed description of the problem and how to " + "recreate it. Attach any screenshots and/or example glabels project " + "files that may illustrate the problem." ); + + p3Label->setText( QString( "

%1

" ).arg( directionsP3 ) ); + + } + + + /// + /// "Copy" Button Clicked Slot + /// + void ReportBugDialog::onCopyButtonClicked() + { + infoText->selectAll(); + infoText->copy(); + } + + + /// + /// "Website" Button Clicked Slot + /// + void ReportBugDialog::onWebsiteButtonClicked() + { + QDesktopServices::openUrl( QUrl(model::Version::BUG_WEBSITE) ); + } + +} // namespace glabels diff --git a/glabels/ReportBugDialog.h b/glabels/ReportBugDialog.h new file mode 100644 index 00000000..b0c51efc --- /dev/null +++ b/glabels/ReportBugDialog.h @@ -0,0 +1,58 @@ +/* ReportBugDialog.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#ifndef ReportBugDialog_h +#define ReportBugDialog_h + + +#include "ui_ReportBugDialog.h" + + +namespace glabels +{ + + /// + /// "Report Bug" Dialog Widget + /// + class ReportBugDialog : public QDialog, public Ui_ReportBugDialog + { + Q_OBJECT + + + ///////////////////////////////// + // Life Cycle + ///////////////////////////////// + public: + ReportBugDialog( QWidget *parent = nullptr ); + + + ///////////////////////////////// + // Slots + ///////////////////////////////// + private slots: + void onCopyButtonClicked(); + void onWebsiteButtonClicked(); + + }; + +} + + +#endif // ReportBugDialog_h diff --git a/glabels/RollTemplatePath.cpp b/glabels/RollTemplatePath.cpp new file mode 100644 index 00000000..90d7bab8 --- /dev/null +++ b/glabels/RollTemplatePath.cpp @@ -0,0 +1,71 @@ +/* RollTemplatePath.cpp + * + * Copyright (C) 2014 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "RollTemplatePath.h" + +#include + + +namespace glabels +{ + + /// + /// Constructor + /// + RollTemplatePath::RollTemplatePath( const model::Template* tmplate ) + { + if ( !tmplate->isRoll() ) + { + qWarning() << "Not a \"Roll\" template type."; + } + + model::Distance x0 = (tmplate->pageWidth() - tmplate->rollWidth()) / 2.0; + model::Distance w = tmplate->rollWidth(); + model::Distance h = tmplate->pageHeight(); + model::Distance c = 0.07*h; + model::Distance b = 0.03*h; + + const int nx = 18; + + // Upper break line + moveTo( x0.pt(), -c.pt() ); + for ( int ix = 1; ix <= nx; ix++ ) + { + model::Distance x = ix * (w/double(nx)) + x0; + double a = ix * (2*M_PI/double(nx)); + + lineTo( x.pt(), b.pt()*sin(a) - c.pt() ); + } + + // Lower break line + lineTo( (x0+w).pt(), (h+c).pt() ); + for ( int ix = nx-1; ix >= 0; ix-- ) + { + model::Distance x = ix * (w/double(nx)) + x0; + double a = ix * (2*M_PI/double(nx)); + + lineTo( x.pt(), b.pt()*sin(a) + h.pt() + c.pt() ); + } + + // Close path + closeSubpath(); + } + +} // namespace glabels diff --git a/glabels/RollTemplatePath.h b/glabels/RollTemplatePath.h new file mode 100644 index 00000000..194f733a --- /dev/null +++ b/glabels/RollTemplatePath.h @@ -0,0 +1,49 @@ +/* RollTemplatePath.h + * + * Copyright (C) 2018 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#ifndef RollTemplatePath_h +#define RollTemplatePath_h + + +#include "model/Template.h" +#include + + +namespace glabels +{ + + /// + /// Painter path for "Roll" templates + /// + class RollTemplatePath : public QPainterPath + { + + ///////////////////////////////// + // Life Cycle + ///////////////////////////////// + public: + RollTemplatePath( const model::Template* tmplate ); + + }; + +} + + +#endif // RollTemplatePath_h diff --git a/glabels/SimplePreview.cpp b/glabels/SimplePreview.cpp index b19bde85..00b26e7b 100644 --- a/glabels/SimplePreview.cpp +++ b/glabels/SimplePreview.cpp @@ -20,6 +20,9 @@ #include "SimplePreview.h" +#include "RollTemplatePath.h" + +#include #include #include #include @@ -33,7 +36,7 @@ namespace glabels // namespace { - const QColor paperColor( 255, 255, 255 ); + const QColor paperColor( 242, 242, 242 ); const QColor paperOutlineColor( 0, 0, 0 ); const double paperOutlineWidthPixels = 1; @@ -105,43 +108,39 @@ namespace glabels /// void SimplePreview::update() { - clearScene(); + mScene->clear(); if ( mTmplate != nullptr ) { + // For "Roll" templates, allow extra room to draw continuation break lines. + model::Distance drawHeight = mTmplate->pageHeight(); + model::Distance drawOffset = 0; + if ( mTmplate->isRoll() ) + { + drawHeight = 1.2 * mTmplate->pageHeight(); + drawOffset = 0.1 * mTmplate->pageHeight(); + } + // Set scene up with a 5% margin around paper model::Distance x = -0.05 * mTmplate->pageWidth(); - model::Distance y = -0.05 * mTmplate->pageHeight(); + model::Distance y = -0.05 * drawHeight - drawOffset; model::Distance w = 1.10 * mTmplate->pageWidth(); - model::Distance h = 1.10 * mTmplate->pageHeight(); + model::Distance h = 1.10 * drawHeight; mScene->setSceneRect( x.pt(), y.pt(), w.pt(), h.pt() ); fitInView( mScene->sceneRect(), Qt::KeepAspectRatio ); - drawPaper( mTmplate->pageWidth(), mTmplate->pageHeight() ); + drawPaper(); drawLabels(); drawArrow(); } } - /// - /// Clear View - /// - void SimplePreview::clearScene() - { - foreach ( QGraphicsItem *item, mScene->items() ) - { - mScene->removeItem( item ); - delete item; - } - } - - /// /// Draw Paper /// - void SimplePreview::drawPaper( const model::Distance& pw, const model::Distance& ph ) + void SimplePreview::drawPaper() { auto *shadowEffect = new QGraphicsDropShadowEffect(); shadowEffect->setColor( shadowColor ); @@ -153,7 +152,15 @@ namespace glabels pen.setCosmetic( true ); pen.setWidthF( paperOutlineWidthPixels ); - auto *pageItem = new QGraphicsRectItem( 0, 0, pw.pt(), ph.pt() ); + QAbstractGraphicsShapeItem* pageItem; + if ( !mTmplate->isRoll() ) + { + pageItem = new QGraphicsRectItem( 0, 0, mTmplate->pageWidth().pt(), mTmplate->pageHeight().pt() ); + } + else + { + pageItem = new QGraphicsPathItem( RollTemplatePath( mTmplate ) ); + } pageItem->setBrush( brush ); pageItem->setPen( pen ); pageItem->setGraphicsEffect( shadowEffect ); diff --git a/glabels/SimplePreview.h b/glabels/SimplePreview.h index 3148ceeb..7fdbae19 100644 --- a/glabels/SimplePreview.h +++ b/glabels/SimplePreview.h @@ -67,8 +67,7 @@ namespace glabels ///////////////////////////////// private: void update(); - void clearScene(); - void drawPaper( const model::Distance& pw, const model::Distance& ph ); + void drawPaper(); void drawLabels(); void drawLabel( const model::Distance& x, const model::Distance& y, const QPainterPath& path ); void drawArrow(); diff --git a/glabels/StartupView.cpp b/glabels/StartupView.cpp index 1dbae82b..eeedf63d 100644 --- a/glabels/StartupView.cpp +++ b/glabels/StartupView.cpp @@ -23,6 +23,11 @@ #include "File.h" #include "MainWindow.h" +#include "model/Settings.h" + +#include +#include +#include #include @@ -39,6 +44,20 @@ namespace glabels QString titleImage = ":images/glabels-label-designer.png"; titleLabel->setPixmap( QPixmap( titleImage ) ); + + recentProjectButton->setEnabled( model::Settings::recentFileList().size() > 0 ); + + auto* recentMenu = new QMenu(); + for ( auto& filename : model::Settings::recentFileList() ) + { + QString basename = QFileInfo( filename ).completeBaseName(); + auto* action = new QAction( basename, this ); + action->setData( filename ); + connect( action, SIGNAL(triggered()), this, SLOT(onOpenRecentAction()) ); + recentMenu->addAction( action ); + } + recentMenu->setMinimumWidth( recentProjectButton->minimumWidth() ); + recentProjectButton->setMenu( recentMenu ); } @@ -59,4 +78,17 @@ namespace glabels File::open( mWindow ); } + + /// + /// "Open Recent" Action Activated Slot + /// + void StartupView::onOpenRecentAction() + { + QAction* action = qobject_cast( sender() ); + if ( action ) + { + File::open( action->data().toString(), mWindow ); + } + } + } // namespace glabels diff --git a/glabels/StartupView.h b/glabels/StartupView.h index 164197f3..670eb49f 100644 --- a/glabels/StartupView.h +++ b/glabels/StartupView.h @@ -53,6 +53,7 @@ namespace glabels private slots: void onNewProjectButtonClicked(); void onOpenProjectButtonClicked(); + void onOpenRecentAction(); ///////////////////////////////// diff --git a/glabels/TemplateDesigner.cpp b/glabels/TemplateDesigner.cpp index f6b8fe60..b4529aa4 100644 --- a/glabels/TemplateDesigner.cpp +++ b/glabels/TemplateDesigner.cpp @@ -24,7 +24,9 @@ #include "model/Db.h" #include "model/Distance.h" #include "model/FrameCd.h" +#include "model/FrameContinuous.h" #include "model/FrameEllipse.h" +#include "model/FramePath.h" #include "model/FrameRect.h" #include "model/FrameRound.h" #include "model/Markup.h" @@ -60,6 +62,8 @@ namespace glabels RoundPageId, EllipsePageId, CdPageId, + PathPageId, + ContinuousPageId, NLayoutsPageId, OneLayoutPageId, TwoLayoutPageId, @@ -109,23 +113,25 @@ namespace glabels : mIsBasedOnCopy(false), QWizard(parent) { setWindowTitle( tr("Product Template Designer") ); - setPixmap( QWizard::LogoPixmap, QPixmap( ":icons/48x48/apps/glabels.svg" ) ); + setPixmap( QWizard::LogoPixmap, QPixmap( ":icons/apps/48x48/glabels.svg" ) ); setWizardStyle( QWizard::ModernStyle ); setOption( QWizard::IndependentPages, false ); setOption( QWizard::NoBackButtonOnStartPage, true ); - setPage( IntroPageId, new TemplateDesignerIntroPage() ); - setPage( NamePageId, new TemplateDesignerNamePage() ); - setPage( PageSizePageId, new TemplateDesignerPageSizePage() ); - setPage( ShapePageId, new TemplateDesignerShapePage() ); - setPage( RectPageId, new TemplateDesignerRectPage() ); - setPage( RoundPageId, new TemplateDesignerRoundPage() ); - setPage( EllipsePageId, new TemplateDesignerEllipsePage() ); - setPage( CdPageId, new TemplateDesignerCdPage() ); - setPage( NLayoutsPageId, new TemplateDesignerNLayoutsPage() ); - setPage( OneLayoutPageId, new TemplateDesignerOneLayoutPage() ); - setPage( TwoLayoutPageId, new TemplateDesignerTwoLayoutPage() ); - setPage( ApplyPageId, new TemplateDesignerApplyPage() ); + setPage( IntroPageId, new TemplateDesignerIntroPage() ); + setPage( NamePageId, new TemplateDesignerNamePage() ); + setPage( PageSizePageId, new TemplateDesignerPageSizePage() ); + setPage( ShapePageId, new TemplateDesignerShapePage() ); + setPage( RectPageId, new TemplateDesignerRectPage() ); + setPage( RoundPageId, new TemplateDesignerRoundPage() ); + setPage( EllipsePageId, new TemplateDesignerEllipsePage() ); + setPage( CdPageId, new TemplateDesignerCdPage() ); + setPage( PathPageId, new TemplateDesignerPathPage() ); + setPage( ContinuousPageId, new TemplateDesignerContinuousPage() ); + setPage( NLayoutsPageId, new TemplateDesignerNLayoutsPage() ); + setPage( OneLayoutPageId, new TemplateDesignerOneLayoutPage() ); + setPage( TwoLayoutPageId, new TemplateDesignerTwoLayoutPage() ); + setPage( ApplyPageId, new TemplateDesignerApplyPage() ); } @@ -136,16 +142,27 @@ namespace glabels { switch (currentId()) { - + case IntroPageId: - return NamePageId; - + if ( mIsTemplatePathBased ) + { + return PathPageId; + } + else if ( mIsTemplateContinuousBased ) + { + return ContinuousPageId; + } + else + { + return NamePageId; + } + case NamePageId: return PageSizePageId; - + case PageSizePageId: return ShapePageId; - + case ShapePageId: if ( field( "shape.rect" ).toBool() ) { @@ -163,19 +180,53 @@ namespace glabels { return CdPageId; } - + case RectPageId: - return NLayoutsPageId; - + if ( field( "pageSize.pageSize" ) != tr("Roll") ) + { + return NLayoutsPageId; + } + else + { + return OneLayoutPageId; + } + case RoundPageId: - return NLayoutsPageId; - + if ( field( "pageSize.pageSize" ) != tr("Roll") ) + { + return NLayoutsPageId; + } + else + { + return OneLayoutPageId; + } + case EllipsePageId: - return NLayoutsPageId; - + if ( field( "pageSize.pageSize" ) != tr("Roll") ) + { + return NLayoutsPageId; + } + else + { + return OneLayoutPageId; + } + case CdPageId: - return NLayoutsPageId; - + if ( field( "pageSize.pageSize" ) != tr("Roll") ) + { + return NLayoutsPageId; + } + else + { + return OneLayoutPageId; + } + + case PathPageId: + return IntroPageId; + + case ContinuousPageId: + return IntroPageId; + case NLayoutsPageId: if ( field( "nLayouts.one" ).toBool() ) { @@ -188,10 +239,10 @@ namespace glabels case OneLayoutPageId: return ApplyPageId; - + case TwoLayoutPageId: return ApplyPageId; - + case ApplyPageId: default: return -1; @@ -326,8 +377,9 @@ namespace glabels QString paperId = model::Db::lookupPaperIdFromName( field( "pageSize.pageSize" ).toString() ); model::Distance pageW( field( "pageSize.w" ).toDouble(), units ); model::Distance pageH( field( "pageSize.h" ).toDouble(), units ); + model::Distance pageRollW( field( "pageSize.rollW" ).toDouble(), units ); - auto t = new model::Template( brand, part, description, paperId, pageW, pageH, true ); + auto t = new model::Template( brand, part, description, paperId, pageW, pageH, pageRollW, true ); model::Frame* frame; if ( field( "shape.rect" ).toBool() ) @@ -337,10 +389,11 @@ namespace glabels model::Distance r( field( "rect.r" ).toDouble(), units ); model::Distance xWaste( field( "rect.xWaste" ).toDouble(), units ); model::Distance yWaste( field( "rect.yWaste" ).toDouble(), units ); - model::Distance margin( field( "rect.margin" ).toDouble(), units ); + model::Distance xMargin( field( "rect.xMargin" ).toDouble(), units ); + model::Distance yMargin( field( "rect.yMargin" ).toDouble(), units ); frame = new model::FrameRect( w, h, r, xWaste, yWaste ); - frame->addMarkup( new model::MarkupMargin( frame, margin ) ); + frame->addMarkup( new model::MarkupMargin( xMargin, yMargin ) ); } else if ( field( "shape.round" ).toBool() ) { @@ -349,7 +402,7 @@ namespace glabels model::Distance margin( field( "round.margin" ).toDouble(), units ); frame = new model::FrameRound( r, waste ); - frame->addMarkup( new model::MarkupMargin( frame, margin ) ); + frame->addMarkup( new model::MarkupMargin( margin ) ); } else if ( field( "shape.ellipse" ).toBool() ) { @@ -359,7 +412,7 @@ namespace glabels model::Distance margin( field( "ellipse.margin" ).toDouble(), units ); frame = new model::FrameEllipse( w, h, waste ); - frame->addMarkup( new model::MarkupMargin( frame, margin ) ); + frame->addMarkup( new model::MarkupMargin( margin ) ); } else { @@ -371,7 +424,7 @@ namespace glabels model::Distance margin( field( "cd.margin" ).toDouble(), units ); frame = new model::FrameCd( r1, r2, xClip, yClip, waste ); - frame->addMarkup( new model::MarkupMargin( frame, margin ) ); + frame->addMarkup( new model::MarkupMargin( margin ) ); } t->addFrame( frame ); @@ -384,7 +437,7 @@ namespace glabels model::Distance dx( field( "oneLayout.dx" ).toDouble(), units ); model::Distance dy( field( "oneLayout.dy" ).toDouble(), units ); - frame->addLayout( new model::Layout( nx, ny, x0, y0, dx, dy ) ); + frame->addLayout( model::Layout( nx, ny, x0, y0, dx, dy ) ); } else { @@ -402,8 +455,8 @@ namespace glabels model::Distance dx2( field( "twoLayout.dx2" ).toDouble(), units ); model::Distance dy2( field( "twoLayout.dy2" ).toDouble(), units ); - frame->addLayout( new model::Layout( nx1, ny1, x01, y01, dx1, dy1 ) ); - frame->addLayout( new model::Layout( nx2, ny2, x02, y02, dx2, dy2 ) ); + frame->addLayout( model::Layout( nx1, ny1, x01, y01, dx1, dy1 ) ); + frame->addLayout( model::Layout( nx2, ny2, x02, y02, dx2, dy2 ) ); } return t; @@ -420,7 +473,7 @@ namespace glabels model::PageRenderer renderer( sheet ); renderer.setNCopies( sheet->frame()->nLabels() ); - renderer.setStartLabel( 0 ); + renderer.setStartItem( 0 ); renderer.setPrintOutlines( true ); QPrinter printer( QPrinter::HighResolution ); @@ -458,6 +511,7 @@ namespace glabels setField( "pageSize.pageSize", model::Db::lookupPaperNameFromId( tmplate->paperId() ) ); setField( "pageSize.w", tmplate->pageWidth().inUnits( units ) ); setField( "pageSize.h", tmplate->pageHeight().inUnits( units ) ); + setField( "pageSize.rollW", tmplate->rollWidth().inUnits( units ) ); const model::Frame* frame = tmplate->frames().first(); if ( auto frameRect = dynamic_cast( frame ) ) @@ -500,38 +554,39 @@ namespace glabels { if ( auto markupMargin = dynamic_cast( markup ) ) { - setField( "rect.margin", markupMargin->size().inUnits( units ) ); - setField( "round.margin", markupMargin->size().inUnits( units ) ); - setField( "ellipse.margin", markupMargin->size().inUnits( units ) ); - setField( "cd.margin", markupMargin->size().inUnits( units ) ); + setField( "rect.xMargin", markupMargin->xSize().inUnits( units ) ); + setField( "rect.yMargin", markupMargin->ySize().inUnits( units ) ); + setField( "round.margin", markupMargin->xSize().inUnits( units ) ); + setField( "ellipse.margin", markupMargin->xSize().inUnits( units ) ); + setField( "cd.margin", markupMargin->xSize().inUnits( units ) ); } } - QList layouts = frame->layouts(); + auto layouts = frame->layouts(); if ( layouts.size() == 1 ) { - setField( "oneLayout.nx", layouts[0]->nx() ); - setField( "oneLayout.ny", layouts[0]->ny() ); - setField( "oneLayout.x0", layouts[0]->x0().inUnits( units ) ); - setField( "oneLayout.y0", layouts[0]->y0().inUnits( units ) ); - setField( "oneLayout.dx", layouts[0]->dx().inUnits( units ) ); - setField( "oneLayout.dy", layouts[0]->dy().inUnits( units ) ); + setField( "oneLayout.nx", layouts[0].nx() ); + setField( "oneLayout.ny", layouts[0].ny() ); + setField( "oneLayout.x0", layouts[0].x0().inUnits( units ) ); + setField( "oneLayout.y0", layouts[0].y0().inUnits( units ) ); + setField( "oneLayout.dx", layouts[0].dx().inUnits( units ) ); + setField( "oneLayout.dy", layouts[0].dy().inUnits( units ) ); } else if ( layouts.size() > 1 ) { - setField( "twoLayout.nx1", layouts[0]->nx() ); - setField( "twoLayout.ny1", layouts[0]->ny() ); - setField( "twoLayout.x01", layouts[0]->x0().inUnits( units ) ); - setField( "twoLayout.y01", layouts[0]->y0().inUnits( units ) ); - setField( "twoLayout.dx1", layouts[0]->dx().inUnits( units ) ); - setField( "twoLayout.dy1", layouts[0]->dy().inUnits( units ) ); - - setField( "twoLayout.nx2", layouts[1]->nx() ); - setField( "twoLayout.ny2", layouts[1]->ny() ); - setField( "twoLayout.x02", layouts[1]->x0().inUnits( units ) ); - setField( "twoLayout.y02", layouts[1]->y0().inUnits( units ) ); - setField( "twoLayout.dx2", layouts[1]->dx().inUnits( units ) ); - setField( "twoLayout.dy2", layouts[1]->dy().inUnits( units ) ); + setField( "twoLayout.nx1", layouts[0].nx() ); + setField( "twoLayout.ny1", layouts[0].ny() ); + setField( "twoLayout.x01", layouts[0].x0().inUnits( units ) ); + setField( "twoLayout.y01", layouts[0].y0().inUnits( units ) ); + setField( "twoLayout.dx1", layouts[0].dx().inUnits( units ) ); + setField( "twoLayout.dy1", layouts[0].dy().inUnits( units ) ); + + setField( "twoLayout.nx2", layouts[1].nx() ); + setField( "twoLayout.ny2", layouts[1].ny() ); + setField( "twoLayout.x02", layouts[1].x0().inUnits( units ) ); + setField( "twoLayout.y02", layouts[1].y0().inUnits( units ) ); + setField( "twoLayout.dx2", layouts[1].dx().inUnits( units ) ); + setField( "twoLayout.dy2", layouts[1].dy().inUnits( units ) ); } } @@ -583,7 +638,20 @@ namespace glabels { if ( auto td = dynamic_cast( wizard() ) ) { - td->loadFromTemplate( tmplate ); + if ( dynamic_cast(tmplate->frames().constFirst()) ) + { + td->mIsTemplatePathBased = true; + } + else if ( dynamic_cast(tmplate->frames().constFirst()) ) + { + td->mIsTemplateContinuousBased = true; + } + else + { + td->mIsTemplatePathBased = false; + td->mIsTemplateContinuousBased = false; + td->loadFromTemplate( tmplate ); + } td->next(); } } @@ -658,20 +726,30 @@ namespace glabels setupUi( widget ); pageSizeCombo->insertItem( 0, tr("Other") ); - pageSizeCombo->insertItems( 1, model::Db::paperNames() ); + pageSizeCombo->insertItem( 1, tr("Roll") ); + pageSizeCombo->insertItems( 2, model::Db::paperNames() ); pageSizeCombo->setCurrentText( defaultPageSize[ model::Settings::preferedPageSizeFamily() ] ); + bool isOther = pageSizeCombo->currentText() == tr("Other"); + bool isRoll = pageSizeCombo->currentText() == tr("Roll"); + wSpin->setSuffix( " " + model::Settings::units().toTrName() ); wSpin->setDecimals( model::Settings::units().resolutionDigits() ); wSpin->setSingleStep( model::Settings::units().resolution() ); wSpin->setMaximum( maxPageSize[ model::Settings::units().toEnum() ] ); - wSpin->setEnabled( pageSizeCombo->currentText() == tr("Other") ); + wSpin->setEnabled( isOther || isRoll ); hSpin->setSuffix( " " + model::Settings::units().toTrName() ); hSpin->setDecimals( model::Settings::units().resolutionDigits() ); hSpin->setSingleStep( model::Settings::units().resolution() ); hSpin->setMaximum( maxPageSize[ model::Settings::units().toEnum() ] ); - hSpin->setEnabled( pageSizeCombo->currentText() == tr("Other") ); + hSpin->setEnabled( isOther || isRoll ); + + rollWSpin->setSuffix( " " + model::Settings::units().toTrName() ); + rollWSpin->setDecimals( model::Settings::units().resolutionDigits() ); + rollWSpin->setSingleStep( model::Settings::units().resolution() ); + rollWSpin->setMaximum( maxPageSize[ model::Settings::units().toEnum() ] ); + rollWSpin->setEnabled( isRoll ); if ( pageSizeCombo->currentText() != tr("Other") ) { @@ -683,6 +761,7 @@ namespace glabels registerField( "pageSize.pageSize", pageSizeCombo, "currentText" ); registerField( "pageSize.w", wSpin, "value" ); registerField( "pageSize.h", hSpin, "value" ); + registerField( "pageSize.rollW", rollWSpin, "value" ); connect( pageSizeCombo, &QComboBox::currentTextChanged, this, &TemplateDesignerPageSizePage::onComboChanged ); @@ -705,15 +784,29 @@ namespace glabels void TemplateDesignerPageSizePage::onComboChanged() { - if ( pageSizeCombo->currentText() != tr("Other") ) + bool isOther = pageSizeCombo->currentText() == tr("Other"); + bool isRoll = pageSizeCombo->currentText() == tr("Roll"); + + if ( !isOther && !isRoll ) { const model::Paper* paper = model::Db::lookupPaperFromName( pageSizeCombo->currentText() ); wSpin->setValue( paper->width().inUnits( model::Settings::units() ) ); hSpin->setValue( paper->height().inUnits( model::Settings::units() ) ); } - wSpin->setEnabled( pageSizeCombo->currentText() == tr("Other") ); - hSpin->setEnabled( pageSizeCombo->currentText() == tr("Other") ); + if ( !isRoll ) + { + rollWSpin->setValue( 0 ); + } + + wLabel->setEnabled( isOther || isRoll ); + wSpin->setEnabled( isOther || isRoll ); + + hLabel->setEnabled( isOther || isRoll ); + hSpin->setEnabled( isOther || isRoll ); + + rollWLabel->setEnabled( isRoll ); + rollWSpin->setEnabled( isRoll ); } @@ -781,16 +874,21 @@ namespace glabels yWasteSpin->setDecimals( model::Settings::units().resolutionDigits() ); yWasteSpin->setSingleStep( model::Settings::units().resolution() ); - marginSpin->setSuffix( " " + model::Settings::units().toTrName() ); - marginSpin->setDecimals( model::Settings::units().resolutionDigits() ); - marginSpin->setSingleStep( model::Settings::units().resolution() ); + xMarginSpin->setSuffix( " " + model::Settings::units().toTrName() ); + xMarginSpin->setDecimals( model::Settings::units().resolutionDigits() ); + xMarginSpin->setSingleStep( model::Settings::units().resolution() ); + + yMarginSpin->setSuffix( " " + model::Settings::units().toTrName() ); + yMarginSpin->setDecimals( model::Settings::units().resolutionDigits() ); + yMarginSpin->setSingleStep( model::Settings::units().resolution() ); - registerField( "rect.w", wSpin, "value" ); - registerField( "rect.h", hSpin, "value" ); - registerField( "rect.r", rSpin, "value" ); - registerField( "rect.xWaste", xWasteSpin, "value" ); - registerField( "rect.yWaste", yWasteSpin, "value" ); - registerField( "rect.margin", marginSpin, "value" ); + registerField( "rect.w", wSpin, "value" ); + registerField( "rect.h", hSpin, "value" ); + registerField( "rect.r", rSpin, "value" ); + registerField( "rect.xWaste", xWasteSpin, "value" ); + registerField( "rect.yWaste", yWasteSpin, "value" ); + registerField( "rect.xMargin", xMarginSpin, "value" ); + registerField( "rect.yMargin", yMarginSpin, "value" ); QVBoxLayout* layout = new QVBoxLayout; layout->addWidget( widget ); @@ -811,7 +909,8 @@ namespace glabels rSpin->setMaximum( std::min(wMax,hMax)/2.0 ); xWasteSpin->setMaximum( std::min(wMax,hMax)/4.0 ); yWasteSpin->setMaximum( std::min(wMax,hMax)/4.0 ); - marginSpin->setMaximum( std::min(wMax,hMax)/4.0 ); + xMarginSpin->setMaximum( std::min(wMax,hMax)/4.0 ); + yMarginSpin->setMaximum( std::min(wMax,hMax)/4.0 ); static bool alreadyInitialized = false; if ( !td->isBasedOnCopy() && !alreadyInitialized ) @@ -824,7 +923,8 @@ namespace glabels rSpin->setValue( defaultRectR.inUnits( model::Settings::units() ) ); xWasteSpin->setValue( defaultWaste.inUnits( model::Settings::units() ) ); yWasteSpin->setValue( defaultWaste.inUnits( model::Settings::units() ) ); - marginSpin->setValue( defaultMargin.inUnits( model::Settings::units() ) ); + xMarginSpin->setValue( defaultMargin.inUnits( model::Settings::units() ) ); + yMarginSpin->setValue( defaultMargin.inUnits( model::Settings::units() ) ); } } } @@ -1059,6 +1159,54 @@ namespace glabels } + /// + /// Path Product Page + /// + TemplateDesignerPathPage::TemplateDesignerPathPage( QWidget* parent ) : QWizardPage(parent) + { + setTitle( tr("Unsupported Product Style") ); + setSubTitle( tr("Path based product templates are not currently supported by the Product Template Designer.") ); + + QWidget* widget = new QWidget; + setupUi( widget ); + + QVBoxLayout* layout = new QVBoxLayout; + layout->addWidget( widget ); + setLayout( layout ); + } + + + bool TemplateDesignerPathPage::isComplete() const + { + // Must "Cancel" or "Back" from this page + return false; + } + + + /// + /// Continuous Product Page + /// + TemplateDesignerContinuousPage::TemplateDesignerContinuousPage( QWidget* parent ) : QWizardPage(parent) + { + setTitle( tr("Unsupported Product Style") ); + setSubTitle( tr("Continuous tape product templates are not currently supported by the Product Template Designer.") ); + + QWidget* widget = new QWidget; + setupUi( widget ); + + QVBoxLayout* layout = new QVBoxLayout; + layout->addWidget( widget ); + setLayout( layout ); + } + + + bool TemplateDesignerContinuousPage::isComplete() const + { + // Must "Cancel" or "Back" from this page + return false; + } + + /// /// Number of Layouts Page /// @@ -1174,7 +1322,7 @@ namespace glabels { alreadyInitialized = true; - // Set some realistic defaults based on symetric sheet using previosly chosen values + // Set some realistic defaults based on symmetric sheet using previously chosen values nxSpin->setValue( nxMax ); nySpin->setValue( nyMax ); x0Spin->setValue( (pageW - (nxMax-1)*dxMin - w) / 2 ); @@ -1333,7 +1481,7 @@ namespace glabels { alreadyInitialized = true; - // Set some realistic defaults based on symetric sheet using previosly chosen values + // Set some realistic defaults based on symmetric sheet using previously chosen values nxSpin1->setValue( nxMax ); nySpin1->setValue( nyMax - nyMax/2 ); x0Spin1->setValue( (pageW - (nxMax-1)*dxMin - w) / 2 ); diff --git a/glabels/TemplateDesigner.h b/glabels/TemplateDesigner.h index 15534f30..b00040b0 100644 --- a/glabels/TemplateDesigner.h +++ b/glabels/TemplateDesigner.h @@ -30,6 +30,8 @@ #include "ui_TemplateDesignerRoundPage.h" #include "ui_TemplateDesignerEllipsePage.h" #include "ui_TemplateDesignerCdPage.h" +#include "ui_TemplateDesignerPathPage.h" +#include "ui_TemplateDesignerContinuousPage.h" #include "ui_TemplateDesignerNLayoutsPage.h" #include "ui_TemplateDesignerOneLayoutPage.h" #include "ui_TemplateDesignerTwoLayoutPage.h" @@ -59,6 +61,8 @@ namespace glabels friend class TemplateDesignerRoundPage; friend class TemplateDesignerEllipsePage; friend class TemplateDesignerCdPage; + friend class TemplateDesignerPathPage; + friend class TemplateDesignerContinuousPage; friend class TemplateDesignerNLayoutsPage; friend class TemplateDesignerOneLayoutPage; friend class TemplateDesignerTwoLayoutPage; @@ -92,7 +96,9 @@ namespace glabels // Private methods ///////////////////////////////// private: - bool mIsBasedOnCopy; + bool mIsBasedOnCopy{false}; + bool mIsTemplatePathBased{false}; + bool mIsTemplateContinuousBased{false}; }; @@ -220,6 +226,32 @@ namespace glabels }; + // + // Path Page + // + class TemplateDesignerPathPage : public QWizardPage, public Ui::TemplateDesignerPathPage + { + Q_OBJECT + public: + TemplateDesignerPathPage( QWidget* parent = nullptr ); + + bool isComplete() const override; + }; + + + // + // Continuous Page + // + class TemplateDesignerContinuousPage : public QWizardPage, public Ui::TemplateDesignerContinuousPage + { + Q_OBJECT + public: + TemplateDesignerContinuousPage( QWidget* parent = nullptr ); + + bool isComplete() const override; + }; + + // // NLayouts Page // diff --git a/glabels/VariablesView.cpp b/glabels/VariablesView.cpp new file mode 100644 index 00000000..ea13f334 --- /dev/null +++ b/glabels/VariablesView.cpp @@ -0,0 +1,254 @@ +/* VariablesView.cpp + * + * Copyright (C) 2016 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "VariablesView.h" + +#include "EditVariableDialog.h" + +#include +#include + + +namespace +{ + enum ICol { + I_COL_NAME, + I_COL_TYPE, + I_COL_VALUE, + I_COL_INCREMENT, + I_COL_STEP_SIZE, + I_COL_DUMMY, + N_COLS + }; +} + + +namespace glabels +{ + + /// + /// Constructor + /// + VariablesView::VariablesView( QWidget *parent ) + : QWidget(parent), mModel(nullptr), mUndoRedoModel(nullptr) + { + setupUi( this ); + + titleLabel->setText( QString( "%1" ).arg( tr("Variables") ) ); + + table->setColumnCount( N_COLS ); + + auto* nameHeaderItem = new QTableWidgetItem( tr("Name") ); + nameHeaderItem->setFlags( nameHeaderItem->flags() ^ Qt::ItemIsEditable ); + table->setHorizontalHeaderItem( I_COL_NAME, nameHeaderItem ); + + auto* typeHeaderItem = new QTableWidgetItem( tr("Type") ); + typeHeaderItem->setFlags( typeHeaderItem->flags() ^ Qt::ItemIsEditable ); + table->setHorizontalHeaderItem( I_COL_TYPE, typeHeaderItem ); + + auto* valueHeaderItem = new QTableWidgetItem( tr("Value") ); + valueHeaderItem->setFlags( valueHeaderItem->flags() ^ Qt::ItemIsEditable ); + table->setHorizontalHeaderItem( I_COL_VALUE, valueHeaderItem ); + + auto* incrementHeaderItem = new QTableWidgetItem( tr("Increment") ); + incrementHeaderItem->setFlags( incrementHeaderItem->flags() ^ Qt::ItemIsEditable ); + table->setHorizontalHeaderItem( I_COL_INCREMENT, incrementHeaderItem ); + + auto* stepSizeHeaderItem = new QTableWidgetItem( tr("Step Size") ); + stepSizeHeaderItem->setFlags( stepSizeHeaderItem->flags() ^ Qt::ItemIsEditable ); + table->setHorizontalHeaderItem( I_COL_STEP_SIZE, stepSizeHeaderItem ); + + auto* dummyHeaderItem = new QTableWidgetItem(); + dummyHeaderItem->setFlags( Qt::NoItemFlags ); + table->setHorizontalHeaderItem( I_COL_DUMMY, dummyHeaderItem ); + table->horizontalHeader()->setStretchLastSection( true ); + } + + + /// + /// Destructor + /// + VariablesView::~VariablesView() + { + // empty + } + + + /// + /// Set Model + /// + void VariablesView::setModel( model::Model* model, UndoRedoModel* undoRedoModel ) + { + mModel = model; + mUndoRedoModel = undoRedoModel; + + updateControls(); + loadTable(); + + connect( mModel, SIGNAL(variablesChanged()), this, SLOT(onVariablesChanged()) ); + } + + + /// + /// table Selection Changed + /// + void VariablesView::onTableSelectionChanged() + { + updateControls(); + } + + + /// + /// addButton Clicked + /// + void VariablesView::onAddButtonClicked() + { + EditVariableDialog dialog( this ); + + model::Variable v( model::Variable::Type::INTEGER, + "x", + "0", + model::Variable::Increment::NEVER, + "1" ); + dialog.setVariable( v ); + dialog.setWindowTitle( tr("Add Variable") ); + + if ( dialog.exec() == QDialog::Accepted ) + { + mModel->variables()->addVariable( dialog.variable() ); + selectVariable( dialog.variable().name() ); + } + } + + + /// + /// editButton Clicked + /// + void VariablesView::onEditButtonClicked() + { + int iRow = table->selectedItems()[0]->row(); + QString name = table->item( iRow, I_COL_NAME )->text(); + + if ( mModel->variables()->hasVariable( name ) ) + { + model::Variable v = mModel->variables()->value( name ); + + EditVariableDialog dialog( this ); + dialog.setVariable( v ); + dialog.setWindowTitle( tr("Edit Variable") ); + + if ( dialog.exec() == QDialog::Accepted ) + { + mModel->variables()->replaceVariable( name, dialog.variable() ); + selectVariable( dialog.variable().name() ); + } + } + } + + + /// + /// deleteButton Clicked + /// + void VariablesView::onDeleteButtonClicked() + { + int iRow = table->selectedItems()[0]->row(); + + QString name = table->item( iRow, I_COL_NAME )->text(); + mModel->variables()->deleteVariable( name ); + } + + + /// + /// Variables Changed + /// + void VariablesView::onVariablesChanged() + { + // Reload table from variables + loadTable(); + } + + + /// + /// update controls + /// + void VariablesView::updateControls() + { + bool hasSelection = !table->selectedItems().isEmpty(); + + editButton->setEnabled( hasSelection ); + deleteButton->setEnabled( hasSelection ); + } + + + /// + /// load table from variables + /// + void VariablesView::loadTable() + { + table->clearContents(); + table->setRowCount( mModel->variables()->size() ); + + int iRow = 0; + for( const auto& v : *mModel->variables() ) + { + auto* typeItem = new QTableWidgetItem( model::Variable::typeToI18nString(v.type()) ); + typeItem->setFlags( typeItem->flags() ^ Qt::ItemIsEditable ); + table->setItem( iRow, I_COL_TYPE, typeItem ); + + auto* nameItem = new QTableWidgetItem( v.name() ); + nameItem->setFlags( nameItem->flags() ^ Qt::ItemIsEditable ); + table->setItem( iRow, I_COL_NAME, nameItem ); + + auto* valueItem = new QTableWidgetItem( v.initialValue() ); + valueItem->setFlags( valueItem->flags() ^ Qt::ItemIsEditable ); + table->setItem( iRow, I_COL_VALUE, valueItem ); + + auto* incrementItem = new QTableWidgetItem( model::Variable::incrementToI18nString(v.increment()) ); + incrementItem->setFlags( incrementItem->flags() ^ Qt::ItemIsEditable ); + table->setItem( iRow, I_COL_INCREMENT, incrementItem ); + + auto* stepSizeItem = new QTableWidgetItem( v.stepSize() ); + stepSizeItem->setFlags( stepSizeItem->flags() ^ Qt::ItemIsEditable ); + table->setItem( iRow, I_COL_STEP_SIZE, stepSizeItem ); + + table->showRow( iRow ); + iRow++; + } + } + + + void VariablesView::selectVariable( const QString& name ) + { + int iRow = 0; + for( const auto& v : *mModel->variables() ) + { + if ( v.name() == name ) + { + table->setCurrentCell( iRow, 0, + (QItemSelectionModel::Select|QItemSelectionModel::Rows) ); + break; + } + + iRow++; + } + } + + +} // namespace glabels diff --git a/glabels/VariablesView.h b/glabels/VariablesView.h new file mode 100644 index 00000000..93dca3d3 --- /dev/null +++ b/glabels/VariablesView.h @@ -0,0 +1,91 @@ +/* VariablesView.h + * + * Copyright (C) 2016 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#ifndef VariablesView_h +#define VariablesView_h + + +#include "ui_VariablesView.h" + +#include "model/Model.h" + + +namespace glabels +{ + + // Forward references + class UndoRedoModel; + + + /// + /// Variables Property Editor Widget + /// + class VariablesView : public QWidget, public Ui_VariablesView + { + Q_OBJECT + + + ///////////////////////////////// + // Life Cycle + ///////////////////////////////// + public: + VariablesView( QWidget *parent = nullptr ); + ~VariablesView() override; + + + ///////////////////////////////// + // Public methods + ///////////////////////////////// + void setModel( model::Model* model, UndoRedoModel* undoRedoModel ); + + + ///////////////////////////////// + // Slots + ///////////////////////////////// + private slots: + void onTableSelectionChanged(); + void onAddButtonClicked(); + void onEditButtonClicked(); + void onDeleteButtonClicked(); + void onVariablesChanged(); + + + ///////////////////////////////// + // Private methods + ///////////////////////////////// + private: + void updateControls(); + void loadTable(); + void selectVariable( const QString& name ); + + + ///////////////////////////////// + // Private Data + ///////////////////////////////// + private: + model::Model* mModel; + UndoRedoModel* mUndoRedoModel; + + }; + +} + + +#endif // VariablesView_h diff --git a/glabels/icons.qrc b/glabels/icons.qrc index bc12df6c..7baf897b 100644 --- a/glabels/icons.qrc +++ b/glabels/icons.qrc @@ -3,106 +3,121 @@ - icons/16x16/actions/edit-clear.svg - icons/16x16/actions/edit-copy.svg - icons/16x16/actions/edit-cut.svg - icons/16x16/actions/edit-paste.svg - icons/16x16/actions/file-new.svg - icons/16x16/actions/file-open.svg - icons/16x16/actions/file-save.svg - icons/16x16/actions/file-save-as.svg - icons/16x16/actions/glabels-align-bottom.svg - icons/16x16/actions/glabels-align-hcenter.svg - icons/16x16/actions/glabels-align-left.svg - icons/16x16/actions/glabels-align-right.svg - icons/16x16/actions/glabels-align-top.svg - icons/16x16/actions/glabels-align-vcenter.svg - icons/16x16/actions/glabels-arrow.svg - icons/16x16/actions/glabels-barcode.svg - icons/16x16/actions/glabels-box.svg - icons/16x16/actions/glabels-center-horiz.svg - icons/16x16/actions/glabels-center-vert.svg - icons/16x16/actions/glabels-ellipse.svg - icons/16x16/actions/glabels-flip-horiz.svg - icons/16x16/actions/glabels-flip-vert.svg - icons/16x16/actions/glabels-image.svg - icons/16x16/actions/glabels-line.svg - icons/16x16/actions/glabels-order-bottom.svg - icons/16x16/actions/glabels-order-top.svg - icons/16x16/actions/glabels-rotate-left.svg - icons/16x16/actions/glabels-rotate-right.svg - icons/16x16/actions/glabels-text.svg - icons/16x16/actions/glabels-zoom-in.svg - icons/16x16/actions/glabels-zoom-one-to-one.svg - icons/16x16/actions/glabels-zoom-out.svg - icons/16x16/actions/glabels-zoom-to-fit.svg - icons/16x16/apps/glabels.svg + icons/flat/16x16/glabels-align-bottom.svg + icons/flat/16x16/glabels-align-hcenter.svg + icons/flat/16x16/glabels-align-left.svg + icons/flat/16x16/glabels-align-right.svg + icons/flat/16x16/glabels-align-top.svg + icons/flat/16x16/glabels-align-vcenter.svg + icons/flat/16x16/glabels-arrow.svg + icons/flat/16x16/glabels-barcode.svg + icons/flat/16x16/glabels-box.svg + icons/flat/16x16/glabels-center-horiz.svg + icons/flat/16x16/glabels-center-vert.svg + icons/flat/16x16/glabels-edit-clear.svg + icons/flat/16x16/glabels-edit-copy.svg + icons/flat/16x16/glabels-edit-cut.svg + icons/flat/16x16/glabels-edit-paste.svg + icons/flat/16x16/glabels-ellipse.svg + icons/flat/16x16/glabels-file-new.svg + icons/flat/16x16/glabels-file-open.svg + icons/flat/16x16/glabels-file-recent.svg + icons/flat/16x16/glabels-file-save.svg + icons/flat/16x16/glabels-file-save-as.svg + icons/flat/16x16/glabels-flip-horiz.svg + icons/flat/16x16/glabels-flip-vert.svg + icons/flat/16x16/glabels-image.svg + icons/flat/16x16/glabels-line.svg + icons/flat/16x16/glabels-order-bottom.svg + icons/flat/16x16/glabels-order-top.svg + icons/flat/16x16/glabels-rotate-left.svg + icons/flat/16x16/glabels-rotate-right.svg + icons/flat/16x16/glabels-text.svg + icons/flat/16x16/glabels-zoom-in.svg + icons/flat/16x16/glabels-zoom-one-to-one.svg + icons/flat/16x16/glabels-zoom-out.svg + icons/flat/16x16/glabels-zoom-to-fit.svg + icons/apps/16x16/glabels.svg - icons/22x22/actions/edit-copy.svg - icons/22x22/actions/edit-cut.svg - icons/22x22/actions/edit-paste.svg - icons/22x22/actions/file-new.svg - icons/22x22/actions/file-open.svg - icons/22x22/actions/file-save.svg - icons/22x22/actions/file-save-as.svg - icons/22x22/actions/glabels-align-text-center.svg - icons/22x22/actions/glabels-align-text-left.svg - icons/22x22/actions/glabels-align-text-right.svg - icons/22x22/actions/glabels-arrow.svg - icons/22x22/actions/glabels-barcode.svg - icons/22x22/actions/glabels-box.svg - icons/22x22/actions/glabels-ellipse.svg - icons/22x22/actions/glabels-format-text-bold.svg - icons/22x22/actions/glabels-format-text-italic.svg - icons/22x22/actions/glabels-format-text-underline.svg - icons/22x22/actions/glabels-image.svg - icons/22x22/actions/glabels-line.svg - icons/22x22/actions/glabels-text.svg - icons/22x22/actions/glabels-valign-text-bottom.svg - icons/22x22/actions/glabels-valign-text-middle.svg - icons/22x22/actions/glabels-valign-text-top.svg - icons/22x22/actions/glabels-zoom-in.svg - icons/22x22/actions/glabels-zoom-one-to-one.svg - icons/22x22/actions/glabels-zoom-out.svg - icons/22x22/actions/glabels-zoom-to-fit.svg - icons/22x22/apps/glabels.svg + icons/flat/22x22/glabels-align-text-center.svg + icons/flat/22x22/glabels-align-text-left.svg + icons/flat/22x22/glabels-align-text-right.svg + icons/flat/22x22/glabels-arrow.svg + icons/flat/22x22/glabels-barcode.svg + icons/flat/22x22/glabels-box.svg + icons/flat/22x22/glabels-edit-copy.svg + icons/flat/22x22/glabels-edit-cut.svg + icons/flat/22x22/glabels-edit-paste.svg + icons/flat/22x22/glabels-ellipse.svg + icons/flat/22x22/glabels-file-new.svg + icons/flat/22x22/glabels-file-open.svg + icons/flat/22x22/glabels-file-recent.svg + icons/flat/22x22/glabels-file-save.svg + icons/flat/22x22/glabels-file-save-as.svg + icons/flat/22x22/glabels-format-text-bold.svg + icons/flat/22x22/glabels-format-text-italic.svg + icons/flat/22x22/glabels-format-text-underline.svg + icons/flat/22x22/glabels-image.svg + icons/flat/22x22/glabels-line.svg + icons/flat/22x22/glabels-text.svg + icons/flat/22x22/glabels-valign-text-bottom.svg + icons/flat/22x22/glabels-valign-text-middle.svg + icons/flat/22x22/glabels-valign-text-top.svg + icons/flat/22x22/glabels-zoom-in.svg + icons/flat/22x22/glabels-zoom-one-to-one.svg + icons/flat/22x22/glabels-zoom-out.svg + icons/flat/22x22/glabels-zoom-to-fit.svg + icons/apps/22x22/glabels.svg - icons/24x24/actions/edit-copy.svg - icons/24x24/actions/edit-cut.svg - icons/24x24/actions/edit-paste.svg - icons/24x24/actions/file-new.svg - icons/24x24/actions/file-open.svg - icons/24x24/actions/file-save.svg - icons/24x24/actions/file-save-as.svg - icons/24x24/actions/glabels-align-text-center.svg - icons/24x24/actions/glabels-align-text-left.svg - icons/24x24/actions/glabels-align-text-right.svg - icons/24x24/actions/glabels-arrow.svg - icons/24x24/actions/glabels-barcode.svg - icons/24x24/actions/glabels-box.svg - icons/24x24/actions/glabels-ellipse.svg - icons/24x24/actions/glabels-format-text-bold.svg - icons/24x24/actions/glabels-format-text-italic.svg - icons/24x24/actions/glabels-format-text-underline.svg - icons/24x24/actions/glabels-image.svg - icons/24x24/actions/glabels-line.svg - icons/24x24/actions/glabels-object-properties.svg - icons/24x24/actions/glabels-text.svg - icons/24x24/actions/glabels-valign-text-bottom.svg - icons/24x24/actions/glabels-valign-text-middle.svg - icons/24x24/actions/glabels-valign-text-top.svg + icons/flat/24x24/glabels-align-text-center.svg + icons/flat/24x24/glabels-align-text-left.svg + icons/flat/24x24/glabels-align-text-right.svg + icons/flat/24x24/glabels-arrow.svg + icons/flat/24x24/glabels-barcode.svg + icons/flat/24x24/glabels-box.svg + icons/flat/24x24/glabels-edit-copy.svg + icons/flat/24x24/glabels-edit-cut.svg + icons/flat/24x24/glabels-edit-paste.svg + icons/flat/24x24/glabels-ellipse.svg + icons/flat/24x24/glabels-file-new.svg + icons/flat/24x24/glabels-file-open.svg + icons/flat/24x24/glabels-file-recent.svg + icons/flat/24x24/glabels-file-save.svg + icons/flat/24x24/glabels-file-save-as.svg + icons/flat/24x24/glabels-format-text-bold.svg + icons/flat/24x24/glabels-format-text-italic.svg + icons/flat/24x24/glabels-format-text-underline.svg + icons/flat/24x24/glabels-image.svg + icons/flat/24x24/glabels-line.svg + icons/flat/24x24/glabels-object-properties.svg + icons/flat/24x24/glabels-text.svg + icons/flat/24x24/glabels-valign-text-bottom.svg + icons/flat/24x24/glabels-valign-text-middle.svg + icons/flat/24x24/glabels-valign-text-top.svg - icons/32x32/actions/label-orientation-horiz.svg - icons/32x32/actions/label-orientation-vert.svg - icons/32x32/actions/print.svg - icons/32x32/actions/select-product.svg - icons/32x32/apps/glabels.svg + icons/flat/32x32/glabels-collated.svg + icons/flat/32x32/glabels-file-new.svg + icons/flat/32x32/glabels-file-open.svg + icons/flat/32x32/glabels-file-recent.svg + icons/flat/32x32/glabels-label-orientation-horiz.svg + icons/flat/32x32/glabels-label-orientation-vert.svg + icons/flat/32x32/glabels-merge-group-contiguous.svg + icons/flat/32x32/glabels-merge-group-page.svg + icons/flat/32x32/glabels-print.svg + icons/flat/32x32/glabels-select-product.svg + icons/flat/32x32/glabels-uncollated.svg + icons/apps/32x32/glabels.svg - icons/48x48/apps/glabels.svg + icons/flat/48x48/glabels-edit.svg + icons/flat/48x48/glabels-merge.svg + icons/flat/48x48/glabels-print.svg + icons/flat/48x48/glabels-properties.svg + icons/flat/48x48/glabels-variables.svg + icons/apps/48x48/glabels.svg - icons/128x128/apps/glabels.svg + icons/apps/128x128/glabels.svg - icons/scalable/apps/glabels.svg + icons/apps/scalable/glabels.svg diff --git a/glabels/icons/128x128/apps/glabels.svg b/glabels/icons/apps/128x128/glabels.svg similarity index 100% rename from glabels/icons/128x128/apps/glabels.svg rename to glabels/icons/apps/128x128/glabels.svg diff --git a/glabels/icons/16x16/apps/glabels.svg b/glabels/icons/apps/16x16/glabels.svg similarity index 100% rename from glabels/icons/16x16/apps/glabels.svg rename to glabels/icons/apps/16x16/glabels.svg diff --git a/glabels/icons/22x22/apps/glabels.svg b/glabels/icons/apps/22x22/glabels.svg similarity index 100% rename from glabels/icons/22x22/apps/glabels.svg rename to glabels/icons/apps/22x22/glabels.svg diff --git a/glabels/icons/32x32/apps/glabels.svg b/glabels/icons/apps/32x32/glabels.svg similarity index 100% rename from glabels/icons/32x32/apps/glabels.svg rename to glabels/icons/apps/32x32/glabels.svg diff --git a/glabels/icons/48x48/apps/glabels.svg b/glabels/icons/apps/48x48/glabels.svg similarity index 100% rename from glabels/icons/48x48/apps/glabels.svg rename to glabels/icons/apps/48x48/glabels.svg diff --git a/glabels/icons/scalable/apps/glabels.svg b/glabels/icons/apps/scalable/glabels.svg similarity index 100% rename from glabels/icons/scalable/apps/glabels.svg rename to glabels/icons/apps/scalable/glabels.svg diff --git a/glabels/icons/16x16/actions/glabels-align-bottom.svg b/glabels/icons/flat/16x16/glabels-align-bottom.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-align-bottom.svg rename to glabels/icons/flat/16x16/glabels-align-bottom.svg diff --git a/glabels/icons/16x16/actions/glabels-align-hcenter.svg b/glabels/icons/flat/16x16/glabels-align-hcenter.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-align-hcenter.svg rename to glabels/icons/flat/16x16/glabels-align-hcenter.svg diff --git a/glabels/icons/16x16/actions/glabels-align-left.svg b/glabels/icons/flat/16x16/glabels-align-left.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-align-left.svg rename to glabels/icons/flat/16x16/glabels-align-left.svg diff --git a/glabels/icons/16x16/actions/glabels-align-right.svg b/glabels/icons/flat/16x16/glabels-align-right.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-align-right.svg rename to glabels/icons/flat/16x16/glabels-align-right.svg diff --git a/glabels/icons/16x16/actions/glabels-align-top.svg b/glabels/icons/flat/16x16/glabels-align-top.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-align-top.svg rename to glabels/icons/flat/16x16/glabels-align-top.svg diff --git a/glabels/icons/16x16/actions/glabels-align-vcenter.svg b/glabels/icons/flat/16x16/glabels-align-vcenter.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-align-vcenter.svg rename to glabels/icons/flat/16x16/glabels-align-vcenter.svg diff --git a/glabels/icons/16x16/actions/glabels-arrow.svg b/glabels/icons/flat/16x16/glabels-arrow.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-arrow.svg rename to glabels/icons/flat/16x16/glabels-arrow.svg diff --git a/glabels/icons/16x16/actions/glabels-barcode.svg b/glabels/icons/flat/16x16/glabels-barcode.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-barcode.svg rename to glabels/icons/flat/16x16/glabels-barcode.svg diff --git a/glabels/icons/16x16/actions/glabels-box.svg b/glabels/icons/flat/16x16/glabels-box.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-box.svg rename to glabels/icons/flat/16x16/glabels-box.svg diff --git a/glabels/icons/16x16/actions/glabels-center-horiz.svg b/glabels/icons/flat/16x16/glabels-center-horiz.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-center-horiz.svg rename to glabels/icons/flat/16x16/glabels-center-horiz.svg diff --git a/glabels/icons/16x16/actions/glabels-center-vert.svg b/glabels/icons/flat/16x16/glabels-center-vert.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-center-vert.svg rename to glabels/icons/flat/16x16/glabels-center-vert.svg diff --git a/glabels/icons/16x16/actions/edit-clear.svg b/glabels/icons/flat/16x16/glabels-edit-clear.svg similarity index 100% rename from glabels/icons/16x16/actions/edit-clear.svg rename to glabels/icons/flat/16x16/glabels-edit-clear.svg diff --git a/glabels/icons/16x16/actions/edit-copy.svg b/glabels/icons/flat/16x16/glabels-edit-copy.svg similarity index 100% rename from glabels/icons/16x16/actions/edit-copy.svg rename to glabels/icons/flat/16x16/glabels-edit-copy.svg diff --git a/glabels/icons/16x16/actions/edit-cut.svg b/glabels/icons/flat/16x16/glabels-edit-cut.svg similarity index 100% rename from glabels/icons/16x16/actions/edit-cut.svg rename to glabels/icons/flat/16x16/glabels-edit-cut.svg diff --git a/glabels/icons/16x16/actions/edit-paste.svg b/glabels/icons/flat/16x16/glabels-edit-paste.svg similarity index 100% rename from glabels/icons/16x16/actions/edit-paste.svg rename to glabels/icons/flat/16x16/glabels-edit-paste.svg diff --git a/glabels/icons/16x16/actions/glabels-ellipse.svg b/glabels/icons/flat/16x16/glabels-ellipse.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-ellipse.svg rename to glabels/icons/flat/16x16/glabels-ellipse.svg diff --git a/glabels/icons/16x16/actions/file-new.svg b/glabels/icons/flat/16x16/glabels-file-new.svg similarity index 69% rename from glabels/icons/16x16/actions/file-new.svg rename to glabels/icons/flat/16x16/glabels-file-new.svg index 3ec7ab7d..7c0d820b 100644 --- a/glabels/icons/16x16/actions/file-new.svg +++ b/glabels/icons/flat/16x16/glabels-file-new.svg @@ -5,27 +5,27 @@ d="M 3.5,1.5 3.5,14.5 13.5,14.5 13.5,4.5 10.5,1.5 Z" /> + + + + + + + + + diff --git a/glabels/icons/16x16/actions/file-save-as.svg b/glabels/icons/flat/16x16/glabels-file-save-as.svg similarity index 100% rename from glabels/icons/16x16/actions/file-save-as.svg rename to glabels/icons/flat/16x16/glabels-file-save-as.svg diff --git a/glabels/icons/16x16/actions/file-save.svg b/glabels/icons/flat/16x16/glabels-file-save.svg similarity index 100% rename from glabels/icons/16x16/actions/file-save.svg rename to glabels/icons/flat/16x16/glabels-file-save.svg diff --git a/glabels/icons/16x16/actions/glabels-flip-horiz.svg b/glabels/icons/flat/16x16/glabels-flip-horiz.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-flip-horiz.svg rename to glabels/icons/flat/16x16/glabels-flip-horiz.svg diff --git a/glabels/icons/16x16/actions/glabels-flip-vert.svg b/glabels/icons/flat/16x16/glabels-flip-vert.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-flip-vert.svg rename to glabels/icons/flat/16x16/glabels-flip-vert.svg diff --git a/glabels/icons/16x16/actions/glabels-image.svg b/glabels/icons/flat/16x16/glabels-image.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-image.svg rename to glabels/icons/flat/16x16/glabels-image.svg diff --git a/glabels/icons/16x16/actions/glabels-line.svg b/glabels/icons/flat/16x16/glabels-line.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-line.svg rename to glabels/icons/flat/16x16/glabels-line.svg diff --git a/glabels/icons/16x16/actions/glabels-order-bottom.svg b/glabels/icons/flat/16x16/glabels-order-bottom.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-order-bottom.svg rename to glabels/icons/flat/16x16/glabels-order-bottom.svg diff --git a/glabels/icons/16x16/actions/glabels-order-top.svg b/glabels/icons/flat/16x16/glabels-order-top.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-order-top.svg rename to glabels/icons/flat/16x16/glabels-order-top.svg diff --git a/glabels/icons/16x16/actions/glabels-rotate-left.svg b/glabels/icons/flat/16x16/glabels-rotate-left.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-rotate-left.svg rename to glabels/icons/flat/16x16/glabels-rotate-left.svg diff --git a/glabels/icons/16x16/actions/glabels-rotate-right.svg b/glabels/icons/flat/16x16/glabels-rotate-right.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-rotate-right.svg rename to glabels/icons/flat/16x16/glabels-rotate-right.svg diff --git a/glabels/icons/16x16/actions/glabels-text.svg b/glabels/icons/flat/16x16/glabels-text.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-text.svg rename to glabels/icons/flat/16x16/glabels-text.svg diff --git a/glabels/icons/16x16/actions/glabels-zoom-in.svg b/glabels/icons/flat/16x16/glabels-zoom-in.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-zoom-in.svg rename to glabels/icons/flat/16x16/glabels-zoom-in.svg diff --git a/glabels/icons/16x16/actions/glabels-zoom-one-to-one.svg b/glabels/icons/flat/16x16/glabels-zoom-one-to-one.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-zoom-one-to-one.svg rename to glabels/icons/flat/16x16/glabels-zoom-one-to-one.svg diff --git a/glabels/icons/16x16/actions/glabels-zoom-out.svg b/glabels/icons/flat/16x16/glabels-zoom-out.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-zoom-out.svg rename to glabels/icons/flat/16x16/glabels-zoom-out.svg diff --git a/glabels/icons/16x16/actions/glabels-zoom-to-fit.svg b/glabels/icons/flat/16x16/glabels-zoom-to-fit.svg similarity index 100% rename from glabels/icons/16x16/actions/glabels-zoom-to-fit.svg rename to glabels/icons/flat/16x16/glabels-zoom-to-fit.svg diff --git a/glabels/icons/22x22/actions/glabels-align-text-center.svg b/glabels/icons/flat/22x22/glabels-align-text-center.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-align-text-center.svg rename to glabels/icons/flat/22x22/glabels-align-text-center.svg diff --git a/glabels/icons/22x22/actions/glabels-align-text-left.svg b/glabels/icons/flat/22x22/glabels-align-text-left.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-align-text-left.svg rename to glabels/icons/flat/22x22/glabels-align-text-left.svg diff --git a/glabels/icons/22x22/actions/glabels-align-text-right.svg b/glabels/icons/flat/22x22/glabels-align-text-right.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-align-text-right.svg rename to glabels/icons/flat/22x22/glabels-align-text-right.svg diff --git a/glabels/icons/22x22/actions/glabels-arrow.svg b/glabels/icons/flat/22x22/glabels-arrow.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-arrow.svg rename to glabels/icons/flat/22x22/glabels-arrow.svg diff --git a/glabels/icons/22x22/actions/glabels-barcode.svg b/glabels/icons/flat/22x22/glabels-barcode.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-barcode.svg rename to glabels/icons/flat/22x22/glabels-barcode.svg diff --git a/glabels/icons/22x22/actions/glabels-box.svg b/glabels/icons/flat/22x22/glabels-box.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-box.svg rename to glabels/icons/flat/22x22/glabels-box.svg diff --git a/glabels/icons/22x22/actions/edit-copy.svg b/glabels/icons/flat/22x22/glabels-edit-copy.svg similarity index 100% rename from glabels/icons/22x22/actions/edit-copy.svg rename to glabels/icons/flat/22x22/glabels-edit-copy.svg diff --git a/glabels/icons/22x22/actions/edit-cut.svg b/glabels/icons/flat/22x22/glabels-edit-cut.svg similarity index 100% rename from glabels/icons/22x22/actions/edit-cut.svg rename to glabels/icons/flat/22x22/glabels-edit-cut.svg diff --git a/glabels/icons/22x22/actions/edit-paste.svg b/glabels/icons/flat/22x22/glabels-edit-paste.svg similarity index 100% rename from glabels/icons/22x22/actions/edit-paste.svg rename to glabels/icons/flat/22x22/glabels-edit-paste.svg diff --git a/glabels/icons/22x22/actions/glabels-ellipse.svg b/glabels/icons/flat/22x22/glabels-ellipse.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-ellipse.svg rename to glabels/icons/flat/22x22/glabels-ellipse.svg diff --git a/glabels/icons/22x22/actions/file-new.svg b/glabels/icons/flat/22x22/glabels-file-new.svg similarity index 65% rename from glabels/icons/22x22/actions/file-new.svg rename to glabels/icons/flat/22x22/glabels-file-new.svg index 9f4dab21..0b3dd35b 100644 --- a/glabels/icons/22x22/actions/file-new.svg +++ b/glabels/icons/flat/22x22/glabels-file-new.svg @@ -5,35 +5,35 @@ d="M 4.5,1.5 4.5,19.5 18.5,19.5 18.5,6.5 13.5,1.5 Z" /> + + + + + + + + + diff --git a/glabels/icons/22x22/actions/file-save-as.svg b/glabels/icons/flat/22x22/glabels-file-save-as.svg similarity index 100% rename from glabels/icons/22x22/actions/file-save-as.svg rename to glabels/icons/flat/22x22/glabels-file-save-as.svg diff --git a/glabels/icons/22x22/actions/file-save.svg b/glabels/icons/flat/22x22/glabels-file-save.svg similarity index 100% rename from glabels/icons/22x22/actions/file-save.svg rename to glabels/icons/flat/22x22/glabels-file-save.svg diff --git a/glabels/icons/22x22/actions/glabels-format-text-bold.svg b/glabels/icons/flat/22x22/glabels-format-text-bold.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-format-text-bold.svg rename to glabels/icons/flat/22x22/glabels-format-text-bold.svg diff --git a/glabels/icons/22x22/actions/glabels-format-text-italic.svg b/glabels/icons/flat/22x22/glabels-format-text-italic.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-format-text-italic.svg rename to glabels/icons/flat/22x22/glabels-format-text-italic.svg diff --git a/glabels/icons/22x22/actions/glabels-format-text-underline.svg b/glabels/icons/flat/22x22/glabels-format-text-underline.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-format-text-underline.svg rename to glabels/icons/flat/22x22/glabels-format-text-underline.svg diff --git a/glabels/icons/22x22/actions/glabels-image.svg b/glabels/icons/flat/22x22/glabels-image.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-image.svg rename to glabels/icons/flat/22x22/glabels-image.svg diff --git a/glabels/icons/22x22/actions/glabels-line.svg b/glabels/icons/flat/22x22/glabels-line.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-line.svg rename to glabels/icons/flat/22x22/glabels-line.svg diff --git a/glabels/icons/22x22/actions/glabels-text.svg b/glabels/icons/flat/22x22/glabels-text.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-text.svg rename to glabels/icons/flat/22x22/glabels-text.svg diff --git a/glabels/icons/22x22/actions/glabels-valign-text-bottom.svg b/glabels/icons/flat/22x22/glabels-valign-text-bottom.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-valign-text-bottom.svg rename to glabels/icons/flat/22x22/glabels-valign-text-bottom.svg diff --git a/glabels/icons/22x22/actions/glabels-valign-text-middle.svg b/glabels/icons/flat/22x22/glabels-valign-text-middle.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-valign-text-middle.svg rename to glabels/icons/flat/22x22/glabels-valign-text-middle.svg diff --git a/glabels/icons/22x22/actions/glabels-valign-text-top.svg b/glabels/icons/flat/22x22/glabels-valign-text-top.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-valign-text-top.svg rename to glabels/icons/flat/22x22/glabels-valign-text-top.svg diff --git a/glabels/icons/22x22/actions/glabels-zoom-in.svg b/glabels/icons/flat/22x22/glabels-zoom-in.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-zoom-in.svg rename to glabels/icons/flat/22x22/glabels-zoom-in.svg diff --git a/glabels/icons/22x22/actions/glabels-zoom-one-to-one.svg b/glabels/icons/flat/22x22/glabels-zoom-one-to-one.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-zoom-one-to-one.svg rename to glabels/icons/flat/22x22/glabels-zoom-one-to-one.svg diff --git a/glabels/icons/22x22/actions/glabels-zoom-out.svg b/glabels/icons/flat/22x22/glabels-zoom-out.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-zoom-out.svg rename to glabels/icons/flat/22x22/glabels-zoom-out.svg diff --git a/glabels/icons/22x22/actions/glabels-zoom-to-fit.svg b/glabels/icons/flat/22x22/glabels-zoom-to-fit.svg similarity index 100% rename from glabels/icons/22x22/actions/glabels-zoom-to-fit.svg rename to glabels/icons/flat/22x22/glabels-zoom-to-fit.svg diff --git a/glabels/icons/24x24/actions/glabels-align-text-center.svg b/glabels/icons/flat/24x24/glabels-align-text-center.svg similarity index 100% rename from glabels/icons/24x24/actions/glabels-align-text-center.svg rename to glabels/icons/flat/24x24/glabels-align-text-center.svg diff --git a/glabels/icons/24x24/actions/glabels-align-text-left.svg b/glabels/icons/flat/24x24/glabels-align-text-left.svg similarity index 100% rename from glabels/icons/24x24/actions/glabels-align-text-left.svg rename to glabels/icons/flat/24x24/glabels-align-text-left.svg diff --git a/glabels/icons/24x24/actions/glabels-align-text-right.svg b/glabels/icons/flat/24x24/glabels-align-text-right.svg similarity index 100% rename from glabels/icons/24x24/actions/glabels-align-text-right.svg rename to glabels/icons/flat/24x24/glabels-align-text-right.svg diff --git a/glabels/icons/24x24/actions/glabels-arrow.svg b/glabels/icons/flat/24x24/glabels-arrow.svg similarity index 100% rename from glabels/icons/24x24/actions/glabels-arrow.svg rename to glabels/icons/flat/24x24/glabels-arrow.svg diff --git a/glabels/icons/24x24/actions/glabels-barcode.svg b/glabels/icons/flat/24x24/glabels-barcode.svg similarity index 100% rename from glabels/icons/24x24/actions/glabels-barcode.svg rename to glabels/icons/flat/24x24/glabels-barcode.svg diff --git a/glabels/icons/24x24/actions/glabels-box.svg b/glabels/icons/flat/24x24/glabels-box.svg similarity index 100% rename from glabels/icons/24x24/actions/glabels-box.svg rename to glabels/icons/flat/24x24/glabels-box.svg diff --git a/glabels/icons/24x24/actions/edit-copy.svg b/glabels/icons/flat/24x24/glabels-edit-copy.svg similarity index 100% rename from glabels/icons/24x24/actions/edit-copy.svg rename to glabels/icons/flat/24x24/glabels-edit-copy.svg diff --git a/glabels/icons/24x24/actions/edit-cut.svg b/glabels/icons/flat/24x24/glabels-edit-cut.svg similarity index 100% rename from glabels/icons/24x24/actions/edit-cut.svg rename to glabels/icons/flat/24x24/glabels-edit-cut.svg diff --git a/glabels/icons/24x24/actions/edit-paste.svg b/glabels/icons/flat/24x24/glabels-edit-paste.svg similarity index 100% rename from glabels/icons/24x24/actions/edit-paste.svg rename to glabels/icons/flat/24x24/glabels-edit-paste.svg diff --git a/glabels/icons/24x24/actions/glabels-ellipse.svg b/glabels/icons/flat/24x24/glabels-ellipse.svg similarity index 100% rename from glabels/icons/24x24/actions/glabels-ellipse.svg rename to glabels/icons/flat/24x24/glabels-ellipse.svg diff --git a/glabels/icons/24x24/actions/file-new.svg b/glabels/icons/flat/24x24/glabels-file-new.svg similarity index 65% rename from glabels/icons/24x24/actions/file-new.svg rename to glabels/icons/flat/24x24/glabels-file-new.svg index f69edb73..8eb1ceff 100644 --- a/glabels/icons/24x24/actions/file-new.svg +++ b/glabels/icons/flat/24x24/glabels-file-new.svg @@ -5,35 +5,35 @@ d="M 5.5,2.5 5.5,20.5 19.5,20.5 19.5,7.5 14.5,2.5 Z" /> + + + + + + + + + diff --git a/glabels/icons/24x24/actions/file-save-as.svg b/glabels/icons/flat/24x24/glabels-file-save-as.svg similarity index 100% rename from glabels/icons/24x24/actions/file-save-as.svg rename to glabels/icons/flat/24x24/glabels-file-save-as.svg diff --git a/glabels/icons/24x24/actions/file-save.svg b/glabels/icons/flat/24x24/glabels-file-save.svg similarity index 100% rename from glabels/icons/24x24/actions/file-save.svg rename to glabels/icons/flat/24x24/glabels-file-save.svg diff --git a/glabels/icons/24x24/actions/glabels-format-text-bold.svg b/glabels/icons/flat/24x24/glabels-format-text-bold.svg similarity index 100% rename from glabels/icons/24x24/actions/glabels-format-text-bold.svg rename to glabels/icons/flat/24x24/glabels-format-text-bold.svg diff --git a/glabels/icons/24x24/actions/glabels-format-text-italic.svg b/glabels/icons/flat/24x24/glabels-format-text-italic.svg similarity index 100% rename from glabels/icons/24x24/actions/glabels-format-text-italic.svg rename to glabels/icons/flat/24x24/glabels-format-text-italic.svg diff --git a/glabels/icons/24x24/actions/glabels-format-text-underline.svg b/glabels/icons/flat/24x24/glabels-format-text-underline.svg similarity index 100% rename from glabels/icons/24x24/actions/glabels-format-text-underline.svg rename to glabels/icons/flat/24x24/glabels-format-text-underline.svg diff --git a/glabels/icons/24x24/actions/glabels-image.svg b/glabels/icons/flat/24x24/glabels-image.svg similarity index 100% rename from glabels/icons/24x24/actions/glabels-image.svg rename to glabels/icons/flat/24x24/glabels-image.svg diff --git a/glabels/icons/24x24/actions/glabels-line.svg b/glabels/icons/flat/24x24/glabels-line.svg similarity index 100% rename from glabels/icons/24x24/actions/glabels-line.svg rename to glabels/icons/flat/24x24/glabels-line.svg diff --git a/glabels/icons/24x24/actions/glabels-object-properties.svg b/glabels/icons/flat/24x24/glabels-object-properties.svg similarity index 100% rename from glabels/icons/24x24/actions/glabels-object-properties.svg rename to glabels/icons/flat/24x24/glabels-object-properties.svg diff --git a/glabels/icons/24x24/actions/glabels-text.svg b/glabels/icons/flat/24x24/glabels-text.svg similarity index 100% rename from glabels/icons/24x24/actions/glabels-text.svg rename to glabels/icons/flat/24x24/glabels-text.svg diff --git a/glabels/icons/24x24/actions/glabels-valign-text-bottom.svg b/glabels/icons/flat/24x24/glabels-valign-text-bottom.svg similarity index 100% rename from glabels/icons/24x24/actions/glabels-valign-text-bottom.svg rename to glabels/icons/flat/24x24/glabels-valign-text-bottom.svg diff --git a/glabels/icons/24x24/actions/glabels-valign-text-middle.svg b/glabels/icons/flat/24x24/glabels-valign-text-middle.svg similarity index 100% rename from glabels/icons/24x24/actions/glabels-valign-text-middle.svg rename to glabels/icons/flat/24x24/glabels-valign-text-middle.svg diff --git a/glabels/icons/24x24/actions/glabels-valign-text-top.svg b/glabels/icons/flat/24x24/glabels-valign-text-top.svg similarity index 100% rename from glabels/icons/24x24/actions/glabels-valign-text-top.svg rename to glabels/icons/flat/24x24/glabels-valign-text-top.svg diff --git a/glabels/icons/flat/32x32/glabels-collated.svg b/glabels/icons/flat/32x32/glabels-collated.svg new file mode 100644 index 00000000..c943fcf6 --- /dev/null +++ b/glabels/icons/flat/32x32/glabels-collated.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + diff --git a/glabels/icons/flat/32x32/glabels-file-new.svg b/glabels/icons/flat/32x32/glabels-file-new.svg new file mode 100644 index 00000000..525b04e4 --- /dev/null +++ b/glabels/icons/flat/32x32/glabels-file-new.svg @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/glabels/icons/flat/32x32/glabels-file-open.svg b/glabels/icons/flat/32x32/glabels-file-open.svg new file mode 100644 index 00000000..0efe1585 --- /dev/null +++ b/glabels/icons/flat/32x32/glabels-file-open.svg @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/glabels/icons/flat/32x32/glabels-file-recent.svg b/glabels/icons/flat/32x32/glabels-file-recent.svg new file mode 100644 index 00000000..95fc9503 --- /dev/null +++ b/glabels/icons/flat/32x32/glabels-file-recent.svg @@ -0,0 +1,16 @@ + + + + + + + + + + diff --git a/glabels/icons/32x32/actions/label-orientation-horiz.svg b/glabels/icons/flat/32x32/glabels-label-orientation-horiz.svg similarity index 100% rename from glabels/icons/32x32/actions/label-orientation-horiz.svg rename to glabels/icons/flat/32x32/glabels-label-orientation-horiz.svg diff --git a/glabels/icons/32x32/actions/label-orientation-vert.svg b/glabels/icons/flat/32x32/glabels-label-orientation-vert.svg similarity index 100% rename from glabels/icons/32x32/actions/label-orientation-vert.svg rename to glabels/icons/flat/32x32/glabels-label-orientation-vert.svg diff --git a/glabels/icons/flat/32x32/glabels-merge-group-contiguous.svg b/glabels/icons/flat/32x32/glabels-merge-group-contiguous.svg new file mode 100644 index 00000000..a69d6892 --- /dev/null +++ b/glabels/icons/flat/32x32/glabels-merge-group-contiguous.svg @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/glabels/icons/flat/32x32/glabels-merge-group-page.svg b/glabels/icons/flat/32x32/glabels-merge-group-page.svg new file mode 100644 index 00000000..7abe79dc --- /dev/null +++ b/glabels/icons/flat/32x32/glabels-merge-group-page.svg @@ -0,0 +1,30 @@ + + + + + + + + + + + + + + + + + + diff --git a/glabels/icons/32x32/actions/print.svg b/glabels/icons/flat/32x32/glabels-print.svg similarity index 100% rename from glabels/icons/32x32/actions/print.svg rename to glabels/icons/flat/32x32/glabels-print.svg diff --git a/glabels/icons/32x32/actions/select-product.svg b/glabels/icons/flat/32x32/glabels-select-product.svg similarity index 100% rename from glabels/icons/32x32/actions/select-product.svg rename to glabels/icons/flat/32x32/glabels-select-product.svg diff --git a/glabels/icons/flat/32x32/glabels-uncollated.svg b/glabels/icons/flat/32x32/glabels-uncollated.svg new file mode 100644 index 00000000..255e1657 --- /dev/null +++ b/glabels/icons/flat/32x32/glabels-uncollated.svg @@ -0,0 +1,19 @@ + + + + + + + + + diff --git a/glabels/icons/flat/48x48/glabels-edit.svg b/glabels/icons/flat/48x48/glabels-edit.svg new file mode 100644 index 00000000..01dfbf25 --- /dev/null +++ b/glabels/icons/flat/48x48/glabels-edit.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + diff --git a/glabels/icons/flat/48x48/glabels-merge.svg b/glabels/icons/flat/48x48/glabels-merge.svg new file mode 100644 index 00000000..55b75dcc --- /dev/null +++ b/glabels/icons/flat/48x48/glabels-merge.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/glabels/icons/flat/48x48/glabels-print.svg b/glabels/icons/flat/48x48/glabels-print.svg new file mode 100644 index 00000000..c2a45e75 --- /dev/null +++ b/glabels/icons/flat/48x48/glabels-print.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + diff --git a/glabels/icons/flat/48x48/glabels-properties.svg b/glabels/icons/flat/48x48/glabels-properties.svg new file mode 100644 index 00000000..27f5baab --- /dev/null +++ b/glabels/icons/flat/48x48/glabels-properties.svg @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/glabels/icons/flat/48x48/glabels-variables.svg b/glabels/icons/flat/48x48/glabels-variables.svg new file mode 100644 index 00000000..9b478135 --- /dev/null +++ b/glabels/icons/flat/48x48/glabels-variables.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + diff --git a/glabels/icons/16x16/mimetypes/x-glabels-project.svg b/glabels/icons/mimetypes/16x16/x-glabels-project.svg similarity index 100% rename from glabels/icons/16x16/mimetypes/x-glabels-project.svg rename to glabels/icons/mimetypes/16x16/x-glabels-project.svg diff --git a/glabels/icons/22x22/mimetypes/x-glabels-project.svg b/glabels/icons/mimetypes/22x22/x-glabels-project.svg similarity index 100% rename from glabels/icons/22x22/mimetypes/x-glabels-project.svg rename to glabels/icons/mimetypes/22x22/x-glabels-project.svg diff --git a/glabels/icons/24x24/mimetypes/x-glabels-project.svg b/glabels/icons/mimetypes/24x24/x-glabels-project.svg similarity index 100% rename from glabels/icons/24x24/mimetypes/x-glabels-project.svg rename to glabels/icons/mimetypes/24x24/x-glabels-project.svg diff --git a/glabels/icons/scalable/mimetypes/x-glabels-project.svg b/glabels/icons/mimetypes/scalable/x-glabels-project.svg similarity index 100% rename from glabels/icons/scalable/mimetypes/x-glabels-project.svg rename to glabels/icons/mimetypes/scalable/x-glabels-project.svg diff --git a/glabels/images/checkerboard.png b/glabels/images/checkerboard.png index c97f40e2..575edc36 100644 Binary files a/glabels/images/checkerboard.png and b/glabels/images/checkerboard.png differ diff --git a/glabels/main.cpp b/glabels/main.cpp index 1acdf1c8..67b9c7c7 100644 --- a/glabels/main.cpp +++ b/glabels/main.cpp @@ -45,7 +45,7 @@ int main( int argc, char **argv ) QCoreApplication::setOrganizationName( "glabels.org" ); QCoreApplication::setOrganizationDomain( "glabels.org" ); QCoreApplication::setApplicationName( "glabels-qt" ); - QCoreApplication::setApplicationVersion( glabels::model::Version::STRING ); + QCoreApplication::setApplicationVersion( glabels::model::Version::LONG_STRING ); // // Setup translators diff --git a/glabels/ui/EditVariableDialog.ui b/glabels/ui/EditVariableDialog.ui new file mode 100644 index 00000000..72f85752 --- /dev/null +++ b/glabels/ui/EditVariableDialog.ui @@ -0,0 +1,274 @@ + + + EditVariableDialog + + + + 0 + 0 + 469 + 297 + + + + Dialog + + + + 12 + + + + + Increment + + + + + + + + + + + Step size: + + + + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Variable + + + + + + + + + + + Value: + + + + + + + Name: + + + + + + + + + + Type: + + + + + + + + + + + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + glabels::ColorButton + QPushButton +
ColorButton.h
+ + colorChanged() + +
+
+ + + + buttonBox + accepted() + EditVariableDialog + accept() + + + 236 + 287 + + + 157 + 236 + + + + + buttonBox + rejected() + EditVariableDialog + reject() + + + 304 + 287 + + + 286 + 236 + + + + + typeCombo + currentIndexChanged(int) + EditVariableDialog + onTypeComboChanged() + + + 252 + 70 + + + 33 + 161 + + + + + incrementCombo + currentIndexChanged(int) + EditVariableDialog + onIncrementComboChanged() + + + 100 + 223 + + + 97 + 176 + + + + + stepSizeEdit + textChanged(QString) + EditVariableDialog + onStepSizeEditChanged() + + + 440 + 223 + + + 333 + 166 + + + + + nameEdit + textChanged(QString) + EditVariableDialog + onNameEditChanged() + + + 440 + 103 + + + 393 + 165 + + + + + valueEdit + textChanged(QString) + EditVariableDialog + onValueEditChanged() + + + 318 + 129 + + + 459 + 157 + + + + + colorValueButton + colorChanged() + EditVariableDialog + onColorValueButtonChanged() + + + 406 + 114 + + + 458 + 122 + + + + + + onTypeComboChanged() + onValueEditChanged() + onIncrementComboChanged() + onStepSizeEditChanged() + onNameEditChanged() + onColorValueButtonChanged() + +
diff --git a/glabels/ui/MergeView.ui b/glabels/ui/MergeView.ui index e73b77aa..c7f682a3 100644 --- a/glabels/ui/MergeView.ui +++ b/glabels/ui/MergeView.ui @@ -11,12 +11,21 @@
- Form + Form - + + 12 + + + 12 + + + 12 + + 12 @@ -37,16 +46,9 @@ Source - - + + - - - - Location - - - @@ -54,6 +56,27 @@ + + + + + + + + + true + + + + + + + Browse... + + + + + @@ -61,24 +84,8 @@ - - - - - - - Qt::Horizontal - - - - 360 - 20 - - - - @@ -89,7 +96,11 @@ - + + + Qt::NoFocus + + @@ -138,8 +149,8 @@ onSelectAllButtonClicked() - 63 - 571 + 97 + 570 69 @@ -164,34 +175,34 @@ - locationButton - clicked() + formatCombo + activated(int) MergeView - onLocationButtonClicked() + onFormatComboActivated() - 174 - 93 + 257 + 109 - 570 - 75 + 563 + 50 - formatCombo - activated(int) + locationBrowseButton + clicked() MergeView - onFormatComboActivated() + onLocationBrowseButtonClicked() - 162 - 48 + 296 + 130 - 563 - 50 + 565 + 149 @@ -199,7 +210,7 @@ onSelectAllButtonClicked() onUnselectAllButtonClicked() - onLocationButtonClicked() onFormatComboActivated() + onLocationBrowseButtonClicked() diff --git a/glabels/ui/ObjectEditor.ui b/glabels/ui/ObjectEditor.ui index c2d366d3..29ca0985 100644 --- a/glabels/ui/ObjectEditor.ui +++ b/glabels/ui/ObjectEditor.ui @@ -7,7 +7,7 @@ 0 0 400 - 640 + 648
@@ -29,9 +29,9 @@ - Form + Form - + @@ -46,7 +46,7 @@ - :/icons/24x24/actions/glabels-object-properties.png + :/icons/flat/24x24/glabels-object-properties.png @@ -107,7 +107,7 @@ - :/icons/22x22/actions/glabels-align-text-left.svg:/icons/22x22/actions/glabels-align-text-left.svg + :/icons/flat/22x22/glabels-align-text-left.svg:/icons/flat/22x22/glabels-align-text-left.svg @@ -133,7 +133,7 @@ - :/icons/22x22/actions/glabels-align-text-center.svg:/icons/22x22/actions/glabels-align-text-center.svg + :/icons/flat/22x22/glabels-align-text-center.svg:/icons/flat/22x22/glabels-align-text-center.svg @@ -156,7 +156,7 @@ - :/icons/22x22/actions/glabels-align-text-right.svg:/icons/22x22/actions/glabels-align-text-right.svg + :/icons/flat/22x22/glabels-align-text-right.svg:/icons/flat/22x22/glabels-align-text-right.svg @@ -186,7 +186,7 @@ - :/icons/22x22/actions/glabels-valign-text-top.svg:/icons/22x22/actions/glabels-valign-text-top.svg + :/icons/flat/22x22/glabels-valign-text-top.svg:/icons/flat/22x22/glabels-valign-text-top.svg @@ -212,7 +212,7 @@ - :/icons/22x22/actions/glabels-valign-text-middle.svg:/icons/22x22/actions/glabels-valign-text-middle.svg + :/icons/flat/22x22/glabels-valign-text-middle.svg:/icons/flat/22x22/glabels-valign-text-middle.svg @@ -235,7 +235,7 @@ - :/icons/22x22/actions/glabels-valign-text-bottom.svg:/icons/22x22/actions/glabels-valign-text-bottom.svg + :/icons/flat/22x22/glabels-valign-text-bottom.svg:/icons/flat/22x22/glabels-valign-text-bottom.svg @@ -389,7 +389,7 @@ - :/icons/22x22/actions/glabels-format-text-bold.svg:/icons/22x22/actions/glabels-format-text-bold.svg + :/icons/flat/22x22/glabels-format-text-bold.svg:/icons/flat/22x22/glabels-format-text-bold.svg @@ -412,7 +412,7 @@ - :/icons/22x22/actions/glabels-format-text-italic.svg:/icons/22x22/actions/glabels-format-text-italic.svg + :/icons/flat/22x22/glabels-format-text-italic.svg:/icons/flat/22x22/glabels-format-text-italic.svg @@ -432,7 +432,7 @@ - :/icons/22x22/actions/glabels-format-text-underline.svg:/icons/22x22/actions/glabels-format-text-underline.svg + :/icons/flat/22x22/glabels-format-text-underline.svg:/icons/flat/22x22/glabels-format-text-underline.svg @@ -579,7 +579,11 @@ - + + + Insert field + + @@ -729,7 +733,11 @@ - + + + Insert field + + @@ -758,14 +766,27 @@ Image - + + + + Qt::Vertical + + + + 20 + 646 + + + + + File - + - + @@ -789,68 +810,47 @@ - - - - - - 0 - 0 - - - - Select File... - - - - - - - - 0 - 0 - - - - or - - - - - - - - 0 - 0 - - - - - Select Merge Field... - - - - - + + + + 0 + 0 + + + + Browse... + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Use field + + - - - - Qt::Vertical - - - - 20 - 646 - - - - @@ -1202,16 +1202,7 @@ 15 - - 0 - - - 0 - - - 0 - - + 0 @@ -1530,19 +1521,19 @@ - glabels::FieldButton - QComboBox -
FieldButton.h
+ glabels::BarcodeMenuButton + QPushButton +
BarcodeMenuButton.h
- keySelected(QString) + selectionChanged()
- glabels::BarcodeMenuButton + glabels::FieldButton QPushButton -
BarcodeMenuButton.h
+
FieldButton.h
- selectionChanged() + keySelected(QString)
@@ -1653,8 +1644,8 @@ onTextControlsChanged() - 157 - 333 + 160 + 332 396 @@ -1669,8 +1660,8 @@ onTextControlsChanged() - 198 - 333 + 200 + 332 398 @@ -1685,8 +1676,8 @@ onTextControlsChanged() - 238 - 333 + 240 + 332 395 @@ -1701,8 +1692,8 @@ onTextControlsChanged() - 284 - 333 + 286 + 332 393 @@ -1717,8 +1708,8 @@ onTextControlsChanged() - 325 - 333 + 326 + 332 396 @@ -1734,7 +1725,7 @@ 365 - 333 + 332 397 @@ -1749,8 +1740,8 @@ onTextControlsChanged() - 184 - 407 + 189 + 404 394 @@ -1766,7 +1757,7 @@ 178 - 143 + 139 392 @@ -1781,8 +1772,8 @@ onLineControlsChanged() - 137 - 179 + 136 + 174 1 @@ -1797,8 +1788,8 @@ onFillControlsChanged() - 136 - 263 + 135 + 256 6 @@ -1813,8 +1804,8 @@ onPositionControlsChanged() - 159 - 142 + 160 + 138 399 @@ -1829,8 +1820,8 @@ onPositionControlsChanged() - 159 - 179 + 160 + 174 325 @@ -1845,8 +1836,8 @@ onRectSizeControlsChanged() - 159 - 265 + 160 + 258 3 @@ -1861,8 +1852,8 @@ onRectSizeControlsChanged() - 159 - 302 + 160 + 294 0 @@ -1877,8 +1868,8 @@ onResetImageSize() - 210 - 372 + 213 + 362 4 @@ -1909,8 +1900,8 @@ onShadowControlsChanged() - 165 - 142 + 166 + 138 398 @@ -1925,8 +1916,8 @@ onShadowControlsChanged() - 165 - 179 + 166 + 174 294 @@ -1941,8 +1932,8 @@ onShadowControlsChanged() - 142 - 215 + 141 + 209 399 @@ -1957,8 +1948,8 @@ onShadowControlsChanged() - 159 - 252 + 162 + 245 399 @@ -1973,8 +1964,8 @@ onLineSizeControlsChanged() - 174 - 456 + 177 + 444 5 @@ -1989,8 +1980,8 @@ onLineSizeControlsChanged() - 174 - 493 + 177 + 480 1 @@ -1999,14 +1990,14 @@ - imageFileButton + imageBrowseButton clicked() ObjectEditor onImageFileButtonClicked() - 133 - 175 + 367 + 135 394 @@ -2014,22 +2005,6 @@ - - imageFieldCombo - keySelected(QString) - ObjectEditor - onImageKeySelected(QString) - - - 302 - 175 - - - 397 - 32 - - - textEdit textChanged() @@ -2046,22 +2021,6 @@ - - textInsertFieldCombo - keySelected(QString) - ObjectEditor - onTextInsertFieldKeySelected(QString) - - - 239 - 599 - - - 395 - 645 - - - barcodeShowTextCheck toggled(bool) @@ -2069,8 +2028,8 @@ onBarcodeControlsChanged() - 178 - 172 + 195 + 167 4 @@ -2085,8 +2044,8 @@ onBarcodeControlsChanged() - 164 - 204 + 195 + 198 1 @@ -2102,7 +2061,7 @@ 126 - 239 + 232 1 @@ -2126,22 +2085,6 @@ - - barcodeInsertFieldCombo - keySelected(QString) - ObjectEditor - onBarcodeInsertFieldKeySelected(QString) - - - 239 - 400 - - - 403 - 625 - - - barcodeStyleButton selectionChanged() @@ -2149,8 +2092,8 @@ onBarcodeControlsChanged() - 178 - 140 + 195 + 136 5 @@ -2190,6 +2133,70 @@ + + sizeAspectCheck + toggled(bool) + ObjectEditor + onRectSizeControlsChanged() + + + 231 + 289 + + + 199 + 319 + + + + + textInsertFieldButton + keySelected(QString) + ObjectEditor + onTextInsertFieldKeySelected(QString) + + + 191 + 589 + + + 227 + 642 + + + + + barcodeInsertFieldButton + keySelected(QString) + ObjectEditor + onBarcodeInsertFieldKeySelected(QString) + + + 208 + 379 + + + 205 + 649 + + + + + imageFieldButton + keySelected(QString) + ObjectEditor + onImageKeySelected(QString) + + + 317 + 160 + + + 331 + -12 + + + onChanged() @@ -2206,5 +2213,6 @@ onTextInsertFieldKeySelected(QString) onBarcodeControlsChanged() onBarcodeInsertFieldKeySelected(QString) + onImageComboChanged() diff --git a/glabels/ui/PrintView.ui b/glabels/ui/PrintView.ui index bf4e3ef4..f0611775 100644 --- a/glabels/ui/PrintView.ui +++ b/glabels/ui/PrintView.ui @@ -6,8 +6,8 @@ 0 0 - 839 - 769 + 852 + 796
@@ -17,112 +17,46 @@ - Form + Form - - - + + + + + 9 + QLayout::SetDefaultConstraint + + 12 + + + 12 + + + 12 + + + 12 + - + - + 0 0 - - - 400 - 520 - - - - - - - - 16 - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - Page - - - - - - - 1 - - - - - - - of - - - - - - - nn - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - QLayout::SetDefaultConstraint - - - 12 - - - <span style=" font-size:18pt;">Print</span> - + - + 0 0 @@ -135,41 +69,53 @@ - 350 + 16777215 16777215 - Copies + Print range - - + + + + 9 + - + - Copies: + Pages + + + true + + buttonGroup + - + + + QAbstractSpinBox::UpDownArrows + + + true + 1 - 96 + 250 - + Qt::Horizontal - - QSizePolicy::MinimumExpanding - 40 @@ -180,43 +126,116 @@ - - - - (Will print a total of xx items on nn pages.) - - - - - + + - + - Start on position: + Positions + + buttonGroup + - + + + + 0 + 0 + + + + + 60 + 0 + + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + 1 - + - on 1st page + to + + + + + + + + 0 + 0 + + + + + 60 + 0 + + + + 1 - + Qt::Horizontal - - QSizePolicy::MinimumExpanding + + + 40 + 20 + + + + + + + + + + + + + Merge control + + + + + + 9 + + + + + Copies: + + + + + + + 1 + + + 9999 + + + + + + + Qt::Horizontal @@ -228,13 +247,75 @@ + + + + text-align:left; padding:3px; + + + + 32 + 32 + + + + + Uncollated (e.g. 1,1,1 2,2,2 3,3,3) + + + + :/icons/flat/32x32/glabels-uncollated.svg:/icons/flat/32x32/glabels-uncollated.svg + + + + + Collated (e.g. 1,2,3 1,2,3 1,2,3) + + + + :/icons/flat/32x32/glabels-collated.svg:/icons/flat/32x32/glabels-collated.svg + + + + + + + + text-align:left; padding:3px; + + + + 32 + 32 + + + + + Merge groups are contiguous + + + + :/icons/flat/32x32/glabels-merge-group-contiguous.svg:/icons/flat/32x32/glabels-merge-group-contiguous.svg + + + + + Merge groups start on a new page + + + + :/icons/flat/32x32/glabels-merge-group-page.svg:/icons/flat/32x32/glabels-merge-group-page.svg + + + + - + 0 0 @@ -247,15 +328,15 @@ - 350 + 16777215 16777215 - Print options + Options - + @@ -278,22 +359,101 @@ + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + Start groups at position: + + + + + + + + 0 + 0 + + + + 1 + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 12 + + + + + + + 0 + 0 + + text-align:left; padding:3px; - Print + Print... - :/icons/32x32/actions/print.svg:/icons/32x32/actions/print.svg + :/icons/flat/32x32/glabels-print.svg:/icons/flat/32x32/glabels-print.svg @@ -301,8 +461,63 @@ 32 + + true + + + + + + + Qt::Horizontal + + + + 2 + 20 + + + + + + + + + 0 + 0 + + + + + 330 + 0 + + + + <html><head/><body><p>(Will print a total of xxxx items on nnn pages.)</p></body></html> + + + Qt::AlignCenter + + + + + + + Qt::Horizontal + + + + 2 + 20 + + + + + + @@ -318,13 +533,97 @@ - + Qt::Vertical + + + + QLayout::SetDefaultConstraint + + + + + + 0 + 0 + + + + + 400 + 520 + + + + + + + + 16 + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Page + + + + + + + 1 + + + + + + + of + + + + + + + nn + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + +
@@ -340,30 +639,14 @@ - copiesStartSpin - valueChanged(int) - PrintView - onFormChanged() - - - 221 - 124 - - - 4 - 309 - - - - - copiesSpin + printRangePagesSpin valueChanged(int) PrintView onFormChanged() - 161 - 84 + 175 + 122 2 @@ -378,8 +661,8 @@ onFormChanged() - 114 - 220 + 182 + 444 2 @@ -394,8 +677,8 @@ onFormChanged() - 84 - 248 + 152 + 475 6 @@ -410,8 +693,8 @@ onFormChanged() - 88 - 276 + 156 + 506 6 @@ -426,8 +709,8 @@ onFormChanged() - 645 - 745 + 641 + 760 1 @@ -442,8 +725,8 @@ onPrintButtonClicked() - 249 - 325 + 291 + 589 5 @@ -451,10 +734,125 @@ + + mergeCopiesSpin + valueChanged(int) + PrintView + onFormChanged() + + + 159 + 243 + + + 7 + 218 + + + + + printRangePagesRadio + toggled(bool) + PrintView + onFormChanged() + + + 59 + 108 + + + 6 + 76 + + + + + printRangeStartPositionSpin + valueChanged(int) + PrintView + onFormChanged() + + + 142 + 149 + + + 5 + 123 + + + + + printRangeLastPositionSpin + valueChanged(int) + PrintView + onFormChanged() + + + 226 + 145 + + + 8 + 155 + + + + + mergeCollateCombo + currentIndexChanged(int) + PrintView + onFormChanged() + + + 52 + 261 + + + 3 + 192 + + + + + mergeGroupCombo + currentIndexChanged(int) + PrintView + onFormChanged() + + + 231 + 314 + + + 5 + 378 + + + + + mergeStartPositionSpin + valueChanged(int) + PrintView + onFormChanged() + + + 199 + 362 + + + 4 + 309 + + + onPrintButtonClicked() onFormChanged() onPrinterPropertiesButtonClicked() + + + diff --git a/glabels/ui/PropertiesView.ui b/glabels/ui/PropertiesView.ui index c005184e..64817333 100644 --- a/glabels/ui/PropertiesView.ui +++ b/glabels/ui/PropertiesView.ui @@ -6,7 +6,7 @@ 0 0 - 806 + 886 742 @@ -23,9 +23,9 @@ - Form + Form - + @@ -42,7 +42,16 @@ - + + 12 + + + 12 + + + 12 + + 12 @@ -232,6 +241,12 @@ + + + 330 + 0 + + <html><head/><body><p>Select another product for this gLabels project.</p></body></html> @@ -243,7 +258,7 @@ - :/icons/32x32/actions/select-product.svg:/icons/32x32/actions/select-product.svg + :/icons/flat/32x32/glabels-select-product.svg:/icons/flat/32x32/glabels-select-product.svg @@ -256,6 +271,51 @@ + + + + Adjustable Parameters + + + + 12 + + + + + false + + + + + + in + + + + + + + Label length: + + + + + + + Qt::Horizontal + + + + 10 + 20 + + + + + + + @@ -293,7 +353,7 @@ - :/icons/32x32/actions/label-orientation-horiz.svg:/icons/32x32/actions/label-orientation-horiz.svg + :/icons/flat/32x32/glabels-label-orientation-horiz.svg:/icons/flat/32x32/glabels-label-orientation-horiz.svg @@ -302,7 +362,7 @@ - :/icons/32x32/actions/label-orientation-vert.svg:/icons/32x32/actions/label-orientation-vert.svg + :/icons/flat/32x32/glabels-label-orientation-vert.svg:/icons/flat/32x32/glabels-label-orientation-vert.svg @@ -385,8 +445,8 @@ onChangeProductButtonClicked() - 309 - 239 + 342 + 291 728 @@ -401,8 +461,8 @@ onOrientationActivated() - 308 - 334 + 342 + 467 802 @@ -410,9 +470,26 @@ + + lengthSpin + valueChanged(double) + PropertiesView + onLengthSpinChanged() + + + 188 + 349 + + + 801 + 376 + + + onChangeProductButtonClicked() onOrientationActivated() + onLengthSpinChanged() diff --git a/glabels/ui/ReportBugDialog.ui b/glabels/ui/ReportBugDialog.ui new file mode 100644 index 00000000..e06816d7 --- /dev/null +++ b/glabels/ui/ReportBugDialog.ui @@ -0,0 +1,294 @@ + + + ReportBugDialog + + + + 0 + 0 + 586 + 675 + + + + + 0 + 0 + + + + + 575 + 675 + + + + gLabels - Report a Bug + + + + 12 + + + 12 + + + 12 + + + 12 + + + 12 + + + + + Title + + + + + + + pp1 + + + true + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + &Launch Issue Tracker + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + pp2 + + + true + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + + 400 + 250 + + + + false + + + Qt::NoTextInteraction + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Copy + + + + :/icons/flat/24x24/glabels-edit-copy.svg:/icons/flat/24x24/glabels-edit-copy.svg + + + + 24 + 24 + + + + Ctrl+C + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + + pp3 + + + true + + + + + + + Qt::Vertical + + + + 20 + 118 + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Close + + + + + + + + + + + buttonBox + accepted() + ReportBugDialog + accept() + + + 230 + 649 + + + 157 + 274 + + + + + buttonBox + rejected() + ReportBugDialog + reject() + + + 298 + 655 + + + 286 + 274 + + + + + websiteButton + clicked() + ReportBugDialog + onWebsiteButtonClicked() + + + 351 + 94 + + + 566 + 103 + + + + + copyButton + clicked() + ReportBugDialog + onCopyButtonClicked() + + + 330 + 437 + + + 514 + 500 + + + + + + onWebsiteButtonClicked() + onCopyButtonClicked() + + diff --git a/glabels/ui/StartupView.ui b/glabels/ui/StartupView.ui index 58fc3f77..f339483b 100644 --- a/glabels/ui/StartupView.ui +++ b/glabels/ui/StartupView.ui @@ -7,11 +7,11 @@ 0 0 460 - 397 + 418
- Form + Form @@ -136,16 +136,16 @@ - New Project + New... - :/icons/24x24/actions/file-new.svg:/icons/24x24/actions/file-new.svg + :/icons/flat/32x32/glabels-file-new.svg:/icons/flat/32x32/glabels-file-new.svg - 24 - 24 + 32 + 32 @@ -168,16 +168,16 @@ - Open Project + Browse... - :/icons/24x24/actions/file-open.svg:/icons/24x24/actions/file-open.svg + :/icons/flat/32x32/glabels-file-open.svg:/icons/flat/32x32/glabels-file-open.svg - 24 - 24 + 32 + 32 @@ -185,6 +185,32 @@ + + + + + 319 + 59 + + + + Recent + + + + :/icons/flat/32x32/glabels-file-recent.svg:/icons/flat/32x32/glabels-file-recent.svg + + + + 32 + 32 + + + + Open a recent gLabels project + + + diff --git a/glabels/ui/TemplateDesignerApplyPage.ui b/glabels/ui/TemplateDesignerApplyPage.ui index d0ed63b9..3b1b9132 100644 --- a/glabels/ui/TemplateDesignerApplyPage.ui +++ b/glabels/ui/TemplateDesignerApplyPage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerCdPage.ui b/glabels/ui/TemplateDesignerCdPage.ui index f61fa315..a9add20f 100644 --- a/glabels/ui/TemplateDesignerCdPage.ui +++ b/glabels/ui/TemplateDesignerCdPage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerContinuousPage.ui b/glabels/ui/TemplateDesignerContinuousPage.ui new file mode 100644 index 00000000..d3cc1123 --- /dev/null +++ b/glabels/ui/TemplateDesignerContinuousPage.ui @@ -0,0 +1,59 @@ + + + TemplateDesignerContinuousPage + + + + 0 + 0 + 640 + 400 + + + + + 0 + 0 + + + + + 640 + 400 + + + + Form + + + + 18 + + + + + <html><head/><body><p>Click &quot;Cancel&quot; to quit, or click &quot;Back&quot; to begin with a different product.</p></body></html> + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/glabels/ui/TemplateDesignerEllipsePage.ui b/glabels/ui/TemplateDesignerEllipsePage.ui index 53dec92c..0ea9ee54 100644 --- a/glabels/ui/TemplateDesignerEllipsePage.ui +++ b/glabels/ui/TemplateDesignerEllipsePage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerIntroPage.ui b/glabels/ui/TemplateDesignerIntroPage.ui index 4f9a594b..9b397cbd 100644 --- a/glabels/ui/TemplateDesignerIntroPage.ui +++ b/glabels/ui/TemplateDesignerIntroPage.ui @@ -23,7 +23,7 @@ - Form + Form @@ -65,7 +65,7 @@ New Product - Create a a new product template from scratch + Create a new product template from scratch diff --git a/glabels/ui/TemplateDesignerNLayoutsPage.ui b/glabels/ui/TemplateDesignerNLayoutsPage.ui index df42ffda..e88dd2ff 100644 --- a/glabels/ui/TemplateDesignerNLayoutsPage.ui +++ b/glabels/ui/TemplateDesignerNLayoutsPage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerNamePage.ui b/glabels/ui/TemplateDesignerNamePage.ui index 9bf0d133..a9ed4d7d 100644 --- a/glabels/ui/TemplateDesignerNamePage.ui +++ b/glabels/ui/TemplateDesignerNamePage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerOneLayoutPage.ui b/glabels/ui/TemplateDesignerOneLayoutPage.ui index 11a7bba6..315266df 100644 --- a/glabels/ui/TemplateDesignerOneLayoutPage.ui +++ b/glabels/ui/TemplateDesignerOneLayoutPage.ui @@ -23,7 +23,7 @@ - Form + Form @@ -162,7 +162,7 @@ - :/icons/32x32/actions/print.svg:/icons/32x32/actions/print.svg + :/icons/flat/32x32/glabels-print.svg:/icons/flat/32x32/glabels-print.svg diff --git a/glabels/ui/TemplateDesignerPageSizePage.ui b/glabels/ui/TemplateDesignerPageSizePage.ui index c65edaaa..41686e71 100644 --- a/glabels/ui/TemplateDesignerPageSizePage.ui +++ b/glabels/ui/TemplateDesignerPageSizePage.ui @@ -23,83 +23,110 @@ - Form + Form - - - - - - - Page size: - - - - - - - Width: - - - - - - - true - - - in - - - - - - - Height: - - - - - - - true - - - in - - - - - - - - - - + + + - Qt::Horizontal + Qt::Vertical - 182 - 20 + 20 + 173 - - + + - Qt::Vertical + Qt::Horizontal - 20 - 173 + 182 + 20 + + + + 12 + + + + + + + true + + + in + + + + + + + Roll width: + + + + + + + Height: + + + + + + + + + + Width: + + + + + + + in + + + + + + + + 0 + 0 + + + + true + + + in + + + + + + + Page size: + + + + + + + diff --git a/glabels/ui/TemplateDesignerPathPage.ui b/glabels/ui/TemplateDesignerPathPage.ui new file mode 100644 index 00000000..3b59a4c7 --- /dev/null +++ b/glabels/ui/TemplateDesignerPathPage.ui @@ -0,0 +1,59 @@ + + + TemplateDesignerPathPage + + + + 0 + 0 + 640 + 400 + + + + + 0 + 0 + + + + + 640 + 400 + + + + Form + + + + 18 + + + + + <html><head/><body><p>Click &quot;Cancel&quot; to quit, or click &quot;Back&quot; to begin with a different product.</p></body></html> + + + true + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/glabels/ui/TemplateDesignerRectPage.ui b/glabels/ui/TemplateDesignerRectPage.ui index d8b21324..a4a47589 100644 --- a/glabels/ui/TemplateDesignerRectPage.ui +++ b/glabels/ui/TemplateDesignerRectPage.ui @@ -23,22 +23,22 @@ - Form + Form - - + + - 2. Height: + 4. Horizontal waste: - - + + true @@ -47,18 +47,22 @@ - - - - true + + + + 3. Corner radius - - in + + + + + + 1. Width: - - + + true @@ -67,8 +71,8 @@ - - + + true @@ -77,15 +81,15 @@ - - + + - 1. Width: + 5. Vertical waste: - - + + true @@ -94,24 +98,27 @@ - - - - 4. Horizontal waste: + + + + true + + + in - - + + - 3. Corner radius + 2. Height: - 6. Margin: + 6. Margin (X): @@ -125,10 +132,20 @@ - - + + - 5. Vertical waste: + 7. Margin (Y): + + + + + + + true + + + in diff --git a/glabels/ui/TemplateDesignerRoundPage.ui b/glabels/ui/TemplateDesignerRoundPage.ui index c51aa036..2a6651da 100644 --- a/glabels/ui/TemplateDesignerRoundPage.ui +++ b/glabels/ui/TemplateDesignerRoundPage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerShapePage.ui b/glabels/ui/TemplateDesignerShapePage.ui index 0466fbb9..a471d6c8 100644 --- a/glabels/ui/TemplateDesignerShapePage.ui +++ b/glabels/ui/TemplateDesignerShapePage.ui @@ -23,7 +23,7 @@ - Form + Form diff --git a/glabels/ui/TemplateDesignerTwoLayoutPage.ui b/glabels/ui/TemplateDesignerTwoLayoutPage.ui index 6483d3e5..e3cc62a3 100644 --- a/glabels/ui/TemplateDesignerTwoLayoutPage.ui +++ b/glabels/ui/TemplateDesignerTwoLayoutPage.ui @@ -23,7 +23,7 @@ - Form + Form @@ -200,7 +200,7 @@ - :/icons/32x32/actions/print.svg:/icons/32x32/actions/print.svg + :/icons/flat/32x32/glabels-print.svg:/icons/flat/32x32/glabels-print.svg diff --git a/glabels/ui/VariablesView.ui b/glabels/ui/VariablesView.ui new file mode 100644 index 00000000..c0953d66 --- /dev/null +++ b/glabels/ui/VariablesView.ui @@ -0,0 +1,190 @@ + + + VariablesView + + + + 0 + 0 + 1105 + 605 + + + + Form + + + + 21 + + + 21 + + + 21 + + + 21 + + + + + + 0 + 0 + + + + <html><head/><body><p><span style=" font-size:18pt;">Variables</span></p></body></html> + + + + + + + Qt::NoFocus + + + QAbstractItemView::SingleSelection + + + QAbstractItemView::SelectRows + + + 0 + + + false + + + true + + + + + + + + + <html><head/><body><p>Add variable</p></body></html> + + + Add + + + + + + + <html><head/><body><p>Edit selected variable</p></body></html> + + + Edit + + + + + + + <html><head/><body><p>Delete selected variable</p></body></html> + + + Delete + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + addButton + clicked() + VariablesView + onAddButtonClicked() + + + 63 + 586 + + + 98 + 598 + + + + + editButton + clicked() + VariablesView + onEditButtonClicked() + + + 167 + 576 + + + 317 + 608 + + + + + deleteButton + clicked() + VariablesView + onDeleteButtonClicked() + + + 245 + 575 + + + 508 + 613 + + + + + table + itemSelectionChanged() + VariablesView + onTableSelectionChanged() + + + 380 + 258 + + + 787 + 610 + + + + + + onSelectAllButtonClicked() + onUnselectAllButtonClicked() + onLocationButtonClicked() + onFormatComboActivated() + onAddButtonClicked() + onEditButtonClicked() + onDeleteButtonClicked() + onTableSelectionChanged() + + diff --git a/glbarcode/Barcode2dBase.cpp b/glbarcode/Barcode2dBase.cpp index 90f8d3e1..1af6b141 100644 --- a/glbarcode/Barcode2dBase.cpp +++ b/glbarcode/Barcode2dBase.cpp @@ -33,7 +33,7 @@ using namespace glbarcode::Constants; namespace { - const double MIN_CELL_SIZE = ( 0.0625 * PTS_PER_INCH ); + const double MIN_CELL_SIZE = ( 1.0/64.0 * PTS_PER_INCH ); } diff --git a/glbarcode/BarcodeCepnet.h b/glbarcode/BarcodeCepnet.h index 211be3cd..2043dfe3 100644 --- a/glbarcode/BarcodeCepnet.h +++ b/glbarcode/BarcodeCepnet.h @@ -31,9 +31,9 @@ namespace glbarcode /** * @class BarcodeCepnet BarcodeCepnet.h glbarcode/BarcodeCepnet.h * - * 8 digit *CEPNET* barcode (Brazillian Post, based on POSTNET), extends BarcodePostnet + * 8 digit *CEPNET* barcode (Brazilian Post, based on POSTNET), extends BarcodePostnet * - * @image html sample-cepnet.svg "Sample Brazillian Post CEPNET Barcode" + * @image html sample-cepnet.svg "Sample Brazilian Post CEPNET Barcode" * * * ### Input Data Format ### diff --git a/glbarcode/BarcodeCode39.cpp b/glbarcode/BarcodeCode39.cpp index 953987d0..821126c8 100644 --- a/glbarcode/BarcodeCode39.cpp +++ b/glbarcode/BarcodeCode39.cpp @@ -85,14 +85,14 @@ namespace const std::string frameSymbol = "NwNnWnWnN"; /* Vectorization constants */ - const double MIN_X = ( 0.01 * PTS_PER_INCH ); + const double MIN_X = ( 0.0075 * PTS_PER_INCH ); const double N = 2.5; const double MIN_I = MIN_X; - const double MIN_HEIGHT = ( 0.25 * PTS_PER_INCH ); - const double MIN_QUIET = ( 0.10 * PTS_PER_INCH ); + const double MIN_HEIGHT = ( 0.19685 * PTS_PER_INCH ); + const double MIN_QUIET = ( 10 * MIN_X ); - const double MIN_TEXT_AREA_HEIGHT = 14.0; - const double MIN_TEXT_SIZE = 10.0; + const double MIN_TEXT_AREA_HEIGHT = 12.0; + const double MIN_TEXT_SIZE = 8.0; } diff --git a/glbarcode/BarcodePostnet.cpp b/glbarcode/BarcodePostnet.cpp index 90497da9..1815fd85 100644 --- a/glbarcode/BarcodePostnet.cpp +++ b/glbarcode/BarcodePostnet.cpp @@ -118,7 +118,7 @@ namespace glbarcode /* Left frame bar */ code += frameSymbol; - /* process each digit, adding approptiate symbol */ + /* process each digit, adding appropriate symbol */ int sum = 0; for (char c : cookedData) { diff --git a/glbarcode/BarcodeUpcBase.cpp b/glbarcode/BarcodeUpcBase.cpp index 177a62fa..8d15e9a8 100644 --- a/glbarcode/BarcodeUpcBase.cpp +++ b/glbarcode/BarcodeUpcBase.cpp @@ -192,15 +192,18 @@ namespace glbarcode { std::string displayText; - for (char c : rawData) + if ( showText() ) { - if ( isdigit( c ) ) + for (char c : rawData) { - displayText += c; + if ( isdigit( c ) ) + { + displayText += c; + } } - } - displayText += (mCheckDigitVal + '0'); + displayText += (mCheckDigitVal + '0'); + } return displayText; } @@ -238,23 +241,10 @@ namespace glbarcode double xQuiet = mscale * QUIET_MODULES; /* determine bar height */ - double hTextArea = scale * BASE_TEXT_AREA_HEIGHT; + double hTextArea = showText() ? scale * BASE_TEXT_AREA_HEIGHT : 0; double hBar1 = std::max( (h - hTextArea), width/2 ); double hBar2 = hBar1 + hTextArea/2; - /* determine text parameters */ - double textSize1 = scale * BASE_FONT_SIZE; - double textSize2 = 0.75*textSize1; - - double textX1Left = xQuiet + mscale*(0.25*nModules + 0.5*mEndBarsModules - 0.75); - double textX1Right = xQuiet + mscale*(0.75*nModules - 0.5*mEndBarsModules + 0.75); - double textX2Left = 0.5*xQuiet; - double textX2Right = 1.5*xQuiet + mscale*nModules; - - double textY1 = hBar2 + textSize1/4; - double textY2 = hBar2 + textSize2/4; - - /* now traverse the code string and draw each bar */ auto nBarsSpaces = int( codedData.size() - 1 ); /* coded data has dummy "0" on end. */ @@ -283,11 +273,29 @@ namespace glbarcode xModules += wSpace; } - /* draw text (call implementation from concrete class) */ - vectorizeText( displayText, - textSize1, textSize2, - textX1Left, textX1Right, textY1, - textX2Left, textX2Right, textY2 ); + + /* draw text */ + if ( showText() ) + { + /* determine text parameters */ + double textSize1 = scale * BASE_FONT_SIZE; + double textSize2 = 0.75*textSize1; + + double textX1Left = xQuiet + mscale*(0.25*nModules + 0.5*mEndBarsModules - 0.75); + double textX1Right = xQuiet + mscale*(0.75*nModules - 0.5*mEndBarsModules + 0.75); + double textX2Left = 0.5*xQuiet; + double textX2Right = 1.5*xQuiet + mscale*nModules; + + double textY1 = hBar2 + textSize1/4; + double textY2 = hBar2 + textSize2/4; + + /* draw text (call implementation from concrete class) */ + vectorizeText( displayText, + textSize1, textSize2, + textX1Left, textX1Right, textY1, + textX2Left, textX2Right, textY2 ); + } + /* Overwrite requested size with actual size. */ w = width; diff --git a/glbarcode/CMakeLists.txt b/glbarcode/CMakeLists.txt index 6471024d..ca561c17 100644 --- a/glbarcode/CMakeLists.txt +++ b/glbarcode/CMakeLists.txt @@ -33,10 +33,9 @@ add_library (glbarcode STATIC ${GLBARCODE_SOURCES} ) -#target_compile_features (glbarcode -# PUBLIC cxx_std_11 -#) -set_property (TARGET glbarcode PROPERTY CXX_STANDARD 11) +target_compile_features (glbarcode + PUBLIC cxx_std_11 +) target_include_directories (glbarcode PUBLIC .. diff --git a/glbarcode/LICENSE b/glbarcode/LICENSE new file mode 100644 index 00000000..cca7fc27 --- /dev/null +++ b/glbarcode/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/model/CMakeLists.txt b/model/CMakeLists.txt index d01a5515..1690772a 100644 --- a/model/CMakeLists.txt +++ b/model/CMakeLists.txt @@ -26,7 +26,9 @@ set (Model_sources FileUtil.cpp Frame.cpp FrameCd.cpp + FrameContinuous.cpp FrameEllipse.cpp + FramePath.cpp FrameRect.cpp FrameRound.cpp Handles.cpp @@ -54,10 +56,13 @@ set (Model_sources Template.cpp TextNode.cpp Units.cpp + Variable.cpp + Variables.cpp Vendor.cpp XmlCategoryParser.cpp XmlLabelCreator.cpp XmlLabelParser.cpp + XmlLabelParser_3.cpp XmlPaperParser.cpp XmlTemplateCreator.cpp XmlTemplateParser.cpp @@ -77,6 +82,7 @@ set (Model_qobject_headers ModelTextObject.h PageRenderer.h Settings.h + Variables.h ) qt5_wrap_cpp (Model_moc_sources ${Model_qobject_headers}) @@ -89,10 +95,9 @@ add_library (Model STATIC ${Model_moc_sources} ) -#target_compile_features (Model -# PUBLIC cxx_std_11 -#) -set_property (TARGET Model PROPERTY CXX_STANDARD 11) +target_compile_features (Model + PUBLIC cxx_std_11 +) target_include_directories (Model PUBLIC .. ${CMAKE_CURRENT_BINARY_DIR}/.. diff --git a/model/ColorNode.cpp b/model/ColorNode.cpp index f399a1a6..ceee003c 100644 --- a/model/ColorNode.cpp +++ b/model/ColorNode.cpp @@ -175,30 +175,32 @@ namespace glabels /// /// Get color, expand if necessary /// - QColor ColorNode::color( merge::Record* record ) const + QColor ColorNode::color( const merge::Record* record, + const Variables* variables ) const { - if ( mIsField ) + QColor value = QColor( 192, 192, 192, 128 ); + + bool haveRecordField = mIsField && record && + record->contains(mKey) && + !record->value(mKey).isEmpty(); + bool haveVariable = mIsField && variables && + variables->contains(mKey) && + !(*variables)[mKey].value().isEmpty(); + + if ( haveRecordField ) { - if ( record == nullptr ) - { - return mColor; - } - else - { - if ( record->contains( mKey ) ) - { - return QColor( (*record)[ mKey ] ); - } - else - { - return mColor; - } - } + value = QColor( record->value(mKey) ); } - else + else if ( haveVariable ) { - return mColor; + value = QColor( (*variables)[mKey].value() ); } + else if ( !mIsField ) + { + value = mColor; + } + + return value; } } diff --git a/model/ColorNode.h b/model/ColorNode.h index 89b0201e..dab30afe 100644 --- a/model/ColorNode.h +++ b/model/ColorNode.h @@ -22,6 +22,7 @@ #define model_ColorNode_h +#include "Variables.h" #include "merge/Record.h" #include @@ -95,7 +96,8 @@ namespace glabels ///////////////////////////////// public: uint32_t rgba() const; - QColor color( merge::Record* record ) const; + QColor color( const merge::Record* record, + const Variables* variables ) const; ///////////////////////////////// diff --git a/model/Db.cpp b/model/Db.cpp index 65921938..9c650098 100644 --- a/model/Db.cpp +++ b/model/Db.cpp @@ -67,13 +67,10 @@ namespace glabels QList Db::mVendors; QStringList Db::mVendorNames; QList Db::mTemplates; - QString Db::mPaperNameOther; Db::Db() { - mPaperNameOther = tr("Other"); - readPapers(); readCategories(); readVendors(); @@ -209,6 +206,15 @@ namespace glabels { if ( !name.isNull() && !name.isEmpty() ) { + if ( name == tr("Other") ) + { + return "other"; + } + else if ( name == tr("Roll") ) + { + return "roll"; + } + const Paper *paper = lookupPaperFromName( name ); if ( paper != nullptr ) { @@ -225,9 +231,13 @@ namespace glabels { if ( !id.isNull() && !id.isEmpty() ) { - if ( isPaperIdOther( id ) ) + if ( id == "roll" ) { - return mPaperNameOther; + return tr("Roll"); + } + else + { + return tr("Other"); } const Paper *paper = lookupPaperFromId( id ); @@ -256,12 +266,6 @@ namespace glabels } - bool Db::isPaperIdOther( const QString& id ) - { - return ( id == "Other" ); - } - - void Db::registerCategory( Category *category ) { if ( !isCategoryIdKnown( category->id() ) ) diff --git a/model/Db.h b/model/Db.h index 6964a819..5f853772 100644 --- a/model/Db.h +++ b/model/Db.h @@ -71,7 +71,6 @@ namespace glabels static QString lookupPaperIdFromName( const QString& name ); static QString lookupPaperNameFromId( const QString& id ); static bool isPaperIdKnown( const QString& id ); - static bool isPaperIdOther( const QString& id ); static void registerCategory( Category *category ); static const Category *lookupCategoryFromName( const QString& name ); @@ -134,8 +133,6 @@ namespace glabels static QList mTemplates; - static QString mPaperNameOther; - }; } diff --git a/model/Distance.cpp b/model/Distance.cpp index 19cd1552..724e200b 100644 --- a/model/Distance.cpp +++ b/model/Distance.cpp @@ -208,3 +208,13 @@ namespace glabels } } + + +QDebug operator<<( QDebug dbg, const glabels::model::Distance& distance ) +{ + QDebugStateSaver saver(dbg); + + dbg.nospace() << distance.pt() << "pt"; + + return dbg; +} diff --git a/model/Distance.h b/model/Distance.h index ba525abd..8222d177 100644 --- a/model/Distance.h +++ b/model/Distance.h @@ -69,6 +69,7 @@ namespace glabels Distance& operator+=( const Distance& d ); Distance& operator-=( const Distance& d ); + Distance& operator*=( double f ); Distance operator-(); friend inline Distance operator+( const Distance& d1, const Distance& d2 ); @@ -100,6 +101,10 @@ namespace glabels } +// Debugging support +QDebug operator<<( QDebug dbg, const glabels::model::Distance& distance ); + + // // Inline methods // @@ -200,6 +205,13 @@ namespace glabels } + inline Distance& Distance::operator*=( double f ) + { + mDPts *= f; + return *this; + } + + inline Distance Distance::operator-() { return Distance::pt( -mDPts ); diff --git a/model/FileUtil.cpp b/model/FileUtil.cpp index 09e61d2e..a3e2bcb0 100644 --- a/model/FileUtil.cpp +++ b/model/FileUtil.cpp @@ -108,5 +108,17 @@ namespace glabels return QDir("/"); } + + QString FileUtil::makeRelativeIfInDir( const QDir& dir, + const QString& filename ) + { + QString relativeFilePath = dir.relativeFilePath( filename ); // Note: directory separators canonicalized to slash by Qt path methods + if ( !relativeFilePath.startsWith( "../" ) ) + { + return relativeFilePath; + } + return filename; + } + } } diff --git a/model/FileUtil.h b/model/FileUtil.h index ed76de01..0cac0db1 100644 --- a/model/FileUtil.h +++ b/model/FileUtil.h @@ -41,6 +41,9 @@ namespace glabels QDir userTemplatesDir(); QDir translationsDir(); + + QString makeRelativeIfInDir( const QDir& dir, + const QString& filename ); } } diff --git a/model/Frame.cpp b/model/Frame.cpp index f020cd95..76c280fa 100644 --- a/model/Frame.cpp +++ b/model/Frame.cpp @@ -20,6 +20,12 @@ #include "Frame.h" +#include "FrameCd.h" +#include "FrameContinuous.h" +#include "FrameEllipse.h" +#include "FramePath.h" +#include "FrameRect.h" +#include "FrameRound.h" #include "Markup.h" #include @@ -42,12 +48,12 @@ namespace glabels mId = other.mId; mNLabels = 0; - foreach ( Layout *layout, mLayouts ) + foreach ( const Layout& layout, other.mLayouts ) { - addLayout( layout->dup() ); + addLayout( layout ); } - foreach ( Markup *markup, mMarkups ) + foreach ( Markup *markup, other.mMarkups ) { addMarkup( markup->dup() ); } @@ -72,7 +78,7 @@ namespace glabels } - const QList& Frame::layouts() const + const QList& Frame::layouts() const { return mLayouts; } @@ -89,13 +95,13 @@ namespace glabels QVector origins( nLabels() ); int i = 0; - foreach ( Layout *layout, mLayouts ) + foreach ( const Layout& layout, mLayouts ) { - for ( int iy = 0; iy < layout->ny(); iy++ ) + for ( int iy = 0; iy < layout.ny(); iy++ ) { - for ( int ix = 0; ix < layout->nx(); ix++ ) + for ( int ix = 0; ix < layout.nx(); ix++ ) { - origins[i++] = Point( ix*layout->dx() + layout->x0(), iy*layout->dy() + layout->y0() ); + origins[i++] = Point( ix*layout.dx() + layout.x0(), iy*layout.dy() + layout.y0() ); } } } @@ -106,12 +112,12 @@ namespace glabels } - void Frame::addLayout( Layout *layout ) + void Frame::addLayout( const Layout& layout ) { mLayouts << layout; // Update total number of labels - mNLabels += layout->nx() * layout->ny(); + mNLabels += layout.nx() * layout.ny(); // Update layout description if ( mLayouts.size() == 1 ) @@ -122,7 +128,7 @@ namespace glabels * %3 = total number of labels on a page (sheet). */ mLayoutDescription = QString( tr("%1 x %2 (%3 per sheet)") ) - .arg(layout->nx()).arg(layout->ny()).arg(mNLabels); + .arg(layout.nx()).arg(layout.ny()).arg(mNLabels); } else { @@ -137,5 +143,54 @@ namespace glabels mMarkups << markup; } + + void Frame::setH( const Distance& h ) + { + // Default implementation does nothing + } + + } +} + + +QDebug operator<<( QDebug dbg, const glabels::model::Frame& frame ) +{ + if ( auto* frameCd = dynamic_cast(&frame) ) + { + dbg << *frameCd; + return dbg; + } + else if ( auto* frameContinuous = dynamic_cast(&frame) ) + { + dbg << *frameContinuous; + return dbg; + } + else if ( auto* frameEllipse = dynamic_cast(&frame) ) + { + dbg << *frameEllipse; + return dbg; + } + else if ( auto* framePath = dynamic_cast(&frame) ) + { + dbg << *framePath; + return dbg; + } + else if ( auto* frameRect = dynamic_cast(&frame) ) + { + dbg << *frameRect; + return dbg; + } + else if ( auto* frameRound = dynamic_cast(&frame) ) + { + dbg << *frameRound; + return dbg; + } + else + { + QDebugStateSaver saver(dbg); + + dbg.nospace() << "UNKNOWN FRAME"; + + return dbg; } } diff --git a/model/Frame.h b/model/Frame.h index b2bbc2c3..da8d55ad 100644 --- a/model/Frame.h +++ b/model/Frame.h @@ -27,6 +27,7 @@ #include "Point.h" #include +#include #include #include #include @@ -56,23 +57,26 @@ namespace glabels QString id() const; int nLabels() const; QString layoutDescription() const; - const QList& layouts() const; + const QList& layouts() const; const QList& markups() const; QVector getOrigins() const; - void addLayout( Layout* layout ); + void addLayout( const Layout& layout ); void addMarkup( Markup* markup ); virtual Distance w() const = 0; virtual Distance h() const = 0; + virtual void setH( const Distance& h ); + virtual QString sizeDescription( const Units& units ) const = 0; virtual bool isSimilarTo( Frame* other ) const = 0; virtual const QPainterPath& path() const = 0; virtual const QPainterPath& clipPath() const = 0; - virtual QPainterPath marginPath( const Distance& size ) const = 0; + virtual QPainterPath marginPath( const Distance& xSize, + const Distance& ySize ) const = 0; private: @@ -80,7 +84,7 @@ namespace glabels int mNLabels; QString mLayoutDescription; - QList mLayouts; + QList mLayouts; QList mMarkups; }; @@ -88,4 +92,8 @@ namespace glabels } +// Debugging support +QDebug operator<<( QDebug dbg, const glabels::model::Frame& frame ); + + #endif // model_Frame_h diff --git a/model/FrameCd.cpp b/model/FrameCd.cpp index ed4aad1f..465ac24c 100644 --- a/model/FrameCd.cpp +++ b/model/FrameCd.cpp @@ -93,15 +93,6 @@ namespace glabels } - FrameCd::FrameCd( const FrameCd& other ) - : Frame(other), - mR1(other.mR1), mR2(other.mR2), mW(other.mW), mH(other.mH), mWaste(other.mWaste), - mPath(other.mPath) - { - // empty - } - - Frame* FrameCd::dup() const { return new FrameCd( *this ); @@ -187,8 +178,12 @@ namespace glabels } - QPainterPath FrameCd::marginPath( const Distance& size ) const + QPainterPath FrameCd::marginPath( const Distance& xSize, + const Distance& ySize ) const { + // Note: ignore ySize, assume xSize == ySize + Distance size = xSize; + Distance wReal = (mW == 0) ? 2*mR1 : mW; Distance hReal = (mH == 0) ? 2*mR1 : mH; @@ -219,3 +214,22 @@ namespace glabels } } + + +QDebug operator<<( QDebug dbg, const glabels::model::FrameCd& frame ) +{ + QDebugStateSaver saver(dbg); + + dbg.nospace() << "FrameCd{ " + << frame.id() << "," + << frame.r1() << "," + << frame.r2() << "," + << frame.waste() << "," + << frame.w() << "," + << frame.h() << "," + << frame.layouts() << "," + << frame.markups() + << " }"; + + return dbg; +} diff --git a/model/FrameCd.h b/model/FrameCd.h index 4805549e..1c800758 100644 --- a/model/FrameCd.h +++ b/model/FrameCd.h @@ -42,7 +42,7 @@ namespace glabels const Distance& waste, const QString& id = "0" ); - FrameCd( const FrameCd &other ); + FrameCd( const FrameCd &other ) = default; Frame *dup() const override; @@ -58,7 +58,8 @@ namespace glabels const QPainterPath& path() const override; const QPainterPath& clipPath() const override; - QPainterPath marginPath( const Distance& size ) const override; + QPainterPath marginPath( const Distance& xSize, + const Distance& ySize ) const override; private: @@ -77,4 +78,8 @@ namespace glabels } +// Debugging support +QDebug operator<<( QDebug dbg, const glabels::model::FrameCd& frame ); + + #endif // model_FrameCd_h diff --git a/model/FrameContinuous.cpp b/model/FrameContinuous.cpp new file mode 100644 index 00000000..16f2bc71 --- /dev/null +++ b/model/FrameContinuous.cpp @@ -0,0 +1,166 @@ +/* FrameContinuous.cpp + * + * Copyright (C) 2018 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "FrameContinuous.h" + +#include "Constants.h" +#include "StrUtil.h" + + +namespace glabels +{ + namespace model + { + + FrameContinuous::FrameContinuous( const Distance& w, + const Distance& hMin, + const Distance& hMax, + const Distance& hDefault, + const QString& id ) + : Frame(id), mW(w), mHMin(hMin), mHMax(hMax), mHDefault(hDefault), mH(hDefault) + { + mPath.addRect( 0, 0, mW.pt(), mH.pt() ); + } + + + Frame* FrameContinuous::dup() const + { + return new FrameContinuous( *this ); + } + + + Distance FrameContinuous::w() const + { + return mW; + } + + + Distance FrameContinuous::h() const + { + return mH; + } + + + Distance FrameContinuous::hMin() const + { + return mHMin; + } + + + Distance FrameContinuous::hMax() const + { + return mHMax; + } + + + Distance FrameContinuous::hDefault() const + { + return mHDefault; + } + + + void FrameContinuous::setH( const Distance& h ) + { + mH = h; + mPath = QPainterPath(); // clear path + mPath.addRect( 0, 0, mW.pt(), mH.pt() ); + } + + + QString FrameContinuous::sizeDescription( const Units& units ) const + { + if ( units.toEnum() == Units::IN ) + { + QString wStr = StrUtil::formatFraction( mW.in() ); + + return QString().sprintf( "%s %s %s", + qPrintable(wStr), + qPrintable(units.toTrName()), + qPrintable(tr("wide")) ); + } + else + { + return QString().sprintf( "%.3f %s %s", + mW.inUnits(units), + qPrintable(units.toTrName()), + qPrintable(tr("wide")) ); + } + } + + + bool FrameContinuous::isSimilarTo( Frame* other ) const + { + if ( auto *otherContinuous = dynamic_cast(other) ) + { + if ( fabs( mW - otherContinuous->mW ) <= EPSILON ) + { + return true; + } + } + return false; + } + + + const QPainterPath& FrameContinuous::path() const + { + return mPath; + } + + + const QPainterPath& FrameContinuous::clipPath() const + { + return mPath; + } + + + QPainterPath FrameContinuous::marginPath( const Distance& xSize, + const Distance& ySize ) const + { + Distance w = mW - 2*xSize; + Distance h = mH - 2*ySize; + + QPainterPath path; + path.addRect( xSize.pt(), ySize.pt(), w.pt(), h.pt() ); + + return path; + } + + + } +} + + +QDebug operator<<( QDebug dbg, const glabels::model::FrameContinuous& frame ) +{ + QDebugStateSaver saver(dbg); + + dbg.nospace() << "FrameContinuous{ " + << frame.id() << "," + << frame.w() << "," + << frame.h() << "," + << frame.hMin() << "," + << frame.hMax() << "," + << frame.hDefault() << "," + << frame.layouts() << "," + << frame.markups() + << " }"; + + return dbg; +} diff --git a/model/FrameContinuous.h b/model/FrameContinuous.h new file mode 100644 index 00000000..72ea0023 --- /dev/null +++ b/model/FrameContinuous.h @@ -0,0 +1,85 @@ +/* FrameContinuous.h + * + * Copyright (C) 2018 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#ifndef model_FrameContinuous_h +#define model_FrameContinuous_h + + +#include "Frame.h" + + +namespace glabels +{ + namespace model + { + + class FrameContinuous : public Frame + { + Q_DECLARE_TR_FUNCTIONS(FrameContinuous) + + public: + FrameContinuous( const Distance& w, + const Distance& hMin, + const Distance& hMax, + const Distance& hDefault, + const QString& id = "0" ); + + FrameContinuous( const FrameContinuous& other ) = default; + + Frame* dup() const override; + + Distance w() const override; + Distance h() const override; + + Distance hMin() const; + Distance hMax() const; + Distance hDefault() const; + + void setH( const Distance& h ) override; + + QString sizeDescription( const Units& units ) const override; + + bool isSimilarTo( Frame* other ) const override; + + const QPainterPath& path() const override; + const QPainterPath& clipPath() const override; + QPainterPath marginPath( const Distance& xSize, + const Distance& ySize ) const override; + + + private: + Distance mW; + Distance mHMin; + Distance mHMax; + Distance mHDefault; + Distance mH; + + QPainterPath mPath; + }; + + } +} + + +// Debugging support +QDebug operator<<( QDebug dbg, const glabels::model::FrameContinuous& frame ); + + +#endif // model_FrameContinuous_h diff --git a/model/FrameEllipse.cpp b/model/FrameEllipse.cpp index 5c27f725..18b38443 100644 --- a/model/FrameEllipse.cpp +++ b/model/FrameEllipse.cpp @@ -40,13 +40,6 @@ namespace glabels } - FrameEllipse::FrameEllipse( const FrameEllipse& other ) - : Frame(other), mW(other.mW), mH(other.mH), mWaste(other.mWaste), mPath(other.mPath) - { - // empty - } - - Frame* FrameEllipse::dup() const { return new FrameEllipse( *this ); @@ -119,8 +112,12 @@ namespace glabels } - QPainterPath FrameEllipse::marginPath( const Distance& size ) const + QPainterPath FrameEllipse::marginPath( const Distance& xSize, + const Distance& ySize ) const { + // Note: ignore ySize, assume xSize == ySize + Distance size = xSize; + Distance w = mW - 2*size; Distance h = mH - 2*size; @@ -132,3 +129,20 @@ namespace glabels } } + + +QDebug operator<<( QDebug dbg, const glabels::model::FrameEllipse& frame ) +{ + QDebugStateSaver saver(dbg); + + dbg.nospace() << "FrameEllipse{ " + << frame.id() << "," + << frame.w() << "," + << frame.h() << "," + << frame.waste() << "," + << frame.layouts() << "," + << frame.markups() + << " }"; + + return dbg; +} diff --git a/model/FrameEllipse.h b/model/FrameEllipse.h index 43c0ed2e..17997403 100644 --- a/model/FrameEllipse.h +++ b/model/FrameEllipse.h @@ -40,7 +40,7 @@ namespace glabels const Distance& waste, const QString& id = "0" ); - FrameEllipse( const FrameEllipse& other ); + FrameEllipse( const FrameEllipse& other ) = default; Frame* dup() const override; @@ -54,7 +54,8 @@ namespace glabels const QPainterPath& path() const override; const QPainterPath& clipPath() const override; - QPainterPath marginPath( const Distance& size ) const override; + QPainterPath marginPath( const Distance& xSize, + const Distance& ySize ) const override; private: @@ -71,4 +72,8 @@ namespace glabels } +// Debugging support +QDebug operator<<( QDebug dbg, const glabels::model::FrameEllipse& frame ); + + #endif // model_FrameEllipse_h diff --git a/model/FramePath.cpp b/model/FramePath.cpp new file mode 100644 index 00000000..dcf9f41d --- /dev/null +++ b/model/FramePath.cpp @@ -0,0 +1,157 @@ +/* FramePath.cpp + * + * Copyright (C) 2018 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "FramePath.h" + +#include "Constants.h" +#include "StrUtil.h" + + +namespace glabels +{ + namespace model + { + + FramePath::FramePath( const QPainterPath& path, + const Distance& xWaste, + const Distance& yWaste, + const Units& originalUnits, + const QString& id ) + : Frame(id), mXWaste(xWaste), mYWaste(yWaste), mPath(path), mOriginalUnits(originalUnits) + { + QRectF r = path.boundingRect(); + + mW = Distance::pt( r.width() ); + mH = Distance::pt( r.height() ); + + mClipPath.addRect( r.x()-mXWaste.pt(), r.y()-mYWaste.pt(), + r.width() + 2*mXWaste.pt(), r.height() + 2*mYWaste.pt() ); + } + + + Frame* FramePath::dup() const + { + return new FramePath( *this ); + } + + + Distance FramePath::w() const + { + return mW; + } + + + Distance FramePath::h() const + { + return mH; + } + + + Distance FramePath::xWaste() const + { + return mXWaste; + } + + + Distance FramePath::yWaste() const + { + return mYWaste; + } + + + Units FramePath::originalUnits() const + { + return mOriginalUnits; + } + + + QString FramePath::sizeDescription( const Units& units ) const + { + if ( units.toEnum() == Units::IN ) + { + QString wStr = StrUtil::formatFraction( mW.in() ); + QString hStr = StrUtil::formatFraction( mH.in() ); + + return QString().sprintf( "%s x %s %s", + qPrintable(wStr), + qPrintable(hStr), + qPrintable(units.toTrName()) ); + } + else + { + return QString().sprintf( "%.5g x %.5g %s", + mW.inUnits(units), + mH.inUnits(units), + qPrintable(units.toTrName()) ); + } + } + + + bool FramePath::isSimilarTo( Frame* other ) const + { + if ( auto *otherPath = dynamic_cast(other) ) + { + if ( mPath == otherPath->mPath ) + { + return true; + } + } + return false; + } + + + const QPainterPath& FramePath::path() const + { + return mPath; + } + + + const QPainterPath& FramePath::clipPath() const + { + return mClipPath; + } + + + QPainterPath FramePath::marginPath( const Distance& xSize, + const Distance& ySize ) const + { + return mPath; // No margin + } + + + } +} + + +QDebug operator<<( QDebug dbg, const glabels::model::FramePath& frame ) +{ + QDebugStateSaver saver(dbg); + + dbg.nospace() << "FramePath{ " + << frame.id() << "," + << frame.path() << "," + << frame.xWaste() << "," + << frame.yWaste() << "," + << frame.layouts() << "," + << frame.markups() + << " }"; + + return dbg; +} diff --git a/model/FramePath.h b/model/FramePath.h new file mode 100644 index 00000000..774b2fb9 --- /dev/null +++ b/model/FramePath.h @@ -0,0 +1,86 @@ +/* FramePath.h + * + * Copyright (C) 2018 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#ifndef model_FramePath_h +#define model_FramePath_h + + +#include "Frame.h" + + +namespace glabels +{ + namespace model + { + + class FramePath : public Frame + { + Q_DECLARE_TR_FUNCTIONS(FramePath) + + public: + FramePath( const QPainterPath& path, + const Distance& xWaste, + const Distance& yWaste, + const Units& originalUnits, + const QString& id = "0" ); + + FramePath( const FramePath& other ) = default; + + Frame* dup() const override; + + Distance xWaste() const; + Distance yWaste() const; + + Units originalUnits() const; + + Distance w() const override; + Distance h() const override; + + QString sizeDescription( const Units& units ) const override; + + bool isSimilarTo( Frame* other ) const override; + + const QPainterPath& path() const override; + const QPainterPath& clipPath() const override; + QPainterPath marginPath( const Distance& xSize, + const Distance& ySize ) const override; + + + private: + Distance mW; + Distance mH; + Distance mXWaste; + Distance mYWaste; + + QPainterPath mPath; + QPainterPath mClipPath; + + Units mOriginalUnits; + }; + + } +} + + +// Debugging support +QDebug operator<<( QDebug dbg, const glabels::model::FramePath& frame ); + + +#endif // model_FramePath_h diff --git a/model/FrameRect.cpp b/model/FrameRect.cpp index 373e2891..204eb74a 100644 --- a/model/FrameRect.cpp +++ b/model/FrameRect.cpp @@ -44,16 +44,7 @@ namespace glabels mR.pt(), mR.pt() ); } - - FrameRect::FrameRect( const FrameRect &other ) - : Frame(other), - mW(other.mW), mH(other.mH), mR(other.mR), mXWaste(other.mXWaste), - mYWaste(other.mYWaste), mPath(other.mPath) - { - // empty - } - Frame* FrameRect::dup() const { return new FrameRect( *this ); @@ -138,14 +129,15 @@ namespace glabels } - QPainterPath FrameRect::marginPath( const Distance& size ) const + QPainterPath FrameRect::marginPath( const Distance& xSize, + const Distance& ySize ) const { - Distance w = mW - 2*size; - Distance h = mH - 2*size; - Distance r = std::max( mR - size, Distance(0.0) ); + Distance w = mW - 2*xSize; + Distance h = mH - 2*ySize; + Distance r = std::max( mR - std::min(xSize, ySize), Distance(0.0) ); QPainterPath path; - path.addRoundedRect( size.pt(), size.pt(), w.pt(), h.pt(), r.pt(), r.pt() ); + path.addRoundedRect( xSize.pt(), ySize.pt(), w.pt(), h.pt(), r.pt(), r.pt() ); return path; } @@ -153,3 +145,22 @@ namespace glabels } } + + +QDebug operator<<( QDebug dbg, const glabels::model::FrameRect& frame ) +{ + QDebugStateSaver saver(dbg); + + dbg.nospace() << "FrameRect{ " + << frame.id() << "," + << frame.w() << "," + << frame.h() << "," + << frame.r() << "," + << frame.xWaste() << "," + << frame.yWaste() << "," + << frame.layouts() << "," + << frame.markups() + << " }"; + + return dbg; +} diff --git a/model/FrameRect.h b/model/FrameRect.h index 03438aee..315b714c 100644 --- a/model/FrameRect.h +++ b/model/FrameRect.h @@ -42,7 +42,7 @@ namespace glabels const Distance& yWaste, const QString& id = "0" ); - FrameRect( const FrameRect& other ); + FrameRect( const FrameRect& other ) = default; Frame* dup() const override; @@ -59,7 +59,8 @@ namespace glabels const QPainterPath& path() const override; const QPainterPath& clipPath() const override; - QPainterPath marginPath( const Distance& size ) const override; + QPainterPath marginPath( const Distance& xSize, + const Distance& ySize ) const override; private: @@ -78,4 +79,8 @@ namespace glabels } +// Debugging support +QDebug operator<<( QDebug dbg, const glabels::model::FrameRect& frame ); + + #endif // model_FrameRect_h diff --git a/model/FrameRound.cpp b/model/FrameRound.cpp index fe39ecb1..b1c4f7bc 100644 --- a/model/FrameRound.cpp +++ b/model/FrameRound.cpp @@ -40,13 +40,6 @@ namespace glabels } - FrameRound::FrameRound( const FrameRound& other ) - : Frame(other), mR(other.mR), mWaste(other.mWaste), mPath(other.mPath) - { - // empty - } - - Frame* FrameRound::dup() const { return new FrameRound( *this ); @@ -123,8 +116,12 @@ namespace glabels } - QPainterPath FrameRound::marginPath( const Distance& size ) const + QPainterPath FrameRound::marginPath( const Distance& xSize, + const Distance& ySize ) const { + // Note: ignore ySize, assume xSize == ySize + Distance size = xSize; + Distance r = mR - size; QPainterPath path; @@ -135,3 +132,19 @@ namespace glabels } } + + +QDebug operator<<( QDebug dbg, const glabels::model::FrameRound& frame ) +{ + QDebugStateSaver saver(dbg); + + dbg.nospace() << "FrameRound{ " + << frame.id() << "," + << frame.r() << "," + << frame.waste() << "," + << frame.layouts() << "," + << frame.markups() + << " }"; + + return dbg; +} diff --git a/model/FrameRound.h b/model/FrameRound.h index 41c53e97..ad296c9f 100644 --- a/model/FrameRound.h +++ b/model/FrameRound.h @@ -39,7 +39,7 @@ namespace glabels const Distance& waste, const QString& id = "0" ); - FrameRound( const FrameRound &other ); + FrameRound( const FrameRound &other ) = default; Frame *dup() const override; @@ -54,7 +54,8 @@ namespace glabels const QPainterPath& path() const override; const QPainterPath& clipPath() const override; - QPainterPath marginPath( const Distance& size ) const override; + QPainterPath marginPath( const Distance& xSize, + const Distance& ySize ) const override; private: @@ -70,4 +71,8 @@ namespace glabels } +// Debugging support +QDebug operator<<( QDebug dbg, const glabels::model::FrameRound& frame ); + + #endif // model_FrameRound_h diff --git a/model/Handles.h b/model/Handles.h index 10d4ab22..233177b9 100644 --- a/model/Handles.h +++ b/model/Handles.h @@ -65,7 +65,7 @@ namespace glabels //////////////////////////// - // Attribue Methods + // Attribute Methods //////////////////////////// ModelObject* owner() const; Location location() const; diff --git a/model/Layout.cpp b/model/Layout.cpp index dc5069c2..abc9f9f5 100644 --- a/model/Layout.cpp +++ b/model/Layout.cpp @@ -86,22 +86,32 @@ namespace glabels } - bool Layout::isSimilarTo( const Layout *other ) + bool Layout::isSimilarTo( const Layout& other ) const { - return ( (mNx == other->mNx) && - (mNy == other->mNy) && - (fabs(mX0 - other->mX0) < EPSILON) && - (fabs(mY0 - other->mY0) < EPSILON) && - (fabs(mDx - other->mDx) < EPSILON) && - (fabs(mDy - other->mDy) < EPSILON) ); + return ( (mNx == other.mNx) && + (mNy == other.mNy) && + (fabs(mX0 - other.mX0) < EPSILON) && + (fabs(mY0 - other.mY0) < EPSILON) && + (fabs(mDx - other.mDx) < EPSILON) && + (fabs(mDy - other.mDy) < EPSILON) ); } + } +} - Layout* Layout::dup() const - { - auto *other = new Layout( *this ); - return other; - } - } +QDebug operator<<( QDebug dbg, const glabels::model::Layout& layout ) +{ + QDebugStateSaver saver(dbg); + + dbg.nospace() << "Layout{ " + << layout.nx() << "," + << layout.ny() << "," + << layout.x0() << "," + << layout.y0() << "," + << layout.dx() << "," + << layout.dy() + << " }"; + + return dbg; } diff --git a/model/Layout.h b/model/Layout.h index 34857806..853c10df 100644 --- a/model/Layout.h +++ b/model/Layout.h @@ -24,6 +24,8 @@ #include "Distance.h" +#include + namespace glabels { @@ -52,9 +54,7 @@ namespace glabels Distance dx() const; Distance dy() const; - bool isSimilarTo( const Layout *other ); - - Layout* dup() const; + bool isSimilarTo( const Layout& other ) const; private: @@ -71,4 +71,8 @@ namespace glabels } +// Debugging support +QDebug operator<<( QDebug dbg, const glabels::model::Layout& layout ); + + #endif // model_Layout_h diff --git a/model/Markup.cpp b/model/Markup.cpp index 4f16ae91..83edb0d5 100644 --- a/model/Markup.cpp +++ b/model/Markup.cpp @@ -26,29 +26,48 @@ namespace glabels namespace model { - const QPainterPath& Markup::path() const + QPainterPath Markup::path( const Frame* frame ) const { + // Use cached path -- default does not depend on frame size return mPath; } - MarkupMargin::MarkupMargin( const Frame* frame, - const Distance& size ) - : mFrame(frame), mSize(size) + MarkupMargin::MarkupMargin( const Distance& size ) + : mXSize(size), mYSize(size) { - mPath = frame->marginPath( size ); } + MarkupMargin::MarkupMargin( const Distance& xSize, + const Distance& ySize ) + : mXSize(xSize), mYSize(ySize) + { + } + + + QPainterPath MarkupMargin::path( const Frame* frame ) const + { + // Re-calculate path -- frame size may have changed + return frame->marginPath( mXSize, mYSize ); + } + + Markup* MarkupMargin::dup() const { - return new MarkupMargin( mFrame, mSize ); + return new MarkupMargin( mXSize, mYSize ); + } + + + Distance MarkupMargin::xSize() const + { + return mXSize; } - Distance MarkupMargin::size() const + Distance MarkupMargin::ySize() const { - return mSize; + return mYSize; } diff --git a/model/Markup.h b/model/Markup.h index 1c578599..fa393656 100644 --- a/model/Markup.h +++ b/model/Markup.h @@ -37,7 +37,7 @@ namespace glabels public: virtual Markup* dup() const = 0; - const QPainterPath& path() const; + virtual QPainterPath path( const Frame* frame ) const; protected: QPainterPath mPath; @@ -47,16 +47,21 @@ namespace glabels class MarkupMargin : public Markup { public: - MarkupMargin( const Frame* frame, - const Distance& size ); + MarkupMargin( const Distance& size ); - Distance size() const; + MarkupMargin( const Distance& xSize, + const Distance& ySize ); + + QPainterPath path( const Frame* frame ) const override; + + Distance xSize() const; + Distance ySize() const; Markup* dup() const override; private: - const Frame* mFrame; - Distance mSize; + Distance mXSize; + Distance mYSize; }; diff --git a/model/Model.cpp b/model/Model.cpp index 774ebabf..4bcf47fc 100644 --- a/model/Model.cpp +++ b/model/Model.cpp @@ -55,9 +55,30 @@ namespace glabels /// Default constructor. /// Model::Model() - : mUntitledInstance(0), mModified(true), mTmplate(nullptr), mFrame(nullptr), mRotate(false) + : mUntitledInstance(0), mModified(true), mRotate(false) { + mVariables = new Variables(); mMerge = new merge::None(); + + connect( mVariables, SIGNAL(changed()), this, SLOT(onVariablesChanged()) ); + } + + + Model::Model( merge::Merge* merge, Variables* variables ) + : mUntitledInstance(0), mModified(true), mRotate(false) + { + mVariables = variables; // Shared + mMerge = merge; // Shared + } + + + /// + /// Destructor. + /// + Model::~Model() + { + qDeleteAll( mObjectList ); + // Final instance of mMerge and mVariables to be deleted by Model owner } @@ -66,7 +87,13 @@ namespace glabels /// Model* Model::save() const { - auto* savedModel = new Model; + auto* savedModel = new Model( mMerge, mVariables ); // mMerge and mVariables shared between models + + if ( mFileName.isEmpty() && mUntitledInstance == 0 ) + { + qDebug() << "Model::save: Warning: called before mUntitledInstance has been initialized: untitled names will differ"; + } + savedModel->restore( this ); return savedModel; @@ -90,7 +117,6 @@ namespace glabels mModified = savedModel->mModified; mFileName = savedModel->mFileName; mTmplate = savedModel->mTmplate; - mFrame = savedModel->mFrame; mRotate = savedModel->mRotate; foreach ( ModelObject* savedObject, savedModel->mObjectList ) @@ -104,18 +130,12 @@ namespace glabels connect( object, SIGNAL(moved()), this, SLOT(onObjectMoved()) ); } - delete mMerge; - mMerge = savedModel->mMerge->clone(); - // Emit signals based on potential changes emit changed(); emit selectionChanged(); emit modifiedChanged(); emit nameChanged(); emit sizeChanged(); - emit mergeChanged(); - emit mergeSourceChanged(); - emit mergeSelectionChanged(); } @@ -155,7 +175,7 @@ namespace glabels /// const Template* Model::tmplate() const { - return mTmplate; + return &mTmplate; } @@ -164,7 +184,7 @@ namespace glabels /// const Frame* Model::frame() const { - return mFrame; + return mTmplate.frames().constFirst(); } @@ -173,18 +193,14 @@ namespace glabels /// void Model::setTmplate( const Template* tmplate ) { - if (mTmplate != tmplate) - { - mTmplate = tmplate; - mFrame = tmplate->frames().first(); + mTmplate = *tmplate; - setModified(); + setModified(); - emit changed(); - emit sizeChanged(); + emit changed(); + emit sizeChanged(); - Settings::addToRecentTemplateList( tmplate->name() ); - } + Settings::addToRecentTemplateList( tmplate->name() ); } @@ -219,7 +235,14 @@ namespace glabels /// Distance Model::w() const { - return mRotate ? mFrame->h() : mFrame->w(); + if ( auto* frame = mTmplate.frames().constFirst() ) + { + return mRotate ? frame->h() : frame->w(); + } + else + { + return Distance::pt(0); + } } @@ -228,7 +251,31 @@ namespace glabels /// Distance Model::h() const { - return mRotate ? mFrame->w() : mFrame->h(); + if ( auto* frame = mTmplate.frames().constFirst() ) + { + return mRotate ? frame->w() : frame->h(); + } + else + { + return Distance::pt(0); + } + } + + + /// + /// Set height (if variable length) + /// + void Model::setH( const Distance& h ) + { + if ( auto* frame = mTmplate.frames().first() ) + { + frame->setH( h ); + + setModified(); + + emit changed(); + emit sizeChanged(); + } } @@ -241,6 +288,38 @@ namespace glabels } + /// + /// Get directory as a QDir. + /// + QDir Model::dir() const + { + if ( mFileName.isEmpty() ) + { + return QDir::current(); + } + else + { + return QFileInfo( mFileName ).absoluteDir(); + } + } + + + /// + /// Get directory as a path. + /// + QString Model::dirPath() const + { + if ( mFileName.isEmpty() ) + { + return QDir::currentPath(); + } + else + { + return QFileInfo( mFileName ).absolutePath(); + } + } + + /// /// Get short name. /// @@ -267,6 +346,15 @@ namespace glabels } + /// + /// Get variables object + /// + Variables* Model::variables() const + { + return mVariables; + } + + /// /// Get merge object /// @@ -416,6 +504,17 @@ namespace glabels } + /// + /// Variables Changed Slot + /// + void Model::onVariablesChanged() + { + setModified(); + emit changed(); + emit variablesChanged(); + } + + /// /// Merge Source Changed Slot /// @@ -1324,7 +1423,7 @@ namespace glabels QClipboard *clipboard = QApplication::clipboard(); QByteArray buffer; - XmlLabelCreator::serializeObjects( getSelection(), buffer ); + XmlLabelCreator::serializeObjects( getSelection(), this, buffer ); auto *mimeData = new QMimeData; mimeData->setData( MIME_TYPE, buffer ); @@ -1380,7 +1479,7 @@ namespace glabels { // Native objects QByteArray buffer = mimeData->data( MIME_TYPE ); - QList objects = XmlLabelParser::deserializeObjects( buffer ); + QList objects = XmlLabelParser::deserializeObjects( buffer, this ); unselectAll(); foreach ( ModelObject* object, objects ) @@ -1417,11 +1516,11 @@ namespace glabels /// /// Draw label objects /// - void Model::draw( QPainter* painter, bool inEditor, merge::Record* record ) const + void Model::draw( QPainter* painter, bool inEditor, merge::Record* record, Variables* variables ) const { foreach ( ModelObject* object, mObjectList ) { - object->draw( painter, inEditor, record ); + object->draw( painter, inEditor, record, variables ); } } diff --git a/model/Model.h b/model/Model.h index e1d0cfa4..e31574dc 100644 --- a/model/Model.h +++ b/model/Model.h @@ -24,10 +24,12 @@ #include "Settings.h" #include "Template.h" +#include "Variables.h" #include "merge/Merge.h" #include "merge/Record.h" +#include #include #include #include @@ -57,7 +59,8 @@ namespace glabels ///////////////////////////////// public: Model(); - ~Model() override = default; + Model( merge::Merge* merge, Variables* variables ); + ~Model(); ///////////////////////////////// @@ -76,6 +79,7 @@ namespace glabels void sizeChanged(); void selectionChanged(); void modifiedChanged(); + void variablesChanged(); void mergeChanged(); void mergeSourceChanged(); void mergeSelectionChanged(); @@ -89,6 +93,8 @@ namespace glabels void setModified(); void clearModified(); + QDir dir() const; + QString dirPath() const; QString shortName(); const QString& fileName() const; void setFileName( const QString &fileName ); @@ -103,8 +109,12 @@ namespace glabels Distance w() const; Distance h() const; + void setH( const Distance& h ); + const QList& objectList() const; + Variables* variables() const; + merge::Merge* merge() const; void setMerge( merge::Merge* merge ); @@ -202,7 +212,10 @@ namespace glabels // Drawing operations ///////////////////////////////// public: - void draw( QPainter* painter, bool inEditor = true, merge::Record* record = nullptr ) const; + void draw( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const; ///////////////////////////////// @@ -211,6 +224,7 @@ namespace glabels private slots: void onObjectChanged(); void onObjectMoved(); + void onVariablesChanged(); void onMergeSourceChanged(); void onMergeSelectionChanged(); @@ -222,12 +236,12 @@ namespace glabels int mUntitledInstance; bool mModified; QString mFileName; - const Template* mTmplate; - const Frame* mFrame; + Template mTmplate; bool mRotate; - QList mObjectList; + QList mObjectList; + Variables* mVariables; merge::Merge* mMerge; }; diff --git a/model/ModelBarcodeObject.cpp b/model/ModelBarcodeObject.cpp index 315632ee..ed4f1bf6 100644 --- a/model/ModelBarcodeObject.cpp +++ b/model/ModelBarcodeObject.cpp @@ -89,13 +89,14 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const barcode::Style& bcStyle, bool bcTextFlag, bool bcChecksumFlag, QString bcData, const ColorNode& bcColorNode, const QMatrix& matrix ) - : ModelObject( x0, y0, w, h, matrix ) + : ModelObject( x0, y0, w, h, lockAspectRatio, matrix ) { mOutline = new Outline( this ); @@ -311,7 +312,8 @@ namespace glabels /// void ModelBarcodeObject::drawShadow( QPainter* painter, bool inEditor, - merge::Record* record ) const + merge::Record* record, + Variables* variables ) const { // Barcodes don't support shadows. } @@ -322,9 +324,10 @@ namespace glabels /// void ModelBarcodeObject::drawObject( QPainter* painter, bool inEditor, - merge::Record* record ) const + merge::Record* record, + Variables* variables ) const { - QColor bcColor = mBcColorNode.color( record ); + QColor bcColor = mBcColorNode.color( record, variables ); if ( inEditor ) { @@ -332,7 +335,7 @@ namespace glabels } else { - drawBc( painter, bcColor, record ); + drawBc( painter, bcColor, record, variables ); } } @@ -450,7 +453,8 @@ namespace glabels void ModelBarcodeObject::drawBc( QPainter* painter, const QColor& color, - merge::Record* record ) const + merge::Record* record, + Variables* variables ) const { painter->setPen( QPen( color ) ); @@ -458,7 +462,7 @@ namespace glabels bc->setChecksum(mBcChecksumFlag); bc->setShowText(mBcTextFlag); - bc->build( mBcData.expand( record ).toStdString(), mW.pt(), mH.pt() ); + bc->build( mBcData.expand( record, variables ).toStdString(), mW.pt(), mH.pt() ); glbarcode::QtRenderer renderer(painter); bc->render( renderer ); diff --git a/model/ModelBarcodeObject.h b/model/ModelBarcodeObject.h index 4931ddfb..2a49304f 100644 --- a/model/ModelBarcodeObject.h +++ b/model/ModelBarcodeObject.h @@ -51,6 +51,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const barcode::Style& bcStyle, bool bcTextFlag, bool bcChecksumFlag, @@ -126,8 +127,16 @@ namespace glabels // Drawing operations /////////////////////////////////////////////////////////////// protected: - void drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const override; - void drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const override; + void drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + + void drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + QPainterPath hoverPath( double scale ) const override; @@ -139,7 +148,12 @@ namespace glabels void update(); void drawBcInEditor( QPainter* painter, const QColor& color ) const; - void drawBc( QPainter* painter, const QColor& color, merge::Record* record ) const; + + void drawBc( QPainter* painter, + const QColor& color, + merge::Record* record, + Variables* variables ) const; + void drawPlaceHolder( QPainter* painter, const QColor& color, const QString& text ) const; diff --git a/model/ModelBoxObject.cpp b/model/ModelBoxObject.cpp index 94e81d5a..4fd66e75 100644 --- a/model/ModelBoxObject.cpp +++ b/model/ModelBoxObject.cpp @@ -54,6 +54,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const Distance& lineWidth, const ColorNode& lineColorNode, const ColorNode& fillColorNode, @@ -63,7 +64,7 @@ namespace glabels const Distance& shadowY, double shadowOpacity, const ColorNode& shadowColorNode ) - : ModelShapeObject( x0, y0, w, h, + : ModelShapeObject( x0, y0, w, h, lockAspectRatio, lineWidth, lineColorNode, fillColorNode, matrix, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ) @@ -103,11 +104,14 @@ namespace glabels /// /// Draw shadow of object /// - void ModelBoxObject::drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const + void ModelBoxObject::drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const { - QColor lineColor = mLineColorNode.color( record ); - QColor fillColor = mFillColorNode.color( record ); - QColor shadowColor = mShadowColorNode.color( record ); + QColor lineColor = mLineColorNode.color( record, variables ); + QColor fillColor = mFillColorNode.color( record, variables ); + QColor shadowColor = mShadowColorNode.color( record, variables ); shadowColor.setAlphaF( mShadowOpacity ); @@ -148,10 +152,13 @@ namespace glabels /// /// Draw object itself /// - void ModelBoxObject::drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const + void ModelBoxObject::drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const { - QColor lineColor = mLineColorNode.color( record ); - QColor fillColor = mFillColorNode.color( record ); + QColor lineColor = mLineColorNode.color( record, variables ); + QColor fillColor = mFillColorNode.color( record, variables ); painter->setPen( QPen( lineColor, mLineWidth.pt() ) ); painter->setBrush( fillColor ); diff --git a/model/ModelBoxObject.h b/model/ModelBoxObject.h index 8705c999..21b58269 100644 --- a/model/ModelBoxObject.h +++ b/model/ModelBoxObject.h @@ -47,6 +47,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const Distance& lineWidth, const ColorNode& lineColorNode, const ColorNode& fillColorNode, @@ -72,8 +73,16 @@ namespace glabels // Drawing operations /////////////////////////////////////////////////////////////// protected: - void drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const override; - void drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const override; + void drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + + void drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + QPainterPath hoverPath( double scale ) const override; }; diff --git a/model/ModelEllipseObject.cpp b/model/ModelEllipseObject.cpp index c3ca82ed..7e35791e 100644 --- a/model/ModelEllipseObject.cpp +++ b/model/ModelEllipseObject.cpp @@ -54,6 +54,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const Distance& lineWidth, const ColorNode& lineColorNode, const ColorNode& fillColorNode, @@ -63,7 +64,7 @@ namespace glabels const Distance& shadowY, double shadowOpacity, const ColorNode& shadowColorNode ) - : ModelShapeObject( x0, y0, w, h, + : ModelShapeObject( x0, y0, w, h, lockAspectRatio, lineWidth, lineColorNode, fillColorNode, matrix, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ) @@ -103,11 +104,14 @@ namespace glabels /// /// Draw shadow of object /// - void ModelEllipseObject::drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const + void ModelEllipseObject::drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const { - QColor lineColor = mLineColorNode.color( record ); - QColor fillColor = mFillColorNode.color( record ); - QColor shadowColor = mShadowColorNode.color( record ); + QColor lineColor = mLineColorNode.color( record, variables ); + QColor fillColor = mFillColorNode.color( record, variables ); + QColor shadowColor = mShadowColorNode.color( record, variables ); shadowColor.setAlphaF( mShadowOpacity ); @@ -148,10 +152,13 @@ namespace glabels /// /// Draw object itself /// - void ModelEllipseObject::drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const + void ModelEllipseObject::drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const { - QColor lineColor = mLineColorNode.color( record ); - QColor fillColor = mFillColorNode.color( record ); + QColor lineColor = mLineColorNode.color( record, variables ); + QColor fillColor = mFillColorNode.color( record, variables ); painter->setPen( QPen( lineColor, mLineWidth.pt() ) ); painter->setBrush( fillColor ); diff --git a/model/ModelEllipseObject.h b/model/ModelEllipseObject.h index e26e9ba7..26ae6d4b 100644 --- a/model/ModelEllipseObject.h +++ b/model/ModelEllipseObject.h @@ -47,6 +47,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const Distance& lineWidth, const ColorNode& lineColorNode, const ColorNode& fillColorNode, @@ -72,8 +73,16 @@ namespace glabels // Drawing operations /////////////////////////////////////////////////////////////// protected: - void drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const override; - void drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const override; + void drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + + void drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + QPainterPath hoverPath( double scale ) const override; }; diff --git a/model/ModelImageObject.cpp b/model/ModelImageObject.cpp index 5848625b..3de24380 100644 --- a/model/ModelImageObject.cpp +++ b/model/ModelImageObject.cpp @@ -20,9 +20,11 @@ #include "ModelImageObject.h" +#include "Model.h" #include "Size.h" #include +#include #include #include #include @@ -40,6 +42,17 @@ namespace glabels QImage* ModelImageObject::smDefaultImage = nullptr; + // + // Private + // + namespace + { + const QColor fillColor = QColor( 224, 224, 224, 255 ); + const QColor labelColor = QColor( 102, 102, 102, 255 ); + const Distance pad = Distance::pt(2); + } + + /// /// Constructor /// @@ -70,6 +83,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const TextNode& filenameNode, const QMatrix& matrix, bool shadowState, @@ -77,7 +91,7 @@ namespace glabels const Distance& shadowY, double shadowOpacity, const ColorNode& shadowColorNode ) - : ModelObject( x0, y0, w, h, + : ModelObject( x0, y0, w, h, lockAspectRatio, matrix, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ) { @@ -101,6 +115,8 @@ namespace glabels mImage = nullptr; mSvgRenderer = nullptr; + + loadImage(); } @@ -111,6 +127,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const QString& filename, const QImage& image, const QMatrix& matrix, @@ -119,7 +136,7 @@ namespace glabels const Distance& shadowY, double shadowOpacity, const ColorNode& shadowColorNode ) - : ModelObject( x0, y0, w, h, + : ModelObject( x0, y0, w, h, lockAspectRatio, matrix, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ) { @@ -152,6 +169,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const QString& filename, const QByteArray& svg, const QMatrix& matrix, @@ -160,7 +178,7 @@ namespace glabels const Distance& shadowY, double shadowOpacity, const ColorNode& shadowColorNode ) - : ModelObject( x0, y0, w, h, + : ModelObject( x0, y0, w, h, lockAspectRatio, matrix, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ) { @@ -395,27 +413,58 @@ namespace glabels /// /// Draw shadow of object /// - void ModelImageObject::drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const + void ModelImageObject::drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const { QRectF destRect( 0, 0, mW.pt(), mH.pt() ); - QColor shadowColor = mShadowColorNode.color( record ); + QColor shadowColor = mShadowColorNode.color( record, variables ); shadowColor.setAlphaF( mShadowOpacity ); if ( mImage && mImage->hasAlphaChannel() && (mImage->depth() == 32) ) { - QImage* shadowImage = createShadowImage( shadowColor ); + QImage* shadowImage = createShadowImage( *mImage, shadowColor ); painter->drawImage( destRect, *shadowImage ); delete shadowImage; } + else if ( mImage || mSvgRenderer || inEditor ) + { + painter->setBrush( shadowColor ); + painter->setPen( QPen( Qt::NoPen ) ); + + painter->drawRect( destRect ); + } else { - if ( mImage || inEditor ) + QString filename = mFilenameNode.text( record, variables ).trimmed(); + QImage* image; + QSvgRenderer* svgRenderer; + QByteArray svg; + if ( readImageFile( filename, image, svgRenderer, svg ) ) { - painter->setBrush( shadowColor ); - painter->setPen( QPen( Qt::NoPen ) ); + if ( image && image->hasAlphaChannel() && (image->depth() == 32) ) + { + QImage* shadowImage = createShadowImage( *image, shadowColor ); + painter->drawImage( destRect, *shadowImage ); + delete shadowImage; + } + else + { + painter->setBrush( shadowColor ); + painter->setPen( QPen( Qt::NoPen ) ); - painter->drawRect( destRect ); + painter->drawRect( destRect ); + } + if ( image ) + { + delete image; + } + else + { + delete svgRenderer; + } } } } @@ -424,16 +473,70 @@ namespace glabels /// /// Draw object itself /// - void ModelImageObject::drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const + void ModelImageObject::drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const { QRectF destRect( 0, 0, mW.pt(), mH.pt() ); if ( inEditor && (mFilenameNode.isField() || (!mImage && !mSvgRenderer) ) ) { + // + // Render default place holder image + // painter->save(); painter->setRenderHint( QPainter::SmoothPixmapTransform, false ); painter->drawImage( destRect, *smDefaultImage ); painter->restore(); + + // + // Print label on top of place holder image, if we have room + // + if ( (mW > 6*pad) && (mH > 4*pad) ) + { + QString labelText = tr("No image"); + if ( mFilenameNode.isField() ) + { + labelText = QString( "${%1}" ).arg( mFilenameNode.data() ); + } + + // Determine font size for labelText + QFont font( "Sans" ); + font.setPointSizeF( 6 ); + + QFontMetricsF fm( font ); + QRectF textRect = fm.boundingRect( labelText ); + + double wPts = (mW - 2*pad).pt(); + double hPts = (mH - 2*pad).pt(); + if ( (wPts < textRect.width()) || (hPts < textRect.height()) ) + { + double scaleX = wPts / textRect.width(); + double scaleY = hPts / textRect.height(); + font.setPointSizeF( 6 * std::min( scaleX, scaleY ) ); + } + + // Render hole for text (font size may have changed above) + fm = QFontMetricsF( font ); + textRect = fm.boundingRect( labelText ); + + QRectF holeRect( (mW.pt() - textRect.width())/2 - pad.pt(), + (mH.pt() - textRect.height())/2 - pad.pt(), + textRect.width() + 2*pad.pt(), + textRect.height() + 2*pad.pt() ); + + painter->setPen( Qt::NoPen ); + painter->setBrush( QBrush( fillColor ) ); + painter->drawRect( holeRect ); + + // Render text + painter->setFont( font ); + painter->setPen( QPen( labelColor ) ); + painter->drawText( QRectF( 0, 0, mW.pt(), mH.pt() ), + Qt::AlignCenter, + labelText ); + } } else if ( mImage ) { @@ -445,7 +548,23 @@ namespace glabels } else if ( mFilenameNode.isField() ) { - // TODO + QString filename = mFilenameNode.text( record, variables ).trimmed(); + QImage* image; + QSvgRenderer* svgRenderer; + QByteArray svg; + if ( readImageFile( filename, image, svgRenderer, svg ) ) + { + if ( image ) + { + painter->drawImage( destRect, *image ); + delete image; + } + else + { + svgRenderer->render( painter, destRect ); + delete svgRenderer; + } + } } } @@ -481,77 +600,108 @@ namespace glabels if ( !mFilenameNode.isField() ) { QString filename = mFilenameNode.data(); - QFileInfo fileInfo( filename ); + if ( readImageFile( filename, mImage, mSvgRenderer, mSvg ) ) + { + double aspectRatio = 0; + if ( mSvgRenderer ) + { + // Adjust size based on aspect ratio of SVG image + QRectF rect = mSvgRenderer->viewBoxF(); + aspectRatio = rect.width() ? rect.height() / rect.width() : 0; + } + else + { + // Adjust size based on aspect ratio of image + double imageW = mImage->width(); + double imageH = mImage->height(); + aspectRatio = imageW ? imageH / imageW : 0; + } + + if ( aspectRatio ) + { + if ( mH > mW*aspectRatio ) + { + mH = mW*aspectRatio; + } + else + { + mW = mH/aspectRatio; + } + } + } + } + } + + + /// + /// Read an image or svg file + /// + bool ModelImageObject::readImageFile( const QString& fileName, + QImage*& image, + QSvgRenderer*& svgRenderer, + QByteArray& svg ) const + { + image = nullptr; + svgRenderer = nullptr; + svg.clear(); + + if ( !fileName.isEmpty() ) + { + QFileInfo fileInfo( fileName ); + if ( fileInfo.isRelative() ) + { + // Look for image file relative to project file 1st then CWD 2nd + auto* model = dynamic_cast( parent() ); + QDir::setSearchPaths( "images", {model ? model->dirPath() : "", QDir::currentPath()} ); + fileInfo.setFile( QString("images:") + fileName ); + } if ( fileInfo.isReadable() ) { - if ( (fileInfo.suffix() == "svg") || (fileInfo.suffix() == "SVG") ) + if ( fileInfo.suffix().toLower() == "svg" ) { - QFile file( filename ); + QFile file( fileInfo.filePath() ); if ( file.open( QFile::ReadOnly ) ) { - mSvg = file.readAll(); + svg = file.readAll(); file.close(); - mSvgRenderer = new QSvgRenderer( mSvg ); - if ( !mSvgRenderer->isValid() ) - { - mSvgRenderer = nullptr; - } - else + svgRenderer = new QSvgRenderer( svg ); + if ( !svgRenderer->isValid() ) { - // Adjust size based on aspect ratio of SVG image - QRectF rect = mSvgRenderer->viewBoxF(); - double aspectRatio = rect.height() / rect.width(); - if ( mH > mW*aspectRatio ) - { - mH = mW*aspectRatio; - } - else - { - mW = mH/aspectRatio; - } + delete svgRenderer; + svgRenderer = nullptr; + svg.clear(); } } } else { - mImage = new QImage( filename ); - if ( mImage->isNull() ) + image = new QImage( fileInfo.filePath() ); + if ( image->isNull() ) { - mImage = nullptr; - } - else - { - // Adjust size based on aspect ratio of image - double imageW = mImage->width(); - double imageH = mImage->height(); - double aspectRatio = imageH / imageW; - if ( mH > mW*aspectRatio ) - { - mH = mW*aspectRatio; - } - else - { - mW = mH/aspectRatio; - } + delete image; + image = nullptr; } } } } + + return image != nullptr || svgRenderer != nullptr; } /// /// Create shadow image /// - QImage* ModelImageObject::createShadowImage( const QColor& color ) const + QImage* ModelImageObject::createShadowImage( const QImage& image, + const QColor& color ) const { int r = color.red(); int g = color.green(); int b = color.blue(); int a = color.alpha(); - auto* shadow = new QImage( *mImage ); + auto* shadow = new QImage( image ); for ( int iy = 0; iy < shadow->height(); iy++ ) { auto* scanLine = (QRgb*)shadow->scanLine( iy ); diff --git a/model/ModelImageObject.h b/model/ModelImageObject.h index ba87078f..3e982f5c 100644 --- a/model/ModelImageObject.h +++ b/model/ModelImageObject.h @@ -49,6 +49,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const TextNode& filenameNode, const QMatrix& matrix = QMatrix(), bool shadowState = false, @@ -61,6 +62,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const QString& filename, const QImage& image, const QMatrix& matrix = QMatrix(), @@ -74,6 +76,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const QString& filename, const QByteArray& svg, const QMatrix& matrix = QMatrix(), @@ -132,8 +135,16 @@ namespace glabels // Drawing operations /////////////////////////////////////////////////////////////// protected: - void drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const override; - void drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const override; + void drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + + void drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + QPainterPath hoverPath( double scale ) const override; @@ -141,7 +152,14 @@ namespace glabels // Private /////////////////////////////////////////////////////////////// void loadImage(); - QImage* createShadowImage( const QColor& color ) const; + + bool readImageFile( const QString& fileName, + QImage*& image, + QSvgRenderer*& svgRenderer, + QByteArray& svg ) const; + + QImage* createShadowImage( const QImage& image, + const QColor& color ) const; /////////////////////////////////////////////////////////////// diff --git a/model/ModelLineObject.cpp b/model/ModelLineObject.cpp index 5d7b5e15..5bef0657 100644 --- a/model/ModelLineObject.cpp +++ b/model/ModelLineObject.cpp @@ -68,7 +68,7 @@ namespace glabels const Distance& shadowY, double shadowOpacity, const ColorNode& shadowColorNode ) - : ModelObject( x0, y0, dx, dy, + : ModelObject( x0, y0, dx, dy, false /*lockAspectRatio*/, matrix, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ) { @@ -186,10 +186,13 @@ namespace glabels /// /// Draw shadow of object /// - void ModelLineObject::drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const + void ModelLineObject::drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const { - QColor lineColor = mLineColorNode.color( record ); - QColor shadowColor = mShadowColorNode.color( record ); + QColor lineColor = mLineColorNode.color( record, variables ); + QColor shadowColor = mShadowColorNode.color( record, variables ); shadowColor.setAlphaF( mShadowOpacity ); @@ -204,9 +207,12 @@ namespace glabels /// /// Draw object itself /// - void ModelLineObject::drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const + void ModelLineObject::drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const { - QColor lineColor = mLineColorNode.color( record ); + QColor lineColor = mLineColorNode.color( record, variables ); painter->setPen( QPen( lineColor, mLineWidth.pt() ) ); painter->drawLine( 0, 0, mW.pt(), mH.pt() ); diff --git a/model/ModelLineObject.h b/model/ModelLineObject.h index 16cfdb68..a09670e2 100644 --- a/model/ModelLineObject.h +++ b/model/ModelLineObject.h @@ -97,8 +97,16 @@ namespace glabels // Drawing operations /////////////////////////////////////////////////////////////// protected: - void drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const override; - void drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const override; + void drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + + void drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + QPainterPath hoverPath( double scale ) const override; diff --git a/model/ModelObject.cpp b/model/ModelObject.cpp index 637352c3..65ed72a4 100644 --- a/model/ModelObject.cpp +++ b/model/ModelObject.cpp @@ -51,6 +51,7 @@ namespace glabels mY0 = 0; mW = 0; mH = 0; + mLockAspectRatio = false; mMatrix = QMatrix(); mShadowState = false; @@ -72,6 +73,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const QMatrix& matrix, bool shadowState, const Distance& shadowX, @@ -85,6 +87,7 @@ namespace glabels mY0 = y0; mW = w; mH = h; + mLockAspectRatio = lockAspectRatio; mMatrix = matrix; mShadowState = shadowState; @@ -112,6 +115,7 @@ namespace glabels mY0 = object->mY0; mW = object->mW; mH = object->mH; + mLockAspectRatio = object->mLockAspectRatio; mShadowState = object->mShadowState; mShadowX = object->mShadowX; @@ -272,6 +276,28 @@ namespace glabels } + /// + /// Lock Aspect Ratio Property Getter + /// + bool ModelObject::lockAspectRatio() const + { + return mLockAspectRatio; + } + + + /// + /// Lock Aspect Ratio Property Setter + /// + void ModelObject::setLockAspectRatio( bool value ) + { + if ( mLockAspectRatio != value ) + { + mLockAspectRatio = value; + emit changed(); + } + } + + /// /// Matrix Property Getter /// @@ -1200,7 +1226,10 @@ namespace glabels /// /// Draw object + shadow /// - void ModelObject::draw( QPainter* painter, bool inEditor, merge::Record* record ) const + void ModelObject::draw( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const { painter->save(); painter->translate( mX0.pt(), mY0.pt() ); @@ -1210,12 +1239,12 @@ namespace glabels painter->save(); painter->translate( mShadowX.pt(), mShadowY.pt() ); painter->setMatrix( mMatrix, true ); - drawShadow( painter, inEditor, record ); + drawShadow( painter, inEditor, record, variables ); painter->restore(); } painter->setMatrix( mMatrix, true ); - drawObject( painter, inEditor, record ); + drawObject( painter, inEditor, record, variables ); painter->restore(); } diff --git a/model/ModelObject.h b/model/ModelObject.h index 6af51e8d..4962ffd3 100644 --- a/model/ModelObject.h +++ b/model/ModelObject.h @@ -27,6 +27,7 @@ #include "Handles.h" #include "Outline.h" #include "TextNode.h" +#include "Variables.h" #include "barcode/Style.h" #include "merge/Record.h" @@ -64,6 +65,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio = false, const QMatrix& matrix = QMatrix(), bool shadowState = false, const Distance& shadowX = 0, @@ -136,6 +138,13 @@ namespace glabels void setH( const Distance& value ); + // + // Lock Aspect Ratio Property + // + bool lockAspectRatio() const; + void setLockAspectRatio( bool value ); + + // // Transformation Matrix Property // @@ -403,12 +412,24 @@ namespace glabels // Drawing operations /////////////////////////////////////////////////////////////// public: - void draw( QPainter* painter, bool inEditor, merge::Record* record ) const; + void draw( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const; + void drawSelectionHighlight( QPainter* painter, double scale ) const; protected: - virtual void drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const = 0; - virtual void drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const = 0; + virtual void drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const = 0; + + virtual void drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const = 0; + virtual QPainterPath hoverPath( double scale ) const = 0; virtual void sizeUpdated(); @@ -424,6 +445,7 @@ namespace glabels Distance mY0; Distance mW; Distance mH; + bool mLockAspectRatio; bool mShadowState; Distance mShadowX; diff --git a/model/ModelShapeObject.cpp b/model/ModelShapeObject.cpp index 989f5af3..afca4874 100644 --- a/model/ModelShapeObject.cpp +++ b/model/ModelShapeObject.cpp @@ -58,6 +58,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const Distance& lineWidth, const ColorNode& lineColorNode, const ColorNode& fillColorNode, @@ -67,7 +68,7 @@ namespace glabels const Distance& shadowY, double shadowOpacity, const ColorNode& shadowColorNode ) - : ModelObject( x0, y0, w, h, + : ModelObject( x0, y0, w, h, lockAspectRatio, matrix, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ) { diff --git a/model/ModelShapeObject.h b/model/ModelShapeObject.h index b33c5a3a..888ac030 100644 --- a/model/ModelShapeObject.h +++ b/model/ModelShapeObject.h @@ -47,6 +47,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const Distance& lineWidth, const ColorNode& lineColorNode, const ColorNode& fillColorNode, diff --git a/model/ModelTextObject.cpp b/model/ModelTextObject.cpp index 434d48bc..bb5c7e60 100644 --- a/model/ModelTextObject.cpp +++ b/model/ModelTextObject.cpp @@ -82,6 +82,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const QString& text, const QString& fontFamily, double fontSize, @@ -100,7 +101,7 @@ namespace glabels const Distance& shadowY, double shadowOpacity, const ColorNode& shadowColorNode ) - : ModelObject( x0, y0, w, h, + : ModelObject( x0, y0, w, h, lockAspectRatio, matrix, shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ) { @@ -518,13 +519,14 @@ namespace glabels /// void ModelTextObject::drawShadow( QPainter* painter, bool inEditor, - merge::Record* record ) const + merge::Record* record, + Variables* variables ) const { - QColor textColor = mTextColorNode.color( record ); + QColor textColor = mTextColorNode.color( record, variables ); if ( textColor.alpha() ) { - QColor shadowColor = mShadowColorNode.color( record ); + QColor shadowColor = mShadowColorNode.color( record, variables ); shadowColor.setAlphaF( mShadowOpacity ); if ( inEditor ) @@ -533,7 +535,7 @@ namespace glabels } else { - drawText( painter, shadowColor, record ); + drawText( painter, shadowColor, record, variables ); } } } @@ -544,9 +546,10 @@ namespace glabels /// void ModelTextObject::drawObject( QPainter* painter, bool inEditor, - merge::Record* record ) const + merge::Record* record, + Variables* variables ) const { - QColor textColor = mTextColorNode.color( record ); + QColor textColor = mTextColorNode.color( record, variables ); if ( inEditor ) { @@ -554,7 +557,7 @@ namespace glabels } else { - drawText( painter, textColor, record ); + drawText( painter, textColor, record, variables ); } } @@ -696,7 +699,8 @@ namespace glabels void ModelTextObject::drawText( QPainter* painter, const QColor& color, - merge::Record* record ) const + merge::Record* record, + Variables* variables ) const { painter->save(); @@ -704,7 +708,7 @@ namespace glabels QFont font; font.setFamily( mFontFamily ); - font.setPointSizeF( mTextAutoShrink ? autoShrinkFontSize( record ) : mFontSize ); + font.setPointSizeF( mTextAutoShrink ? autoShrinkFontSize( record, variables ) : mFontSize ); font.setWeight( mFontWeight ); font.setItalic( mFontItalicFlag ); font.setUnderline( mFontUnderlineFlag ); @@ -716,7 +720,7 @@ namespace glabels QFontMetricsF fontMetrics( font ); double dy = fontMetrics.lineSpacing() * mTextLineSpacing; - QTextDocument document( mText.expand( record ) ); + QTextDocument document( mText.expand( record, variables ) ); QList layouts; @@ -790,7 +794,7 @@ namespace glabels /// Determine auto shrink font size /// double - ModelTextObject::autoShrinkFontSize( merge::Record* record ) const + ModelTextObject::autoShrinkFontSize( merge::Record* record, Variables* variables ) const { QFont font; font.setFamily( mFontFamily ); @@ -802,7 +806,7 @@ namespace glabels textOption.setAlignment( mTextHAlign ); textOption.setWrapMode( mTextWrapMode ); - QTextDocument document( mText.expand( record ) ); + QTextDocument document( mText.expand( record, variables ) ); double candidateSize = mFontSize; while ( candidateSize > 1.0 ) diff --git a/model/ModelTextObject.h b/model/ModelTextObject.h index 1ab62c64..0f7d1e97 100644 --- a/model/ModelTextObject.h +++ b/model/ModelTextObject.h @@ -50,6 +50,7 @@ namespace glabels const Distance& y0, const Distance& w, const Distance& h, + bool lockAspectRatio, const QString& text, const QString& fontFamily, double fontSize, @@ -185,8 +186,16 @@ namespace glabels // Drawing operations /////////////////////////////////////////////////////////////// protected: - void drawShadow( QPainter* painter, bool inEditor, merge::Record* record ) const override; - void drawObject( QPainter* painter, bool inEditor, merge::Record* record ) const override; + void drawShadow( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + + void drawObject( QPainter* painter, + bool inEditor, + merge::Record* record, + Variables* variables ) const override; + QPainterPath hoverPath( double scale ) const override; @@ -196,10 +205,17 @@ namespace glabels private: void sizeUpdated() override; void update(); - void drawTextInEditor( QPainter* painter, const QColor& color ) const; - void drawText( QPainter* painter, const QColor&color, merge::Record* record ) const; - QString expandText( QString text, merge::Record* record ) const; - double autoShrinkFontSize( merge::Record* record ) const; + + void drawTextInEditor( QPainter* painter, + const QColor& color ) const; + + void drawText( QPainter* painter, + const QColor& color, + merge::Record* record, + Variables* variables ) const; + + double autoShrinkFontSize( merge::Record* record, + Variables* variables ) const; /////////////////////////////////////////////////////////////// diff --git a/model/PageRenderer.cpp b/model/PageRenderer.cpp index 3d7d237e..f4a992d4 100644 --- a/model/PageRenderer.cpp +++ b/model/PageRenderer.cpp @@ -47,9 +47,9 @@ namespace glabels PageRenderer::PageRenderer( const Model* model ) - : mModel(nullptr), mMerge(nullptr), mNCopies(0), mStartLabel(0), mLastLabel(0), + : mModel(nullptr), mMerge(nullptr), mVariables(nullptr), mNCopies(0), mStartItem(0), mLastItem(0), mPrintOutlines(false), mPrintCropMarks(false), mPrintReverse(false), - mIPage(0), mIsMerge(false), mNPages(0), mNLabelsPerPage(0) + mIPage(0), mIsMerge(false), mNPages(0), mNItemsPerPage(0) { if ( model ) { @@ -65,6 +65,7 @@ namespace glabels connect( mModel, SIGNAL(changed()), this, SLOT(onModelChanged()) ); onModelChanged(); + mVariables = mModel->variables(); } @@ -78,7 +79,7 @@ namespace glabels { mMerge = mModel->merge(); mOrigins = mModel->frame()->getOrigins(); - mNLabelsPerPage = mModel->frame()->nLabels(); + mNItemsPerPage = mModel->frame()->nLabels(); mIsMerge = ( dynamic_cast(mMerge) == nullptr ); updateNPages(); @@ -95,15 +96,33 @@ namespace glabels } - void PageRenderer::setStartLabel( int startLabel ) + void PageRenderer::setStartItem( int startItem ) { - mStartLabel = startLabel; + mStartItem = startItem; updateNPages(); emit changed(); } + void PageRenderer::setIsCollated( bool isCollated ) + { + mIsCollated = isCollated; + updateNPages(); + + emit changed(); + } + + + void PageRenderer::setAreGroupsContiguous( bool areGroupsContiguous ) + { + mAreGroupsContiguous = areGroupsContiguous; + updateNPages(); + + emit changed(); + } + + void PageRenderer::setPrintOutlines( bool printOutlinesFlag ) { mPrintOutlines = printOutlinesFlag; @@ -138,7 +157,7 @@ namespace glabels int PageRenderer::nItems() const { - return mLastLabel - mStartLabel; + return mNItems; } @@ -165,25 +184,49 @@ namespace glabels { if ( mModel ) { - if ( mIsMerge ) + if ( !mIsMerge ) { - mLastLabel = mStartLabel + mNCopies*mMerge->nSelectedRecords(); + // Simple project + mLastItem = mStartItem + mNCopies - 1; + mNItems = mNCopies; + mNGroups = 1; + mNItemsPerGroup = mNItems; } else { - mLastLabel = mStartLabel + mNCopies; - } - - mNPages = mLastLabel / mNLabelsPerPage; - if ( mLastLabel % mNLabelsPerPage ) - { - mNPages++; + // Merge project + if ( mIsCollated ) + { + // Collated Merge project + mNItemsPerGroup = mMerge->nSelectedRecords(); + mNGroups = mNCopies; + } + else + { + // Un-Collated Merge project + mNItemsPerGroup = mNCopies; + mNGroups = mMerge->nSelectedRecords(); + } + if ( mAreGroupsContiguous ) + { + // Merge groups are contiguous + mLastItem = mStartItem + mNGroups*mNItemsPerGroup - 1; + } + else + { + // Merge groups start on new page + mNPagesPerGroup = (mStartItem + mNItemsPerGroup - 1)/mNItemsPerPage + 1; + mLastItem = (mNGroups-1)*mNPagesPerGroup*mNItemsPerPage + mStartItem + mNItemsPerGroup - 1; + } + mNItems = mNGroups*mNItemsPerGroup; } } else { mNPages = 0; } + + mNPages = mLastItem/mNItemsPerPage + 1; } @@ -203,6 +246,7 @@ namespace glabels QRectF rectPts = printer->paperRect( QPrinter::Point ); painter.scale( rectPx.width()/rectPts.width(), rectPx.height()/rectPts.height() ); + for ( int iPage = 0; iPage < mNPages; iPage++ ) { if ( iPage ) @@ -231,13 +275,20 @@ namespace glabels { if ( mModel ) { - if ( mIsMerge ) + if ( !mIsMerge ) { - printMergePage( painter, iPage ); + printSimplePage( painter, iPage ); } else { - printSimplePage( painter, iPage ); + if ( mIsCollated ) + { + printCollatedMergePage( painter, iPage ); + } + else + { + printUnCollatedMergePage( painter, iPage ); + } } } } @@ -245,83 +296,198 @@ namespace glabels void PageRenderer::printSimplePage( QPainter* painter, int iPage ) const { - int iStart = 0; - int iEnd = mNLabelsPerPage; - - if ( iPage == 0 ) - { - iStart = mStartLabel; - } - - if ( (mLastLabel / mNLabelsPerPage) == iPage ) - { - iEnd = mLastLabel % mNLabelsPerPage; - } - printCropMarks( painter ); - for ( int i = iStart; i < iEnd; i++ ) + int iCopy = 0; + int iItem = mStartItem; + int iCurrentPage = 0; + mVariables->resetVariables(); + + while ( (iCopy < mNCopies) && (iCurrentPage <= iPage) ) { - painter->save(); + if ( iCurrentPage == iPage ) + { + int i = iItem % mNItemsPerPage; + + painter->save(); - painter->translate( mOrigins[i].x().pt(), mOrigins[i].y().pt() ); + painter->translate( mOrigins[i].x().pt(), mOrigins[i].y().pt() ); - painter->save(); + painter->save(); - clipLabel( painter ); - printLabel( painter, nullptr ); + clipLabel( painter ); + printLabel( painter, nullptr, mVariables ); - painter->restore(); // From before clip + painter->restore(); // From before clip - printOutline( painter ); + printOutline( painter ); - painter->restore(); // From before translation + painter->restore(); // From before translation + } + + // Next copy + iCopy++; + iItem++; + iCurrentPage = iItem / mNItemsPerPage; + + // User variable book keeping + mVariables->incrementVariablesOnItem(); + mVariables->incrementVariablesOnCopy(); + if ( (iItem % mNItemsPerPage) == 0 /* starting a new page */ ) + { + mVariables->incrementVariablesOnPage(); + } } } - void PageRenderer::printMergePage( QPainter* painter, int iPage ) const + void PageRenderer::printCollatedMergePage( QPainter* painter, int iPage ) const { + printCropMarks( painter ); + + int iCopy = 0; + int iItem = mStartItem; + int iCurrentPage = 0; + + const QList records = mMerge->selectedRecords(); int iRecord = 0; - int iStart = 0; - int iEnd = mNLabelsPerPage; + int nRecords = records.size(); - if ( iPage == 0 ) + if ( nRecords == 0 ) { - iStart = mStartLabel; + return; } + + mVariables->resetVariables(); - if ( (mLastLabel / mNLabelsPerPage) == iPage ) + while ( (iCopy < mNCopies) && (iCurrentPage <= iPage) ) { - iEnd = mLastLabel % mNLabelsPerPage; + if ( iCurrentPage == iPage ) + { + int i = iItem % mNItemsPerPage; + + painter->save(); + + painter->translate( mOrigins[i].x().pt(), mOrigins[i].y().pt() ); + + painter->save(); + + clipLabel( painter ); + printLabel( painter, records[iRecord], mVariables ); + + painter->restore(); // From before clip + + printOutline( painter ); + + painter->restore(); // From before translation + } + + // Next record + iRecord = (iRecord + 1) % nRecords; + if ( iRecord == 0 ) + { + iCopy++; + if ( mAreGroupsContiguous ) + { + iItem++; + } + else + { + iItem = iCopy*mNPagesPerGroup*mNItemsPerPage + mStartItem; + } + } + else + { + iItem++; + } + iCurrentPage = iItem / mNItemsPerPage; + + // User variable book keeping + mVariables->incrementVariablesOnItem(); + if ( iRecord == 0 ) + { + mVariables->incrementVariablesOnCopy(); + } + if ( (iItem % mNItemsPerPage) == 0 /* starting a new page */ ) + { + mVariables->incrementVariablesOnPage(); + } } + } + + + void PageRenderer::printUnCollatedMergePage( QPainter* painter, int iPage ) const + { + printCropMarks( painter ); + + int iCopy = 0; + int iItem = mStartItem; + int iCurrentPage = 0; const QList records = mMerge->selectedRecords(); - if ( records.size() ) + int iRecord = 0; + int nRecords = records.size(); + + if ( nRecords == 0 ) { - iRecord = (iPage*mNLabelsPerPage + iStart - mStartLabel) % records.size(); + return; } + + mVariables->resetVariables(); - printCropMarks( painter ); - - for ( int i = iStart; i < iEnd; i++ ) + while ( (iRecord < nRecords) && (iCurrentPage <= iPage) ) { - painter->save(); + if ( iCurrentPage == iPage ) + { + int i = iItem % mNItemsPerPage; + + painter->save(); - painter->translate( mOrigins[i].x().pt(), mOrigins[i].y().pt() ); + painter->translate( mOrigins[i].x().pt(), mOrigins[i].y().pt() ); - painter->save(); + painter->save(); - clipLabel( painter ); - printLabel( painter, records[iRecord] ); + clipLabel( painter ); + printLabel( painter, records[iRecord], mVariables ); - painter->restore(); // From before clip + painter->restore(); // From before clip - printOutline( painter ); + printOutline( painter ); - painter->restore(); // From before translation + painter->restore(); // From before translation + } + + // Next copy + iCopy = (iCopy + 1) % mNCopies; + if ( iCopy == 0 ) + { + iRecord++; + if ( mAreGroupsContiguous ) + { + iItem++; + } + else + { + iItem = iRecord*mNPagesPerGroup*mNItemsPerPage + mStartItem; + } + } + else + { + iItem++; + } + iCurrentPage = iItem / mNItemsPerPage; - iRecord = (iRecord + 1) % records.size(); + // User variable book keeping + mVariables->incrementVariablesOnItem(); + mVariables->incrementVariablesOnCopy(); + if ( iCopy == 0 ) + { + mVariables->resetOnCopyVariables(); + } + if ( (iItem % mNItemsPerPage) == 0 /* starting a new page */ ) + { + mVariables->incrementVariablesOnPage(); + } } } @@ -338,16 +504,16 @@ namespace glabels Distance w = mModel->frame()->w(); Distance h = mModel->frame()->h(); - foreach ( Layout* layout, mModel->frame()->layouts() ) + foreach ( const Layout& layout, mModel->frame()->layouts() ) { - Distance xMin = layout->x0(); - Distance yMin = layout->y0(); - Distance xMax = layout->x0() + layout->dx()*(layout->nx()-1) + w; - Distance yMax = layout->y0() + layout->dy()*(layout->ny()-1) + h; + Distance xMin = layout.x0(); + Distance yMin = layout.y0(); + Distance xMax = layout.x0() + layout.dx()*(layout.nx()-1) + w; + Distance yMax = layout.y0() + layout.dy()*(layout.ny()-1) + h; - for ( int ix = 0; ix < layout->nx(); ix++ ) + for ( int ix = 0; ix < layout.nx(); ix++ ) { - Distance x1 = xMin + ix*layout->dx(); + Distance x1 = xMin + ix*layout.dx(); Distance x2 = x1 + w; Distance y1 = max( yMin-tickOffset, Distance::pt(0) ); @@ -362,9 +528,9 @@ namespace glabels painter->drawLine( x2.pt(), y3.pt(), x2.pt(), y4.pt() ); } - for ( int iy = 0; iy < layout->ny(); iy++ ) + for ( int iy = 0; iy < layout.ny(); iy++ ) { - Distance y1 = yMin + iy*layout->dy(); + Distance y1 = yMin + iy*layout.dy(); Distance y2 = y1 + h; Distance x1 = max( xMin-tickOffset, Distance::pt(0) ); @@ -407,7 +573,9 @@ namespace glabels } - void PageRenderer::printLabel( QPainter* painter, merge::Record* record ) const + void PageRenderer::printLabel( QPainter* painter, + merge::Record* record, + Variables* variables ) const { painter->save(); @@ -423,7 +591,7 @@ namespace glabels painter->scale( -1, 1 ); } - mModel->draw( painter, false, record ); + mModel->draw( painter, false, record, variables ); painter->restore(); } diff --git a/model/PageRenderer.h b/model/PageRenderer.h index 2db3ae47..25133dee 100644 --- a/model/PageRenderer.h +++ b/model/PageRenderer.h @@ -23,6 +23,7 @@ #include "Point.h" +#include "Variables.h" #include "merge/Merge.h" #include "merge/Record.h" @@ -63,7 +64,9 @@ namespace glabels void setModel( const Model* model ); const Model* model() const; void setNCopies( int nCopies ); - void setStartLabel( int startLabel ); + void setStartItem( int startItem ); + void setIsCollated( bool isCollated ); + void setAreGroupsContiguous( bool areGroupscontiguous ); void setPrintOutlines( bool printOutlinesFlag ); void setPrintCropMarks( bool printCropMarksFlag ); void setPrintReverse( bool printReverseFlag ); @@ -96,11 +99,12 @@ namespace glabels private: void updateNPages(); void printSimplePage( QPainter* painter, int iPage ) const; - void printMergePage( QPainter* painter, int iPage ) const; + void printCollatedMergePage( QPainter* painter, int iPage ) const; + void printUnCollatedMergePage( QPainter* painter, int iPage ) const; void printCropMarks( QPainter* painter ) const; void printOutline( QPainter* painter ) const; void clipLabel( QPainter* painter ) const; - void printLabel( QPainter* painter, merge::Record* record ) const; + void printLabel( QPainter* painter, merge::Record* record, Variables* variables ) const; ///////////////////////////////// @@ -109,18 +113,27 @@ namespace glabels private: const Model* mModel; const merge::Merge* mMerge; + Variables* mVariables; int mNCopies; - int mStartLabel; - int mLastLabel; - bool mPrintOutlines; - bool mPrintCropMarks; - bool mPrintReverse; + int mStartItem; + int mLastItem; + int mNGroups; + int mNItemsPerGroup; + int mNPagesPerGroup; int mIPage; bool mIsMerge; + + int mNItems; int mNPages; - int mNLabelsPerPage; + int mNItemsPerPage; + + bool mIsCollated; + bool mAreGroupsContiguous; + bool mPrintOutlines; + bool mPrintCropMarks; + bool mPrintReverse; QVector mOrigins; }; diff --git a/model/RawText.cpp b/model/RawText.cpp index 08ee6bf3..ed4cd293 100644 --- a/model/RawText.cpp +++ b/model/RawText.cpp @@ -66,7 +66,7 @@ namespace glabels /// /// Expand all place holders /// - QString RawText::expand( merge::Record* record ) const + QString RawText::expand( merge::Record* record, Variables* variables ) const { QString text; @@ -74,7 +74,7 @@ namespace glabels { if ( token.isField ) { - text += token.field.evaluate( record ); + text += token.field.evaluate( record, variables ); } else { diff --git a/model/RawText.h b/model/RawText.h index 9b190844..2e8ab447 100644 --- a/model/RawText.h +++ b/model/RawText.h @@ -52,7 +52,7 @@ namespace glabels ///////////////////////////////// QString toString() const; std::string toStdString() const; - QString expand( merge::Record* record ) const; + QString expand( merge::Record* record, Variables* variables ) const; bool hasPlaceHolders() const; bool isEmpty() const; diff --git a/model/Settings.cpp b/model/Settings.cpp index b0ac10ee..db9288e0 100644 --- a/model/Settings.cpp +++ b/model/Settings.cpp @@ -294,5 +294,44 @@ namespace glabels emit mInstance->changed(); } + + int Settings::maxRecentFiles() + { + return mMaxRecentFiles; + } + + + QStringList Settings::recentFileList() + { + QStringList defaultList; + + mInstance->beginGroup( "Recent" ); + QStringList returnList = mInstance->value( "files", defaultList ).toStringList(); + mInstance->endGroup(); + + return returnList; + } + + + void Settings::addToRecentFileList( const QString& filePath ) + { + mInstance->beginGroup( "Recent" ); + + QStringList list = mInstance->value( "files" ).toStringList(); + + list.removeAll( filePath ); + list.prepend( filePath ); + while ( list.count() > mMaxRecentFiles ) + { + list.removeLast(); + } + + mInstance->setValue( "files", list ); + + mInstance->endGroup(); + + emit mInstance->changed(); + } + } } diff --git a/model/Settings.h b/model/Settings.h index f16c4f00..44178a5b 100644 --- a/model/Settings.h +++ b/model/Settings.h @@ -90,9 +90,14 @@ namespace glabels static QStringList recentTemplateList(); static void addToRecentTemplateList( const QString& name ); + static int maxRecentFiles(); + static QStringList recentFileList(); + static void addToRecentFileList( const QString& filePath ); + private: static Settings* mInstance; + static const int mMaxRecentFiles{5}; }; diff --git a/model/StrUtil.cpp b/model/StrUtil.cpp index 4c318f76..169f7cde 100644 --- a/model/StrUtil.cpp +++ b/model/StrUtil.cpp @@ -61,7 +61,7 @@ namespace glabels if ( denom[i] == 0.0 ) { /* None of our denominators work. */ - return QString().sprintf( "%.5g", x ); + return QString().sprintf( "%.3f", x ); } if ( denom[i] == 1.0 ) { diff --git a/model/SubstitutionField.cpp b/model/SubstitutionField.cpp index 178b73e8..0798befa 100644 --- a/model/SubstitutionField.cpp +++ b/model/SubstitutionField.cpp @@ -42,21 +42,33 @@ namespace glabels } - QString SubstitutionField::evaluate( const merge::Record* record ) const + QString SubstitutionField::evaluate( const merge::Record* record, + const Variables* variables ) const { QString value = mDefaultValue; - if ( record && record->contains(mFieldName) && !record->value(mFieldName).isEmpty() ) + bool haveRecordField = record && + record->contains(mFieldName) && + !record->value(mFieldName).isEmpty(); + bool haveVariable = variables && + variables->contains(mFieldName) && + !(*variables)[mFieldName].value().isEmpty(); + + if ( haveRecordField ) { value = record->value(mFieldName); } + else if ( haveVariable ) + { + value = (*variables)[mFieldName].value(); + } if ( !mFormatType.isNull() ) { value = formatValue( value ); } - if ( record && record->contains(mFieldName) && !record->value(mFieldName).isEmpty() && mNewLine ) + if ( mNewLine && (haveRecordField || haveVariable) ) { value = "\n" + value; } @@ -133,7 +145,7 @@ namespace glabels { bool success = false; - while ( s.size() && (s[0].isDigit() || s[0].isLetter() || s[0] == '_' || s[0] == '-') ) + while ( s.size() && (s[0].isPrint() && s[0] != ':' && s[0] != '}') ) { field.mFieldName.append( s[0] ); s = s.mid(1); diff --git a/model/SubstitutionField.h b/model/SubstitutionField.h index ec08a006..baaa2558 100644 --- a/model/SubstitutionField.h +++ b/model/SubstitutionField.h @@ -21,6 +21,7 @@ #ifndef model_SubstitutionField_h #define model_SubstitutionField_h +#include "Variables.h" #include "merge/Record.h" @@ -39,7 +40,7 @@ namespace glabels SubstitutionField(); SubstitutionField( const QString& string ); - QString evaluate( const merge::Record* record ) const; + QString evaluate( const merge::Record* record, const Variables* variables ) const; QString fieldName() const; QString defaultValue() const; diff --git a/model/Template.cpp b/model/Template.cpp index 6acc6857..690b56c0 100644 --- a/model/Template.cpp +++ b/model/Template.cpp @@ -21,6 +21,7 @@ #include "Template.h" #include "Db.h" +#include "FrameContinuous.h" #include @@ -36,6 +37,7 @@ namespace glabels const QString& paperId, const Distance& pageWidth, const Distance& pageHeight, + const Distance& rollWidth, bool isUserDefined ) : mBrand(brand), mPart(part), @@ -43,6 +45,7 @@ namespace glabels mPaperId(paperId), mPageWidth(pageWidth), mPageHeight(pageHeight), + mRollWidth(rollWidth), mIsUserDefined(isUserDefined), mIsSizeIso(false), mIsSizeUs(false), @@ -56,6 +59,8 @@ namespace glabels mIsSizeIso = paper->isSizeIso(); mIsSizeUs = paper->isSizeUs(); } + + mIsRoll = (paperId == "roll"); } @@ -67,15 +72,17 @@ namespace glabels mPaperId = other.mPaperId; mPageWidth = other.mPageWidth; mPageHeight = other.mPageHeight; + mRollWidth = other.mRollWidth; mIsSizeIso = other.mIsSizeIso; mIsSizeUs = other.mIsSizeUs; + mIsRoll = other.mIsRoll; mEquivPart = other.mEquivPart; mName = other.mName; mProductUrl = other.mProductUrl; foreach ( Frame* frame, other.mFrames ) { - addFrame( frame ); + addFrame( frame->dup() ); } foreach ( QString categoryId, other.mCategoryIds ) @@ -85,9 +92,50 @@ namespace glabels } - Template* Template::dup() const + Template::~Template() { - return new Template( *this ); + while ( !mFrames.isEmpty() ) + { + delete mFrames.takeFirst(); + } + } + + + Template& Template::operator=( const Template& other ) + { + if ( this != &other ) + { + mBrand = other.mBrand; + mPart = other.mPart; + mDescription = other.mDescription; + mPaperId = other.mPaperId; + mPageWidth = other.mPageWidth; + mPageHeight = other.mPageHeight; + mRollWidth = other.mRollWidth; + mIsSizeIso = other.mIsSizeIso; + mIsSizeUs = other.mIsSizeUs; + mIsRoll = other.mIsRoll; + mEquivPart = other.mEquivPart; + mName = other.mName; + mProductUrl = other.mProductUrl; + + while ( !mFrames.isEmpty() ) + { + delete mFrames.takeFirst(); + } + foreach ( Frame* frame, other.mFrames ) + { + addFrame( frame->dup() ); + } + + mCategoryIds.clear(); + foreach ( QString categoryId, other.mCategoryIds ) + { + addCategory( categoryId ); + } + } + + return *this; } @@ -107,7 +155,7 @@ namespace glabels const Template* other = Db::lookupTemplateFromBrandPart( brand, equivPart ); if ( other != nullptr ) { - Template* tmplate = other->dup(); + Template* tmplate = new Template( *other ); tmplate->mPart = part; tmplate->mEquivPart = equivPart; @@ -159,7 +207,22 @@ namespace glabels Distance Template::pageHeight() const { - return mPageHeight; + // Adjust height if continuous tape + const model::Frame* frame = mFrames.constFirst(); + if ( const auto* frameContinuous = dynamic_cast(frame) ) + { + return frameContinuous->h(); + } + else + { + return mPageHeight; + } + } + + + Distance Template::rollWidth() const + { + return mRollWidth; } @@ -181,6 +244,12 @@ namespace glabels } + bool Template::isRoll() const + { + return mIsRoll; + } + + bool Template::isUserDefined() const { return mIsUserDefined; @@ -274,12 +343,12 @@ namespace glabels } // Are they layed out similarly? - foreach ( Layout* layout1, frame1->layouts() ) + foreach ( const Layout& layout1, frame1->layouts() ) { bool matchFound = false; - foreach ( Layout* layout2, frame2->layouts() ) + foreach ( const Layout& layout2, frame2->layouts() ) { - if ( layout1->isSimilarTo(layout2) ) + if ( layout1.isSimilarTo( layout2 ) ) { matchFound = true; break; @@ -297,3 +366,23 @@ namespace glabels } } + + +QDebug operator<<( QDebug dbg, const glabels::model::Template& tmplate ) +{ + QDebugStateSaver saver(dbg); + + dbg.nospace() << "Template{ " + << tmplate.brand() << "," << tmplate.part() << "," << tmplate.description() << "," + << tmplate.paperId() << "," + << tmplate.pageWidth() << "," + << tmplate.pageHeight() << "," + << tmplate.rollWidth() << "," + << tmplate.isSizeIso() << "," + << tmplate.isSizeUs() << "," + << tmplate.isSizeOther() << "," + << tmplate.isRoll() << "," + << *tmplate.frames().constFirst() << "," + << " }"; + return dbg; +} diff --git a/model/Template.h b/model/Template.h index 144d8a60..c83faf9b 100644 --- a/model/Template.h +++ b/model/Template.h @@ -43,17 +43,22 @@ namespace glabels public: + Template() = default; + Template( const QString& brand, const QString& part, const QString& description, const QString& paperId, const Distance& pageWidth, const Distance& pageHeight, + const Distance& rollWidth = 0, bool isUserDefined = false ); Template( const Template& other ); - Template* dup() const; + ~Template(); + + Template& operator=( const Template& other ); // Generic full page template static Template* fullPage( const QString& paperId ); @@ -71,9 +76,11 @@ namespace glabels QString paperId() const; Distance pageWidth() const; Distance pageHeight() const; + Distance rollWidth() const; bool isSizeIso() const; bool isSizeUs() const; bool isSizeOther() const; + bool isRoll() const; bool isUserDefined() const; @@ -104,8 +111,10 @@ namespace glabels QString mPaperId; Distance mPageWidth; Distance mPageHeight; + Distance mRollWidth; bool mIsSizeIso; bool mIsSizeUs; + bool mIsRoll; bool mIsUserDefined; @@ -119,7 +128,12 @@ namespace glabels }; } + } +// Debugging support +QDebug operator<<( QDebug dbg, const glabels::model::Template& tmplate ); + + #endif // model_Template_h diff --git a/model/TextNode.cpp b/model/TextNode.cpp index 332367e9..b0e22719 100644 --- a/model/TextNode.cpp +++ b/model/TextNode.cpp @@ -105,48 +105,34 @@ namespace glabels /// /// Get text, expand if necessary /// - QString TextNode::text( merge::Record* record ) const + QString TextNode::text( const merge::Record* record, + const Variables* variables ) const { - if ( mIsField ) + QString value(""); + + bool haveRecordField = mIsField && record && + record->contains(mData) && + !record->value(mData).isEmpty(); + bool haveVariable = mIsField && variables && + variables->contains(mData) && + !(*variables)[mData].value().isEmpty(); + + if ( haveRecordField ) { - if ( !record ) - { - return QString("${%1}").arg( mData ); - } - else - { - if ( record->contains( mData ) ) - { - return (*record)[ mData ]; - } - else - { - return ""; - } - } + value = record->value(mData); } - else + else if ( haveVariable ) { - return mData; + value = (*variables)[mData].value(); } - } - - - /// - /// Is it an empty field - /// - bool TextNode::isEmptyField( merge::Record* record ) const - { - if ( record && mIsField ) + else if ( !mIsField ) { - if ( record->contains( mData ) ) - { - return (*record)[mData].isEmpty(); - } + value = mData; } - return false; + return value; } + } } diff --git a/model/TextNode.h b/model/TextNode.h index 499f863b..c7716d77 100644 --- a/model/TextNode.h +++ b/model/TextNode.h @@ -22,6 +22,7 @@ #define model_TextNode_h +#include "Variables.h" #include "merge/Record.h" #include @@ -76,8 +77,8 @@ namespace glabels ///////////////////////////////// // Misc. Methods ///////////////////////////////// - QString text( merge::Record* record ) const; - bool isEmptyField( merge::Record* record ) const; + QString text( const merge::Record* record, + const Variables* variables ) const; ///////////////////////////////// diff --git a/model/Variable.cpp b/model/Variable.cpp new file mode 100644 index 00000000..56f8b6a8 --- /dev/null +++ b/model/Variable.cpp @@ -0,0 +1,329 @@ +/* Variable.cpp + * + * Copyright (C) 2013-2016 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "Variable.h" + + +namespace glabels +{ + namespace model + { + + Variable::Variable() + : mType(Type::STRING), + mIncrement(Increment::NEVER), + mStepSize("0"), + mIntegerValue(0), + mIntegerStep(0), + mFloatingPointValue(0), + mFloatingPointStep(0) + { + // empty + } + + + Variable::Variable( Variable::Type type, + const QString& name, + const QString& initialValue, + Variable::Increment increment, + const QString& stepSize ) + : mType(type), + mName(name), + mInitialValue(initialValue), + mIncrement(increment), + mStepSize(stepSize), + mIntegerValue(0), + mIntegerStep(0), + mFloatingPointValue(0), + mFloatingPointStep(0) + { + resetValue(); + } + + + Variable::Type Variable::type() const + { + return mType; + } + + + QString Variable::name() const + { + return mName; + } + + + QString Variable::initialValue() const + { + return mInitialValue; + } + + + Variable::Increment Variable::increment() const + { + return mIncrement; + } + + + QString Variable::stepSize() const + { + return mStepSize; + } + + + void Variable::setInitialValue( const QString& value ) + { + mInitialValue = value; + } + + + void Variable::resetValue() + { + switch (mType) + { + case Type::STRING: + // do nothing + break; + case Type::INTEGER: + mIntegerValue = mInitialValue.toLongLong(); + mIntegerStep = mStepSize.toLongLong(); + break; + case Type::FLOATING_POINT: + mFloatingPointValue = mInitialValue.toDouble(); + mFloatingPointStep = mStepSize.toDouble(); + break; + case Type::COLOR: + // do nothing + break; + } + } + + + void Variable::incrementValueOnItem() + { + if ( mIncrement == Increment::PER_ITEM ) + { + switch (mType) + { + case Type::STRING: + // do nothing + break; + case Type::INTEGER: + mIntegerValue += mIntegerStep; + break; + case Type::FLOATING_POINT: + mFloatingPointValue += mFloatingPointStep; + break; + case Type::COLOR: + // do nothing + break; + } + } + } + + + void Variable::incrementValueOnCopy() + { + if ( mIncrement == Increment::PER_COPY ) + { + switch (mType) + { + case Type::STRING: + // do nothing + break; + case Type::INTEGER: + mIntegerValue += mIntegerStep; + break; + case Type::FLOATING_POINT: + mFloatingPointValue += mFloatingPointStep; + break; + case Type::COLOR: + // do nothing + break; + } + } + } + + + void Variable::incrementValueOnPage() + { + if ( mIncrement == Increment::PER_PAGE ) + { + switch (mType) + { + case Type::STRING: + // do nothing + break; + case Type::INTEGER: + mIntegerValue += mIntegerStep; + break; + case Type::FLOATING_POINT: + mFloatingPointValue += mFloatingPointStep; + break; + case Type::COLOR: + // do nothing + break; + } + } + } + + + QString Variable::value() const + { + switch (mType) + { + case Type::STRING: + return mInitialValue; + case Type::INTEGER: + return QString::number( mIntegerValue ); + case Type::FLOATING_POINT: + return QString::number( mFloatingPointValue, 'g', 15 ); + case Type::COLOR: + return mInitialValue; + default: + return mInitialValue; + } + } + + + QString Variable::typeToI18nString( Type type ) + { + switch (type) + { + case Type::STRING: + return tr("String"); + case Type::INTEGER: + return tr("Integer"); + case Type::FLOATING_POINT: + return tr("Floating Point"); + case Type::COLOR: + return tr("Color"); + default: + return tr("String"); + } + } + + + QString Variable::typeToIdString( Type type ) + { + switch (type) + { + case Type::STRING: + return "string"; + case Type::INTEGER: + return "integer"; + case Type::FLOATING_POINT: + return "float"; + case Type::COLOR: + return "color"; + default: + return "string"; + } + } + + + Variable::Type Variable::idStringToType( const QString& id ) + { + if ( id == "string" ) + { + return Type::STRING; + } + else if ( id == "integer" ) + { + return Type::INTEGER; + } + else if ( id == "float" ) + { + return Type::FLOATING_POINT; + } + if ( id == "color" ) + { + return Type::COLOR; + } + else + { + return Type::STRING; // Default + } + } + + + QString Variable::incrementToI18nString( Increment increment ) + { + switch (increment) + { + case Increment::NEVER: + return tr("Never"); + case Increment::PER_ITEM: + return tr("Per item"); + case Increment::PER_COPY: + return tr("Per copy"); + case Increment::PER_PAGE: + return tr("Per page"); + default: + return tr("Never"); + } + } + + + QString Variable::incrementToIdString( Increment increment ) + { + switch (increment) + { + case Increment::NEVER: + return "never"; + case Increment::PER_ITEM: + return "per_item"; + case Increment::PER_COPY: + return "per_copy"; + case Increment::PER_PAGE: + return "per_page"; + default: + return "never"; + } + } + + + Variable::Increment Variable::idStringToIncrement( const QString& id ) + { + if ( id == "never" ) + { + return Increment::NEVER; + } + else if ( id == "per_item" ) + { + return Increment::PER_ITEM; + } + else if ( id == "per_copy" ) + { + return Increment::PER_COPY; + } + else if ( id == "per_page" ) + { + return Increment::PER_PAGE; + } + else + { + return Increment::NEVER; // Default + } + } + + + } +} diff --git a/model/Variable.h b/model/Variable.h new file mode 100644 index 00000000..e0b8fe0c --- /dev/null +++ b/model/Variable.h @@ -0,0 +1,109 @@ +/* Variable.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#ifndef model_Variable_h +#define model_Variable_h + + +#include +#include + + +namespace glabels +{ + namespace model + { + + class Variable + { + Q_DECLARE_TR_FUNCTIONS(Variable) + + public: + enum class Type + { + STRING, + INTEGER, + FLOATING_POINT, + COLOR + }; + + enum class Increment + { + NEVER, + PER_ITEM, + PER_COPY, + PER_PAGE + }; + + + public: + Variable(); + + Variable( Type type, + const QString& name, + const QString& initialValue, + Increment increment = Increment::NEVER, + const QString& stepSize = "0" ); + + virtual ~Variable() = default; + + + Type type() const; + QString name() const; + QString initialValue() const; + Increment increment() const; + QString stepSize() const; + + void setInitialValue( const QString& value ); + + void resetValue(); + void incrementValueOnItem(); + void incrementValueOnCopy(); + void incrementValueOnPage(); + QString value() const; + + static QString typeToI18nString( Type type ); + static QString typeToIdString( Type type ); + static Type idStringToType( const QString& string ); + + static QString incrementToI18nString( Increment increment ); + static QString incrementToIdString( Increment increment ); + static Increment idStringToIncrement( const QString& string ); + + + private: + Type mType; + QString mName; + QString mInitialValue; + Increment mIncrement; + QString mStepSize; + + long long mIntegerValue; + long long mIntegerStep; + double mFloatingPointValue; + double mFloatingPointStep; + + }; + + } +} + + +#endif // model_Variable_h diff --git a/model/Variables.cpp b/model/Variables.cpp new file mode 100644 index 00000000..4f80e452 --- /dev/null +++ b/model/Variables.cpp @@ -0,0 +1,174 @@ +/* Variables.cpp + * + * Copyright (C) 2013-2016 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "Variables.h" + +#include + + +namespace glabels +{ + namespace model + { + /// + /// Copy constructor + /// + Variables::Variables( const Variables* variables ) + : QMap(*variables) + { + } + + + /// + /// Clone + /// + Variables* Variables::clone() const + { + return new Variables( this ); + } + + + /// + /// Do we have variable? + /// + bool Variables::hasVariable( const QString& name ) const + { + return contains(name); + } + + + /// + /// Add variable ( will replace if name is the same ) + /// + void Variables::addVariable( const Variable& variable ) + { + insert( variable.name(), variable ); + emit changed(); + } + + + /// + /// Delete variable + /// + void Variables::deleteVariable( const QString& name ) + { + remove( name ); + emit changed(); + } + + + /// + /// Replace variable + /// + void Variables::replaceVariable( const QString& origName, const Variable& variable ) + { + remove( origName ); + insert( variable.name(), variable ); + emit changed(); + } + + + /// + /// Set initial value of multiple variables + /// + void Variables::setVariables( const QMap& definitions ) + { + for ( auto& name : definitions.keys() ) + { + if ( hasVariable( name ) ) + { + (*this)[name].setInitialValue( definitions[name] ); + } + else + { + addVariable( Variable( Variable::Type::STRING, + name, + definitions[name] ) ); + } + } + } + + + /// + /// Reset variables to their initial values + /// + void Variables::resetVariables() + { + for ( auto& v : *this ) + { + v.resetValue(); + } + } + + + /// + /// Reset "on copy" variables to their initial values + /// + void Variables::resetOnCopyVariables() + { + for ( auto& v : *this ) + { + if ( v.increment() == Variable::Increment::PER_COPY ) + { + v.resetValue(); + } + } + } + + + /// + /// Increment variables on item + /// + void Variables::incrementVariablesOnItem() + { + for ( auto& v : *this ) + { + v.incrementValueOnItem(); + } + } + + + /// + /// Increment variables on copy + /// + void Variables::incrementVariablesOnCopy() + { + for ( auto& v : *this ) + { + v.incrementValueOnCopy(); + } + } + + + /// + /// Increment variables on page + /// + void Variables::incrementVariablesOnPage() + { + for ( auto& v : *this ) + { + v.incrementValueOnPage(); + } + } + + + } // namespace model + +} // namespace glabels diff --git a/model/Variables.h b/model/Variables.h new file mode 100644 index 00000000..38087859 --- /dev/null +++ b/model/Variables.h @@ -0,0 +1,93 @@ +/* Variables.h + * + * Copyright (C) 2013-2016 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#ifndef model_Variables_h +#define model_Variables_h + + +#include "Variable.h" + +#include +#include +#include + + +namespace glabels +{ + namespace model + { + + /// + /// Variables Collection + /// + class Variables : public QObject, public QMap + { + Q_OBJECT + + ///////////////////////////////// + // Life Cycle + ///////////////////////////////// + public: + Variables() = default; + Variables( const Variables* variables ); + + + ///////////////////////////////// + // Object duplication + ///////////////////////////////// + Variables* clone() const; + + + ///////////////////////////////// + // Methods + ///////////////////////////////// + bool hasVariable( const QString& name ) const; + void addVariable( const Variable& variable ); + void deleteVariable( const QString& name ); + void replaceVariable( const QString& name, const Variable& variable ); + + void setVariables( const QMap& definitions ); + + void resetVariables(); + void resetOnCopyVariables(); + void incrementVariablesOnItem(); + void incrementVariablesOnCopy(); + void incrementVariablesOnPage(); + + + ///////////////////////////////// + // Signals + ///////////////////////////////// + signals: + void changed(); + + + ///////////////////////////////// + // Private data + ///////////////////////////////// + private: + + }; + + } +} + + +#endif // model_Variables_h diff --git a/model/Version.h.in b/model/Version.h.in index 619658d2..04443612 100644 --- a/model/Version.h.in +++ b/model/Version.h.in @@ -29,13 +29,15 @@ namespace glabels namespace Version { - const QString WEBSITE = "http://@Website@"; + const QString WEBSITE = "http://@WEBSITE@"; + const QString BUG_WEBSITE = "@BUG_WEBSITE@"; const int MAJOR = @glabels-qt_VERSION_MAJOR@; const int MINOR = @glabels-qt_VERSION_MINOR@; const int MICRO = @glabels-qt_VERSION_PATCH@; - const QString STRING = "@glabels-qt_VERSION@"; + const QString STRING = "@VERSION_STRING@"; + const QString LONG_STRING = "@LONG_VERSION_STRING@"; } } diff --git a/model/XmlLabelCreator.cpp b/model/XmlLabelCreator.cpp index 87b3114f..b7f7cc9e 100644 --- a/model/XmlLabelCreator.cpp +++ b/model/XmlLabelCreator.cpp @@ -29,9 +29,12 @@ #include "ModelImageObject.h" #include "ModelTextObject.h" #include "DataCache.h" +#include "FileUtil.h" +#include "Variables.h" #include "XmlTemplateCreator.h" #include "XmlUtil.h" +#include "merge/Factory.h" #include "merge/None.h" #include @@ -48,38 +51,41 @@ namespace glabels { void - XmlLabelCreator::writeFile( const Model* label, const QString& fileName ) + XmlLabelCreator::writeFile( Model* model, const QString& fileName ) { - QDomDocument doc; - - createDoc( doc, label ); - QByteArray buffer = doc.toByteArray( 2 ); - QFile file( fileName ); - if ( !file.open( QFile::WriteOnly | QFile::Text) ) { qWarning() << "Error: Cannot write file " << fileName << ": " << file.errorString(); + return; } + model->setFileName( fileName ); + model->clearModified(); + + QDomDocument doc; + createDoc( doc, model ); + + QByteArray buffer = doc.toByteArray( 2 ); file.write( buffer.data(), buffer.size() ); } void - XmlLabelCreator::writeBuffer( const Model* label, QByteArray& buffer ) + XmlLabelCreator::writeBuffer( const Model* model, QByteArray& buffer ) { QDomDocument doc; - createDoc( doc, label ); + createDoc( doc, model ); buffer = doc.toByteArray( 2 ); } void XmlLabelCreator::serializeObjects( const QList& objects, - QByteArray& buffer ) + const Model* model, + QByteArray& buffer ) { QDomDocument doc; @@ -90,15 +96,15 @@ namespace glabels doc.appendChild( root ); XmlUtil::setStringAttr( root, "version", "4.0" ); - createDataNode( root, objects ); - createObjectsNode( root, objects, false ); + createDataNode( root, model, objects ); + createObjectsNode( root, model, objects, false ); buffer = doc.toByteArray( 2 ); } void - XmlLabelCreator::createDoc( QDomDocument& doc, const Model* label ) + XmlLabelCreator::createDoc( QDomDocument& doc, const Model* model ) { QDomNode xmlNode( doc.createProcessingInstruction( "xml", "version=\"1.0\"" ) ); doc.appendChild( xmlNode ); @@ -107,21 +113,29 @@ namespace glabels doc.appendChild( root ); XmlUtil::setStringAttr( root, "version", "4.0" ); - XmlTemplateCreator().createTemplateNode( root, label->tmplate() ); + XmlTemplateCreator().createTemplateNode( root, model->tmplate() ); + + createObjectsNode( root, model, model->objectList(), model->rotate() ); - createObjectsNode( root, label->objectList(), label->rotate() ); + if ( model->merge() && !dynamic_cast(model->merge()) ) + { + createMergeNode( root, model ); + } - if ( label->merge() && !dynamic_cast(label->merge()) ) + if ( model->variables()->size() != 0 ) { - createMergeNode( root, label ); + createVariablesNode( root, model ); } - createDataNode( root, label->objectList() ); + createDataNode( root, model, model->objectList() ); } void - XmlLabelCreator::createObjectsNode( QDomElement &parent, const QList& objects, bool rotate ) + XmlLabelCreator::createObjectsNode( QDomElement& parent, + const Model* model, + const QList& objects, + bool rotate ) { QDomDocument doc = parent.ownerDocument(); QDomElement node = doc.createElement( "Objects" ); @@ -146,7 +160,7 @@ namespace glabels } else if ( auto* imageObject = dynamic_cast(object) ) { - createObjectImageNode( node, imageObject ); + createObjectImageNode( node, model, imageObject ); } else if ( auto* barcodeObject = dynamic_cast(object) ) { @@ -244,7 +258,9 @@ namespace glabels void - XmlLabelCreator::createObjectImageNode( QDomElement &parent, const ModelImageObject* object ) + XmlLabelCreator::createObjectImageNode( QDomElement& parent, + const Model* model, + const ModelImageObject* object ) { QDomDocument doc = parent.ownerDocument(); QDomElement node = doc.createElement( "Object-image" ); @@ -263,7 +279,8 @@ namespace glabels } else { - XmlUtil::setStringAttr( node, "src", object->filenameNode().data() ); + QString fn = FileUtil::makeRelativeIfInDir( model->dir(), object->filenameNode().data() ); + XmlUtil::setStringAttr( node, "src", fn ); } /* affine attrs */ @@ -384,6 +401,7 @@ namespace glabels { XmlUtil::setLengthAttr( node, "w", object->w() ); XmlUtil::setLengthAttr( node, "h", object->h() ); + XmlUtil::setBoolAttr( node, "lock_aspect_ratio", object->lockAspectRatio() ); } @@ -433,41 +451,99 @@ namespace glabels void XmlLabelCreator::createShadowAttrs( QDomElement &node, const ModelObject* object ) { - if ( object->shadow() ) + XmlUtil::setBoolAttr( node, "shadow", object->shadow() ); + + XmlUtil::setLengthAttr( node, "shadow_x", object->shadowX() ); + XmlUtil::setLengthAttr( node, "shadow_y", object->shadowY() ); + + if ( object->fillColorNode().isField() ) + { + XmlUtil::setStringAttr( node, "shadow_color_field", object->shadowColorNode().key() ); + } + else { - XmlUtil::setBoolAttr( node, "shadow", object->shadow() ); + XmlUtil::setUIntAttr( node, "shadow_color", object->shadowColorNode().rgba() ); + } + + XmlUtil::setDoubleAttr( node, "shadow_opacity", object->shadowOpacity() ); + } - XmlUtil::setLengthAttr( node, "shadow_x", object->shadowX() ); - XmlUtil::setLengthAttr( node, "shadow_y", object->shadowY() ); - if ( object->fillColorNode().isField() ) - { - XmlUtil::setStringAttr( node, "shadow_color_field", object->shadowColorNode().key() ); - } - else + void + XmlLabelCreator::createMergeNode( QDomElement &parent, const Model* model ) + { + QDomDocument doc = parent.ownerDocument(); + QDomElement node = doc.createElement( "Merge" ); + parent.appendChild( node ); + + QString id = model->merge()->id(); + QString src = model->merge()->source(); + + XmlUtil::setStringAttr( node, "type", id ); + + switch ( merge::Factory::idToType( id ) ) + { + case merge::Factory::NONE: + case merge::Factory::FIXED: + break; + + case merge::Factory::FILE: { - XmlUtil::setUIntAttr( node, "shadow_color", object->shadowColorNode().rgba() ); + QString fn = FileUtil::makeRelativeIfInDir( model->dir(), src ); + XmlUtil::setStringAttr( node, "src", fn ); } + break; - XmlUtil::setDoubleAttr( node, "shadow_opacity", object->shadowOpacity() ); + default: + qWarning() << "XmlLabelCreator::createMergeNode(): Should not be reached!"; + break; } } void - XmlLabelCreator::createMergeNode( QDomElement &parent, const Model* label ) + XmlLabelCreator::createVariablesNode( QDomElement &parent, const Model* model ) { QDomDocument doc = parent.ownerDocument(); - QDomElement node = doc.createElement( "Merge" ); + QDomElement node = doc.createElement( "Variables" ); + parent.appendChild( node ); + + for ( const auto& v : *model->variables() ) + { + createVariableNode( node, v ); + } + } + + + void + XmlLabelCreator::createVariableNode( QDomElement &parent, const Variable& v ) + { + QDomDocument doc = parent.ownerDocument(); + QDomElement node = doc.createElement( "Variable" ); parent.appendChild( node ); - XmlUtil::setStringAttr( node, "type", label->merge()->id() ); - XmlUtil::setStringAttr( node, "src", label->merge()->source() ); + XmlUtil::setStringAttr( node, "type", Variable::typeToIdString( v.type() ) ); + XmlUtil::setStringAttr( node, "name", v.name() ); + XmlUtil::setStringAttr( node, "initialValue", v.initialValue() ); + + if ( (v.type() == Variable::Type::INTEGER) || + (v.type() == Variable::Type::FLOATING_POINT) ) + { + XmlUtil::setStringAttr( node, "increment", + Variable::incrementToIdString( v.increment() ) ); + + if ( v.increment() != Variable::Increment::NEVER ) + { + XmlUtil::setStringAttr( node, "stepSize", v.stepSize() ); + } + } } void - XmlLabelCreator::createDataNode( QDomElement &parent, const QList& objects ) + XmlLabelCreator::createDataNode( QDomElement& parent, + const Model* model, + const QList& objects ) { QDomDocument doc = parent.ownerDocument(); QDomElement node = doc.createElement( "Data" ); @@ -477,12 +553,14 @@ namespace glabels foreach ( QString name, data.imageNames() ) { - createPngFileNode( node, name, data.getImage( name ) ); + QString fn = FileUtil::makeRelativeIfInDir( model->dir(), name ); + createPngFileNode( node, fn, data.getImage( name ) ); } foreach ( QString name, data.svgNames() ) { - createSvgFileNode( node, name, data.getSvg( name ) ); + QString fn = FileUtil::makeRelativeIfInDir( model->dir(), name ); + createSvgFileNode( node, fn, data.getSvg( name ) ); } } @@ -521,7 +599,5 @@ namespace glabels node.appendChild( doc.createCDATASection( QString( svg ) ) ); } - - } } diff --git a/model/XmlLabelCreator.h b/model/XmlLabelCreator.h index 93c235e5..9c765ac1 100644 --- a/model/XmlLabelCreator.h +++ b/model/XmlLabelCreator.h @@ -40,6 +40,7 @@ namespace glabels class ModelImageObject; class ModelBarcodeObject; class ModelTextObject; + class Variable; /// @@ -50,31 +51,87 @@ namespace glabels Q_OBJECT public: - static void writeFile( const Model* label, const QString& fileName ); - static void writeBuffer( const Model* label, QByteArray& buffer ); - static void serializeObjects( const QList& objects, QByteArray& buffer ); + static void writeFile( Model* model, + const QString& fileName ); + + static void writeBuffer( const Model* model, + QByteArray& buffer ); + + static void serializeObjects( const QList& objects, + const Model* model, + QByteArray& buffer ); private: - static void createDoc( QDomDocument& doc, const Model* label ); - static void createRootNode( const Model* label ); - static void createObjectsNode( QDomElement &parent, const QList& objects, bool rotate ); - static void createObjectBoxNode( QDomElement &parent, const ModelBoxObject* object ); - static void createObjectEllipseNode( QDomElement &parent, const ModelEllipseObject* object ); - static void createObjectLineNode( QDomElement &parent, const ModelLineObject* object ); - static void createObjectImageNode( QDomElement &parent, const ModelImageObject* object ); - static void createObjectBarcodeNode( QDomElement &parent, const ModelBarcodeObject* object ); - static void createObjectTextNode( QDomElement &parent, const ModelTextObject* object ); - static void createPNode( QDomElement &parent, const QString& blockText ); - static void createPositionAttrs( QDomElement &node, const ModelObject* object ); - static void createSizeAttrs( QDomElement &node, const ModelObject* object ); - static void createLineAttrs( QDomElement &node, const ModelObject* object ); - static void createFillAttrs( QDomElement &node, const ModelObject* object ); - static void createAffineAttrs( QDomElement &node, const ModelObject* object ); - static void createShadowAttrs( QDomElement &node, const ModelObject* object ); - static void createMergeNode( QDomElement &parent, const Model* label ); - static void createDataNode( QDomElement &parent, const QList& objects ); - static void createPngFileNode( QDomElement &parent, const QString& name, const QImage& image ); - static void createSvgFileNode( QDomElement &parent, const QString& name, const QByteArray& svg ); + static void createDoc( QDomDocument& doc, + const Model* model ); + + static void createRootNode( const Model* model ); + + static void createObjectsNode( QDomElement& parent, + const Model* model, + const QList& objects, + bool rotate ); + + static void createObjectBoxNode( QDomElement& parent, + const ModelBoxObject* object ); + + static void createObjectEllipseNode( QDomElement& parent, + const ModelEllipseObject* object ); + + static void createObjectLineNode( QDomElement& parent, + const ModelLineObject* object ); + + static void createObjectImageNode( QDomElement& parent, + const Model* model, + const ModelImageObject* object ); + + static void createObjectBarcodeNode( QDomElement& parent, + const ModelBarcodeObject* object ); + + static void createObjectTextNode( QDomElement& parent, + const ModelTextObject* object ); + + static void createPNode( QDomElement& parent, + const QString& blockText ); + + static void createPositionAttrs( QDomElement& node, + const ModelObject* object ); + + static void createSizeAttrs( QDomElement& node, + const ModelObject* object ); + + static void createLineAttrs( QDomElement& node, + const ModelObject* object ); + + static void createFillAttrs( QDomElement& node, + const ModelObject* object ); + + static void createAffineAttrs( QDomElement& node, + const ModelObject* object ); + + static void createShadowAttrs( QDomElement& node, + const ModelObject* object ); + + static void createMergeNode( QDomElement& parent, + const Model* model ); + + static void createVariablesNode( QDomElement& parent, + const Model* model ); + + static void createVariableNode( QDomElement& parent, + const Variable& v ); + + static void createDataNode( QDomElement& parent, + const Model* model, + const QList& objects ); + + static void createPngFileNode( QDomElement& parent, + const QString& name, + const QImage& image ); + + static void createSvgFileNode( QDomElement& parent, + const QString& name, + const QByteArray& svg ); }; diff --git a/model/XmlLabelParser.cpp b/model/XmlLabelParser.cpp index 5fbced51..fce0f2de 100644 --- a/model/XmlLabelParser.cpp +++ b/model/XmlLabelParser.cpp @@ -32,6 +32,8 @@ #include "XmlUtil.h" #include "DataCache.h" +#include "XmlLabelParser_3.h" + #include "barcode/Backends.h" #include "merge/Factory.h" @@ -77,7 +79,7 @@ namespace glabels gunzip( rawData, unzippedData ); success = doc.setContent( unzippedData, false, &errorString, &errorLine, &errorColumn ); #else - qWarning() << "Warning: Cannot read compressed glabels project file! gLabels not build with ZLIB."; + qWarning() << "Warning: Cannot read compressed glabels project file! gLabels not built with ZLIB."; return nullptr; #endif } @@ -103,7 +105,7 @@ namespace glabels return nullptr; } - return parseRootNode( root ); + return parseRootNode( root, fileName ); } @@ -130,12 +132,12 @@ namespace glabels return nullptr; } - return parseRootNode( root ); + return parseRootNode( root, QString() ); } QList - XmlLabelParser::deserializeObjects( const QByteArray& buffer ) + XmlLabelParser::deserializeObjects( const QByteArray& buffer, const Model* model ) { QList list; @@ -165,7 +167,7 @@ namespace glabels { if ( child.toElement().tagName() == "Data" ) { - parseDataNode( child.toElement(), data ); + parseDataNode( child.toElement(), model, data ); } } @@ -174,9 +176,10 @@ namespace glabels { if ( child.toElement().tagName() == "Objects" ) { - list = parseObjectsNode( child.toElement(), data ); + list = parseObjectsNode( child.toElement(), model, data ); } } + return list; } @@ -234,15 +237,22 @@ namespace glabels Model* - XmlLabelParser::parseRootNode( const QDomElement &node ) + XmlLabelParser::parseRootNode( const QDomElement &node, const QString& fileName ) { QString version = XmlUtil::getStringAttr( node, "version", "" ); if ( version != "4.0" ) { - qWarning() << "TODO: compatability mode."; + // Attempt to import as version 3.0 format (glabels 2.0 - glabels 3.4) + auto* model = XmlLabelParser_3::parseRootNode( node ); + if ( model ) + { + model->setFileName( fileName ); + } + return model; } - auto* label = new Model(); + auto* model = new Model(); + model->setFileName( fileName ); /* Pass 1, extract data nodes to pre-load cache. */ DataCache data; @@ -250,7 +260,7 @@ namespace glabels { if ( child.toElement().tagName() == "Data" ) { - parseDataNode( child.toElement(), data ); + parseDataNode( child.toElement(), model, data ); } } @@ -265,23 +275,28 @@ namespace glabels if ( tmplate == nullptr ) { qWarning() << "Unable to parse template"; - delete label; + delete model; return nullptr; } - label->setTmplate( tmplate ); + model->setTmplate( tmplate ); // Copies arg + delete tmplate; } else if ( tagName == "Objects" ) { - label->setRotate( parseRotateAttr( child.toElement() ) ); - QList list = parseObjectsNode( child.toElement(), data ); + model->setRotate( parseRotateAttr( child.toElement() ) ); + auto list = parseObjectsNode( child.toElement(), model, data ); foreach ( ModelObject* object, list ) { - label->addObject( object ); + model->addObject( object ); } } else if ( tagName == "Merge" ) { - parseMergeNode( child.toElement(), label ); + parseMergeNode( child.toElement(), model ); + } + else if ( tagName == "Variables" ) + { + parseVariablesNode( child.toElement(), model ); } else if ( tagName == "Data" ) { @@ -293,13 +308,15 @@ namespace glabels } } - label->clearModified(); - return label; + model->clearModified(); + return model; } QList - XmlLabelParser::parseObjectsNode( const QDomElement &node, const DataCache& data ) + XmlLabelParser::parseObjectsNode( const QDomElement& node, + const Model* model, + const DataCache& data ) { QList list; @@ -325,7 +342,7 @@ namespace glabels } else if ( tagName == "Object-image" ) { - list.append( parseObjectImageNode( child.toElement(), data ) ); + list.append( parseObjectImageNode( child.toElement(), model, data ) ); } else if ( tagName == "Object-barcode" ) { @@ -351,19 +368,20 @@ namespace glabels /* size attrs */ Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); + bool lockAspectRatio = XmlUtil::getBoolAttr( node, "lock_aspect_ratio", false ); /* line attrs */ Distance lineWidth = XmlUtil::getLengthAttr( node, "line_width", 1.0 ); QString key = XmlUtil::getStringAttr( node, "line_color_field", "" ); bool field_flag = !key.isEmpty(); - uint32_t color = XmlUtil::getUIntAttr( node, "line_color", 0 ); + uint32_t color = XmlUtil::getUIntAttr( node, "line_color", 0xFF ); ColorNode lineColorNode( field_flag, color, key ); /* fill attrs */ key = XmlUtil::getStringAttr( node, "fill_color_field", "" ); field_flag = !key.isEmpty(); - color = XmlUtil::getUIntAttr( node, "fill_color", 0 ); + color = XmlUtil::getUIntAttr( node, "fill_color", 0xFF ); ColorNode fillColorNode( field_flag, color, key ); /* affine attrs */ @@ -383,10 +401,10 @@ namespace glabels key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); field_flag = !key.isEmpty(); - color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); + color = XmlUtil::getUIntAttr( node, "shadow_color", 0xFF ); ColorNode shadowColorNode( field_flag, color, key ); - return new ModelBoxObject( x0, y0, w, h, + return new ModelBoxObject( x0, y0, w, h, lockAspectRatio, lineWidth, lineColorNode, fillColorNode, QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), @@ -404,19 +422,20 @@ namespace glabels /* size attrs */ Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); + bool lockAspectRatio = XmlUtil::getBoolAttr( node, "lock_aspect_ratio", false ); /* line attrs */ Distance lineWidth = XmlUtil::getLengthAttr( node, "line_width", 1.0 ); QString key = XmlUtil::getStringAttr( node, "line_color_field", "" ); bool field_flag = !key.isEmpty(); - uint32_t color = XmlUtil::getUIntAttr( node, "line_color", 0 ); + uint32_t color = XmlUtil::getUIntAttr( node, "line_color", 0xFF ); ColorNode lineColorNode( field_flag, color, key ); /* fill attrs */ key = XmlUtil::getStringAttr( node, "fill_color_field", "" ); field_flag = !key.isEmpty(); - color = XmlUtil::getUIntAttr( node, "fill_color", 0 ); + color = XmlUtil::getUIntAttr( node, "fill_color", 0xFF ); ColorNode fillColorNode( field_flag, color, key ); /* affine attrs */ @@ -436,10 +455,10 @@ namespace glabels key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); field_flag = !key.isEmpty(); - color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); + color = XmlUtil::getUIntAttr( node, "shadow_color", 0xFF ); ColorNode shadowColorNode( field_flag, color, key ); - return new ModelEllipseObject( x0, y0, w, h, + return new ModelEllipseObject( x0, y0, w, h, lockAspectRatio, lineWidth, lineColorNode, fillColorNode, QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), @@ -463,7 +482,7 @@ namespace glabels QString key = XmlUtil::getStringAttr( node, "line_color_field", "" ); bool field_flag = !key.isEmpty(); - uint32_t color = XmlUtil::getUIntAttr( node, "line_color", 0 ); + uint32_t color = XmlUtil::getUIntAttr( node, "line_color", 0xFF ); ColorNode lineColorNode( field_flag, color, key ); /* affine attrs */ @@ -483,7 +502,7 @@ namespace glabels key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); field_flag = !key.isEmpty(); - color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); + color = XmlUtil::getUIntAttr( node, "shadow_color", 0xFF ); ColorNode shadowColorNode( field_flag, color, key ); return new ModelLineObject( x0, y0, dx, dy, @@ -494,7 +513,9 @@ namespace glabels ModelImageObject* - XmlLabelParser::parseObjectImageNode( const QDomElement &node, const DataCache& data ) + XmlLabelParser::parseObjectImageNode( const QDomElement& node, + const Model* model, + const DataCache& data ) { /* position attrs */ Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 ); @@ -503,6 +524,7 @@ namespace glabels /* size attrs */ Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); + bool lockAspectRatio = XmlUtil::getBoolAttr( node, "lock_aspect_ratio", false ); /* file attrs */ QString key = XmlUtil::getStringAttr( node, "src_field", "" ); @@ -527,36 +549,42 @@ namespace glabels key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); field_flag = !key.isEmpty(); - uint32_t color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); + uint32_t color = XmlUtil::getUIntAttr( node, "shadow_color", 0xFF ); ColorNode shadowColorNode( field_flag, color, key ); if ( filenameNode.isField() ) { - return new ModelImageObject( x0, y0, w, h, + return new ModelImageObject( x0, y0, w, h, lockAspectRatio, filenameNode, QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); } else { - if ( data.hasImage( filename ) ) + QString fn = QDir::cleanPath( model->dir().absoluteFilePath( filename ) ); + + if ( data.hasImage( fn ) ) { - return new ModelImageObject( x0, y0, w, h, - filename, data.getImage( filename ), + return new ModelImageObject( x0, y0, w, h, lockAspectRatio, + filename, data.getImage( fn ), QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); } - else if ( data.hasSvg( filename ) ) + else if ( data.hasSvg( fn ) ) { - return new ModelImageObject( x0, y0, w, h, - filename, data.getSvg( filename ), + return new ModelImageObject( x0, y0, w, h, lockAspectRatio, + filename, data.getSvg( fn ), QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); } else { - qWarning() << "Embedded file" << filename << "missing. Trying actual file."; - return new ModelImageObject( x0, y0, w, h, + if ( !filename.isEmpty() ) + { + qWarning() << "Embedded file" << fn << "missing. Trying actual file."; + filenameNode.setData( fn ); + } + return new ModelImageObject( x0, y0, w, h, lockAspectRatio, filenameNode, QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ), shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); @@ -575,6 +603,7 @@ namespace glabels /* size attrs */ Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); + bool lockAspectRatio = XmlUtil::getBoolAttr( node, "lock_aspect_ratio", false ); /* barcode attrs */ barcode::Style bcStyle = barcode::Backends::style( XmlUtil::getStringAttr( node, "backend", "" ), @@ -584,7 +613,7 @@ namespace glabels QString key = XmlUtil::getStringAttr( node, "color_field", "" ); bool field_flag = !key.isEmpty(); - uint32_t color = XmlUtil::getUIntAttr( node, "color", 0 ); + uint32_t color = XmlUtil::getUIntAttr( node, "color", 0xFF ); ColorNode bcColorNode( field_flag, color, key ); QString bcData = XmlUtil::getStringAttr( node, "data", "" ); @@ -598,7 +627,7 @@ namespace glabels a[4] = XmlUtil::getDoubleAttr( node, "a4", 0.0 ); a[5] = XmlUtil::getDoubleAttr( node, "a5", 0.0 ); - return new ModelBarcodeObject( x0, y0, w, h, + return new ModelBarcodeObject( x0, y0, w, h, lockAspectRatio, bcStyle, bcTextFlag, bcChecksumFlag, bcData, bcColorNode, QMatrix( a[0], a[1], a[2], a[3], a[4], a[5] ) ); } @@ -614,11 +643,12 @@ namespace glabels /* size attrs */ Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); + bool lockAspectRatio = XmlUtil::getBoolAttr( node, "lock_aspect_ratio", false ); /* color attr */ QString key = XmlUtil::getStringAttr( node, "color_field", "" ); bool field_flag = !key.isEmpty(); - uint32_t color = XmlUtil::getUIntAttr( node, "color", 0 ); + uint32_t color = XmlUtil::getUIntAttr( node, "color", 0xFF ); ColorNode textColorNode( field_flag, color, key ); /* font attrs */ @@ -652,7 +682,7 @@ namespace glabels key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); field_flag = !key.isEmpty(); - color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); + color = XmlUtil::getUIntAttr( node, "shadow_color", 0xFF ); ColorNode shadowColorNode( field_flag, color, key ); /* deserialize contents. */ @@ -679,7 +709,7 @@ namespace glabels } QString text = document.toPlainText(); - return new ModelTextObject( x0, y0, w, h, + return new ModelTextObject( x0, y0, w, h, lockAspectRatio, text, fontFamily, fontSize, fontWeight, fontItalicFlag, fontUnderlineFlag, textColorNode, textHAlign, textVAlign, textWrapMode, textLineSpacing, @@ -704,28 +734,45 @@ namespace glabels void - XmlLabelParser::parseMergeNode( const QDomElement &node, Model* label ) + XmlLabelParser::parseMergeNode( const QDomElement &node, Model* model ) { - QString type = XmlUtil::getStringAttr( node, "type", "None" ); - QString src = XmlUtil::getStringAttr( node, "src", "" ); + QString id = XmlUtil::getStringAttr( node, "type", "None" ); + QString src = XmlUtil::getStringAttr( node, "src", "" ); - merge::Merge* merge = merge::Factory::createMerge( type ); - merge->setSource( src ); + merge::Merge* merge = merge::Factory::createMerge( id ); - label->setMerge( merge ); + switch ( merge::Factory::idToType( id ) ) + { + case merge::Factory::NONE: + case merge::Factory::FIXED: + break; + + case merge::Factory::FILE: + { + QString fn = QDir::cleanPath( model->dir().absoluteFilePath( src ) ); + merge->setSource( fn ); + } + break; + + default: + qWarning() << "XmlLabelParser::parseMergeNode(): Should not be reached!"; + break; + } + + model->setMerge( merge ); } void - XmlLabelParser::parseDataNode( const QDomElement &node, DataCache& data ) + XmlLabelParser::parseVariablesNode( const QDomElement &node, Model* model ) { for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) { QString tagName = child.toElement().tagName(); - if ( tagName == "File" ) + if ( tagName == "Variable" ) { - parseFileNode( child.toElement(), data ); + parseVariableNode( child.toElement(), model ); } else if ( !child.isComment() ) { @@ -736,19 +783,55 @@ namespace glabels void - XmlLabelParser::parsePixdataNode( const QDomElement& node, DataCache& data ) + XmlLabelParser::parseVariableNode( const QDomElement &node, Model* model ) { - // TODO, compatability with glabels-3 + QString typeString = XmlUtil::getStringAttr( node, "type", "string" ); + QString name = XmlUtil::getStringAttr( node, "name", "unknown" ); + QString initialValue = XmlUtil::getStringAttr( node, "initialValue", "0" ); + QString incrementString = XmlUtil::getStringAttr( node, "increment", "never" ); + QString stepSize = XmlUtil::getStringAttr( node, "stepSize", "0" ); + + auto type = Variable::idStringToType( typeString ); + auto increment = Variable::idStringToIncrement( incrementString ); + + Variable v( type, name, initialValue, increment, stepSize ); + model->variables()->addVariable( v ); } void - XmlLabelParser::parseFileNode( const QDomElement& node, DataCache& data ) + XmlLabelParser::parseDataNode( const QDomElement &node, + const Model* model, + DataCache& data ) + { + for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) + { + QString tagName = child.toElement().tagName(); + + if ( tagName == "File" ) + { + parseFileNode( child.toElement(), model, data ); + } + else if ( !child.isComment() ) + { + qWarning() << "Unexpected" << node.tagName() << "child:" << tagName; + } + } + } + + + void + XmlLabelParser::parseFileNode( const QDomElement& node, + const Model* model, + DataCache& data ) { QString name = XmlUtil::getStringAttr( node, "name", "" ); QString mimetype = XmlUtil::getStringAttr( node, "mimetype", "image/png" ); QString encoding = XmlUtil::getStringAttr( node, "encoding", "base64" ); + // Rewrite name as absolute file path + QString fn = QDir::cleanPath( model->dir().absoluteFilePath( name ) ); + if ( mimetype == "image/png" ) { if ( encoding == "base64" ) @@ -758,7 +841,7 @@ namespace glabels QImage image; image.loadFromData( ba, "PNG" ); - data.addImage( name, image ); + data.addImage( fn, image ); } else { @@ -767,7 +850,7 @@ namespace glabels } else if ( mimetype == "image/svg+xml" ) { - data.addSvg( name, node.text().toUtf8() ); + data.addSvg( fn, node.text().toUtf8() ); } } diff --git a/model/XmlLabelParser.h b/model/XmlLabelParser.h index f1358603..93e90e8a 100644 --- a/model/XmlLabelParser.h +++ b/model/XmlLabelParser.h @@ -52,25 +52,57 @@ namespace glabels public: static Model* readFile( const QString& fileName ); + static Model* readBuffer( const QByteArray& buffer ); - static QList deserializeObjects( const QByteArray& buffer ); + + static QList deserializeObjects( const QByteArray& buffer, + const Model* model ); private: - static void gunzip( const QByteArray& gzippedData, QByteArray& data ); - static Model* parseRootNode( const QDomElement &node ); - static QList parseObjectsNode( const QDomElement &node, const DataCache& data ); - static ModelBoxObject* parseObjectBoxNode( const QDomElement &node ); - static ModelEllipseObject* parseObjectEllipseNode( const QDomElement &node ); - static ModelLineObject* parseObjectLineNode( const QDomElement &node ); - static ModelImageObject* parseObjectImageNode( const QDomElement &node, const DataCache& data ); - static ModelBarcodeObject* parseObjectBarcodeNode( const QDomElement &node ); - static ModelTextObject* parseObjectTextNode( const QDomElement &node ); - static QString parsePNode( const QDomElement &node ); - static bool parseRotateAttr( const QDomElement &node ); - static void parseMergeNode( const QDomElement &node, Model* label ); - static void parseDataNode( const QDomElement &node, DataCache& data ); - static void parsePixdataNode( const QDomElement &node, DataCache& data ); - static void parseFileNode( const QDomElement &node, DataCache& data ); + static void gunzip( const QByteArray& gzippedData, + QByteArray& data ); + + static Model* parseRootNode( const QDomElement& node, + const QString& fileName ); + + static QList parseObjectsNode( const QDomElement& node, + const Model* model, + const DataCache& data ); + + static ModelBoxObject* parseObjectBoxNode( const QDomElement& node ); + + static ModelEllipseObject* parseObjectEllipseNode( const QDomElement& node ); + + static ModelLineObject* parseObjectLineNode( const QDomElement& node ); + + static ModelImageObject* parseObjectImageNode( const QDomElement& node, + const Model* model, + const DataCache& data ); + + static ModelBarcodeObject* parseObjectBarcodeNode( const QDomElement& node ); + + static ModelTextObject* parseObjectTextNode( const QDomElement& node ); + + static QString parsePNode( const QDomElement& node ); + + static bool parseRotateAttr( const QDomElement& node ); + + static void parseMergeNode( const QDomElement& node, + Model* model ); + + static void parseVariablesNode( const QDomElement& node, + Model* model ); + + static void parseVariableNode( const QDomElement& node, + Model* model ); + + static void parseDataNode( const QDomElement& node, + const Model* model, + DataCache& data ); + + static void parseFileNode( const QDomElement& node, + const Model* model, + DataCache& data ); }; diff --git a/model/XmlLabelParser_3.cpp b/model/XmlLabelParser_3.cpp new file mode 100644 index 00000000..fe129d8b --- /dev/null +++ b/model/XmlLabelParser_3.cpp @@ -0,0 +1,853 @@ +/* XmlLabelParser_3.cpp + * + * Copyright (C) 2014-2016 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ +#include "XmlLabelParser_3.h" + +#include "DataCache.h" +#include "Model.h" +#include "ModelBarcodeObject.h" +#include "ModelBoxObject.h" +#include "ModelEllipseObject.h" +#include "ModelImageObject.h" +#include "ModelLineObject.h" +#include "ModelObject.h" +#include "ModelTextObject.h" +#include "XmlTemplateParser.h" +#include "XmlUtil.h" +#include "Size.h" + +#include "barcode/Backends.h" +#include "merge/Factory.h" + +#include +#include +#include +#include +#include + +#if HAVE_ZLIB +#include +#endif + + +namespace +{ + const uint32_t GDK_PIXBUF_MAGIC_NUMBER {0x47646b50}; + const double FONT_SCALE_FACTOR {0.75}; + + typedef enum + { + /* colorspace + alpha */ + GDK_PIXDATA_COLOR_TYPE_RGB = 0x01, + GDK_PIXDATA_COLOR_TYPE_RGBA = 0x02, + GDK_PIXDATA_COLOR_TYPE_MASK = 0xff, + /* width, support 8bits only currently */ + GDK_PIXDATA_SAMPLE_WIDTH_8 = 0x01 << 16, + GDK_PIXDATA_SAMPLE_WIDTH_MASK = 0x0f << 16, + /* encoding */ + GDK_PIXDATA_ENCODING_RAW = 0x01 << 24, + GDK_PIXDATA_ENCODING_RLE = 0x02 << 24, + GDK_PIXDATA_ENCODING_MASK = 0x0f << 24 + } GdkPixdataType; +} + + +namespace glabels +{ + + namespace model + { + + + Model* + XmlLabelParser_3::parseRootNode( const QDomElement &node ) + { + auto* label = new Model(); + + /* Pass 1, extract data nodes to pre-load cache. */ + DataCache data; + for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) + { + auto element = child.toElement(); + if (!element.isNull() && element.tagName() == "Data" ) + { + parseDataNode( element, data ); + } + } + + /* Pass 2, now extract everything else. */ + for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) + { + const auto childElement = child.toElement(); + if(childElement.isNull()) + { + qCritical()<<"Can't convert the node to an element. Try to continue."; + continue; + } + const QString tagName = childElement.tagName(); + + if ( tagName == "Template" ) + { + Template* tmplate = XmlTemplateParser().parseTemplateNode( childElement ); + if ( tmplate == nullptr ) + { + qWarning() << "Unable to parse template"; + delete label; + return nullptr; + } + label->setTmplate( tmplate ); // Copies arg + delete tmplate; + } + else if ( tagName == "Objects" ) + { + label->setRotate( parseRotateAttr( childElement ) ); + QList list = parseObjectsNode( childElement, data ); + foreach ( ModelObject* object, list ) + { + label->addObject( object ); + } + } + else if ( tagName == "Merge" ) + { + parseMergeNode( childElement, label ); + } + else if ( tagName == "Data" ) + { + /* Handled in pass 1. */ + } + else if ( !child.isComment() ) + { + qWarning() << "Unexpected" << node.tagName() << "child:" << tagName; + } + } + + label->clearModified(); + return label; + } + + + QList + XmlLabelParser_3::parseObjectsNode( const QDomElement &node, const DataCache& data ) + { + QList list; + + for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) + { + const auto childElement = child.toElement(); + if(childElement.isNull()) + { + qCritical()<<"Can't convert the node to an element. Try to continue."; + continue; + } + const QString tagName = childElement.tagName(); + + if ( tagName == "Object-box" ) + { + list.append( parseObjectBoxNode( childElement ) ); + } + else if ( tagName == "Object-ellipse" ) + { + list.append( parseObjectEllipseNode( childElement ) ); + } + else if ( tagName == "Object-line" ) + { + list.append( parseObjectLineNode( childElement ) ); + } + else if ( tagName == "Object-text" ) + { + list.append( parseObjectTextNode( childElement ) ); + } + else if ( tagName == "Object-image" ) + { + list.append( parseObjectImageNode( childElement, data ) ); + } + else if ( tagName == "Object-barcode" ) + { + list.append( parseObjectBarcodeNode( childElement ) ); + } + else if ( !child.isComment() ) + { + qWarning() << "Unexpected" << node.tagName() << "child:" << tagName; + } + } + + return list; + } + + + ModelBoxObject* + XmlLabelParser_3::parseObjectBoxNode( const QDomElement &node ) + { + /* position attrs */ + const Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 ); + const Distance y0 = XmlUtil::getLengthAttr( node, "y", 0.0 ); + + /* size attrs */ + const Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); + const Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); + + /* line attrs */ + const Distance lineWidth = XmlUtil::getLengthAttr( node, "line_width", 1.0 ); + + QString key = XmlUtil::getStringAttr( node, "line_color_field", "" ); + bool field_flag = !key.isEmpty(); + uint32_t color = XmlUtil::getUIntAttr( node, "line_color", 0 ); + const ColorNode lineColorNode( field_flag, color, key ); + + /* fill attrs */ + key = XmlUtil::getStringAttr( node, "fill_color_field", "" ); + field_flag = !key.isEmpty(); + color = XmlUtil::getUIntAttr( node, "fill_color", 0 ); + const ColorNode fillColorNode( field_flag, color, key ); + + /* affine attrs */ + const auto affineTransformation = parseAffineTransformation(node); + + /* shadow attrs */ + const bool shadowState = XmlUtil::getBoolAttr( node, "shadow", false ); + const Distance shadowX = XmlUtil::getLengthAttr( node, "shadow_x", 0.0 ); + const Distance shadowY = XmlUtil::getLengthAttr( node, "shadow_y", 0.0 ); + const double shadowOpacity = XmlUtil::getDoubleAttr( node, "shadow_opacity", 1.0 ); + + key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); + field_flag = !key.isEmpty(); + color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); + const ColorNode shadowColorNode( field_flag, color, key ); + + return new ModelBoxObject( x0, y0, w, h, false /*lockAspectRatio*/, + lineWidth, lineColorNode, + fillColorNode, + affineTransformation, + shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); + } + + + ModelEllipseObject* + XmlLabelParser_3::parseObjectEllipseNode( const QDomElement &node ) + { + /* position attrs */ + const Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 ); + const Distance y0 = XmlUtil::getLengthAttr( node, "y", 0.0 ); + + /* size attrs */ + const Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); + const Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); + + /* line attrs */ + const Distance lineWidth = XmlUtil::getLengthAttr( node, "line_width", 1.0 ); + + QString key = XmlUtil::getStringAttr( node, "line_color_field", "" ); + bool field_flag = !key.isEmpty(); + uint32_t color = XmlUtil::getUIntAttr( node, "line_color", 0 ); + const ColorNode lineColorNode( field_flag, color, key ); + + /* fill attrs */ + key = XmlUtil::getStringAttr( node, "fill_color_field", "" ); + field_flag = !key.isEmpty(); + color = XmlUtil::getUIntAttr( node, "fill_color", 0 ); + const ColorNode fillColorNode( field_flag, color, key ); + + /* affine attrs */ + const auto affineTransformation = parseAffineTransformation(node); + + /* shadow attrs */ + const bool shadowState = XmlUtil::getBoolAttr( node, "shadow", false ); + const Distance shadowX = XmlUtil::getLengthAttr( node, "shadow_x", 0.0 ); + const Distance shadowY = XmlUtil::getLengthAttr( node, "shadow_y", 0.0 ); + const double shadowOpacity = XmlUtil::getDoubleAttr( node, "shadow_opacity", 1.0 ); + + key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); + field_flag = !key.isEmpty(); + color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); + const ColorNode shadowColorNode( field_flag, color, key ); + + return new ModelEllipseObject( x0, y0, w, h, false /*lockAspectRatio*/, + lineWidth, lineColorNode, + fillColorNode, + affineTransformation, + shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); + } + + + ModelLineObject* + XmlLabelParser_3::parseObjectLineNode( const QDomElement &node ) + { + /* position attrs */ + const Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 ); + const Distance y0 = XmlUtil::getLengthAttr( node, "y", 0.0 ); + + /* size attrs of line */ + const Distance dx = XmlUtil::getLengthAttr( node, "dx", 0 ); + const Distance dy = XmlUtil::getLengthAttr( node, "dy", 0 ); + + /* line attrs */ + const Distance lineWidth = XmlUtil::getLengthAttr( node, "line_width", 1.0 ); + + QString key = XmlUtil::getStringAttr( node, "line_color_field", "" ); + bool field_flag = !key.isEmpty(); + uint32_t color = XmlUtil::getUIntAttr( node, "line_color", 0 ); + const ColorNode lineColorNode( field_flag, color, key ); + + /* affine attrs */ + const auto affineTransformation = parseAffineTransformation(node); + + /* shadow attrs */ + const bool shadowState = XmlUtil::getBoolAttr( node, "shadow", false ); + const Distance shadowX = XmlUtil::getLengthAttr( node, "shadow_x", 0.0 ); + const Distance shadowY = XmlUtil::getLengthAttr( node, "shadow_y", 0.0 ); + const double shadowOpacity = XmlUtil::getDoubleAttr( node, "shadow_opacity", 1.0 ); + + key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); + field_flag = !key.isEmpty(); + color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); + const ColorNode shadowColorNode( field_flag, color, key ); + + return new ModelLineObject( x0, y0, dx, dy, + lineWidth, lineColorNode, + affineTransformation, + shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); + } + + + ModelImageObject* + XmlLabelParser_3::parseObjectImageNode( const QDomElement &node, const DataCache& data ) + { + /* position attrs */ + const Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 ); + const Distance y0 = XmlUtil::getLengthAttr( node, "y", 0.0 ); + + /* size attrs */ + const Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); + const Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); + + /* file attrs */ + QString key = XmlUtil::getStringAttr( node, "field", "" ); + bool field_flag = !key.isEmpty(); + const QString filename = XmlUtil::getStringAttr( node, "src", "" ); + const TextNode filenameNode( field_flag, field_flag ? key : filename ); + + /* affine attrs */ + const auto affineTransformation = parseAffineTransformation(node); + + /* shadow attrs */ + const bool shadowState = XmlUtil::getBoolAttr( node, "shadow", false ); + const Distance shadowX = XmlUtil::getLengthAttr( node, "shadow_x", 0.0 ); + const Distance shadowY = XmlUtil::getLengthAttr( node, "shadow_y", 0.0 ); + const double shadowOpacity = XmlUtil::getDoubleAttr( node, "shadow_opacity", 1.0 ); + + key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); + field_flag = !key.isEmpty(); + uint32_t color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); + const ColorNode shadowColorNode( field_flag, color, key ); + + if ( filenameNode.isField() ) + { + return new ModelImageObject( x0, y0, w, h, false /*lockAspectRatio*/, + filenameNode, + affineTransformation, + shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); + } + if ( data.hasImage( filename ) ) + { + return new ModelImageObject( x0, y0, w, h, false /*lockAspectRatio*/, + filename, data.getImage( filename ), + affineTransformation, + shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); + } + if ( data.hasSvg( filename ) ) + { + return new ModelImageObject( x0, y0, w, h, false /*lockAspectRatio*/, + filename, data.getSvg( filename ), + affineTransformation, + shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); + } + qWarning() << "Embedded file" << filename << "missing. Trying actual file."; + return new ModelImageObject( x0, y0, w, h, false /*lockAspectRatio*/, + filenameNode, + affineTransformation, + shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); + } + + + ModelBarcodeObject* + XmlLabelParser_3::parseObjectBarcodeNode( const QDomElement &node ) + { + /* position attrs */ + const Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 ); + const Distance y0 = XmlUtil::getLengthAttr( node, "y", 0.0 ); + + /* size attrs */ + const Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); + const Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); + + /* barcode attrs */ + auto backend = XmlUtil::getStringAttr( node, "backend", "" ); + // one major difference between glabels-3.0.dtd and glabels-4.0.dtd + // is the lowercase of the style names + auto style = XmlUtil::getStringAttr( node, "style", "" ).toLower(); + + if ( backend == "built-in" ) + { + backend = ""; + } + else if ( backend == "libiec16022" ) + { + backend = ""; + style = "datamatrix"; + } + else if ( backend == "libqrencode" ) + { + if ( barcode::Backends::style( "qrencode", "qrcode" ) != barcode::Backends::defaultStyle() ) + { + backend = "qrencode"; + style = "qrcode"; + } + else + { + // Will use defaultStyle if Zint not available + backend = "zint"; + style = "qr"; + } + } + + const barcode::Style bcStyle = barcode::Backends::style( backend, style ); + const bool bcTextFlag = XmlUtil::getBoolAttr( node, "text", true ); + const bool bcChecksumFlag = XmlUtil::getBoolAttr( node, "checksum", true ); + + const QString key = XmlUtil::getStringAttr( node, "color_field", "" ); + const bool field_flag = !key.isEmpty(); + const uint32_t color = XmlUtil::getUIntAttr( node, "color", 0 ); + const ColorNode bcColorNode( field_flag, color, key ); + + QString bcData = XmlUtil::getStringAttr( node, "data", "" ); + if(bcData.isEmpty()) + { + bcData = "${" + XmlUtil::getStringAttr( node, "field", "" ) + "}"; + } + + /* affine attrs */ + const auto affineTransformation = parseAffineTransformation(node); + + return new ModelBarcodeObject( x0, y0, w, h, false /*lockAspectRatio*/, + bcStyle, bcTextFlag, bcChecksumFlag, bcData, bcColorNode, + affineTransformation ); + } + + + QMatrix + XmlLabelParser_3::parseAffineTransformation(const QDomElement &node) + { + return {XmlUtil::getDoubleAttr( node, "a0", 1.0 ), + XmlUtil::getDoubleAttr( node, "a1", 0.0 ), + XmlUtil::getDoubleAttr( node, "a2", 0.0 ), + XmlUtil::getDoubleAttr( node, "a3", 1.0 ), + XmlUtil::getDoubleAttr( node, "a4", 0.0 ), + XmlUtil::getDoubleAttr( node, "a5", 0.0 )}; + } + + + ModelTextObject* + XmlLabelParser_3::parseObjectTextNode( const QDomElement &node ) + { + /* position attrs */ + const Distance x0 = XmlUtil::getLengthAttr( node, "x", 0.0 ); + const Distance y0 = XmlUtil::getLengthAttr( node, "y", 0.0 ); + + /* size attrs */ + const Distance w = XmlUtil::getLengthAttr( node, "w", 0 ); + const Distance h = XmlUtil::getLengthAttr( node, "h", 0 ); + + /* justify attr */ + const Qt::Alignment textHAlign = getHAlignmentAttr( node, "justify", Qt::AlignLeft ); + + /* valign attr */ + const Qt::Alignment textVAlign = getVAlignmentAttr( node, "valign", Qt::AlignTop ); + + /* auto_shrink attr */ + const bool textAutoShrink = XmlUtil::getBoolAttr( node, "auto_shrink", false ); + + /* affine attrs */ + const auto affineTransformation = parseAffineTransformation(node); + + /* shadow attrs */ + const bool shadowState = XmlUtil::getBoolAttr( node, "shadow", false ); + const Distance shadowX = XmlUtil::getLengthAttr( node, "shadow_x", 0.0 ); + const Distance shadowY = XmlUtil::getLengthAttr( node, "shadow_y", 0.0 ); + const double shadowOpacity = XmlUtil::getDoubleAttr( node, "shadow_opacity", 1.0 ); + + QString key = XmlUtil::getStringAttr( node, "shadow_color_field", "" ); + bool field_flag = !key.isEmpty(); + uint32_t color = XmlUtil::getUIntAttr( node, "shadow_color", 0 ); + const ColorNode shadowColorNode( field_flag, color, key ); + + /* font attrs */ + QString fontFamily = "Sans"; + double fontSize = 10.; + QFont::Weight fontWeight = QFont::Normal; + bool fontItalicFlag = false; + + /* text attrs */ + double textLineSpacing = 1.; + QTextOption::WrapMode textWrapMode = QTextOption::WordWrap; + + ColorNode textColorNode; + + /* deserialize contents. */ + QTextDocument document; + QTextCursor cursor( &document ); + bool firstBlock = true; + for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) + { + const auto element = child.toElement(); + if(element.isNull()) + { + qCritical()<<"Can't convert the node to an element. Try to continue."; + continue; + } + + if(element.tagName() == "Span") + { + QString text; + for(QDomNode textPartElement = element.firstChild() + ; !textPartElement.isNull() + ; textPartElement = textPartElement.nextSibling()) + { + const QDomText textpart = textPartElement.toText(); + if (!textpart.isNull()) + { + text += textpart.data(); + } + else + { + if(textPartElement.toElement().tagName() == "NL") + { + text += "\n"; + } + else if (textPartElement.toElement().tagName() == "Field") + { + text += "${" + XmlUtil::getStringAttr( textPartElement.toElement(), "name", "" ) + "}"; + } + } + } + + if ( !firstBlock ) + { + cursor.insertBlock(); + } + firstBlock = false; + + cursor.insertText( text ); + + /* font attrs */ + fontFamily = XmlUtil::getStringAttr( element, "font_family", "Sans" ); + fontSize = XmlUtil::getDoubleAttr( element, "font_size", 10 ) * FONT_SCALE_FACTOR; + fontWeight = getWeightAttr( element, "font_weight", QFont::Normal ); + fontItalicFlag = XmlUtil::getBoolAttr( element, "font_italic", false ); + + /* color attr */ + key = XmlUtil::getStringAttr( element, "color_field", "" ); + field_flag = !key.isEmpty(); + color = XmlUtil::getUIntAttr( element, "color", 0 ); + + textColorNode = ColorNode(field_flag, color, key ); + + /* text attrs */ + textLineSpacing = XmlUtil::getDoubleAttr( element, "line_spacing", 1 ); + textWrapMode = QTextOption::WordWrap; + + } + else if ( !child.isComment() ) + { + qWarning() << "Unexpected" << node.tagName() << "child:" << node.tagName(); + } + } + const QString text = document.toPlainText(); + + auto textNode = new ModelTextObject( x0, y0, w, h, false /*lockAspectRatio*/, text, + fontFamily, fontSize, fontWeight, fontItalicFlag, false, + textColorNode, textHAlign, textVAlign, textWrapMode, textLineSpacing, + textAutoShrink, + affineTransformation, + shadowState, shadowX, shadowY, shadowOpacity, shadowColorNode ); + + // The size of the textnode does not fit the qt world. So it's needed to + // recalculate the size depending on the data. + textNode->setSize(textNode->naturalSize()); + + return textNode; + } + + + bool + XmlLabelParser_3::parseRotateAttr( const QDomElement &node ) + { + return XmlUtil::getBoolAttr( node, "rotate", false ); + } + + + void + XmlLabelParser_3::parseMergeNode( const QDomElement &node, Model* label ) + { + const QString type = XmlUtil::getStringAttr( node, "type", "None" ); + const QString src = XmlUtil::getStringAttr( node, "src", "" ); + + merge::Merge* merge = merge::Factory::createMerge( type ); + merge->setSource( src ); + + label->setMerge( merge ); + } + + + void + XmlLabelParser_3::parseDataNode( const QDomElement &node, DataCache& data ) + { + for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) + { + const auto childElement = child.toElement(); + if(childElement.isNull()) + { + qCritical()<<"Can't convert the node to an element. Try to continue."; + continue; + } + const QString tagName = childElement.tagName(); + + if ( tagName == "File" ) + { + parseFileNode( childElement, data ); + } + else if (tagName == "Pixdata") + { + parsePixdataNode( childElement, data); + } + else if ( !child.isComment() ) + { + qWarning() << "Unexpected" << node.tagName() << "child:" << tagName; + } + } + } + + + void + XmlLabelParser_3::parsePixdataNode( const QDomElement& node, DataCache& data ) + { + const QString name = XmlUtil::getStringAttr( node, "name", "" ); + const QString encoding = XmlUtil::getStringAttr( node, "encoding", "base64" ); + + /* + * Struct of the GdkPixdata from the header file + */ + struct _GdkPixdata + { + uint32_t magic; /* GDK_PIXBUF_MAGIC_NUMBER */ + int32_t length; /* <1 to disable length checks, otherwise: + * GDK_PIXDATA_HEADER_LENGTH + pixel_data length*/ + uint32_t pixdata_type; /* GdkPixdataType */ + uint32_t rowstride; + uint32_t width; + uint32_t height; + //uint8_t *pixel_data; + } mypixdata{}; + + if ( encoding.toLower() == "base64" ) + { + + QByteArray ba = QByteArray::fromBase64( node.text().toUtf8() ); + + // Use the QDataStream to import the header cause it is in big endian + QDataStream ds(ba); + ds.setByteOrder(QDataStream::ByteOrder::BigEndian); + + ds >> mypixdata.magic + >> mypixdata.length + >> mypixdata.pixdata_type + >> mypixdata.rowstride + >> mypixdata.width + >> mypixdata.height; + + if(mypixdata.magic !=GDK_PIXBUF_MAGIC_NUMBER) + { + qCritical() << "GDK pixbuf magic is not correct. Abort reading of pixdata. " + << "Node:" << node.tagName(); + return; + } + + // check if the data fits in a sigend int + if(mypixdata.width > INT32_MAX || mypixdata.height > INT32_MAX || mypixdata.rowstride > INT32_MAX) + { + qCritical() << "rowstride, width or height is to large. Abort reading of pixdata. " + << "Node:" << node.tagName();; + return; + } + + const auto width = static_cast(mypixdata.width); + const auto height = static_cast(mypixdata.height); + const auto rowstride = static_cast(mypixdata.rowstride); + + QImage::Format pixformat; + int32_t rawpadding; + if((mypixdata.pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGB) + { + pixformat = QImage::Format_RGB888; + rawpadding = rowstride - (3 * width); + } + else if((mypixdata.pixdata_type & GDK_PIXDATA_COLOR_TYPE_MASK) == GDK_PIXDATA_COLOR_TYPE_RGBA) + { + pixformat = QImage::Format_RGBA8888; + rawpadding = rowstride - (4 * width); + } + else + { + qCritical() << "pixdata color type is unknown. Abort reading of pixdata. " + << "Node:" << node.tagName(); + return; + } + + if(rawpadding < 0){ + qCritical() << "padding to is negativ. Abort reading of pixdata. " + << "Node:" << node.tagName(); + return; + } + + QImage image(width, height, pixformat); + + const auto padding = static_cast(rawpadding); + + int x = 0; + int y = 0; + uint8_t r,g,b,a; + for(y = 0; y < height; y++) + { + for(x = 0; x < width; x++) + { + if(pixformat == QImage::Format_RGB888) + { + ds >> r >> g >> b; + image.setPixelColor(x, y, QColor(r, g, b)); + } + else + { + ds >> r >> g >> b >> a; + image.setPixelColor(x, y, QColor(r, g, b, a)); + } + + } + ds.skipRawData(padding); + } + + data.addImage( name, image ); + } + else + { + qWarning() << "Unexpected encoding:" << encoding << "node:" << node.tagName(); + } + } + + + void + XmlLabelParser_3::parseFileNode( const QDomElement& node, DataCache& data ) + { + const QString name = XmlUtil::getStringAttr( node, "name", "" ); + const QString format = XmlUtil::getStringAttr( node, "format", "invalid" ); + + if ( format == "SVG" ) + { + data.addSvg( name, node.text().toUtf8() ); + } + else + { + qCritical() << "Unknown embedded file format:" << format; + } + } + + + Qt::Alignment + XmlLabelParser_3::getHAlignmentAttr( const QDomElement& node, const QString& name, + const Qt::Alignment default_value ) + { + + const QString valueString = node.attribute( name, "" ); + if ( !valueString.isEmpty()) + { + if ( valueString == "Right" ) + { + return Qt::AlignRight; + } + if ( valueString == "Center") + { + return Qt::AlignHCenter; + } + if ( valueString == "Left" ) + { + return Qt::AlignLeft; + } + } + return default_value; + } + + + Qt::Alignment + XmlLabelParser_3::getVAlignmentAttr( const QDomElement& node, const QString& name, + const Qt::Alignment default_value ) + { + + const QString valueString = node.attribute( name, "" ); + if ( ! valueString.isEmpty() ) + { + if ( valueString == "Bottom" ) + { + return Qt::AlignBottom; + } + if ( valueString == "Center" ) + { + return Qt::AlignVCenter; + } + if ( valueString == "Top" ) + { + return Qt::AlignTop; + } + } + + return default_value; + } + + + QFont::Weight + XmlLabelParser_3::getWeightAttr( const QDomElement& node, const QString& name, + const QFont::Weight default_value ) + { + const QString valueString = node.attribute( name, "" ); + if ( !valueString.isEmpty() ) + { + if ( valueString == "Bold" ) + { + return QFont::Bold; + } + if ( valueString == "Regular" ) + { + return QFont::Normal; + } + } + + return default_value; + } + + + } // namespace model + +} // namespace glabels diff --git a/model/XmlLabelParser_3.h b/model/XmlLabelParser_3.h new file mode 100644 index 00000000..b7484fb6 --- /dev/null +++ b/model/XmlLabelParser_3.h @@ -0,0 +1,85 @@ +/* XmlLabelParser_3.h + * + * Copyright (C) 2014-2016 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#ifndef model_XmlLabelParser_3_h +#define model_XmlLabelParser_3_h + + +#include +#include +#include +#include + +namespace glabels +{ + namespace model + { + + // Forward references + class Model; + class ModelObject; + class ModelBoxObject; + class ModelEllipseObject; + class ModelLineObject; + class ModelImageObject; + class ModelBarcodeObject; + class ModelTextObject; + class DataCache; + + /// + /// XmlLabelParser + /// + class XmlLabelParser_3 + { + + public: + static Model* parseRootNode( const QDomElement &node ); + + private: + + static QList parseObjectsNode( const QDomElement &node, const DataCache& data ); + static ModelBoxObject* parseObjectBoxNode( const QDomElement &node ); + static ModelEllipseObject* parseObjectEllipseNode( const QDomElement &node ); + static ModelLineObject* parseObjectLineNode( const QDomElement &node ); + static ModelImageObject* parseObjectImageNode( const QDomElement &node, const DataCache& data ); + static ModelBarcodeObject* parseObjectBarcodeNode( const QDomElement &node ); + static ModelTextObject* parseObjectTextNode( const QDomElement &node ); + static bool parseRotateAttr( const QDomElement &node ); + static void parseMergeNode( const QDomElement &node, Model* label ); + static void parseDataNode( const QDomElement &node, DataCache& data ); + static void parsePixdataNode( const QDomElement &node, DataCache& data ); + static void parseFileNode( const QDomElement &node, DataCache& data ); + + static Qt::Alignment getHAlignmentAttr( const QDomElement& node, + const QString& name, const Qt::Alignment default_value ); + static Qt::Alignment getVAlignmentAttr( const QDomElement& node, + const QString& name, const Qt::Alignment default_value ); + static QFont::Weight getWeightAttr( const QDomElement& node, + const QString& name, const QFont::Weight default_value ); + + static QMatrix parseAffineTransformation(const QDomElement &node); + + }; + + } // namespace model +} // namespace glabels + + +#endif // model_XmlLabelParser_3_h diff --git a/model/XmlTemplateCreator.cpp b/model/XmlTemplateCreator.cpp index e4ef1b72..8892a632 100644 --- a/model/XmlTemplateCreator.cpp +++ b/model/XmlTemplateCreator.cpp @@ -91,7 +91,10 @@ namespace glabels XmlUtil::setLengthAttr( node, "width", tmplate->pageWidth() ); XmlUtil::setLengthAttr( node, "height", tmplate->pageHeight() ); } - + if ( tmplate->isRoll() ) + { + XmlUtil::setLengthAttr( node, "roll_width", tmplate->rollWidth() ); + } XmlUtil::setStringAttr( node, "description", tmplate->description() ); @@ -141,6 +144,14 @@ namespace glabels { createLabelCdNode( parent, frameCd ); } + else if ( const auto* framePath = dynamic_cast(frame) ) + { + createLabelPathNode( parent, framePath ); + } + else if ( const auto* frameContinuous = dynamic_cast(frame) ) + { + createLabelContinuousNode( parent, frameContinuous ); + } else { Q_ASSERT_X( false, "XmlTemplateCreator::createLabelNode", "Invalid frame type." ); @@ -217,6 +228,39 @@ namespace glabels } + void XmlTemplateCreator::createLabelPathNode( QDomElement &parent, const FramePath* frame ) + { + QDomDocument doc = parent.ownerDocument(); + QDomElement node = doc.createElement( "Label-path" ); + parent.appendChild( node ); + + XmlUtil::setStringAttr( node, "id", frame->id() ); + XmlUtil::setLengthAttr( node, "x_waste", frame->xWaste() ); + XmlUtil::setLengthAttr( node, "y_waste", frame->yWaste() ); + XmlUtil::setUnitsAttr( node, "d_units", frame->originalUnits() ); + XmlUtil::setPathDataAttr( node, "d", frame->path(), frame->originalUnits() ); + + createLabelNodeCommon( node, frame ); + } + + + void XmlTemplateCreator::createLabelContinuousNode( QDomElement &parent, const FrameContinuous* frame ) + { + QDomDocument doc = parent.ownerDocument(); + QDomElement node = doc.createElement( "Label-continuous" ); + parent.appendChild( node ); + + XmlUtil::setStringAttr( node, "id", frame->id() ); + XmlUtil::setLengthAttr( node, "width", frame->w() ); + XmlUtil::setLengthAttr( node, "height", frame->h() ); + XmlUtil::setLengthAttr( node, "min_height", frame->hMin() ); + XmlUtil::setLengthAttr( node, "max_height", frame->hMin() ); + XmlUtil::setLengthAttr( node, "default_height", frame->hDefault() ); + + createLabelNodeCommon( node, frame ); + } + + void XmlTemplateCreator::createLabelNodeCommon( QDomElement &node, const Frame *frame ) { foreach ( Markup* markup, frame->markups() ) @@ -247,27 +291,27 @@ namespace glabels } } - foreach ( Layout* layout, frame->layouts() ) + foreach ( const Layout& layout, frame->layouts() ) { createLayoutNode( node, layout ); } } - void XmlTemplateCreator::createLayoutNode( QDomElement& parent, const Layout* layout ) + void XmlTemplateCreator::createLayoutNode( QDomElement& parent, const Layout& layout ) { QDomDocument doc = parent.ownerDocument(); QDomElement node = doc.createElement( "Layout" ); parent.appendChild( node ); - XmlUtil::setIntAttr( node, "nx", layout->nx() ); - XmlUtil::setIntAttr( node, "ny", layout->ny() ); + XmlUtil::setIntAttr( node, "nx", layout.nx() ); + XmlUtil::setIntAttr( node, "ny", layout.ny() ); - XmlUtil::setLengthAttr( node, "x0", layout->x0() ); - XmlUtil::setLengthAttr( node, "y0", layout->y0() ); + XmlUtil::setLengthAttr( node, "x0", layout.x0() ); + XmlUtil::setLengthAttr( node, "y0", layout.y0() ); - XmlUtil::setLengthAttr( node, "dx", layout->dx() ); - XmlUtil::setLengthAttr( node, "dy", layout->dy() ); + XmlUtil::setLengthAttr( node, "dx", layout.dx() ); + XmlUtil::setLengthAttr( node, "dy", layout.dy() ); } @@ -277,7 +321,15 @@ namespace glabels QDomElement node = doc.createElement( "Markup-margin" ); parent.appendChild( node ); - XmlUtil::setLengthAttr( node, "size", markup->size() ); + if ( markup->xSize() == markup->ySize() ) + { + XmlUtil::setLengthAttr( node, "size", markup->xSize() ); + } + else + { + XmlUtil::setLengthAttr( node, "x_size", markup->xSize() ); + XmlUtil::setLengthAttr( node, "y_size", markup->ySize() ); + } } diff --git a/model/XmlTemplateCreator.h b/model/XmlTemplateCreator.h index 1b88eb02..46e50ca2 100644 --- a/model/XmlTemplateCreator.h +++ b/model/XmlTemplateCreator.h @@ -23,7 +23,9 @@ #include "FrameCd.h" +#include "FrameContinuous.h" #include "FrameEllipse.h" +#include "FramePath.h" #include "FrameRect.h" #include "FrameRound.h" #include "Layout.h" @@ -55,8 +57,10 @@ namespace glabels void createLabelEllipseNode( QDomElement& parent, const FrameEllipse* frame ); void createLabelRoundNode( QDomElement& parent, const FrameRound* frame ); void createLabelCdNode( QDomElement& parent, const FrameCd* frame ); + void createLabelPathNode( QDomElement& parent, const FramePath* frame ); + void createLabelContinuousNode( QDomElement& parent, const FrameContinuous* frame ); void createLabelNodeCommon( QDomElement& node, const Frame* frame ); - void createLayoutNode( QDomElement& parent, const Layout* layout ); + void createLayoutNode( QDomElement& parent, const Layout& layout ); void createMarkupMarginNode( QDomElement& parent, const MarkupMargin* markupMargin ); void createMarkupLineNode( QDomElement& parent, const MarkupLine* markupLine ); void createMarkupCircleNode( QDomElement& parent, const MarkupCircle* markupCircle ); diff --git a/model/XmlTemplateParser.cpp b/model/XmlTemplateParser.cpp index 3bf74520..561a4dd4 100644 --- a/model/XmlTemplateParser.cpp +++ b/model/XmlTemplateParser.cpp @@ -25,6 +25,8 @@ #include "FrameCd.h" #include "FrameRound.h" #include "FrameEllipse.h" +#include "FramePath.h" +#include "FrameContinuous.h" #include "Layout.h" #include "Markup.h" #include "Template.h" @@ -153,7 +155,7 @@ namespace glabels QString description = XmlUtil::getI18nAttr( node, "description", "" ); QString paperId = XmlUtil::getStringAttr( node, "size", "" ); - if ( !Db::isPaperIdOther( paperId ) ) + if ( Db::isPaperIdKnown( paperId ) ) { const Paper *paper = Db::lookupPaperFromId( paperId ); if ( paper == nullptr ) @@ -169,8 +171,9 @@ namespace glabels { Distance width = XmlUtil::getLengthAttr( node, "width", Distance(0) ); Distance height = XmlUtil::getLengthAttr( node, "height", Distance(0) ); + Distance rollWidth = XmlUtil::getLengthAttr( node, "roll_width", Distance(0) ); - tmplate = new Template( brand, part, description, paperId, width, height, isUserDefined ); + tmplate = new Template( brand, part, description, paperId, width, height, rollWidth, isUserDefined ); } for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) @@ -195,6 +198,14 @@ namespace glabels { parseLabelCdNode( child.toElement(), tmplate ); } + else if ( child.toElement().tagName() == "Label-path" ) + { + parseLabelPathNode( child.toElement(), tmplate ); + } + else if ( child.toElement().tagName() == "Label-continuous" ) + { + parseLabelContinuousNode( child.toElement(), tmplate ); + } else if ( !child.isComment() ) { qWarning() << "Warning: bad element: " @@ -303,6 +314,57 @@ namespace glabels } + void XmlTemplateParser::parseLabelPathNode( const QDomElement &node, Template *tmplate ) + { + QString id = XmlUtil::getStringAttr( node, "id", "0" ); + + Units dUnits = XmlUtil::getUnitsAttr( node, "d_units", Units::pc() ); + QPainterPath d = XmlUtil::getPathDataAttr( node, "d", dUnits ); + + Distance xWaste, yWaste; + + Distance waste = XmlUtil::getLengthAttr( node, "waste", Distance(-1) ); + if ( waste >= Distance(0) ) + { + xWaste = waste; + yWaste = waste; + } + else + { + xWaste = XmlUtil::getLengthAttr( node, "x_waste", Distance(0) ); + yWaste = XmlUtil::getLengthAttr( node, "y_waste", Distance(0) ); + } + + Frame *frame = new FramePath( d, xWaste, yWaste, dUnits, id ); + + parseLabelNodeCommon( node, frame ); + + tmplate->addFrame( frame ); + } + + + void XmlTemplateParser::parseLabelContinuousNode( const QDomElement &node, Template *tmplate ) + { + QString id = XmlUtil::getStringAttr( node, "id", "0" ); + + Distance w = XmlUtil::getLengthAttr( node, "width", Distance(0) ); + Distance h = XmlUtil::getLengthAttr( node, "height", Distance(0) ); + Distance hMin = XmlUtil::getLengthAttr( node, "min_height", Distance(0) ); + Distance hMax = XmlUtil::getLengthAttr( node, "max_height", Distance(0) ); + Distance hDefault = XmlUtil::getLengthAttr( node, "default_height", Distance(0) ); + + Frame *frame = new FrameContinuous( w, hMin, hMax, hDefault, id ); + if ( h > Distance(0) ) + { + frame->setH( h ); + } + + parseLabelNodeCommon( node, frame ); + + tmplate->addFrame( frame ); + } + + void XmlTemplateParser::parseLabelNodeCommon( const QDomElement &node, Frame *frame ) { for ( QDomNode child = node.firstChild(); !child.isNull(); child = child.nextSibling() ) @@ -352,15 +414,24 @@ namespace glabels Distance dX = XmlUtil::getLengthAttr( node, "dx", Distance(0) ); Distance dY = XmlUtil::getLengthAttr( node, "dy", Distance(0) ); - frame->addLayout( new Layout( nX, nY, x0, y0, dX, dY ) ); + frame->addLayout( Layout( nX, nY, x0, y0, dX, dY ) ); } void XmlTemplateParser::parseMarkupMarginNode( const QDomElement &node, Frame *frame ) { - Distance size = XmlUtil::getLengthAttr( node, "size", Distance(0) ); + Distance size = XmlUtil::getLengthAttr( node, "size", Distance(0) ); + Distance xSize = XmlUtil::getLengthAttr( node, "x_size", Distance(0) ); + Distance ySize = XmlUtil::getLengthAttr( node, "y_size", Distance(0) ); - frame->addMarkup( new MarkupMargin( frame, size ) ); + if ( size > Distance(0) ) + { + frame->addMarkup( new MarkupMargin( size ) ); + } + else + { + frame->addMarkup( new MarkupMargin( xSize, ySize ) ); + } } diff --git a/model/XmlTemplateParser.h b/model/XmlTemplateParser.h index e48434fa..4683d0d4 100644 --- a/model/XmlTemplateParser.h +++ b/model/XmlTemplateParser.h @@ -48,6 +48,8 @@ namespace glabels void parseLabelEllipseNode( const QDomElement &node, Template *tmplate ); void parseLabelRoundNode( const QDomElement &node, Template *tmplate ); void parseLabelCdNode( const QDomElement &node, Template *tmplate ); + void parseLabelPathNode( const QDomElement &node, Template *tmplate ); + void parseLabelContinuousNode( const QDomElement &node, Template *tmplate ); void parseLabelNodeCommon( const QDomElement &node, Frame *frame ); void parseLayoutNode( const QDomElement &node, Frame *frame ); void parseMarkupMarginNode( const QDomElement &node, Frame *frame ); diff --git a/model/XmlUtil.cpp b/model/XmlUtil.cpp index 0d4a5fe9..04f58cb7 100644 --- a/model/XmlUtil.cpp +++ b/model/XmlUtil.cpp @@ -87,7 +87,7 @@ namespace glabels if ( valueString != "" ) { bool ok; - double value = valueString.toDouble(& ok ); + double value = valueString.toDouble( &ok ); if ( !ok ) { @@ -112,12 +112,13 @@ namespace glabels QString valueString = node.attribute( name, "" ); if ( valueString != "" ) { - int intValue = valueString.toInt(); + bool ok; + int intValue = valueString.toInt( &ok ); if ( (valueString == "True") || (valueString == "TRUE") || (valueString == "true") || - (intValue == 1) ) + (ok && (intValue == 1) ) ) { return true; } @@ -125,7 +126,7 @@ namespace glabels if ( (valueString == "False") || (valueString == "FALSE") || (valueString == "false") || - (intValue == 0) ) + (ok && (intValue == 0) ) ) { return false; } @@ -149,7 +150,7 @@ namespace glabels if ( valueString != "" ) { bool ok; - int value = valueString.toInt(& ok ); + int value = valueString.toInt( &ok ); if ( !ok ) { @@ -175,7 +176,7 @@ namespace glabels if ( valueString != "" ) { bool ok; - uint32_t value = valueString.toUInt(& ok, 0 ); + uint32_t value = valueString.toUInt( &ok, 0 ); if ( !ok ) { @@ -227,6 +228,7 @@ namespace glabels { qWarning() << "Error: bad length value in attribute " << node.tagName() << ":" << name << "=" << valueString; + return default_value; } return Distance( value, unitsString ); @@ -253,6 +255,12 @@ namespace glabels { return QFont::Normal; } + else + { + qWarning() << "Error: bad weight value in attribute " + << node.tagName() << ":" << name << "=" << valueString; + return default_value; + } } return default_value; @@ -292,6 +300,12 @@ namespace glabels { return Qt::AlignTop; } + else + { + qWarning() << "Error: bad alignment value in attribute " + << node.tagName() << ":" << name << "=" << valueString; + return default_value; + } } return default_value; @@ -319,12 +333,161 @@ namespace glabels { return QTextOption::NoWrap; } + else + { + qWarning() << "Error: bad wrap mode value in attribute " + << node.tagName() << ":" << name << "=" << valueString; + return default_value; + } } return default_value; } + Units XmlUtil::getUnitsAttr( const QDomElement& node, + const QString& name, + const Units& default_value ) + { + init(); + + QString valueString = node.attribute( name, "" ); + if ( valueString != "" ) + { + return Units( valueString ); + } + + return default_value; + } + + + QPainterPath XmlUtil::getPathDataAttr( const QDomElement& node, + const QString& name, + const Units& units ) + { + init(); + + QPainterPath d; + + // + // Simple path data parser + // + QStringList tokens = node.attribute( name, "" ).split( " ", QString::SkipEmptyParts ); + + enum { CMD, MX, MY, MDX, MDY, LX, LY, LDX, LDY, HX, HDX, VY, VDY } state = CMD; + Distance x = 0; + Distance y = 0; + Distance dx = 0; + Distance dy = 0; + QPointF c; + + for ( int i = 0; i < tokens.size(); i++ ) + { + switch (state) + { + case CMD: + switch (tokens[i][0].unicode()) + { + case 'M': + state = MX; + break; + case 'm': + state = MDX; + break; + case 'L': + state = LX; + break; + case 'l': + state = LDX; + break; + case 'H': + state = HX; + break; + case 'h': + state = HDX; + break; + case 'V': + state = VY; + break; + case 'v': + state = VDY; + break; + case 'Z': + case 'z': + d.closeSubpath(); + state = CMD; + break; + } + break; + case MX: + x = Distance( tokens[i].toDouble(), units ); + state = MY; + break; + case MY: + y = Distance( tokens[i].toDouble(), units ); + d.moveTo( x.pt(), y.pt() ); + state = CMD; + break; + case MDX: + dx = Distance( tokens[i].toDouble(), units ); + state = MDY; + break; + case MDY: + dy = Distance( tokens[i].toDouble(), units ); + c = d.currentPosition(); + d.moveTo( c.x()+x.pt(), c.y()+y.pt() ); + state = CMD; + break; + case LX: + x = Distance( tokens[i].toDouble(), units ); + state = LY; + break; + case LY: + y = Distance( tokens[i].toDouble(), units ); + d.lineTo( x.pt(), y.pt() ); + state = CMD; + break; + case LDX: + dx = Distance( tokens[i].toDouble(), units ); + state = LDY; + break; + case LDY: + dy = Distance( tokens[i].toDouble(), units ); + c = d.currentPosition(); + d.lineTo( c.x()+dx.pt(), c.y()+dy.pt() ); + state = CMD; + break; + case HX: + x = Distance( tokens[i].toDouble(), units ); + c = d.currentPosition(); + d.lineTo( x.pt(), c.y() ); + state = CMD; + break; + case HDX: + dx = Distance( tokens[i].toDouble(), units ); + c = d.currentPosition(); + d.lineTo( c.x()+dx.pt(), c.y() ); + state = CMD; + break; + case VY: + y = Distance( tokens[i].toDouble(), units ); + c = d.currentPosition(); + d.lineTo( c.x(), y.pt() ); + state = CMD; + break; + case VDY: + dy = Distance( tokens[i].toDouble(), units ); + c = d.currentPosition(); + d.lineTo( c.x(), c.y()+dy.pt() ); + state = CMD; + break; + } + } + + return d; + } + + void XmlUtil::setStringAttr( QDomElement& node, const QString& name, const QString& value ) @@ -456,5 +619,50 @@ namespace glabels } + void XmlUtil::setUnitsAttr( QDomElement& node, + const QString& name, + const Units& value ) + { + node.setAttribute( name, value.toIdString() ); + } + + + void XmlUtil::setPathDataAttr( QDomElement& node, + const QString& name, + const QPainterPath& path, + const Units& units ) + { + QString pathString; + for ( int i = 0; i < path.elementCount(); i++ ) + { + auto element = path.elementAt( i ); + + // QPainterPath is natively in pts + Distance x = Distance::pt( element.x ); + Distance y = Distance::pt( element.y ); + + // Translate desired units for path data + double xValue = x.inUnits( units ); + double yValue = y.inUnits( units ); + + if ( element.isMoveTo() ) + { + pathString.append( QString( "M %1 %2" ).arg( xValue ).arg( yValue ) ); + } + else if ( element.isLineTo() ) + { + pathString.append( QString( "L %1 %2" ).arg( xValue ).arg( yValue ) ); + } + + if ( i < (path.elementCount() - 1) ) + { + pathString.append( " " ); + } + } + + node.setAttribute( name, pathString ); + } + + } } diff --git a/model/XmlUtil.h b/model/XmlUtil.h index 833f0b8b..a576c572 100644 --- a/model/XmlUtil.h +++ b/model/XmlUtil.h @@ -26,6 +26,7 @@ #include #include +#include #include #include #include @@ -91,6 +92,14 @@ namespace glabels const QString& name, QTextOption::WrapMode default_value ); + static Units getUnitsAttr( const QDomElement& node, + const QString& name, + const Units& default_value ); + + static QPainterPath getPathDataAttr( const QDomElement& node, + const QString& name, + const Units& units ); + static void setStringAttr( QDomElement& node, const QString& name, @@ -128,6 +137,16 @@ namespace glabels const QString& name, QTextOption::WrapMode value ); + static void setUnitsAttr( QDomElement& node, + const QString& name, + const Units& value ); + + static void setPathDataAttr( QDomElement& node, + const QString& name, + const QPainterPath& value, + const Units& units ); + + private: Units mUnits; diff --git a/model/unit_tests/CMakeLists.txt b/model/unit_tests/CMakeLists.txt index 16adeedc..21fc314c 100644 --- a/model/unit_tests/CMakeLists.txt +++ b/model/unit_tests/CMakeLists.txt @@ -8,4 +8,92 @@ if (Qt5Test_FOUND) target_link_libraries (TestSubstitutionField Model Qt5::Test) add_test (NAME SubstitutionField COMMAND TestSubstitutionField) + #======================================= + # Test XmlUtil class + #======================================= + qt5_wrap_cpp (TestXmlUtil_moc_sources TestXmlUtil.h) + add_executable (TestXmlUtil TestXmlUtil.cpp ${TestXmlUtil_moc_sources}) + target_link_libraries (TestXmlUtil Model Qt5::Test) + add_test (NAME XmlUtil COMMAND TestXmlUtil) + + #======================================= + # Test XmlLabelCreator/Parser classes + #======================================= + qt5_wrap_cpp (TestXmlLabel_moc_sources TestXmlLabel.h) + add_executable (TestXmlLabel TestXmlLabel.cpp ${TestXmlLabel_moc_sources}) + target_link_libraries (TestXmlLabel Model Qt5::Test) + add_test (NAME XmlLabel COMMAND TestXmlLabel) + + #======================================= + # Test ColorNode class + #======================================= + qt5_wrap_cpp (TestColorNode_moc_sources TestColorNode.h) + add_executable (TestColorNode TestColorNode.cpp ${TestColorNode_moc_sources}) + target_link_libraries (TestColorNode Model Qt5::Test) + add_test (NAME ColorNode COMMAND TestColorNode) + + #======================================= + # Test FileUtil class + #======================================= + qt5_wrap_cpp (TestFileUtil_moc_sources TestFileUtil.h) + add_executable (TestFileUtil TestFileUtil.cpp ${TestFileUtil_moc_sources}) + target_link_libraries (TestFileUtil Model Qt5::Test) + add_test (NAME FileUtil COMMAND TestFileUtil) + + #======================================= + # Test Merge classes + #======================================= + qt5_wrap_cpp (TestMerge_moc_sources TestMerge.h) + add_executable (TestMerge TestMerge.cpp ${TestMerge_moc_sources}) + target_link_libraries (TestMerge Model Qt5::Test) + add_test (NAME Merge COMMAND TestMerge) + + #======================================= + # Test Model class + #======================================= + qt5_wrap_cpp (TestModel_moc_sources TestModel.h) + add_executable (TestModel TestModel.cpp ${TestModel_moc_sources}) + target_link_libraries (TestModel Model Qt5::Test) + add_test (NAME Model COMMAND TestModel) + + #======================================= + # Test ModelImageObject class + #======================================= + qt5_wrap_cpp (TestModelImageObject_moc_sources TestModelImageObject.h) + add_executable (TestModelImageObject TestModelImageObject.cpp ${TestModelImageObject_moc_sources}) + target_link_libraries (TestModelImageObject Model Qt5::Test) + add_test (NAME ModelImageObject COMMAND TestModelImageObject) + + #======================================= + # Test RawText class + #======================================= + qt5_wrap_cpp (TestRawText_moc_sources TestRawText.h) + add_executable (TestRawText TestRawText.cpp ${TestRawText_moc_sources}) + target_link_libraries (TestRawText Model Qt5::Test) + add_test (NAME RawText COMMAND TestRawText) + + #======================================= + # Test TextNode class + #======================================= + qt5_wrap_cpp (TestTextNode_moc_sources TestTextNode.h) + add_executable (TestTextNode TestTextNode.cpp ${TestTextNode_moc_sources}) + target_link_libraries (TestTextNode Model Qt5::Test) + add_test (NAME TextNode COMMAND TestTextNode) + + #======================================= + # Test Variable class + #======================================= + qt5_wrap_cpp (TestVariable_moc_sources TestVariable.h) + add_executable (TestVariable TestVariable.cpp ${TestVariable_moc_sources}) + target_link_libraries (TestVariable Model Qt5::Test) + add_test (NAME Variable COMMAND TestVariable) + + #======================================= + # Test Variables class + #======================================= + qt5_wrap_cpp (TestVariables_moc_sources TestVariables.h) + add_executable (TestVariables TestVariables.cpp ${TestVariables_moc_sources}) + target_link_libraries (TestVariables Model Qt5::Test) + add_test (NAME Variables COMMAND TestVariables) + endif (Qt5Test_FOUND) diff --git a/model/unit_tests/TestColorNode.cpp b/model/unit_tests/TestColorNode.cpp new file mode 100644 index 00000000..251ac97f --- /dev/null +++ b/model/unit_tests/TestColorNode.cpp @@ -0,0 +1,177 @@ +/* TestColorNode.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestColorNode.h" + +#include "model/ColorNode.h" + +#include + + +QTEST_MAIN(TestColorNode) + +using namespace glabels::model; +using namespace glabels::merge; + + +void TestColorNode::colorNode() +{ + uint32_t rgbaBlackTransparent = 0; + uint32_t rgbaWhite = 0xFFFFFFFF; + uint32_t rgbaRed = 0xFF0000FF; // ColorNode uses RGBA. QColor set alpha to opaque 0xFF by default + uint32_t qRgbaRed = 0xFFFF0000; // QColor uses ARGB, ie alpha at top + uint32_t qRgbaGreen80 = 0x8000FF00; + + QColor blackTransparent = QColor::fromRgba( rgbaBlackTransparent ); + QColor white = QColor::fromRgba( rgbaWhite ); + QColor red = QColor::fromRgba( qRgbaRed ); + QColor green80 = QColor::fromRgba( qRgbaGreen80 ); + QColor silver80 = QColor( 192, 192, 192, 128 ); + + Record record; + Variables vars; + + ColorNode colorNode; + QVERIFY( !colorNode.isField() ); + QCOMPARE( colorNode.color(), blackTransparent ); + QCOMPARE( colorNode.key(), QString( "" ) ); + QCOMPARE( colorNode.rgba(), rgbaBlackTransparent ); + QCOMPARE( colorNode.color( nullptr, nullptr ), blackTransparent ); + QCOMPARE( colorNode.color( &record, nullptr ), blackTransparent ); + QCOMPARE( colorNode.color( nullptr, &vars ), blackTransparent ); + QCOMPARE( colorNode.color( &record, &vars ), blackTransparent ); + + colorNode.setField( true ); + QVERIFY( colorNode.isField() ); + colorNode.setField( false ); + QVERIFY( !colorNode.isField() ); + + colorNode.setColor( white ); + QCOMPARE( colorNode.color(), white ); + QCOMPARE( colorNode.rgba(), rgbaWhite ); + QCOMPARE( colorNode.color( nullptr, nullptr ), white ); + QCOMPARE( colorNode.color( &record, nullptr ), white ); + QCOMPARE( colorNode.color( nullptr, &vars ), white ); + QCOMPARE( colorNode.color( &record, &vars ), white ); + + colorNode.setKey( "key1" ); + QCOMPARE( colorNode.key(), QString( "key1" ) ); + + /// + /// Constructors + /// + ColorNode colorNode2( true, white, QString( "key2" ) ); + QVERIFY( colorNode2.isField() ); + QCOMPARE( colorNode2.key(), QString( "key2" ) ); + QCOMPARE( colorNode2.color(), white ); + + QVERIFY( colorNode2 != colorNode ); + colorNode.setField( true ); + QVERIFY( colorNode2 != colorNode ); + colorNode.setKey( "key2" ); + QVERIFY( colorNode2 == colorNode ); + + ColorNode colorNode3( red ); + QVERIFY( !colorNode3.isField() ); + QCOMPARE( colorNode3.key(), QString( "" ) ); + QCOMPARE( colorNode3.color(), red ); + QCOMPARE( colorNode3.rgba(), rgbaRed ); + + QVERIFY( colorNode3 != colorNode ); + colorNode.setField( false ); + QVERIFY( colorNode3 != colorNode ); + colorNode.setKey( "" ); + QVERIFY( colorNode3 != colorNode ); + colorNode.setColor( red ); + QVERIFY( colorNode3 == colorNode ); + + /// + /// Record + /// + colorNode = ColorNode( QString( "key1" ) ); + QVERIFY( colorNode.isField() ); // Defaults to true if given key only + QCOMPARE( colorNode.key(), QString( "key1" ) ); + QCOMPARE( colorNode.color(), blackTransparent ); + QCOMPARE( colorNode.color( &record, &vars ), silver80 ); // Defaults to silver if given non-matching record/variables + + record["key1"] = "white"; + QCOMPARE( colorNode.color( &record, nullptr ), white ); + + record["key1"] = "red"; + QCOMPARE( colorNode.color( &record, nullptr ), red ); + + record["key1"] = "#FF0000"; + QCOMPARE( colorNode.color( &record, nullptr ), red ); + + record["key1"] = "#FFFF0000"; // ARGB + QCOMPARE( colorNode.color( &record, nullptr ), red ); + + record["key1"] = "#8000FF00"; + QCOMPARE( colorNode.color( &record, nullptr ), green80 ); + + colorNode.setKey( "key2" ); + QCOMPARE( colorNode.color( &record, nullptr ), silver80 ); + record["key2"] = "#8000FF00"; + QCOMPARE( colorNode.color( &record, nullptr ), green80 ); + + /// + /// Variable + /// + colorNode = ColorNode( QString( "c1" ) ); + QVERIFY( colorNode.isField() ); // Defaults to true if given key only + QCOMPARE( colorNode.key(), QString( "c1" ) ); + QCOMPARE( colorNode.color(), blackTransparent ); + QCOMPARE( colorNode.color( &record, &vars ), silver80 ); // Defaults to silver if given non-matching record/variables + + { + Variable c1( Variable::Type::COLOR, "c1", "white", Variable::Increment::PER_ITEM ); + vars.addVariable( c1 ); + } + QCOMPARE( colorNode.color( nullptr, &vars ), white ); + vars.incrementVariablesOnItem(); + QCOMPARE( colorNode.color( nullptr, &vars ), white ); + + { + Variable c1( Variable::Type::COLOR, "c1", "red", Variable::Increment::PER_ITEM ); + vars.addVariable( c1 ); + } + QCOMPARE( colorNode.color( nullptr, &vars ), red ); + + { + Variable c1( Variable::Type::COLOR, "c1", "#8000FF00", Variable::Increment::PER_ITEM ); + vars.addVariable( c1 ); + } + QCOMPARE( colorNode.color( nullptr, &vars ), green80 ); + + colorNode.setKey( "c2" ); + QCOMPARE( colorNode.color( &record, nullptr ), silver80 ); + + { + Variable c2( Variable::Type::COLOR, "c2", "#8000FF00", Variable::Increment::PER_ITEM ); + vars.addVariable( c2 ); + } + QCOMPARE( colorNode.color( nullptr, &vars ), green80 ); + + /// + /// Record beats variable + /// + record["c2"] = "red"; + QCOMPARE( colorNode.color( &record, &vars ), red ); +} diff --git a/model/unit_tests/TestColorNode.h b/model/unit_tests/TestColorNode.h new file mode 100644 index 00000000..4837c5a8 --- /dev/null +++ b/model/unit_tests/TestColorNode.h @@ -0,0 +1,30 @@ +/* TestColorNode.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestColorNode : public QObject +{ + Q_OBJECT + +private slots: + void colorNode(); +}; diff --git a/model/unit_tests/TestFileUtil.cpp b/model/unit_tests/TestFileUtil.cpp new file mode 100644 index 00000000..7625d449 --- /dev/null +++ b/model/unit_tests/TestFileUtil.cpp @@ -0,0 +1,104 @@ +/* TestFileUtil.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestFileUtil.h" + +#include "model/FileUtil.h" + +#include + + +QTEST_MAIN(TestFileUtil) + +using namespace glabels::model; + + +void TestFileUtil::addExtension() +{ + QCOMPARE( FileUtil::addExtension( "/tmp/file", ".ext" ), QString( "/tmp/file.ext" ) ); + QCOMPARE( FileUtil::addExtension( "/tmp/file.ext", ".ext" ), QString( "/tmp/file.ext" ) ); + QCOMPARE( FileUtil::addExtension( "/tmp/file.ext", ".txt" ), QString( "/tmp/file.ext.txt" ) ); + QCOMPARE( FileUtil::addExtension( "/tmp/file", "txt" ), QString( "/tmp/filetxt" ) ); + QCOMPARE( FileUtil::addExtension( "/tmp/filetxt", "txt" ), QString( "/tmp/filetxt" ) ); +} + + +void TestFileUtil::systemTemplatesDir() +{ + QDir dir = FileUtil::systemTemplatesDir(); + QVERIFY( dir.exists() ); + QVERIFY( dir.isReadable() ); + QVERIFY( dir.path().endsWith( "templates" ) ); +} + + +void TestFileUtil::manualUserTemplatesDir() +{ + QDir dir = FileUtil::manualUserTemplatesDir(); + QVERIFY( dir.exists() ); + QVERIFY( dir.isReadable() ); + QVERIFY( dir.path().endsWith( ".glabels" ) ); +} + + +void TestFileUtil::userTemplatesDir() +{ + QDir dir = FileUtil::userTemplatesDir(); + QVERIFY( dir.exists() ); + QVERIFY( dir.isReadable() ); + QFileInfo fileInfo( dir.path() ); + QVERIFY( fileInfo.isWritable() ); +} + + +void TestFileUtil::translationsDir() +{ + QDir dir = FileUtil::translationsDir(); + QVERIFY( dir.exists() ); + QVERIFY( dir.isReadable() ); + QVERIFY( dir.path().endsWith( "translations" ) ); +} + + +void TestFileUtil::makeRelativeIfInDir_data() +{ + QTest::addColumn( "dir" ); + QTest::addColumn( "filename" ); + QTest::addColumn( "expected" ); + + QTest::newRow( "1" ) << "/dir/subdir" << "/dir/subdir/filename" << "filename"; + QTest::newRow( "2" ) << "/dir/subdir" << "filename" << "filename"; + QTest::newRow( "3" ) << "/dir" << "subdir/filename" << "subdir/filename"; + QTest::newRow( "4" ) << "/dir" << "/dir/subdir/subdir/filename" << "subdir/subdir/filename"; + QTest::newRow( "5" ) << "/dir/subdir" << "/dir/subdir/subdir/filename" << "subdir/filename"; + QTest::newRow( "6" ) << "/dir/subdir" << "/dir/subdir2/filename" << "/dir/subdir2/filename"; + QTest::newRow( "7" ) << "/dir2/subdir" << "/dir/subdir/filename" << "/dir/subdir/filename"; + QTest::newRow( "8" ) << "/dir/subdir" << "/dir/filename" << "/dir/filename"; +} + + +void TestFileUtil::makeRelativeIfInDir() +{ + QFETCH( QString, dir ); + QFETCH( QString, filename ); + QFETCH( QString, expected ); + + QCOMPARE( FileUtil::makeRelativeIfInDir( QDir( dir ), filename ), expected ); +} diff --git a/model/unit_tests/TestFileUtil.h b/model/unit_tests/TestFileUtil.h new file mode 100644 index 00000000..50e1e025 --- /dev/null +++ b/model/unit_tests/TestFileUtil.h @@ -0,0 +1,36 @@ +/* TestFileUtil.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestFileUtil : public QObject +{ + Q_OBJECT + +private slots: + void addExtension(); + void systemTemplatesDir(); + void manualUserTemplatesDir(); + void userTemplatesDir(); + void translationsDir(); + void makeRelativeIfInDir_data(); + void makeRelativeIfInDir(); +}; diff --git a/model/unit_tests/TestMerge.cpp b/model/unit_tests/TestMerge.cpp new file mode 100644 index 00000000..72c9dd3b --- /dev/null +++ b/model/unit_tests/TestMerge.cpp @@ -0,0 +1,347 @@ +/* TestMerge.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestMerge.h" + +#include "merge/Factory.h" +#include "merge/None.h" +#include "merge/TextCsv.h" +#include "merge/TextCsvKeys.h" +#include "merge/TextTsv.h" +#include "merge/TextTsvKeys.h" +#include "merge/TextColon.h" +#include "merge/TextColonKeys.h" +#include "merge/TextSemicolon.h" +#include "merge/TextSemicolonKeys.h" + +#include "merge/Record.h" + +#include + + +QTEST_MAIN(TestMerge) + +Q_DECLARE_METATYPE(glabels::merge::Factory::SourceType) + +using namespace glabels::merge; + + +void TestMerge::initTestCase() +{ + Factory::init(); +} + + +void TestMerge::factory_data() +{ + QTest::addColumn( "id" ); + QTest::addColumn( "name" ); + QTest::addColumn( "type" ); + QTest::addColumn( "index" ); + + int index = 0; + QTest::newRow( "None" ) << None::id() << "None" << Factory::NONE << index++; + QTest::newRow( "TextCsv" ) << TextCsv::id() << "Text: Comma Separated Values (CSV)" << Factory::FILE << index++; + QTest::newRow( "TextCsvKeys" ) << TextCsvKeys::id() << "Text: Comma Separated Values (CSV), keys on line 1" << Factory::FILE << index++; + QTest::newRow( "TextTsv" ) << TextTsv::id() << "Text: Tab Separated Values (TSV)" << Factory::FILE << index++; + QTest::newRow( "TextTsvKeys" ) << TextTsvKeys::id() << "Text: Tab Separated Values (TSV), keys on line 1" << Factory::FILE << index++; + QTest::newRow( "TextColon" ) << TextColon::id() << "Text: Colon Separated Values" << Factory::FILE << index++; + QTest::newRow( "TextColonKeys" ) << TextColonKeys::id() << "Text: Colon Separated Values, keys on line 1" << Factory::FILE << index++; + QTest::newRow( "TextSemicolon" ) << TextSemicolon::id() << "Text: Semicolon Separated Values" << Factory::FILE << index++; + QTest::newRow( "TextSemicolonKeys" ) << TextSemicolonKeys::id() << "Text: Semicolon Separated Values, keys on line 1" << Factory::FILE << index++; +} + + +void TestMerge::factory() +{ + QFETCH( QString, id ); + QFETCH( QString, name ); + QFETCH( Factory::SourceType, type ); + QFETCH( int, index ); + + QVERIFY( Factory::nameList().contains( name ) ); + + QString outName = Factory::idToName( id ); + QCOMPARE( outName, name ); + + QString outId = Factory::nameToId( name ); + QCOMPARE( outId, id ); + + Factory::SourceType outType = Factory::idToType( id ); + QCOMPARE( outType, type ); + + outId = Factory::indexToId( index ); + QCOMPARE( outId, id ); + + Merge* merge = Factory::createMerge( id ); + QVERIFY( merge ); + QCOMPARE( merge->id(), id ); + + Merge* cloneMerge = merge->clone(); + QCOMPARE( cloneMerge->id(), merge->id() ); + delete cloneMerge; + delete merge; +} + + +void TestMerge::factoryNotRegistered() +{ + QString id( "unregistered" ); + Merge* merge = Factory::createMerge( id ); + QVERIFY( merge ); + QVERIFY( merge->id() != id ); + QCOMPARE( merge->id(), None::id() ); + delete merge; +} + + +void TestMerge::text_data() +{ + QTest::addColumn( "id" ); + QTest::addColumn( "keyed" ); + QTest::addColumn( "delim" ); + + QTest::newRow( "TextCsv" ) << TextCsv::id() << false << ','; + QTest::newRow( "TextCsvKeys" ) << TextCsvKeys::id() << true << ','; + QTest::newRow( "TextTsv" ) << TextTsv::id() << false << '\t'; + QTest::newRow( "TextTsvKeys" ) << TextTsvKeys::id() << true << '\t'; + QTest::newRow( "TextColon" ) << TextColon::id() << false << ':'; + QTest::newRow( "TextColonKeys" ) << TextColonKeys::id() << true << ':'; + QTest::newRow( "TextSemicolon" ) << TextSemicolon::id() << false << ';'; + QTest::newRow( "TextSemicolonKeys" ) << TextSemicolonKeys::id() << true << ';'; +} + + +void TestMerge::text() +{ + QFETCH( QString, id ); + QFETCH( bool, keyed ); + QFETCH( char, delim ); + + QTemporaryFile file; + file.open(); + if ( keyed ) + { + file.write( "header1" ); + file.putChar( delim ); + file.write( "\"header 2\"" ); + file.putChar( delim ); + file.write( "header3\r\n" ); + } + file.write( " val11" ); // Leading spaces in SIMPLE entry + file.putChar( delim ); + file.write( "\"\"\"val 12\"\"\"" ); // 2DQUOTE at beginning and end of DQUOTE entry + file.putChar( delim ); + file.write( " \"val 13\"\n" ); // Leading spaces before DQUOTE entry, end line with LF only + + file.write( "\" val21\"\"\"" ); // Leading spaces within DQUOTE entry, 2DQUOTE at end + file.putChar( delim ); + file.write( "\"\"\"val 22\"" ); // 2DQUOTE at beginning of DQUOTE entry + file.putChar( delim ); + file.write( "\r\n" ); // Last field blank + + file.write( "\"\"\"\"\"\"" ); // 2 2DQUOTES alone in DQUOTE entry + file.putChar( delim ); + file.write( "val \"32" ); // DQUOTE in SIMPLE entry + file.putChar( delim ); + file.write( "val \"\\\"33\r\n" ); // DQUOTE backslashed-DQUOTE in SIMPLE entry + + file.putChar( delim ); file.putChar( delim ); // All fields blank + file.write( "\r\n" ); + + file.write( "val\\n \\t \\r \\\\ \\x51" ); // Backslashed-n/-t/-r/-backslash/-x in SIMPLE entry + file.putChar( delim ); + file.write( "\"val\\n \\t \\r \\\\ \\x52\"" ); // Backslashed-n/-t/-r/-backslash/-x in QUOTE entry + file.write( "\r\n" ); // No last delim + + file.write( "\"val \"\"61\"" ); // 2DQUOTE in middle of DQUOTE entry + file.putChar( delim ); + file.write( "\"val\"\"" ); file.putChar( delim ); file.write( "\r\n\\\"\u2019\\\\52\"" ); // 2DQUOTE delim CRLF backslashed-DQUOTE U+2019 backslashed-backslash + file.putChar( delim ); + file.write( "\"val63\"" ); // End without CRLF + file.close(); + + Merge* merge = Factory::createMerge( id ); + QCOMPARE( merge->id(), id ); + + merge->setSource( file.fileName() ); + QCOMPARE( merge->source(), file.fileName() ); + + const QList& recordList = merge->recordList(); + QCOMPARE( recordList.size(), 6 ); + + // + // Records + // + const char* h1 = keyed ? "header1" : "1"; + const char* h2 = keyed ? "header 2" : "2"; + const char* h3 = keyed ? "header3" : "3"; + const Record* record; + + record = recordList[0]; + QVERIFY( record->contains( h1 ) ); + QCOMPARE( record->value( h1 ), QString( " val11" ) ); + QVERIFY( record->contains( h2 ) ); + QCOMPARE( record->value( h2 ), QString( "\"val 12\"" ) ); + QVERIFY( record->contains( h3 ) ); + QCOMPARE( record->value( h3 ), QString( " \"val 13\"" ) ); // NOTE: Treats as unquoted due to leading spaces + + record = recordList[1]; + QVERIFY( record->contains( h1 ) ); + QCOMPARE( record->value( h1 ), QString( " val21\"" ) ); + QVERIFY( record->contains( h2 ) ); + QCOMPARE( record->value( h2 ), QString( "\"val 22" ) ); + QVERIFY( record->contains( h3 ) ); + QCOMPARE( record->value( h3 ), QString( "" ) ); + + record = recordList[2]; + QVERIFY( record->contains( h1 ) ); + QCOMPARE( record->value( h1 ), QString( "\"\"" ) ); + QVERIFY( record->contains( h2 ) ); + QCOMPARE( record->value( h2 ), QString( "val \"32" ) ); + QVERIFY( record->contains( h3 ) ); + QCOMPARE( record->value( h3 ), QString( "val \"\"33" ) ); + + record = recordList[3]; + QVERIFY( record->contains( h1 ) ); + QCOMPARE( record->value( h1 ), QString( "" ) ); + QVERIFY( record->contains( h2 ) ); + QCOMPARE( record->value( h2 ), QString( "" ) ); + QVERIFY( record->contains( h3 ) ); + QCOMPARE( record->value( h3 ), QString( "" ) ); + + record = recordList[4]; + QVERIFY( record->contains( h1 ) ); + QCOMPARE( record->value( h1 ), QString( "val\n \t r \\ x51" ) ); + QVERIFY( record->contains( h2 ) ); + QCOMPARE( record->value( h2 ), QString( "val\n \t r \\ x52" ) ); + QVERIFY( !record->contains( h3 ) ); + + record = recordList[5]; + QVERIFY( record->contains( h1 ) ); + QCOMPARE( record->value( h1 ), QString( "val \"61" ) ); + QVERIFY( record->contains( h2 ) ); + QCOMPARE( record->value( h2 ), QString( "val\"" ).append( delim ).append( "\n\"\u2019\\52" ) ); // NOTE: CR missing (QIODevice::Text strips all CRs from stream) + QVERIFY( record->contains( h3 ) ); + QCOMPARE( record->value( h3 ), QString( "val63" ) ); + + // + // Selection + // + QCOMPARE( merge->nSelectedRecords(), 6 ); // Initially all selected + merge->unselectAll(); + QCOMPARE( merge->nSelectedRecords(), 0 ); + + record = recordList[1]; + merge->select( (Record*)record ); + QCOMPARE( merge->nSelectedRecords(), 1 ); + QCOMPARE( merge->selectedRecords().size(), 1 ); + QCOMPARE( merge->selectedRecords().first(), record ); // Pointers same + + merge->unselect( (Record*)record ); + QCOMPARE( merge->nSelectedRecords(), 0 ); + QCOMPARE( merge->selectedRecords().size(), 0 ); + + merge->setSelected( 0 ); + merge->setSelected( 3 ); + QCOMPARE( merge->nSelectedRecords(), 2 ); + QCOMPARE( merge->selectedRecords().size(), 2 ); + QCOMPARE( merge->selectedRecords().first(), recordList[0] ); + QCOMPARE( merge->selectedRecords().last(), recordList[3] ); + + merge->setSelected( 0, false ); + QCOMPARE( merge->nSelectedRecords(), 1 ); + QCOMPARE( merge->selectedRecords().size(), 1 ); + + // + // Keys + // + QStringList keys = merge->keys(); + QCOMPARE( keys.size(), 3 ); + QCOMPARE( keys[0], QString( h1 ) ); + QCOMPARE( keys[1], QString( h2 ) ); + QCOMPARE( keys[2], QString( h3 ) ); + QCOMPARE( merge->primaryKey(), QString( h1 ) ); + + // + // Clone + // + merge->unselectAll(); + merge->setSelected( 0 ); + QCOMPARE( merge->nSelectedRecords(), 1 ); + + Merge* cloneMerge = merge->clone(); + QCOMPARE( cloneMerge->id(), merge->id() ); + QCOMPARE( cloneMerge->source(), merge->source() ); + QCOMPARE( cloneMerge->recordList().size(), merge->recordList().size() ); + QCOMPARE( *(cloneMerge->recordList()[0]), *(merge->recordList()[0]) ); // Pointers different + QCOMPARE( *(cloneMerge->recordList()[1]), *(merge->recordList()[1]) ); + QCOMPARE( *(cloneMerge->recordList()[2]), *(merge->recordList()[2]) ); + QCOMPARE( *(cloneMerge->recordList()[3]), *(merge->recordList()[3]) ); + QCOMPARE( *(cloneMerge->recordList()[4]), *(merge->recordList()[4]) ); + QCOMPARE( *(cloneMerge->recordList()[5]), *(merge->recordList()[5]) ); + QCOMPARE( cloneMerge->nSelectedRecords(), merge->nSelectedRecords() ); + QCOMPARE( cloneMerge->selectedRecords().size(), merge->selectedRecords().size() ); + QCOMPARE( *(cloneMerge->selectedRecords()[0]), *(merge->selectedRecords()[0]) ); + QCOMPARE( cloneMerge->keys(), merge->keys() ); + QCOMPARE( cloneMerge->primaryKey(), merge->primaryKey() ); + delete cloneMerge; + delete merge; +} + + +void TestMerge::none() +{ + None none; + QCOMPARE( none.id(), QString( "None" ) ); + + None* cloneNone = none.clone(); + QCOMPARE( cloneNone->id(), none.id() ); + QCOMPARE( cloneNone->keys(), none.keys() ); + QCOMPARE( cloneNone->primaryKey(), none.primaryKey() ); + delete cloneNone; +} + + +void TestMerge::record() +{ + Record record; + QCOMPARE( record.isSelected(), true ); + record.setSelected( false ); + QCOMPARE( record.isSelected(), false ); + record.setSelected( true ); + QCOMPARE( record.isSelected(), true ); + + record["key"] = "val"; + QVERIFY( record.contains( "key" ) ); + QCOMPARE( record["key"], QString( "val" ) ); + + Record* cloneRecord = record.clone(); + QCOMPARE( cloneRecord->isSelected(), true ); + QVERIFY( cloneRecord->contains( "key" ) ); + QCOMPARE( cloneRecord->value( "key" ), QString( "val" ) ); + delete cloneRecord; + + record.setSelected( false ); + Record record2( &record ); + QCOMPARE( record2.isSelected(), false ); + QVERIFY( record2.contains( "key" ) ); + QCOMPARE( record2["key"], QString( "val" ) ); +} diff --git a/model/unit_tests/TestMerge.h b/model/unit_tests/TestMerge.h new file mode 100644 index 00000000..5fd43bd0 --- /dev/null +++ b/model/unit_tests/TestMerge.h @@ -0,0 +1,37 @@ +/* TestMerge.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestMerge : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void factory_data(); + void factory(); + void factoryNotRegistered(); + void text_data(); + void text(); + void none(); + void record(); +}; diff --git a/model/unit_tests/TestModel.cpp b/model/unit_tests/TestModel.cpp new file mode 100644 index 00000000..f52b4c47 --- /dev/null +++ b/model/unit_tests/TestModel.cpp @@ -0,0 +1,496 @@ +/* TestModel.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestModel.h" + +#include "model/Model.h" +#include "model/ModelBoxObject.h" +#include "model/ModelEllipseObject.h" +#include "model/ModelLineObject.h" +#include "model/ModelTextObject.h" +#include "model/FrameRect.h" +#include "model/FrameContinuous.h" +#include "model/Region.h" +#include "model/Settings.h" + +#include "merge/Factory.h" +#include "merge/Merge.h" +#include "merge/None.h" +#include "merge/TextCsv.h" +#include "merge/TextCsvKeys.h" + +#include + + +QTEST_MAIN(TestModel) + +using namespace glabels::model; +using namespace glabels::merge; + + +void TestModel::initTestCase() +{ + Factory::init(); + Settings::init(); +} + + +void TestModel::model() +{ + Model model; + QVERIFY( model.isModified() ); + model.clearModified(); + QVERIFY( !model.isModified() ); + + QVERIFY( model.shortName().contains( QRegExp( "^Untitled[1-9][0-9]*$" ) ) ); + model.setFileName( "dir/file1.ext" ); + QCOMPARE( model.fileName(), QString( "dir/file1.ext" ) ); + QCOMPARE( model.shortName(), QString( "file1" ) ); + QVERIFY( !model.isModified() ); + + QCOMPARE( model.w(), Distance( 0 ) ); + QCOMPARE( model.h(), Distance( 0 ) ); + + Template tmplate( "Test Brand", "part", "desc", "testPaperId", 100, 400 ); + FrameRect* frame = new FrameRect( 100, 200, 5, 0, 0, "rect1" ); + QVERIFY( frame->w() != frame->h() ); + tmplate.addFrame( frame ); + model.setTmplate( &tmplate ); // Copies + QCOMPARE( model.tmplate()->brand(), QString( "Test Brand" ) ); + QCOMPARE( model.tmplate()->part(), QString( "part" ) ); + QCOMPARE( model.tmplate()->description(), QString( "desc" ) ); + QCOMPARE( model.tmplate()->paperId(), QString( "testPaperId" ) ); + QCOMPARE( model.tmplate()->pageWidth(), Distance( 100 ) ); + QCOMPARE( model.tmplate()->pageHeight(), Distance( 400 ) ); + QVERIFY( model.isModified() ); + + QVERIFY( model.frame()->id() == frame->id() ); + QCOMPARE( model.w(), Distance( 100 ) ); + QCOMPARE( model.h(), Distance( 200 ) ); + QCOMPARE( model.w(), frame->w() ); + QCOMPARE( model.h(), frame->h() ); + + model.clearModified(); + QVERIFY( !model.isModified() ); + + QVERIFY( !model.rotate() ); + model.setRotate( false ); + QVERIFY( !model.rotate() ); + QVERIFY( !model.isModified() ); + model.setRotate( true ); + QVERIFY( model.rotate() ); + QVERIFY( model.isModified() ); + + QCOMPARE( model.w(), frame->h() ); + QCOMPARE( model.h(), frame->w() ); + + model.setRotate( false ); + QVERIFY( !model.rotate() ); + + model.clearModified(); + QVERIFY( !model.isModified() ); + + model.setH( 300 ); // Default does nothing + QCOMPARE( model.h(), Distance( 200 ) ); + QVERIFY( model.isModified() ); // Set anyway + + // Continuous frame implements setH() + Template tmplate2( "Test Brand2", "part2", "desc2", "testPaperId2", 100, 400 ); + FrameContinuous* frame2 = new FrameContinuous( 100, 0, 500, 200, "continuous1" ); + QCOMPARE( frame2->h(), Distance( 200 ) ); + tmplate2.addFrame( frame2 ); + model.setTmplate( &tmplate2 ); + QVERIFY( model.frame()->id() == frame2->id() ); + QCOMPARE( model.w(), Distance( 100 ) ); + QCOMPARE( model.h(), Distance( 200 ) ); + QCOMPARE( model.w(), frame2->w() ); + QCOMPARE( model.h(), frame2->h() ); + + model.clearModified(); + QVERIFY( !model.isModified() ); + + model.setH( 300 ); + QCOMPARE( model.h(), Distance( 300 ) ); + QVERIFY( model.isModified() ); + + // + // Objects + // + ColorNode black( Qt::black ); + ModelObject* ellipse = new ModelEllipseObject( 1, 0, 100, 100, false, 1, black, black ); + ModelObject* box = new ModelBoxObject( 1, 100, 100, 100, false, 1, black, black ); + ModelObject* line = new ModelLineObject( 1, 200, 99 /*dx*/, 1 /*dy*/, 1.0, black ); + ModelObject* text = new ModelTextObject( 1, 201, 100, 30, false, "", "Sans", 10, QFont::Normal, false, false, black, Qt::AlignLeft, Qt::AlignTop, QTextOption::WordWrap, 1, false ); + + model.clearModified(); + QVERIFY( !model.isModified() ); + + model.addObject( ellipse ); + QVERIFY( model.isModified() ); + model.addObject( box ); + model.addObject( line ); + model.addObject( text ); + + QCOMPARE( model.objectList().size(), 4 ); + + ModelObject* line2 = new ModelLineObject( 1, 231, 100 /*dx*/, 1 /*dy*/, 1.0, black ); + model.addObject( line2 ); + QCOMPARE( model.objectList().size(), 5 ); + + model.clearModified(); + QVERIFY( !model.isModified() ); + + model.deleteObject( line2 ); + QCOMPARE( model.objectList().size(), 4 ); + QVERIFY( model.isModified() ); + + ModelObject* object; + + object = model.objectAt( 1 /*scale*/, 1, 200 ); + QVERIFY( object ); + QVERIFY( dynamic_cast(object) ); + QCOMPARE( object->id(), line->id() ); + + object = model.objectAt( 1 /*scale*/, 100, 150 ); + QVERIFY( object ); + QVERIFY( dynamic_cast(object) ); + QCOMPARE( object->id(), box->id() ); + + object = model.objectAt( 1 /*scale*/, 50, 0 ); + QVERIFY( object ); + QVERIFY( dynamic_cast(object) ); + QCOMPARE( object->id(), ellipse->id() ); + + object = model.objectAt( 1 /*scale*/, 1 + 3, 201 + 3 ); // Allow for text offset + QVERIFY( object ); + QVERIFY( dynamic_cast(object) ); + QCOMPARE( object->id(), text->id() ); + + // + // Selection + // + QVERIFY( model.isSelectionEmpty() ); + QVERIFY( !model.isSelectionAtomic() ); + QVERIFY( model.getSelection().isEmpty() ); + QVERIFY( !model.getFirstSelectedObject() ); + + QVERIFY( !model.canSelectionText() ); + QVERIFY( !model.canSelectionFill() ); + QVERIFY( !model.canSelectionLineColor() ); + QVERIFY( !model.canSelectionLineWidth() ); + + model.selectAll(); + QVERIFY( !model.isSelectionEmpty() ); + QVERIFY( !model.isSelectionAtomic() ); + QVERIFY( !model.getSelection().isEmpty() ); + QCOMPARE( model.getSelection().size(), 4 ); + QCOMPARE( model.getSelection().first()->id(), ellipse->id() ); + QCOMPARE( model.getSelection().at(1)->id(), box->id() ); + QCOMPARE( model.getSelection().at(2)->id(), line->id() ); + QCOMPARE( model.getSelection().at(3)->id(), text->id() ); + QVERIFY( model.getFirstSelectedObject() ); + QCOMPARE( model.getFirstSelectedObject()->id(), ellipse->id() ); + + QVERIFY( model.canSelectionText() ); + QVERIFY( model.canSelectionFill() ); + QVERIFY( model.canSelectionLineColor() ); + QVERIFY( model.canSelectionLineWidth() ); + + model.unselectAll(); + QVERIFY( model.isSelectionEmpty() ); + QVERIFY( !model.isSelectionAtomic() ); + QVERIFY( model.getSelection().isEmpty() ); + QVERIFY( !model.getFirstSelectedObject() ); + + model.selectObject( text ); + QVERIFY( !model.isSelectionEmpty() ); + QVERIFY( model.isSelectionAtomic() ); + QCOMPARE( model.getSelection().size(), 1 ); + QCOMPARE( model.getFirstSelectedObject()->id(), text->id() ); + + QVERIFY( model.canSelectionText() ); + QVERIFY( !model.canSelectionFill() ); + QVERIFY( !model.canSelectionLineColor() ); + QVERIFY( !model.canSelectionLineWidth() ); + + model.unselectObject( text ); + QVERIFY( model.isSelectionEmpty() ); + + model.selectObject( line ); + QVERIFY( !model.isSelectionEmpty() ); + QVERIFY( model.isSelectionAtomic() ); + QCOMPARE( model.getSelection().size(), 1 ); + QCOMPARE( model.getFirstSelectedObject()->id(), line->id() ); + + QVERIFY( !model.canSelectionText() ); + QVERIFY( !model.canSelectionFill() ); + QVERIFY( model.canSelectionLineColor() ); + QVERIFY( model.canSelectionLineWidth() ); + + model.unselectAll(); + QVERIFY( model.isSelectionEmpty() ); + + double margin = 0.5; // Allow 0.5pt margin + Region region( 1 - margin, 302 - margin, 101 + margin /*x2*/, 302 + margin /*y2*/ ); // Outside all objects + model.selectRegion( region ); + QVERIFY( model.getSelection().isEmpty() ); + QVERIFY( model.isSelectionEmpty() ); + + region.setY1( 0 - margin ); // Ellipse + region.setY2( 100 + margin ); + model.selectRegion( region ); + QVERIFY( !model.isSelectionEmpty() ); + QVERIFY( model.isSelectionAtomic() ); + QCOMPARE( model.getSelection().size(), 1 ); + QCOMPARE( model.getFirstSelectedObject()->id(), ellipse->id() ); + + QVERIFY( !model.canSelectionText() ); + QVERIFY( model.canSelectionFill() ); + QVERIFY( model.canSelectionLineColor() ); + QVERIFY( model.canSelectionLineWidth() ); + + region.setY1( 200 - margin ); // Line + region.setY2( 201 + margin ); + model.selectRegion( region ); + QVERIFY( !model.isSelectionEmpty() ); + QVERIFY( !model.isSelectionAtomic() ); // Accumulative + QCOMPARE( model.getSelection().size(), 2 ); + QCOMPARE( model.getSelection().at(0)->id(), ellipse->id() ); + QCOMPARE( model.getSelection().at(1)->id(), line->id() ); + + model.unselectObject( ellipse ); + QVERIFY( !model.isSelectionEmpty() ); + QVERIFY( model.isSelectionAtomic() ); + QCOMPARE( model.getSelection().size(), 1 ); + QCOMPARE( model.getFirstSelectedObject()->id(), line->id() ); + + model.unselectAll(); + QVERIFY( model.isSelectionEmpty() ); + + // TODO: Operations on selections etc +} + + +void TestModel::saveRestore() +{ + Model* model = new Model; + QVERIFY( model->isModified() ); + model->clearModified(); + QVERIFY( !model->isModified() ); + + // + // Set template/frame + // + Template tmplate( "Test Brand", "part", "desc", "testPaperId", 110, 410 ); + FrameRect* frame = new FrameRect( 120, 220, 5, 0, 0, "rect1" ); + tmplate.addFrame( frame ); + model->setTmplate( &tmplate ); // Copies + QCOMPARE( model->tmplate()->brand(), QString( "Test Brand" ) ); + QVERIFY( model->isModified() ); + + model->clearModified(); + QVERIFY( !model->isModified() ); + + // + // Set merge + // + Merge* merge = Factory::createMerge( TextCsvKeys::id() ); + QCOMPARE( merge->id(), TextCsvKeys::id() ); + + model->setMerge( merge ); + QCOMPARE( model->merge(), merge ); + QVERIFY( model->isModified() ); + + // + // Add some variables + // + model->clearModified(); + QVERIFY( !model->isModified() ); + + Variable i( Variable::Type::INTEGER, "i", "2", Variable::Increment::PER_ITEM, "2" ); + Variable f( Variable::Type::FLOATING_POINT, "f", "6.54", Variable::Increment::PER_COPY, "0.12" ); + model->variables()->addVariable( i ); + QVERIFY( model->isModified() ); + model->variables()->addVariable( f ); + QVERIFY( model->isModified() ); + + model->clearModified(); + QVERIFY( !model->isModified() ); + + QTemporaryFile csv; + csv.open(); + csv.write( "id,text\n1,text1\n2,text2\n3,text3\n" ); + csv.close(); + + merge->setSource( csv.fileName() ); + QCOMPARE( merge->source(), csv.fileName() ); + QCOMPARE( merge->recordList().size(), 3 ); + QVERIFY( model->isModified() ); + + model->clearModified(); + QVERIFY( !model->isModified() ); + + // + // Add some objects + // + ColorNode black( Qt::black ); + ModelObject* object1 = new ModelLineObject( 1, 1, 90, 80, 1.0, black ); + model->addObject( object1 ); + QVERIFY( model->isModified() ); + QCOMPARE( model->objectList().size(), 1 ); + QCOMPARE( model->objectList().first(), object1 ); + + model->clearModified(); + QVERIFY( !model->isModified() ); + + ModelObject* object2 = new ModelTextObject( 2, 2, 70, 30, false, "", "Sans", 10, QFont::Normal, false, false, black, Qt::AlignLeft, Qt::AlignTop, QTextOption::WordWrap, 1, false ); + model->addObject( object2 ); + QVERIFY( model->isModified() ); + QCOMPARE( model->objectList().size(), 2 ); + QCOMPARE( model->objectList().last(), object2 ); + + QString modelShortName = model->shortName(); // If no fileName set then model expects to have have this called before being saved/restored (otherwise get differing untitled names) + + // + // Test + // + Model* saved = model->save(); + QVERIFY( saved->isModified() ); + QCOMPARE( saved->merge(), model->merge() ); // Shared + QCOMPARE( saved->variables(), model->variables() ); // Shared + QCOMPARE( saved->isModified(), model->isModified() ); + QCOMPARE( saved->shortName(), modelShortName ); + QCOMPARE( saved->shortName(), model->shortName() ); + QCOMPARE( saved->fileName(), model->fileName() ); + QCOMPARE( saved->rotate(), model->rotate() ); + QCOMPARE( saved->objectList().size(), model->objectList().size() ); + QVERIFY( saved->objectList().at(0) != object1 ); // Objects copied + QVERIFY( saved->objectList().at(1) != object2 ); // Objects copied + QCOMPARE( saved->objectList().at(0)->x0(), model->objectList().at(0)->x0() ); + QCOMPARE( saved->objectList().at(0)->y0(), model->objectList().at(0)->y0() ); + QCOMPARE( saved->objectList().at(1)->x0(), model->objectList().at(1)->x0() ); + QCOMPARE( saved->objectList().at(1)->y0(), model->objectList().at(1)->y0() ); + + // Modify original + Template tmplate2( "Test Brand2", "part2", "desc2", "testPaperId2", 230, 630 ); + FrameRect* frame2 = new FrameRect( 240, 340, 5, 0, 0, "rect2" ); + tmplate2.addFrame( frame2 ); + model->setTmplate( &tmplate2 ); + QCOMPARE( model->tmplate()->brand(), QString( "Test Brand2" ) ); + QCOMPARE( model->w(), Distance( 240 ) ); + QCOMPARE( model->h(), Distance( 340 ) ); + + model->setFileName( "dir/file1.ext" ); + QCOMPARE( model->shortName(), QString( "file1" ) ); + + model->setRotate( true ); + QVERIFY( model->rotate() ); + QCOMPARE( model->w(), Distance( 340 ) ); + QCOMPARE( model->h(), Distance( 240 ) ); + + model->deleteObject( model->objectList().first() ); + QCOMPARE( model->objectList().size(), 1 ); + QCOMPARE( model->objectList().first(), object2 ); + + model->objectList().first()->setY0( model->objectList().first()->y0() + 1 ); + + Merge* merge2 = Factory::createMerge( TextCsv::id() ); + QCOMPARE( merge2->id(), TextCsv::id() ); + QTemporaryFile csv2; csv2.open(); csv2.write( "21,text21\n22,text22\n23,text23\n24,text24\n" ); csv2.close(); + merge2->setSource( csv2.fileName() ); + QCOMPARE( merge2->source(), csv2.fileName() ); + QCOMPARE( merge2->recordList().size(), 4 ); + + model->setMerge( merge2 ); // Deletes original so saved->merge() now invalid + QCOMPARE( model->merge(), merge2 ); + + Model* modified = model->save(); + QCOMPARE( modified->merge(), merge2 ); // Shared + + Variable c( Variable::Type::COLOR, "c", "blue", Variable::Increment::PER_PAGE ); + model->variables()->addVariable( c ); + QCOMPARE( model->variables(), saved->variables() ); // Shared. + + // Verify differences + QVERIFY( model->shortName() != modelShortName ); + QVERIFY( model->shortName() != saved->shortName() ); + QVERIFY( model->fileName() != saved->fileName() ); + QVERIFY( model->tmplate()->brand() != saved->tmplate()->brand() ); + QVERIFY( model->rotate() != saved->rotate() ); + QVERIFY( model->w() != saved->w() ); + QVERIFY( model->h() != saved->h() ); + QVERIFY( model->objectList().size() != saved->objectList().size() ); + QVERIFY( model->objectList().at(0)->x0() != saved->objectList().at(0)->x0() ); + QVERIFY( model->objectList().at(0)->y0() != saved->objectList().at(0)->y0() ); + QCOMPARE( model->objectList().at(0)->x0(), saved->objectList().at(1)->x0() ); // Unchanged + QVERIFY( model->objectList().at(0)->y0() != saved->objectList().at(1)->y0() ); + + // Restore + model->restore( saved ); + QCOMPARE( model->shortName(), modelShortName ); + QCOMPARE( model->shortName(), saved->shortName() ); + QCOMPARE( model->fileName(), saved->fileName() ); + QCOMPARE( model->tmplate()->brand(), saved->tmplate()->brand() ); + QCOMPARE( model->rotate(), saved->rotate() ); + QCOMPARE( model->w(), saved->w() ); + QCOMPARE( model->h(), saved->h() ); + QCOMPARE( model->objectList().size(), saved->objectList().size() ); + QCOMPARE( model->objectList().size(), 2 ); + QCOMPARE( model->objectList().at(0)->x0(), saved->objectList().at(0)->x0() ); + QCOMPARE( model->objectList().at(0)->y0(), saved->objectList().at(0)->y0() ); + QCOMPARE( model->objectList().at(1)->x0(), saved->objectList().at(1)->x0() ); + QCOMPARE( model->objectList().at(1)->y0(), saved->objectList().at(1)->y0() ); + + QCOMPARE( model->merge(), merge2 ); // Unchanged + QVERIFY( model->merge() != saved->merge() ); // NOTE saved->merge() now points to deleted object + QCOMPARE( model->variables(), saved->variables() ); // Unchanged + + // Unrestore + model->restore( modified ); + QVERIFY( model->shortName() != modelShortName ); + QVERIFY( model->shortName() != saved->shortName() ); + QVERIFY( model->fileName() != saved->fileName() ); + QVERIFY( model->tmplate()->brand() != saved->tmplate()->brand() ); + QVERIFY( model->rotate() != saved->rotate() ); + QVERIFY( model->w() != saved->w() ); + QVERIFY( model->h() != saved->h() ); + QCOMPARE( model->objectList().size(), 1 ); + QVERIFY( model->objectList().size() != saved->objectList().size() ); + QVERIFY( model->objectList().at(0)->x0() != saved->objectList().at(0)->x0() ); + QVERIFY( model->objectList().at(0)->y0() != saved->objectList().at(0)->y0() ); + QCOMPARE( model->merge(), merge2 ); // Same + QCOMPARE( model->variables(), saved->variables() ); // Same + + QCOMPARE( model->shortName(), modified->shortName() ); + QCOMPARE( model->fileName(), modified->fileName() ); + QCOMPARE( model->tmplate()->brand(), modified->tmplate()->brand() ); + QCOMPARE( model->rotate(), modified->rotate() ); + QCOMPARE( model->w(), modified->w() ); + QCOMPARE( model->h(), modified->h() ); + QCOMPARE( model->objectList().size(), modified->objectList().size() ); + QCOMPARE( model->objectList().at(0)->x0(), modified->objectList().at(0)->x0() ); + QCOMPARE( model->objectList().at(0)->y0(), modified->objectList().at(0)->y0() ); + + delete model->merge(); // Final instance owned by us + delete model->variables(); // Instance owned by us + delete model; + delete saved; + delete modified; +} diff --git a/model/unit_tests/TestModel.h b/model/unit_tests/TestModel.h new file mode 100644 index 00000000..5a7f5808 --- /dev/null +++ b/model/unit_tests/TestModel.h @@ -0,0 +1,32 @@ +/* TestModel.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestModel : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void model(); + void saveRestore(); +}; diff --git a/model/unit_tests/TestModelImageObject.cpp b/model/unit_tests/TestModelImageObject.cpp new file mode 100644 index 00000000..3bcdcb4a --- /dev/null +++ b/model/unit_tests/TestModelImageObject.cpp @@ -0,0 +1,252 @@ +/* TestModelImageObject.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestModelImageObject.h" +#include "Test_Constants.h" + +#include "model/Model.h" +#include "model/ModelImageObject.h" +#include "model/Size.h" + +#include "merge/Factory.h" +#include "merge/TextCsvKeys.h" +#include "merge/Record.h" + +#include + + +QTEST_MAIN(TestModelImageObject) + +using namespace glabels::model; +using namespace glabels::merge; + + +void TestModelImageObject::initTestCase() +{ + Factory::init(); +} + + +void TestModelImageObject::readImageFile() +{ + QByteArray pngArray; + QImage png; + QString svgTemplate = QDir::tempPath().append( "/TestModelImageObject_XXXXXX.svg" ); // Note: directory separators canonicalized to slash by Qt path methods + + Model model; + + // Needed for relative file names to work + QString modelFileName = QDir::tempPath().append( "/TestModelImageObject.glabels" ); + model.setFileName( modelFileName ); + + ModelImageObject object; + + /// + /// Merge object, no shadow + /// + object.setX0( 1 ); + object.setY0( 1 ); + object.setSize( 8, 8 ); + object.setFilenameNode( TextNode( true, "image" ) ); + + model.addObject( object.clone() ); + + /// + /// Variable object, green pgn, gray shadow + /// + object.setY0( 11 ); + object.setShadow( true ); + object.setShadowColorNode( ColorNode( Qt::gray ) ); + object.setShadowOpacity( 1 ); + TextNode( true, "var" ); + object.setFilenameNode( TextNode( true, "var" ) ); + + // Green 8x8 square pgn + pngArray = QByteArray::fromBase64( glabels::test::green_8x8_png ); + QVERIFY( png.loadFromData( pngArray, "PNG" ) ); + QTemporaryFile pngGreen; pngGreen.open(); pngGreen.close(); png.save( pngGreen.fileName(), "PNG" ); + QFileInfo pngGreenFileInfo( pngGreen.fileName() ); + + Variable var( Variable::Type::STRING, "var", pngGreenFileInfo.fileName(), Variable::Increment::PER_ITEM ); // Relative path + model.variables()->addVariable( var ); + + model.addObject( object.clone() ); + + /// + /// Variable object 2, magenta svg, yellow shadow + /// + object.setY0( 21 ); + object.setShadow( true ); + object.setShadowColorNode( ColorNode( Qt::yellow ) ); + object.setShadowOpacity( 1 ); + object.setFilenameNode( TextNode( true, "var2" ) ); + + // Magenta 8x8 square svg + QTemporaryFile svgMagenta( svgTemplate ); svgMagenta.open(); svgMagenta.write( glabels::test::magenta_8x8_svg ); svgMagenta.close(); + QFileInfo svgMagentaFileInfo( svgMagenta.fileName() ); + + Variable var2( Variable::Type::STRING, "var2", svgMagentaFileInfo.fileName(), Variable::Increment::PER_ITEM ); // Absolute path + model.variables()->addVariable( var2 ); + + model.addObject( object.clone() ); + + /// + /// Filename object, yellow png, cyan shadow + /// + object.setY0( 31 ); + object.setShadow( true ); + object.setShadowColorNode( ColorNode( Qt::cyan ) ); + object.setShadowOpacity( 1 ); + + // Yellow 8x8 square pgn + pngArray = QByteArray::fromBase64( glabels::test::yellow_8x8_png ); + QVERIFY( png.loadFromData( pngArray, "PNG" ) ); + QTemporaryFile pngYellowFile; pngYellowFile.open(); pngYellowFile.close(); png.save( pngYellowFile.fileName(), "PNG" ); + + QFileInfo pngYellowFileInfo( pngYellowFile.fileName() ); + + // Need to set object parent for relative paths to work + object.setParent( &model ); + + object.setFilenameNode( TextNode( false, pngYellowFileInfo.fileName() ) ); // Relative path + + model.addObject( object.clone() ); + + /// + /// Filename object, cyan svg, magenta shadow + /// + object.setY0( 41 ); + object.setSize( 8, 8 ); + object.setShadow( true ); + object.setShadowColorNode( ColorNode( Qt::magenta ) ); + object.setShadowOpacity( 1 ); + + // Cyan 8x8 square svg + QTemporaryFile svgCyanFile( svgTemplate ); svgCyanFile.open(); svgCyanFile.write( glabels::test::cyan_8x8_svg ); svgCyanFile.close(); + + QFileInfo svgCyanFileInfo( svgCyanFile.fileName() ); + object.setFilenameNode( TextNode( false, svgCyanFileInfo.filePath() ) ); // Absolute path + + model.addObject( object.clone() ); + + /// + /// Set up merge + /// + + // Blue 8x8 square pgn + pngArray = QByteArray::fromBase64( glabels::test::blue_8x8_png ); + QVERIFY( png.loadFromData( pngArray, "PNG" ) ); + QTemporaryFile png1; png1.open(); png1.close(); png.save( png1.fileName(), "PNG" ); + QTemporaryFile png2; png2.open(); png2.close(); png.save( png2.fileName(), "PNG" ); + + // Red 8x8 square svg + QTemporaryFile svg1( svgTemplate ); svg1.open(); svg1.write( glabels::test::red_8x8_svg ); svg1.close(); + QTemporaryFile svg2( svgTemplate ); svg2.open(); svg2.write( glabels::test::red_8x8_svg ); svg2.close(); + + QFileInfo png1FileInfo( png1.fileName() ); + QFileInfo png2FileInfo( png2.fileName() ); + QFileInfo svg1FileInfo( svg1.fileName() ); + QFileInfo svg2FileInfo( svg2.fileName() ); + + QTemporaryFile csv; + csv.open(); + csv.write( "id,image,type\n" ); + csv.write( "1," ); csv.write( png1FileInfo.fileName().toUtf8() ); csv.write( ",png\n" ); + csv.write( "2," ); csv.write( png1FileInfo.filePath().toUtf8() ); csv.write( ",png\n" ); + csv.write( "3," ); csv.write( svg1FileInfo.fileName().toUtf8() ); csv.write( ",svg\n" ); + csv.write( "4," ); csv.write( svg2FileInfo.filePath().toUtf8() ); csv.write( ",svg\n" ); + csv.write( "5," ); csv.write( svg2FileInfo.fileName().toUtf8() ); csv.write( ",svg\n" ); + csv.write( "6," ); csv.write( png2FileInfo.fileName().toUtf8() ); csv.write( ",png\n" ); + csv.write( "7," ); csv.write( svg1FileInfo.filePath().toUtf8() ); csv.write( ",svg\n" ); + csv.write( "8," ); csv.write( png2FileInfo.filePath().toUtf8() ); csv.write( ",png\n" ); + csv.close(); + + Merge* merge = Factory::createMerge( TextCsvKeys::id() ); + QVERIFY( merge ); + QCOMPARE( merge->id(), TextCsvKeys::id() ); + merge->setSource( csv.fileName() ); + model.setMerge( merge ); + + /// + /// Draw + /// + const QList records = merge->selectedRecords(); + QCOMPARE( records.size(), 8 ); + + QImage paintDevice( 10, 10 * model.objectList().size() * records.size(), QImage::Format_RGB32 ); + paintDevice.fill( Qt::white ); + QPainter painter( &paintDevice ); + + int i, cnt; + int yTranslate = 10 * model.objectList().size(); + for ( i = 0, cnt = records.size(); i < cnt; i++ ) + { + model.draw( &painter, false, records[i], model.variables() ); + painter.translate( 0, yTranslate ); + } + + QColor color, white = Qt::white, grayShadow = Qt::gray, yellowShadow = Qt::yellow, cyanShadow = Qt::cyan, magentaShadow = Qt::magenta; + for ( i = 0, cnt = records.size(); i < cnt; i++ ) + { + // Merge + qDebug() << "record" << i; + color = records[i]->value( "type" ) == "png" ? Qt::blue : Qt::red; + QCOMPARE( paintDevice.pixelColor( 1, 0 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 1, 1 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 8 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 9 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 9, 9 + i * yTranslate ), white ); // No shadow + + // Variable + color = Qt::green; + QCOMPARE( paintDevice.pixelColor( 1, 10 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 1, 11 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 18 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 19 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 9, 19 + i * yTranslate ), grayShadow ); + + // Variable 2 + color = Qt::magenta; + QCOMPARE( paintDevice.pixelColor( 1, 20 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 1, 21 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 28 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 29 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 9, 29 + i * yTranslate ), yellowShadow ); + + // Filename pgn + color = Qt::yellow; + QCOMPARE( paintDevice.pixelColor( 1, 30 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 1, 31 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 38 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 39 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 9, 39 + i * yTranslate ), cyanShadow ); + + // Filename svg + color = Qt::cyan; + QCOMPARE( paintDevice.pixelColor( 1, 40 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 1, 41 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 48 + i * yTranslate ), color ); + QCOMPARE( paintDevice.pixelColor( 1, 49 + i * yTranslate ), white ); + QCOMPARE( paintDevice.pixelColor( 9, 49 + i * yTranslate ), magentaShadow ); + } + + delete model.merge(); + delete model.variables(); +} diff --git a/model/unit_tests/TestModelImageObject.h b/model/unit_tests/TestModelImageObject.h new file mode 100644 index 00000000..b508d1d8 --- /dev/null +++ b/model/unit_tests/TestModelImageObject.h @@ -0,0 +1,31 @@ +/* TestModelImageObject.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestModelImageObject : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void readImageFile(); +}; diff --git a/model/unit_tests/TestRawText.cpp b/model/unit_tests/TestRawText.cpp new file mode 100644 index 00000000..f5233286 --- /dev/null +++ b/model/unit_tests/TestRawText.cpp @@ -0,0 +1,94 @@ +/* TestRawText.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestRawText.h" + +#include "model/RawText.h" + +#include "merge/Record.h" + +#include + + +QTEST_MAIN(TestRawText) + +using namespace glabels::model; +using namespace glabels::merge; + + +void TestRawText::rawText() +{ + RawText rawText; + Record record; + + QVERIFY( rawText.isEmpty() ); + QVERIFY( !rawText.hasPlaceHolders() ); + QCOMPARE( rawText.toString(), QString( "" ) ); + QCOMPARE( rawText.toStdString(), std::string( "" ) ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "" ) ); + + rawText = "text"; + QVERIFY( !rawText.isEmpty() ); + QVERIFY( !rawText.hasPlaceHolders() ); + QCOMPARE( rawText.toString(), QString( "text" ) ); + QCOMPARE( rawText.toStdString(), std::string( "text" ) ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "text" ) ); + + RawText rawText2( "text" ); + QVERIFY( !rawText2.isEmpty() ); + QVERIFY( !rawText2.hasPlaceHolders() ); + QCOMPARE( rawText2.toString(), QString( "text" ) ); + + rawText = "${key1}"; + QVERIFY( !rawText.isEmpty() ); + QVERIFY( rawText.hasPlaceHolders() ); + QCOMPARE( rawText.toString(), QString( "${key1}" ) ); + QCOMPARE( rawText.toStdString(), std::string( "${key1}" ) ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "" ) ); + + /// + /// Record + /// + record["key1"] = "val1"; + QCOMPARE( rawText.expand( &record, nullptr ), QString( "val1" ) ); + + rawText = "${key1}${key2}"; + QVERIFY( rawText.hasPlaceHolders() ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "val1" ) ); + + record["key2"] = "val2"; + QCOMPARE( rawText.expand( &record, nullptr ), QString( "val1val2" ) ); + + rawText = "${key1}text${key2}"; + QVERIFY( rawText.hasPlaceHolders() ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "val1textval2" ) ); + + rawText = "text1${key1}text2${key2}text3"; + QVERIFY( rawText.hasPlaceHolders() ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "text1val1text2val2text3" ) ); + + rawText = "${key1}text${key2}${key3}"; + QVERIFY( rawText.hasPlaceHolders() ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "val1textval2" ) ); + + rawText = "${key2}${key3}${key1}"; + QVERIFY( rawText.hasPlaceHolders() ); + QCOMPARE( rawText.expand( &record, nullptr ), QString( "val2val1" ) ); +} diff --git a/model/unit_tests/TestRawText.h b/model/unit_tests/TestRawText.h new file mode 100644 index 00000000..781ecf80 --- /dev/null +++ b/model/unit_tests/TestRawText.h @@ -0,0 +1,30 @@ +/* TestRawText.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestRawText : public QObject +{ + Q_OBJECT + +private slots: + void rawText(); +}; diff --git a/model/unit_tests/TestSubstitutionField.cpp b/model/unit_tests/TestSubstitutionField.cpp index fa9135a2..16995f72 100644 --- a/model/unit_tests/TestSubstitutionField.cpp +++ b/model/unit_tests/TestSubstitutionField.cpp @@ -33,7 +33,7 @@ void TestSubstitutionField::parseValid() // // Valid substitution fields (concatenated in single input string) // - QString input = "${1234}${abc:=ABC}${x:%08.2f}${y:%08.2f:=12.34}${ADDR2:n}"; + QString input = "${1234}${abc:=ABC}${x:%08.2f}${y:%08.2f:=12.34}${ADDR2:n}${FüññýßútLæg@lѪmê}${Also_a legal-name}"; QStringRef s = &input; model::SubstitutionField f1; @@ -66,6 +66,16 @@ void TestSubstitutionField::parseValid() QCOMPARE( model::SubstitutionField::parse( s, f5 ), true ); QCOMPARE( f5.fieldName(), QString( "ADDR2" ) ); QCOMPARE( f5.newLine(), true ); + + model::SubstitutionField f6; + QCOMPARE( model::SubstitutionField::parse( s, f6 ), true ); + QCOMPARE( f6.fieldName(), QString( "FüññýßútLæg@lѪmê" ) ); + QCOMPARE( f6.newLine(), false ); + + model::SubstitutionField f7; + QCOMPARE( model::SubstitutionField::parse( s, f7 ), true ); + QCOMPARE( f7.fieldName(), QString( "Also_a legal-name" ) ); + QCOMPARE( f7.newLine(), false ); } @@ -108,6 +118,18 @@ void TestSubstitutionField::parseInvalid() QStringRef s9 = &input9; model::SubstitutionField f9; QCOMPARE( model::SubstitutionField::parse( s9, f9 ), true ); + + QString input10 = "${embedded\nnew-line}"; + QStringRef s10 = &input10; + model::SubstitutionField f10; + QCOMPARE( model::SubstitutionField::parse( s10, f10 ), false ); + QCOMPARE( s10, QStringRef( &input10 ) ); // Should not advance string reference + + QString input11 = "${how-about-a\ttab}"; + QStringRef s11 = &input11; + model::SubstitutionField f11; + QCOMPARE( model::SubstitutionField::parse( s11, f11 ), false ); + QCOMPARE( s11, QStringRef( &input11 ) ); // Should not advance string reference } @@ -139,6 +161,8 @@ void TestSubstitutionField::simpleEvaluation() { using namespace glabels; + model::Variables variables; + model::SubstitutionField f1( "${1}" ); model::SubstitutionField f2( "${2}" ); model::SubstitutionField f3( "${3}" ); @@ -150,10 +174,10 @@ void TestSubstitutionField::simpleEvaluation() record1[ "3" ] = "Opqrstu"; record1[ "4" ] = "Vwxyz!@"; - QCOMPARE( f1.evaluate( &record1 ), QString( "Abcdefg" ) ); - QCOMPARE( f2.evaluate( &record1 ), QString( "Hijklmn" ) ); - QCOMPARE( f3.evaluate( &record1 ), QString( "Opqrstu" ) ); - QCOMPARE( f4.evaluate( &record1 ), QString( "Vwxyz!@" ) ); + QCOMPARE( f1.evaluate( &record1, &variables ), QString( "Abcdefg" ) ); + QCOMPARE( f2.evaluate( &record1, &variables ), QString( "Hijklmn" ) ); + QCOMPARE( f3.evaluate( &record1, &variables ), QString( "Opqrstu" ) ); + QCOMPARE( f4.evaluate( &record1, &variables ), QString( "Vwxyz!@" ) ); merge::Record record2; record2[ "1" ] = "1234567"; @@ -161,10 +185,10 @@ void TestSubstitutionField::simpleEvaluation() record2[ "3" ] = "8901234"; record2[ "4" ] = "#$%^&*"; - QCOMPARE( f1.evaluate( &record2 ), QString( "1234567" ) ); - QCOMPARE( f2.evaluate( &record2 ), QString( "FooBar" ) ); - QCOMPARE( f3.evaluate( &record2 ), QString( "8901234" ) ); - QCOMPARE( f4.evaluate( &record2 ), QString( "#$%^&*" ) ); + QCOMPARE( f1.evaluate( &record2, &variables ), QString( "1234567" ) ); + QCOMPARE( f2.evaluate( &record2, &variables ), QString( "FooBar" ) ); + QCOMPARE( f3.evaluate( &record2, &variables ), QString( "8901234" ) ); + QCOMPARE( f4.evaluate( &record2, &variables ), QString( "#$%^&*" ) ); } @@ -172,6 +196,8 @@ void TestSubstitutionField::defaultValueEvaluation() { using namespace glabels; + model::Variables variables; + model::SubstitutionField f1( "${1:=foo1}" ); model::SubstitutionField f2( "${2:=foo2}" ); model::SubstitutionField f3( "${3:=foo3}" ); @@ -183,17 +209,17 @@ void TestSubstitutionField::defaultValueEvaluation() record1[ "3" ] = "Opqrstu"; record1[ "4" ] = "Vwxyz!@"; - QCOMPARE( f1.evaluate( &record1 ), QString( "Abcdefg" ) ); - QCOMPARE( f2.evaluate( &record1 ), QString( "Hijklmn" ) ); - QCOMPARE( f3.evaluate( &record1 ), QString( "Opqrstu" ) ); - QCOMPARE( f4.evaluate( &record1 ), QString( "Vwxyz!@" ) ); + QCOMPARE( f1.evaluate( &record1, &variables ), QString( "Abcdefg" ) ); + QCOMPARE( f2.evaluate( &record1, &variables ), QString( "Hijklmn" ) ); + QCOMPARE( f3.evaluate( &record1, &variables ), QString( "Opqrstu" ) ); + QCOMPARE( f4.evaluate( &record1, &variables ), QString( "Vwxyz!@" ) ); merge::Record record2; // All fields empty - QCOMPARE( f1.evaluate( &record2 ), QString( "foo1" ) ); - QCOMPARE( f2.evaluate( &record2 ), QString( "foo2" ) ); - QCOMPARE( f3.evaluate( &record2 ), QString( "foo3" ) ); - QCOMPARE( f4.evaluate( &record2 ), QString( "foo4" ) ); + QCOMPARE( f1.evaluate( &record2, &variables ), QString( "foo1" ) ); + QCOMPARE( f2.evaluate( &record2, &variables ), QString( "foo2" ) ); + QCOMPARE( f3.evaluate( &record2, &variables ), QString( "foo3" ) ); + QCOMPARE( f4.evaluate( &record2, &variables ), QString( "foo4" ) ); merge::Record record3; record3[ "1" ] = "xyzzy"; @@ -201,10 +227,10 @@ void TestSubstitutionField::defaultValueEvaluation() // Field "3" empty record3[ "4" ] = "plugh"; - QCOMPARE( f1.evaluate( &record3 ), QString( "xyzzy" ) ); - QCOMPARE( f2.evaluate( &record3 ), QString( "foo2" ) ); - QCOMPARE( f3.evaluate( &record3 ), QString( "foo3" ) ); - QCOMPARE( f4.evaluate( &record3 ), QString( "plugh" ) ); + QCOMPARE( f1.evaluate( &record3, &variables ), QString( "xyzzy" ) ); + QCOMPARE( f2.evaluate( &record3, &variables ), QString( "foo2" ) ); + QCOMPARE( f3.evaluate( &record3, &variables ), QString( "foo3" ) ); + QCOMPARE( f4.evaluate( &record3, &variables ), QString( "plugh" ) ); } @@ -212,6 +238,8 @@ void TestSubstitutionField::formattedStringEvaluation() { using namespace glabels; + model::Variables variables; + model::SubstitutionField f1( "${1:%10s}" ); model::SubstitutionField f2( "${2:%10s}" ); model::SubstitutionField f3( "${3:%10s}" ); @@ -233,15 +261,15 @@ void TestSubstitutionField::formattedStringEvaluation() record1[ "7" ] = "-100"; record1[ "8" ] = "3.14"; - QCOMPARE( f1.evaluate( &record1 ), QString( " 0" ) ); - QCOMPARE( f2.evaluate( &record1 ), QString( " 1" ) ); - QCOMPARE( f3.evaluate( &record1 ), QString( " -1" ) ); - QCOMPARE( f4.evaluate( &record1 ), QString( " 3.14" ) ); - - QCOMPARE( f5.evaluate( &record1 ), QString( "0 " ) ); - QCOMPARE( f6.evaluate( &record1 ), QString( "100 " ) ); - QCOMPARE( f7.evaluate( &record1 ), QString( "-100 " ) ); - QCOMPARE( f8.evaluate( &record1 ), QString( "3.14 " ) ); + QCOMPARE( f1.evaluate( &record1, &variables ), QString( " 0" ) ); + QCOMPARE( f2.evaluate( &record1, &variables ), QString( " 1" ) ); + QCOMPARE( f3.evaluate( &record1, &variables ), QString( " -1" ) ); + QCOMPARE( f4.evaluate( &record1, &variables ), QString( " 3.14" ) ); + + QCOMPARE( f5.evaluate( &record1, &variables ), QString( "0 " ) ); + QCOMPARE( f6.evaluate( &record1, &variables ), QString( "100 " ) ); + QCOMPARE( f7.evaluate( &record1, &variables ), QString( "-100 " ) ); + QCOMPARE( f8.evaluate( &record1, &variables ), QString( "3.14 " ) ); } @@ -249,6 +277,8 @@ void TestSubstitutionField::formattedFloatEvaluation() { using namespace glabels; + model::Variables variables; + model::SubstitutionField f1( "${1:%+5.2f}" ); model::SubstitutionField f2( "${2:%+5.2f}" ); model::SubstitutionField f3( "${3:%+5.2f}" ); @@ -270,15 +300,15 @@ void TestSubstitutionField::formattedFloatEvaluation() record1[ "7" ] = "-100"; record1[ "8" ] = "3.14"; - QCOMPARE( f1.evaluate( &record1 ), QString( "+0.00" ) ); - QCOMPARE( f2.evaluate( &record1 ), QString( "+1.00" ) ); - QCOMPARE( f3.evaluate( &record1 ), QString( "-1.00" ) ); - QCOMPARE( f4.evaluate( &record1 ), QString( "+3.14" ) ); - - QCOMPARE( f5.evaluate( &record1 ), QString( "+0.00e+00" ) ); - QCOMPARE( f6.evaluate( &record1 ), QString( "+1.00e+02" ) ); - QCOMPARE( f7.evaluate( &record1 ), QString( "-1.00e+02" ) ); - QCOMPARE( f8.evaluate( &record1 ), QString( "+3.14e+00" ) ); + QCOMPARE( f1.evaluate( &record1, &variables ), QString( "+0.00" ) ); + QCOMPARE( f2.evaluate( &record1, &variables ), QString( "+1.00" ) ); + QCOMPARE( f3.evaluate( &record1, &variables ), QString( "-1.00" ) ); + QCOMPARE( f4.evaluate( &record1, &variables ), QString( "+3.14" ) ); + + QCOMPARE( f5.evaluate( &record1, &variables ), QString( "+0.00e+00" ) ); + QCOMPARE( f6.evaluate( &record1, &variables ), QString( "+1.00e+02" ) ); + QCOMPARE( f7.evaluate( &record1, &variables ), QString( "-1.00e+02" ) ); + QCOMPARE( f8.evaluate( &record1, &variables ), QString( "+3.14e+00" ) ); } @@ -286,6 +316,8 @@ void TestSubstitutionField::formattedIntEvaluation() { using namespace glabels; + model::Variables variables; + model::SubstitutionField f1( "${1:%08d}" ); model::SubstitutionField f2( "${2:%08d}" ); model::SubstitutionField f3( "${3:%08d}" ); @@ -307,15 +339,15 @@ void TestSubstitutionField::formattedIntEvaluation() record1[ "7" ] = "-1"; record1[ "8" ] = "314"; - QCOMPARE( f1.evaluate( &record1 ), QString( "00000000" ) ); - QCOMPARE( f2.evaluate( &record1 ), QString( "00000001" ) ); - QCOMPARE( f3.evaluate( &record1 ), QString( "-0000001" ) ); - QCOMPARE( f4.evaluate( &record1 ), QString( "00000000" ) ); // Invalid integer value - - QCOMPARE( f5.evaluate( &record1 ), QString( "00000064" ) ); // 100(decimal) == 64(hex) - QCOMPARE( f6.evaluate( &record1 ), QString( "00000100" ) ); - QCOMPARE( f7.evaluate( &record1 ), QString( "00000000" ) ); // Invalid unsigned integer - QCOMPARE( f8.evaluate( &record1 ), QString( "0000013a" ) ); // 314(decimal) == 13a(hex) + QCOMPARE( f1.evaluate( &record1, &variables ), QString( "00000000" ) ); + QCOMPARE( f2.evaluate( &record1, &variables ), QString( "00000001" ) ); + QCOMPARE( f3.evaluate( &record1, &variables ), QString( "-0000001" ) ); + QCOMPARE( f4.evaluate( &record1, &variables ), QString( "00000000" ) ); // Invalid integer value + + QCOMPARE( f5.evaluate( &record1, &variables ), QString( "00000064" ) ); // 100(decimal) == 64(hex) + QCOMPARE( f6.evaluate( &record1, &variables ), QString( "00000100" ) ); + QCOMPARE( f7.evaluate( &record1, &variables ), QString( "00000000" ) ); // Invalid unsigned integer + QCOMPARE( f8.evaluate( &record1, &variables ), QString( "0000013a" ) ); // 314(decimal) == 13a(hex) } @@ -323,6 +355,8 @@ void TestSubstitutionField::newLineEvaluation() { using namespace glabels; + model::Variables variables; + model::SubstitutionField addr2( "${ADDR2:n}" ); QCOMPARE( addr2.fieldName(), QString( "ADDR2" ) ); QCOMPARE( addr2.newLine(), true ); @@ -336,7 +370,7 @@ void TestSubstitutionField::newLineEvaluation() merge::Record record3; // ADDR2 not defined - QCOMPARE( addr2.evaluate( &record1 ), QString( "\nApt. 5B" ) ); // Prepends a newline - QCOMPARE( addr2.evaluate( &record2 ), QString( "" ) ); // Evaluates empty - QCOMPARE( addr2.evaluate( &record3 ), QString( "" ) ); // Evaluates empty + QCOMPARE( addr2.evaluate( &record1, &variables ), QString( "\nApt. 5B" ) ); // Prepends a newline + QCOMPARE( addr2.evaluate( &record2, &variables ), QString( "" ) ); // Evaluates empty + QCOMPARE( addr2.evaluate( &record3, &variables ), QString( "" ) ); // Evaluates empty } diff --git a/model/unit_tests/TestTextNode.cpp b/model/unit_tests/TestTextNode.cpp new file mode 100644 index 00000000..19b25326 --- /dev/null +++ b/model/unit_tests/TestTextNode.cpp @@ -0,0 +1,124 @@ +/* TestTextNode.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestTextNode.h" + +#include "model/TextNode.h" + +#include + + +QTEST_MAIN(TestTextNode) + +using namespace glabels::model; +using namespace glabels::merge; + + +void TestTextNode::textNode() +{ + Record record; + Variables vars; + + TextNode textNode; + QVERIFY( !textNode.isField() ); + QCOMPARE( textNode.data(), QString( "" ) ); + QVERIFY( textNode == TextNode() ); + QVERIFY( !(textNode != TextNode()) ); + QCOMPARE( textNode.text( nullptr, nullptr ), QString( "" ) ); + QCOMPARE( textNode.text( &record, nullptr ), QString( "" ) ); + QCOMPARE( textNode.text( nullptr, &vars ), QString( "" ) ); + QCOMPARE( textNode.text( &record, &vars ), QString( "" ) ); + + textNode.setField( true ); + QVERIFY( textNode.isField() ); + QCOMPARE( textNode.text( &record, nullptr ), QString( "" ) ); + + textNode.setField( false ); + QVERIFY( !textNode.isField() ); + + textNode.setData( QString( "data1" ) ); + QCOMPARE( textNode.data(), QString( "data1" ) ); + QCOMPARE( textNode.text( nullptr, nullptr ), QString( "data1" ) ); + QCOMPARE( textNode.text( &record, nullptr ), QString( "data1" ) ); + QCOMPARE( textNode.text( nullptr, &vars ), QString( "data1" ) ); + QCOMPARE( textNode.text( &record, &vars ), QString( "data1" ) ); + + textNode.setField( true ); + QCOMPARE( textNode.text( nullptr, nullptr ), QString( "" ) ); + QCOMPARE( textNode.text( &record, nullptr ), QString( "" ) ); + QCOMPARE( textNode.text( nullptr, &vars ), QString( "" ) ); + QCOMPARE( textNode.text( &record, &vars ), QString( "" ) ); + + /// + /// Constructors + /// + TextNode textNode2( true, "data2" ); + QVERIFY( textNode2.isField() ); + QCOMPARE( textNode2.data(), QString( "data2" ) ); + textNode.setField( false ); + QVERIFY( !(textNode2 == textNode) ); + QVERIFY( textNode2 != textNode ); + textNode.setField( true ); + QVERIFY( !(textNode2 == textNode) ); + QVERIFY( textNode2 != textNode ); + textNode.setData( QString( "data2" ) ); + QVERIFY( textNode2 == textNode ); + QVERIFY( !(textNode2 != textNode) ); + + /// + /// Record + /// + record["key1"] = ""; + QCOMPARE( textNode.text( &record, nullptr ), QString( "" ) ); + + textNode.setData( QString( "key1" ) ); + QCOMPARE( textNode.text( &record, nullptr ), QString( "" ) ); + + record["key1"] = "val1"; + QCOMPARE( textNode.text( &record, nullptr ), QString( "val1" ) ); + + /// + /// Variable + /// + { + Variable key1( Variable::Type::STRING, "key1", "", Variable::Increment::PER_ITEM ); + vars.addVariable( key1 ); + } + QCOMPARE( textNode.text( nullptr, &vars ), QString( "" ) ); + + { + Variable key1( Variable::Type::STRING, "key1", "val1", Variable::Increment::PER_ITEM ); + vars.addVariable( key1 ); + } + QCOMPARE( textNode.text( nullptr, &vars ), QString( "val1" ) ); + + { + Variable key1( Variable::Type::INTEGER, "key1", "1", Variable::Increment::PER_ITEM, "1" ); + vars.addVariable( key1 ); + } + QCOMPARE( textNode.text( nullptr, &vars ), QString( "1" ) ); + vars.incrementVariablesOnItem(); + QCOMPARE( textNode.text( nullptr, &vars ), QString( "2" ) ); + + /// + /// Record beats variable + /// + QCOMPARE( textNode.text( &record, &vars ), QString( "val1" ) ); +} diff --git a/model/unit_tests/TestTextNode.h b/model/unit_tests/TestTextNode.h new file mode 100644 index 00000000..76553d36 --- /dev/null +++ b/model/unit_tests/TestTextNode.h @@ -0,0 +1,30 @@ +/* TestTextNode.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestTextNode : public QObject +{ + Q_OBJECT + +private slots: + void textNode(); +}; diff --git a/model/unit_tests/TestVariable.cpp b/model/unit_tests/TestVariable.cpp new file mode 100644 index 00000000..85efadcd --- /dev/null +++ b/model/unit_tests/TestVariable.cpp @@ -0,0 +1,300 @@ +/* TestVariable.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestVariable.h" + +#include "model/Variable.h" + +#include + + +QTEST_MAIN(TestVariable) + +using namespace glabels::model; + + +void TestVariable::variable() +{ + { + Variable var; + + QCOMPARE( var.type(), Variable::Type::STRING ); + QCOMPARE( var.name(), QString() ); + QCOMPARE( var.initialValue(), QString() ); + QCOMPARE( var.increment(), Variable::Increment::NEVER ); + QCOMPARE( var.stepSize(), QString( "0" ) ); + QCOMPARE( var.value(), QString() ); + + var.resetValue(); + QCOMPARE( var.type(), Variable::Type::STRING ); + QCOMPARE( var.name(), QString() ); + QCOMPARE( var.initialValue(), QString() ); + QCOMPARE( var.increment(), Variable::Increment::NEVER ); + QCOMPARE( var.stepSize(), QString( "0" ) ); + QCOMPARE( var.value(), QString() ); + + var.incrementValueOnItem(); + QCOMPARE( var.value(), QString() ); + + var.incrementValueOnCopy(); + QCOMPARE( var.value(), QString() ); + + var.incrementValueOnPage(); + QCOMPARE( var.value(), QString() ); + } + { + Variable var( Variable::Type::STRING, "s", "initial", Variable::Increment::PER_ITEM, "2" ); + + QCOMPARE( var.type(), Variable::Type::STRING ); + QCOMPARE( var.name(), QString( "s" ) ); + QCOMPARE( var.initialValue(), QString( "initial" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_ITEM ); + QCOMPARE( var.stepSize(), QString( "2" ) ); + QCOMPARE( var.value(), QString( "initial" ) ); + + var.resetValue(); + QCOMPARE( var.name(), QString( "s" ) ); + QCOMPARE( var.initialValue(), QString( "initial" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_ITEM ); + QCOMPARE( var.stepSize(), QString( "2" ) ); + QCOMPARE( var.value(), QString( "initial" ) ); + + var.incrementValueOnItem(); + QCOMPARE( var.value(), QString( "initial" ) ); + + var.incrementValueOnCopy(); + QCOMPARE( var.value(), QString( "initial" ) ); + + var.incrementValueOnPage(); + QCOMPARE( var.value(), QString( "initial" ) ); + + var.resetValue(); + QCOMPARE( var.name(), QString( "s" ) ); + QCOMPARE( var.initialValue(), QString( "initial" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_ITEM ); + QCOMPARE( var.stepSize(), QString( "2" ) ); + QCOMPARE( var.value(), QString( "initial" ) ); + } + { + Variable var( Variable::Type::INTEGER, "i", "123", Variable::Increment::PER_ITEM, "1" ); + + QCOMPARE( var.type(), Variable::Type::INTEGER ); + QCOMPARE( var.name(), QString( "i" ) ); + QCOMPARE( var.initialValue(), QString( "123" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_ITEM ); + QCOMPARE( var.stepSize(), QString( "1" ) ); + QCOMPARE( var.value(), QString( "123" ) ); + + var.resetValue(); + QCOMPARE( var.type(), Variable::Type::INTEGER ); + QCOMPARE( var.name(), QString( "i" ) ); + QCOMPARE( var.initialValue(), QString( "123" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_ITEM ); + QCOMPARE( var.stepSize(), QString( "1" ) ); + QCOMPARE( var.value(), QString( "123" ) ); + + var.incrementValueOnItem(); + QCOMPARE( var.value(), QString( "124" ) ); + + var.incrementValueOnCopy(); + QCOMPARE( var.value(), QString( "124" ) ); + + var.incrementValueOnPage(); + QCOMPARE( var.value(), QString( "124" ) ); + + var.incrementValueOnItem(); + QCOMPARE( var.value(), QString( "125" ) ); + + var.incrementValueOnCopy(); + QCOMPARE( var.value(), QString( "125" ) ); + + var.incrementValueOnPage(); + QCOMPARE( var.value(), QString( "125" ) ); + + var.resetValue(); + QCOMPARE( var.type(), Variable::Type::INTEGER ); + QCOMPARE( var.name(), QString( "i" ) ); + QCOMPARE( var.initialValue(), QString( "123" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_ITEM ); + QCOMPARE( var.stepSize(), QString( "1" ) ); + QCOMPARE( var.value(), QString( "123" ) ); + } + { + Variable var( Variable::Type::INTEGER, "i", "1", Variable::Increment::PER_PAGE, "2" ); + + QCOMPARE( var.type(), Variable::Type::INTEGER ); + QCOMPARE( var.name(), QString( "i" ) ); + QCOMPARE( var.initialValue(), QString( "1" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_PAGE ); + QCOMPARE( var.stepSize(), QString( "2" ) ); + QCOMPARE( var.value(), QString( "1" ) ); + + var.resetValue(); + QCOMPARE( var.type(), Variable::Type::INTEGER ); + QCOMPARE( var.name(), QString( "i" ) ); + QCOMPARE( var.initialValue(), QString( "1" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_PAGE ); + QCOMPARE( var.stepSize(), QString( "2" ) ); + QCOMPARE( var.value(), QString( "1" ) ); + + var.incrementValueOnItem(); + QCOMPARE( var.value(), QString( "1" ) ); + + var.incrementValueOnCopy(); + QCOMPARE( var.value(), QString( "1" ) ); + + var.incrementValueOnPage(); + QCOMPARE( var.value(), QString( "3" ) ); + + var.incrementValueOnItem(); + QCOMPARE( var.value(), QString( "3" ) ); + + var.incrementValueOnCopy(); + QCOMPARE( var.value(), QString( "3" ) ); + + var.incrementValueOnPage(); + QCOMPARE( var.value(), QString( "5" ) ); + + var.resetValue(); + QCOMPARE( var.type(), Variable::Type::INTEGER ); + QCOMPARE( var.name(), QString( "i" ) ); + QCOMPARE( var.initialValue(), QString( "1" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_PAGE ); + QCOMPARE( var.stepSize(), QString( "2" ) ); + QCOMPARE( var.value(), QString( "1" ) ); + } + { + Variable var( Variable::Type::FLOATING_POINT, "f", "1.2", Variable::Increment::PER_COPY, "0.2" ); + + QCOMPARE( var.type(), Variable::Type::FLOATING_POINT ); + QCOMPARE( var.name(), QString( "f" ) ); + QCOMPARE( var.initialValue(), QString( "1.2" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_COPY ); + QCOMPARE( var.stepSize(), QString( "0.2" ) ); + QCOMPARE( var.value(), QString( "1.2" ) ); + + var.resetValue(); + QCOMPARE( var.type(), Variable::Type::FLOATING_POINT ); + QCOMPARE( var.name(), QString( "f" ) ); + QCOMPARE( var.initialValue(), QString( "1.2" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_COPY ); + QCOMPARE( var.stepSize(), QString( "0.2" ) ); + QCOMPARE( var.value(), QString( "1.2" ) ); + + var.incrementValueOnItem(); + QCOMPARE( var.value(), QString( "1.2" ) ); + + var.incrementValueOnCopy(); + QCOMPARE( var.value(), QString( "1.4" ) ); + + var.incrementValueOnPage(); + QCOMPARE( var.value(), QString( "1.4" ) ); + + var.incrementValueOnItem(); + QCOMPARE( var.value(), QString( "1.4" ) ); + + var.incrementValueOnCopy(); + QCOMPARE( var.value(), QString( "1.6" ) ); + + var.incrementValueOnPage(); + QCOMPARE( var.value(), QString( "1.6" ) ); + + var.resetValue(); + QCOMPARE( var.type(), Variable::Type::FLOATING_POINT ); + QCOMPARE( var.name(), QString( "f" ) ); + QCOMPARE( var.initialValue(), QString( "1.2" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_COPY ); + QCOMPARE( var.stepSize(), QString( "0.2" ) ); + QCOMPARE( var.value(), QString( "1.2" ) ); + } + { + Variable var( Variable::Type::COLOR, "c", "white", Variable::Increment::PER_PAGE ); + + QCOMPARE( var.type(), Variable::Type::COLOR ); + QCOMPARE( var.name(), QString( "c" ) ); + QCOMPARE( var.initialValue(), QString( "white" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_PAGE ); + QCOMPARE( var.stepSize(), QString( "0" ) ); + QCOMPARE( var.value(), QString( "white" ) ); + + var.resetValue(); + QCOMPARE( var.name(), QString( "c" ) ); + QCOMPARE( var.initialValue(), QString( "white" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_PAGE ); + QCOMPARE( var.stepSize(), QString( "0" ) ); + QCOMPARE( var.value(), QString( "white" ) ); + + var.incrementValueOnItem(); + QCOMPARE( var.value(), QString( "white" ) ); + + var.incrementValueOnCopy(); + QCOMPARE( var.value(), QString( "white" ) ); + + var.incrementValueOnPage(); + QCOMPARE( var.value(), QString( "white" ) ); + + var.resetValue(); + QCOMPARE( var.name(), QString( "c" ) ); + QCOMPARE( var.initialValue(), QString( "white" ) ); + QCOMPARE( var.increment(), Variable::Increment::PER_PAGE ); + QCOMPARE( var.stepSize(), QString( "0" ) ); + QCOMPARE( var.value(), QString( "white" ) ); + } +} + + +void TestVariable::statics() +{ + QCOMPARE( Variable::typeToI18nString( Variable::Type::STRING ), QString( "String" ) ); + QCOMPARE( Variable::typeToI18nString( Variable::Type::INTEGER ), QString( "Integer" ) ); + QCOMPARE( Variable::typeToI18nString( Variable::Type::FLOATING_POINT ), QString( "Floating Point" ) ); + QCOMPARE( Variable::typeToI18nString( Variable::Type::COLOR ), QString( "Color" ) ); + QCOMPARE( Variable::typeToI18nString( (Variable::Type)4 ), QString( "String" ) ); + + QCOMPARE( Variable::typeToIdString( Variable::Type::STRING ), QString( "string" ) ); + QCOMPARE( Variable::typeToIdString( Variable::Type::INTEGER ), QString( "integer" ) ); + QCOMPARE( Variable::typeToIdString( Variable::Type::FLOATING_POINT ), QString( "float" ) ); + QCOMPARE( Variable::typeToIdString( Variable::Type::COLOR ), QString( "color" ) ); + QCOMPARE( Variable::typeToIdString( (Variable::Type)4 ), QString( "string" ) ); + + QCOMPARE( Variable::idStringToType( "string" ), Variable::Type::STRING ); + QCOMPARE( Variable::idStringToType( "integer"), Variable::Type::INTEGER ); + QCOMPARE( Variable::idStringToType( "float" ), Variable::Type::FLOATING_POINT ); + QCOMPARE( Variable::idStringToType( "color" ), Variable::Type::COLOR ); + QCOMPARE( Variable::idStringToType( "non_existent" ), Variable::Type::STRING ); + + QCOMPARE( Variable::incrementToI18nString( Variable::Increment::NEVER ), QString( "Never" ) ); + QCOMPARE( Variable::incrementToI18nString( Variable::Increment::PER_ITEM ), QString( "Per item" ) ); + QCOMPARE( Variable::incrementToI18nString( Variable::Increment::PER_COPY ), QString( "Per copy" ) ); + QCOMPARE( Variable::incrementToI18nString( Variable::Increment::PER_PAGE ), QString( "Per page" ) ); + QCOMPARE( Variable::incrementToI18nString( (Variable::Increment)4 ), QString( "Never" ) ); + + QCOMPARE( Variable::incrementToIdString( Variable::Increment::NEVER ), QString( "never" ) ); + QCOMPARE( Variable::incrementToIdString( Variable::Increment::PER_ITEM ), QString( "per_item" ) ); + QCOMPARE( Variable::incrementToIdString( Variable::Increment::PER_COPY ), QString( "per_copy" ) ); + QCOMPARE( Variable::incrementToIdString( Variable::Increment::PER_PAGE ), QString( "per_page" ) ); + QCOMPARE( Variable::incrementToIdString( (Variable::Increment)4 ), QString( "never" ) ); + + QCOMPARE( Variable::idStringToIncrement( "never" ), Variable::Increment::NEVER ); + QCOMPARE( Variable::idStringToIncrement( "per_item" ), Variable::Increment::PER_ITEM ); + QCOMPARE( Variable::idStringToIncrement( "per_copy" ), Variable::Increment::PER_COPY ); + QCOMPARE( Variable::idStringToIncrement( "per_page" ), Variable::Increment::PER_PAGE ); + QCOMPARE( Variable::idStringToIncrement( "non_existent" ), Variable::Increment::NEVER ); +} diff --git a/model/unit_tests/TestVariable.h b/model/unit_tests/TestVariable.h new file mode 100644 index 00000000..9cad55d1 --- /dev/null +++ b/model/unit_tests/TestVariable.h @@ -0,0 +1,31 @@ +/* TestVariable.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestVariable : public QObject +{ + Q_OBJECT + +private slots: + void variable(); + void statics(); +}; diff --git a/model/unit_tests/TestVariables.cpp b/model/unit_tests/TestVariables.cpp new file mode 100644 index 00000000..c7f6ce45 --- /dev/null +++ b/model/unit_tests/TestVariables.cpp @@ -0,0 +1,157 @@ +/* TestVariables.cpp + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestVariables.h" + +#include "model/Variables.h" + +#include + + +QTEST_MAIN(TestVariables) + +using namespace glabels::model; + + +void TestVariables::variables() +{ + Variables vars; + + Variable i( Variable::Type::INTEGER, "i", "3", Variable::Increment::PER_ITEM, "3" ); + QCOMPARE( i.value(), QString( "3" ) ); + + Variable i2( Variable::Type::INTEGER, "i2", "100", Variable::Increment::PER_COPY, "2" ); + QCOMPARE( i2.value(), QString( "100" ) ); + + Variable f( Variable::Type::FLOATING_POINT, "f", "0.0", Variable::Increment::PER_PAGE, "0.1" ); + QCOMPARE( f.value(), QString( "0" ) ); + + Variable s( Variable::Type::STRING, "s", "initial", Variable::Increment::PER_ITEM, "1" ); + QCOMPARE( s.value(), QString( "initial" ) ); + + Variable c( Variable::Type::COLOR, "c", "white", Variable::Increment::PER_ITEM, "01" ); + QCOMPARE( c.value(), QString( "white" ) ); + + QVERIFY( !vars.hasVariable( "i" ) ); + QVERIFY( !vars.hasVariable( "i2" ) ); + + // Add, delete + vars.addVariable( i ); + QVERIFY( vars.hasVariable( "i" ) ); + QCOMPARE( vars["i"].value(), i.value() ); + vars.deleteVariable( "i" ); + QVERIFY( !vars.hasVariable( "i" ) ); + QCOMPARE( vars["i"].value(), QString() ); + + // Add, replace + vars.addVariable( i ); + QVERIFY( vars.hasVariable( "i" ) ); + QCOMPARE( vars["i"].value(), i.value() ); + vars.replaceVariable( "i", i2 ); + QVERIFY( !vars.hasVariable( "i" ) ); + QVERIFY( vars.hasVariable( "i2" ) ); + QCOMPARE( vars["i2"].value(), i2.value() ); + QCOMPARE( vars["i"].value(), QString() ); + vars.deleteVariable( "i2" ); + QVERIFY( !vars.hasVariable( "i2" ) ); + + // Increment + vars.addVariable( i ); + vars.addVariable( i2 ); + vars.addVariable( f ); + vars.addVariable( s ); + vars.addVariable( c ); + + QVERIFY( vars.hasVariable( "i" ) ); // PER_ITEM + QVERIFY( vars.hasVariable( "i2" ) ); // PER_COPY + QVERIFY( vars.hasVariable( "f" ) ); // PER_PAGE + QVERIFY( vars.hasVariable( "s" ) ); + QVERIFY( vars.hasVariable( "c" ) ); + + QCOMPARE( vars["i"].value(), QString( "3" ) ); + QCOMPARE( vars["i2"].value(), QString( "100" ) ); // PRE_COPY + QCOMPARE( vars["f"].value(), QString( "0" ) ); // PER_PAGE + QCOMPARE( vars["s"].value(), QString( "initial" ) ); + QCOMPARE( vars["c"].value(), QString( "white" ) ); + + vars.resetVariables(); + + QCOMPARE( vars["i"].value(), QString( "3" ) ); // PER_ITEM + QCOMPARE( vars["i2"].value(), QString( "100" ) ); // PRE_COPY + QCOMPARE( vars["f"].value(), QString( "0" ) ); // PER_PAGE + QCOMPARE( vars["s"].value(), QString( "initial" ) ); + QCOMPARE( vars["c"].value(), QString( "white" ) ); + + vars.incrementVariablesOnItem(); + + QCOMPARE( vars["i"].value(), QString( "6" ) ); // PER_ITEM + QCOMPARE( vars["i2"].value(), QString( "100" ) ); // PER_COPY + QCOMPARE( vars["f"].value(), QString( "0" ) ); // PER_PAGE + QCOMPARE( vars["s"].value(), QString( "initial" ) ); + QCOMPARE( vars["c"].value(), QString( "white" ) ); + + vars.incrementVariablesOnItem(); + + QCOMPARE( vars["i"].value(), QString( "9" ) ); // PER_ITEM + QCOMPARE( vars["i2"].value(), QString( "100" ) ); // PER_COPY + QCOMPARE( vars["f"].value(), QString( "0" ) ); // PER_PAGE + QCOMPARE( vars["s"].value(), QString( "initial" ) ); + QCOMPARE( vars["c"].value(), QString( "white" ) ); + + vars.incrementVariablesOnCopy(); + + QCOMPARE( vars["i"].value(), QString( "9" ) ); // PER_ITEM + QCOMPARE( vars["i2"].value(), QString( "102" ) ); // PER_COPY + QCOMPARE( vars["f"].value(), QString( "0" ) ); // PER_PAGE + QCOMPARE( vars["s"].value(), QString( "initial" ) ); + QCOMPARE( vars["c"].value(), QString( "white" ) ); + + vars.incrementVariablesOnCopy(); + + QCOMPARE( vars["i"].value(), QString( "9" ) ); // PER_ITEM + QCOMPARE( vars["i2"].value(), QString( "104" ) ); // PER_COPY + QCOMPARE( vars["f"].value(), QString( "0" ) ); // PER_PAGE + QCOMPARE( vars["s"].value(), QString( "initial" ) ); + QCOMPARE( vars["c"].value(), QString( "white" ) ); + + vars.incrementVariablesOnPage(); + + QCOMPARE( vars["i"].value(), QString( "9" ) ); // PER_ITEM + QCOMPARE( vars["i2"].value(), QString( "104" ) ); // PER_COPY + QCOMPARE( vars["f"].value(), QString( "0.1" ) ); // PER_PAGE + QCOMPARE( vars["s"].value(), QString( "initial" ) ); + QCOMPARE( vars["c"].value(), QString( "white" ) ); + + vars.incrementVariablesOnPage(); + + QCOMPARE( vars["i"].value(), QString( "9" ) ); // PER_ITEM + QCOMPARE( vars["i2"].value(), QString( "104" ) ); // PER_COPY + QCOMPARE( vars["f"].value(), QString( "0.2" ) ); // PER_PAGE + QCOMPARE( vars["s"].value(), QString( "initial" ) ); + QCOMPARE( vars["c"].value(), QString( "white" ) ); + + vars.resetVariables(); + + QCOMPARE( vars["i"].value(), QString( "3" ) ); // PER_ITEM + QCOMPARE( vars["i2"].value(), QString( "100" ) ); // PRE_COPY + QCOMPARE( vars["f"].value(), QString( "0" ) ); // PER_PAGE + QCOMPARE( vars["s"].value(), QString( "initial" ) ); + QCOMPARE( vars["c"].value(), QString( "white" ) ); +} diff --git a/model/unit_tests/TestVariables.h b/model/unit_tests/TestVariables.h new file mode 100644 index 00000000..93b56b42 --- /dev/null +++ b/model/unit_tests/TestVariables.h @@ -0,0 +1,30 @@ +/* TestVariables.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestVariables : public QObject +{ + Q_OBJECT + +private slots: + void variables(); +}; diff --git a/model/unit_tests/TestXmlLabel.cpp b/model/unit_tests/TestXmlLabel.cpp new file mode 100644 index 00000000..e70221fc --- /dev/null +++ b/model/unit_tests/TestXmlLabel.cpp @@ -0,0 +1,703 @@ +/* TestXmlLabel.cpp + * + * Copyright (C) 2018 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestXmlLabel.h" +#include "Test_Constants.h" + +#include "model/XmlLabelCreator.h" +#include "model/XmlLabelParser.h" + +#include "barcode/Backends.h" +#include "model/ColorNode.h" +#include "model/FrameRect.h" +#include "model/Markup.h" +#include "model/Model.h" +#include "model/PageRenderer.h" +#include "model/Size.h" + +#include "model/ModelBarcodeObject.h" +#include "model/ModelBoxObject.h" +#include "model/ModelEllipseObject.h" +#include "model/ModelLineObject.h" +#include "model/ModelImageObject.h" +#include "model/ModelTextObject.h" + +#include "model/Db.h" +#include "merge/Factory.h" +#include "merge/Merge.h" +#include "merge/TextCsvKeys.h" + +#include + + +QTEST_MAIN(TestXmlLabel) + +using namespace glabels::model; +using namespace glabels::barcode; +using namespace glabels::merge; + + +namespace +{ + const double FONT_SCALE_FACTOR {0.75}; +} + + +void TestXmlLabel::initTestCase() +{ + Settings::init(); + Db::init(); + Factory::init(); + Backends::init(); +} + + +void TestXmlLabel::serializeDeserialize() +{ + Model* model = new Model(); + + QList objects, outObjects; + QByteArray buffer, outBuffer; + + // + // Empty object list + // + QCOMPARE( objects.count(), 0 ); + XmlLabelCreator::serializeObjects( objects, model, buffer ); + outObjects = XmlLabelParser::deserializeObjects( buffer, model ); + QCOMPARE( objects.count(), outObjects.count() ); + QCOMPARE( objects, outObjects ); + XmlLabelCreator::serializeObjects( outObjects, model, outBuffer ); + QCOMPARE( buffer, outBuffer ); + + // + // All objects list + // + QImage png; + QVERIFY( png.loadFromData( QByteArray::fromBase64( glabels::test::blue_8x8_png ), "PNG" ) ); + + QString svgTemplate = QDir::tempPath().append( "/TestXmlLabel_XXXXXX.svg" ); // Note: directory separators canonicalized to slash by Qt path methods + QTemporaryFile svgRelative( svgTemplate ); svgRelative.open(); svgRelative.write( glabels::test::cyan_8x8_svg ); svgRelative.close(); + + bool lock = true, noLock = false, shadow = true, noShadow = false; + ColorNode black( Qt::black ), white( Qt::white ), red( Qt::red ), green( Qt::green ), blue( Qt::blue ); + QMatrix tMatrix( 1, 0, 0, 1, 50.0, 50.0 ), sMatrix( 0.5, 0, 0, 1.0, 0, 0 ); + Style bcStyle = Backends::defaultStyle(); + + objects << new ModelBoxObject( 0, 1, 10, 20, lock, 2, red, green, tMatrix, shadow, 1, 2, 0.7, black ); + objects << new ModelEllipseObject( 1, 2, 30, 40, noLock, 3, black, white, sMatrix, shadow, 2, 3, 0.8, blue ); + objects << new ModelImageObject( 2, 3, 50, 50, lock, TextNode( false, "" ), tMatrix, noShadow, 3, 4, 0.9, white ); + objects << new ModelImageObject( 3, 4, 60, 70, noLock, "image3.png", png, sMatrix, shadow, 6, 4, 0.9, black ); + objects << new ModelImageObject( 4, 5, 70, 80, lock, "image4.svg", glabels::test::red_8x8_svg ); + objects << new ModelImageObject( 5, 6, 80, 90, noLock, TextNode( true, "${key}" ), tMatrix, shadow ); + objects << new ModelImageObject( 6, 7, 90, 100, lock, TextNode( false, "image6.jpg" ) ); // Will give warning on parse that embedded file missing + objects << new ModelLineObject( 7, 8, 100, 110, 4, green, sMatrix, shadow, 5, 5, 0.5, red ); + objects << new ModelTextObject( 8, 9, 110, 120, lock, "text", "Serif", 12, QFont::Bold, true, true, red, + Qt::AlignHCenter, Qt::AlignBottom, QTextOption::NoWrap, 1.3, false, sMatrix, shadow, 5, 5, 0.5, red ); + objects << new ModelBarcodeObject( 9, 10, 50, 50, noLock, bcStyle, true, true, QString("1234"), black, tMatrix ); + objects << new ModelImageObject( 10, 11, 8, 8, lock, TextNode( false, svgRelative.fileName() ) ); + + QCOMPARE( objects.count(), 11 ); + + buffer.clear(); + XmlLabelCreator::serializeObjects( objects, model, buffer ); + + QVERIFY( svgRelative.remove() ); // Delete to make sure it's not read from file on parse + + QTest::ignoreMessage( QtWarningMsg, QRegularExpression( "^Embedded file \"[^\"]+image6.jpg\" missing\\. Trying actual file\\.$" ) ); + outObjects = XmlLabelParser::deserializeObjects( buffer, model ); + QCOMPARE( objects.count(), outObjects.count() ); + + QString modelDirPath = model->dir().path() + "/"; + + for ( int i = 0; i < objects.count(); i++ ) + { + qDebug() << "object" << i; + QVERIFY( objects.at(i)->id() != outObjects.at(i)->id() ); // Ids are generated and unique + QCOMPARE( objects.at(i)->x0(), outObjects.at(i)->x0() ); + QCOMPARE( objects.at(i)->x0().pt(), (double)i ); + QCOMPARE( objects.at(i)->y0(), outObjects.at(i)->y0() ); + QCOMPARE( objects.at(i)->y0().pt(), (double)(i + 1) ); + QCOMPARE( objects.at(i)->w().pt(), outObjects.at(i)->w().pt() ); // Use `pt()` so invoke `qFuzzyCompare(double, double)` otherwise get rounding difference for Barcode + QCOMPARE( objects.at(i)->h().pt(), outObjects.at(i)->h().pt() ); // Fuzzy + QCOMPARE( objects.at(i)->lockAspectRatio(), outObjects.at(i)->lockAspectRatio() ); + QCOMPARE( objects.at(i)->lockAspectRatio(), (bool)((i + 1) % 2) ); + QCOMPARE( objects.at(i)->matrix(), outObjects.at(i)->matrix() ); + QCOMPARE( objects.at(i)->shadow(), outObjects.at(i)->shadow() ); + QCOMPARE( objects.at(i)->shadowX(), outObjects.at(i)->shadowX() ); + QCOMPARE( objects.at(i)->shadowY(), outObjects.at(i)->shadowY() ); + QCOMPARE( objects.at(i)->shadowOpacity(), outObjects.at(i)->shadowOpacity() ); + QVERIFY( objects.at(i)->shadowColorNode() == outObjects.at(i)->shadowColorNode() ); + QCOMPARE( objects.at(i)->naturalSize().w().pt(), outObjects.at(i)->naturalSize().w().pt() ); // Fuzzy + QCOMPARE( objects.at(i)->naturalSize().h().pt(), outObjects.at(i)->naturalSize().h().pt() ); // Fuzzy + + QCOMPARE( objects.at(i)->text(), outObjects.at(i)->text() ); + QCOMPARE( objects.at(i)->fontFamily(), outObjects.at(i)->fontFamily() ); + QCOMPARE( objects.at(i)->fontSize(), outObjects.at(i)->fontSize() ); + QCOMPARE( objects.at(i)->fontWeight(), outObjects.at(i)->fontWeight() ); + QCOMPARE( objects.at(i)->fontItalicFlag(), outObjects.at(i)->fontItalicFlag() ); + QCOMPARE( objects.at(i)->fontUnderlineFlag(), outObjects.at(i)->fontUnderlineFlag() ); + QVERIFY( objects.at(i)->textColorNode() == outObjects.at(i)->textColorNode() ); + QCOMPARE( objects.at(i)->textHAlign(), outObjects.at(i)->textHAlign() ); + QCOMPARE( objects.at(i)->textVAlign(), outObjects.at(i)->textVAlign() ); + QCOMPARE( objects.at(i)->textWrapMode(), outObjects.at(i)->textWrapMode() ); + QCOMPARE( objects.at(i)->textLineSpacing(), outObjects.at(i)->textLineSpacing() ); + QCOMPARE( objects.at(i)->textAutoShrink(), outObjects.at(i)->textAutoShrink() ); + + QCOMPARE( objects.at(i)->filenameNode().isField(), outObjects.at(i)->filenameNode().isField() ); + if ( i == 6 /*image6.jpg*/ ) + { + // Not in data so absolute path set + QCOMPARE( modelDirPath + objects.at(i)->filenameNode().data(), outObjects.at(i)->filenameNode().data() ); + } + else + { + QCOMPARE( objects.at(i)->filenameNode().data(), outObjects.at(i)->filenameNode().data() ); + } + + if ( objects.at(i)->image() ) + { + QCOMPARE( *(objects.at(i)->image()), *(outObjects.at(i)->image()) ); + } + else + { + QCOMPARE( objects.at(i)->image(), outObjects.at(i)->image() ); + } + QCOMPARE( objects.at(i)->svg(), outObjects.at(i)->svg() ); + + QCOMPARE( objects.at(i)->lineWidth(), outObjects.at(i)->lineWidth() ); + QVERIFY( objects.at(i)->lineColorNode() == outObjects.at(i)->lineColorNode() ); + QVERIFY( objects.at(i)->fillColorNode() == outObjects.at(i)->fillColorNode() ); + + QCOMPARE( objects.at(i)->bcData(), outObjects.at(i)->bcData() ); + QCOMPARE( objects.at(i)->bcTextFlag(), outObjects.at(i)->bcTextFlag() ); + QCOMPARE( objects.at(i)->bcChecksumFlag(), outObjects.at(i)->bcChecksumFlag() ); + QVERIFY( objects.at(i)->bcColorNode() == outObjects.at(i)->bcColorNode() ); + QVERIFY( !( objects.at(i)->bcStyle() != outObjects.at(i)->bcStyle() ) ); // Only != operator + QCOMPARE( objects.at(i)->bcFormatDigits(), outObjects.at(i)->bcFormatDigits() ); + + QCOMPARE( objects.at(i)->canText(), outObjects.at(i)->canText() ); + QCOMPARE( objects.at(i)->canFill(), outObjects.at(i)->canFill() ); + QCOMPARE( objects.at(i)->canLineColor(), outObjects.at(i)->canLineColor() ); + QCOMPARE( objects.at(i)->canLineWidth(), outObjects.at(i)->canLineWidth() ); + } + + outBuffer.clear(); + XmlLabelCreator::serializeObjects( outObjects, model, outBuffer ); + + QCOMPARE( buffer, outBuffer ); + + delete model->merge(); + delete model->variables(); + delete model; +} + + +void TestXmlLabel::writeReadFile() +{ + Model* model = new Model(); + + // Make subdir in temp dir to use as model dir + QTemporaryDir subDir; + QVERIFY( subDir.isValid() ); + + QString glabelsTemplate = subDir.path().append( "/TestXmlLabel_XXXXXX.glabels" ); // Note: directory separators canonicalized to slash by Qt path methods + QTemporaryFile glabels( glabelsTemplate ); + glabels.open(); glabels.close(); + + model->setFileName( glabels.fileName() ); + + // Make subdir in subdir + QString subSubTemplate = subDir.path().append( "/TestXmlLabel_XXXXXX" ); + QTemporaryDir subSubDir( subSubTemplate ); + QVERIFY( subSubDir.isValid() ); + + QString relPath = model->dir().relativeFilePath( subSubDir.path() ); + QVERIFY( !relPath.contains( '/' ) ); // Make sure subdir of model dir + + QImage png; + QVERIFY( png.loadFromData( QByteArray::fromBase64( glabels::test::blue_8x8_png ), "PNG" ) ); + + // Make png file in temp dir (ie not in model dir) + QImage pngAbsoluteImage; + QVERIFY( pngAbsoluteImage.loadFromData( QByteArray::fromBase64( glabels::test::green_8x8_png ), "PNG" ) ); + QTemporaryFile pngAbsolute; pngAbsolute.open(); pngAbsolute.close(); pngAbsoluteImage.save( pngAbsolute.fileName(), "PNG" ); + + // Make png file in model dir + QImage pngRelativeImage; + QVERIFY( pngRelativeImage.loadFromData( QByteArray::fromBase64( glabels::test::yellow_8x8_png ), "PNG" ) ); + QString pngTemplate = model->dir().path().append( "/TestXmlLabel_XXXXXX.png" ); + QTemporaryFile pngRelative( pngTemplate ); pngRelative.open(); pngRelative.close(); pngRelativeImage.save( pngRelative.fileName(), "PNG" ); + + // Make svg file in subdir of model dir + QString svgTemplate = subSubDir.path().append( "/TestXmlLabel_XXXXXX.svg" ); + QTemporaryFile svgRelative( svgTemplate ); svgRelative.open(); svgRelative.write( glabels::test::cyan_8x8_svg ); svgRelative.close(); + + bool lock = true, noLock = false, shadow = true, noShadow = false; + ColorNode black( Qt::black ), white( Qt::white ), red( Qt::red ), green( Qt::green ), blue( Qt::blue ); + QMatrix tMatrix( 1, 0, 0, 1, 50.0, 50.0 ), sMatrix( 0.5, 0, 0, 1.0, 0, 0 ); + Style bcStyle = Backends::defaultStyle(); + + /// + /// Add objects + /// + model->addObject( new ModelBoxObject( 0, 1, 10, 20, noLock, 2, red, green, tMatrix, shadow, 1, 2, 0.7, black ) ); + model->addObject( new ModelEllipseObject( 1, 2, 30, 40, lock, 3, black, white, sMatrix, shadow, 2, 3, 0.8, blue ) ); + model->addObject( new ModelImageObject( 2, 3, 50, 50, noLock, TextNode( false, "" ), tMatrix, noShadow, 3, 4, 0.9, white ) ); + model->addObject( new ModelImageObject( 3, 4, 60, 70, lock, "image3.png", png, sMatrix, shadow, 6, 4, 0.9, black ) ); + model->addObject( new ModelImageObject( 4, 5, 70, 80, noLock, "image4.svg", glabels::test::red_8x8_svg ) ); + model->addObject( new ModelImageObject( 5, 6, 80, 90, lock, TextNode( true, "${key}" ), tMatrix, shadow ) ); + model->addObject( new ModelImageObject( 6, 7, 90, 100, noLock, TextNode( false, "image6.jpg" ) ) ); // Will give warning on parse that embedded file missing + model->addObject( new ModelTextObject( 7, 8, 110, 120, lock, "text", "Serif", 12, QFont::Bold, true, true, red, + Qt::AlignHCenter, Qt::AlignBottom, QTextOption::NoWrap, 1.3, false, sMatrix, shadow, 5, 5, 0.5, red ) ); + model->addObject( new ModelLineObject( 8, 9, 100, 110, 4, green, sMatrix, shadow, 5, 5, 0.5, red ) ); + model->addObject( new ModelBarcodeObject( 9, 10, 50, 50, lock, bcStyle, true, true, QString("1234"), black, tMatrix ) ); + model->addObject( new ModelImageObject( 10, 11, 8, 8, noLock, TextNode( false, pngAbsolute.fileName() ) ) ); + model->addObject( new ModelImageObject( 11, 12, 8, 8, lock, TextNode( false, pngRelative.fileName() ) ) ); + model->addObject( new ModelImageObject( 12, 13, 8, 8, noLock, TextNode( false, svgRelative.fileName() ) ) ); + + QCOMPARE( model->objectList().size(), 13 ); + + /// + /// Add template + /// + Template tmplate( "Test Brand", "part", "desc", "testPaperId", 110, 410 ); + FrameRect* frame = new FrameRect( 120, 220, 5, 0, 0, "rect1" ); + tmplate.addFrame( frame ); + model->setTmplate( &tmplate ); // Copies + + /// + /// Add variables + /// + Variables vars; + Variable s( Variable::Type::STRING, "s", "initial", Variable::Increment::NEVER ); + Variable c( Variable::Type::COLOR, "c", "red", Variable::Increment::PER_COPY ); + Variable i( Variable::Type::INTEGER, "i", "123", Variable::Increment::PER_ITEM, "1" ); + Variable f( Variable::Type::FLOATING_POINT, "f", "12.3", Variable::Increment::PER_PAGE, "0.2" ); + model->variables()->addVariable( s ); + model->variables()->addVariable( c ); + model->variables()->addVariable( i ); + model->variables()->addVariable( f ); + QCOMPARE( model->variables()->size(), 4 ); + + // + // Add merge + // + Merge* merge = Factory::createMerge( TextCsvKeys::id() ); + QCOMPARE( merge->id(), TextCsvKeys::id() ); + + model->setMerge( merge ); + QCOMPARE( model->merge(), merge ); + + QString csvTemplate = subDir.path().append( "/TestXmlLabel_XXXXXX.csv" ); + QTemporaryFile csv( csvTemplate ); + csv.open(); + csv.write( "id,text\n1,text1\n2,text2\n3,text3\n" ); + csv.close(); + + merge->setSource( csv.fileName() ); + QCOMPARE( merge->source(), csv.fileName() ); + + QCOMPARE( merge->recordList().size(), 3 ); + + model->setRotate( true ); + QVERIFY( model->rotate() ); + + /// + /// Write to file and read + /// + XmlLabelCreator::writeFile( model, glabels.fileName() ); + + QCOMPARE( model->dir(), QFileInfo( glabels.fileName() ).dir() ); + + // Copy before deletion else nulled + QString pngAbsoluteFileName = pngAbsolute.fileName(); + QString pngRelativeFileName = model->dir().relativeFilePath( pngRelative.fileName() ); + QString svgRelativeFileName = model->dir().relativeFilePath( svgRelative.fileName() ); + + QFileInfo pngAbsoluteFileInfo( pngAbsoluteFileName ); + QVERIFY( pngAbsoluteFileInfo.isAbsolute() ); + QFileInfo pngRelativeFileInfo( pngRelativeFileName ); + QVERIFY( pngRelativeFileInfo.isRelative() ); + QFileInfo svgRelativeFileInfo( svgRelativeFileName ); + QVERIFY( svgRelativeFileInfo.isRelative() ); + + // Delete to make sure they're not read from file on parse + QVERIFY( pngAbsolute.remove() ); + QVERIFY( pngRelative.remove() ); + QVERIFY( svgRelative.remove() ); + + QTest::ignoreMessage( QtWarningMsg, QRegularExpression( "^Embedded file \"[^\"]+image6.jpg\" missing\\. Trying actual file\\.$" ) ); + Model* readModel = XmlLabelParser::readFile( glabels.fileName() ); + QVERIFY( readModel ); + QCOMPARE( readModel->dir(), model->dir() ); + QCOMPARE( readModel->fileName(), model->fileName() ); + + QCOMPARE( readModel->tmplate()->brand(), model->tmplate()->brand() ); + QCOMPARE( readModel->tmplate()->part(), model->tmplate()->part() ); + QCOMPARE( readModel->tmplate()->description(), model->tmplate()->description() ); + QCOMPARE( readModel->tmplate()->paperId(), model->tmplate()->paperId() ); + QCOMPARE( readModel->tmplate()->pageWidth().pt(), model->tmplate()->pageWidth().pt() ); + QCOMPARE( readModel->tmplate()->pageHeight().pt(), model->tmplate()->pageHeight().pt() ); + + QCOMPARE( readModel->frame()->id(), model->frame()->id() ); + QCOMPARE( readModel->frame()->w().pt(), model->frame()->w().pt() ); + QCOMPARE( readModel->frame()->h().pt(), model->frame()->h().pt() ); + + QCOMPARE( readModel->rotate(), model->rotate() ); + QCOMPARE( readModel->w(), model->w() ); + QCOMPARE( readModel->h(), model->h() ); + + const QList& readObjects = readModel->objectList(); + const QList& modelObjects = model->objectList(); + QCOMPARE( readObjects.size(), modelObjects.size() ); + + QString modelDirPath = model->dir().path() + "/"; + + for ( int i = 0; i < readObjects.count(); i++ ) + { + qDebug() << "object" << i; + QVERIFY( readObjects.at(i)->id() != modelObjects.at(i)->id() ); // Ids are generated and unique + QCOMPARE( readObjects.at(i)->x0(), modelObjects.at(i)->x0() ); + QCOMPARE( readObjects.at(i)->x0().pt(), (double)i ); + QCOMPARE( readObjects.at(i)->y0(), modelObjects.at(i)->y0() ); + QCOMPARE( readObjects.at(i)->y0().pt(), (double)(i + 1) ); + QCOMPARE( readObjects.at(i)->w().pt(), modelObjects.at(i)->w().pt() ); // Use `pt()` so invoke `qFuzzyCompare(double, double)` otherwise get rounding difference for Barcode + QCOMPARE( readObjects.at(i)->h().pt(), modelObjects.at(i)->h().pt() ); // Fuzzy + QCOMPARE( readObjects.at(i)->lockAspectRatio(), modelObjects.at(i)->lockAspectRatio() ); + QCOMPARE( readObjects.at(i)->lockAspectRatio(), (bool)(i % 2) ); + QCOMPARE( readObjects.at(i)->matrix(), modelObjects.at(i)->matrix() ); + QCOMPARE( readObjects.at(i)->shadow(), modelObjects.at(i)->shadow() ); + QCOMPARE( readObjects.at(i)->shadowX(), modelObjects.at(i)->shadowX() ); + QCOMPARE( readObjects.at(i)->shadowY(), modelObjects.at(i)->shadowY() ); + QCOMPARE( readObjects.at(i)->shadowOpacity(), modelObjects.at(i)->shadowOpacity() ); + QVERIFY( readObjects.at(i)->shadowColorNode() == modelObjects.at(i)->shadowColorNode() ); + QCOMPARE( readObjects.at(i)->naturalSize().w().pt(), modelObjects.at(i)->naturalSize().w().pt() ); // Fuzzy + QCOMPARE( readObjects.at(i)->naturalSize().h().pt(), modelObjects.at(i)->naturalSize().h().pt() ); // Fuzzy + + QCOMPARE( readObjects.at(i)->text(), modelObjects.at(i)->text() ); + QCOMPARE( readObjects.at(i)->fontFamily(), modelObjects.at(i)->fontFamily() ); + QCOMPARE( readObjects.at(i)->fontSize(), modelObjects.at(i)->fontSize() ); + QCOMPARE( readObjects.at(i)->fontWeight(), modelObjects.at(i)->fontWeight() ); + QCOMPARE( readObjects.at(i)->fontItalicFlag(), modelObjects.at(i)->fontItalicFlag() ); + QCOMPARE( readObjects.at(i)->fontUnderlineFlag(), modelObjects.at(i)->fontUnderlineFlag() ); + QVERIFY( readObjects.at(i)->textColorNode() == modelObjects.at(i)->textColorNode() ); + QCOMPARE( readObjects.at(i)->textHAlign(), modelObjects.at(i)->textHAlign() ); + QCOMPARE( readObjects.at(i)->textVAlign(), modelObjects.at(i)->textVAlign() ); + QCOMPARE( readObjects.at(i)->textWrapMode(), modelObjects.at(i)->textWrapMode() ); + QCOMPARE( readObjects.at(i)->textLineSpacing(), modelObjects.at(i)->textLineSpacing() ); + QCOMPARE( readObjects.at(i)->textAutoShrink(), modelObjects.at(i)->textAutoShrink() ); + + QCOMPARE( readObjects.at(i)->filenameNode().isField(), modelObjects.at(i)->filenameNode().isField() ); + if ( i == 6 /*image6.jpg*/ ) + { + // Not in data so absolute path set + QCOMPARE( readObjects.at(i)->filenameNode().data(), modelDirPath + modelObjects.at(i)->filenameNode().data() ); + } + else if ( modelObjects.at(i)->filenameNode().data().startsWith( modelDirPath ) ) + { + // Made relative to model dir + QCOMPARE( modelDirPath + readObjects.at(i)->filenameNode().data(), modelObjects.at(i)->filenameNode().data() ); + } + else + { + QCOMPARE( readObjects.at(i)->filenameNode().data(), modelObjects.at(i)->filenameNode().data() ); + } + + if ( readObjects.at(i)->image() ) + { + QCOMPARE( *(readObjects.at(i)->image()), *(modelObjects.at(i)->image()) ); + } + else + { + QCOMPARE( readObjects.at(i)->image(), modelObjects.at(i)->image() ); + } + QCOMPARE( readObjects.at(i)->svg(), modelObjects.at(i)->svg() ); + + QCOMPARE( readObjects.at(i)->lineWidth(), modelObjects.at(i)->lineWidth() ); + QVERIFY( readObjects.at(i)->lineColorNode() == modelObjects.at(i)->lineColorNode() ); + QVERIFY( readObjects.at(i)->fillColorNode() == modelObjects.at(i)->fillColorNode() ); + + QCOMPARE( readObjects.at(i)->bcData(), modelObjects.at(i)->bcData() ); + QCOMPARE( readObjects.at(i)->bcTextFlag(), modelObjects.at(i)->bcTextFlag() ); + QCOMPARE( readObjects.at(i)->bcChecksumFlag(), modelObjects.at(i)->bcChecksumFlag() ); + QVERIFY( readObjects.at(i)->bcColorNode() == modelObjects.at(i)->bcColorNode() ); + QVERIFY( !( readObjects.at(i)->bcStyle() != modelObjects.at(i)->bcStyle() ) ); // Only != operator + QCOMPARE( readObjects.at(i)->bcFormatDigits(), modelObjects.at(i)->bcFormatDigits() ); + + QCOMPARE( readObjects.at(i)->canText(), modelObjects.at(i)->canText() ); + QCOMPARE( readObjects.at(i)->canFill(), modelObjects.at(i)->canFill() ); + QCOMPARE( readObjects.at(i)->canLineColor(), modelObjects.at(i)->canLineColor() ); + QCOMPARE( readObjects.at(i)->canLineWidth(), modelObjects.at(i)->canLineWidth() ); + } + + QCOMPARE( readObjects[10]->filenameNode().data(), pngAbsoluteFileName ); + QCOMPARE( readObjects[11]->filenameNode().data(), pngRelativeFileName ); + QCOMPARE( readObjects[12]->filenameNode().data(), svgRelativeFileName ); + + QCOMPARE( readModel->variables()->size(), model->variables()->size() ); + for ( const auto& modelV : *model->variables() ) + { + QVERIFY( readModel->variables()->hasVariable( modelV.name() ) ); + const auto& readV = readModel->variables()->value( modelV.name() ); + QCOMPARE( readV.type(), modelV.type() ); + QCOMPARE( readV.initialValue(), modelV.initialValue() ); + if ( readV.type() == Variable::Type::INTEGER || readV.type() == Variable::Type::FLOATING_POINT ) + { + QCOMPARE( readV.stepSize(), modelV.stepSize() ); + QCOMPARE( readV.increment(), modelV.increment() ); + } + QCOMPARE( readV.value(), modelV.value() ); + } + + QCOMPARE( readModel->merge()->id(), model->merge()->id() ); + QCOMPARE( readModel->merge()->source(), model->merge()->source() ); + QCOMPARE( readModel->merge()->recordList().size(), model->merge()->recordList().size() ); + for ( int i = 0; i < readModel->merge()->recordList().size(); i++ ) + { + QCOMPARE( readModel->merge()->recordList().at(i)->keys(), model->merge()->recordList().at(i)->keys() ); + QCOMPARE( readModel->merge()->recordList().at(i)->values(), model->merge()->recordList().at(i)->values() ); + } + + delete readModel->merge(); + delete readModel->variables(); + delete readModel; + + delete model->merge(); + delete model->variables(); + delete model; +} + + +void TestXmlLabel::parser_3ReadFile() +{ + // Current path is "build/model/unit_tests" so go up 3 levels + QFileInfo glabelsFileInfo( "../../../model/unit_tests/data/glabels-3/crew-orientation-name-tags-7.glabels" ); + QVERIFY( glabelsFileInfo.isReadable() ); + + Model* model = XmlLabelParser::readFile( glabelsFileInfo.filePath() ); + QVERIFY( model ); + + QCOMPARE( model->fileName(), glabelsFileInfo.filePath() ); + + QCOMPARE( model->tmplate()->brand(), QString( "Avery" ) ); + QCOMPARE( model->tmplate()->part(), QString( "5395" ) ); + QCOMPARE( model->tmplate()->description(), QString( "Name Badge Labels" ) ); + QCOMPARE( model->tmplate()->paperId(), QString( "US-Letter" ) ); + QCOMPARE( model->tmplate()->pageWidth().in(), 8.5 ); + QCOMPARE( model->tmplate()->pageHeight().in(), 11.0 ); + + QCOMPARE( model->frame()->id(), QString( "0" ) ); + const FrameRect* frameRect = dynamic_cast( model->frame() ); + QVERIFY( frameRect ); + QCOMPARE( frameRect->w().in(), 3.375 ); + QCOMPARE( frameRect->h().in(), 2.33333 ); + QCOMPARE( frameRect->r().in(), 0.1875 ); + QCOMPARE( frameRect->xWaste().in(), 0.0625 ); + QCOMPARE( frameRect->yWaste().in(), 0.0625 ); + + QCOMPARE( model->frame()->markups().size(), 1 ); + MarkupMargin* markupMargin = dynamic_cast( model->frame()->markups()[0] ); + QVERIFY( markupMargin ); + QCOMPARE( markupMargin->xSize().in(), 0.0625 ); + QCOMPARE( markupMargin->ySize().in(), 0.0625 ); + + QCOMPARE( model->frame()->layouts().size(), 1 ); + QCOMPARE( model->frame()->layouts()[0].nx(), 2 ); + QCOMPARE( model->frame()->layouts()[0].ny(), 4 ); + QCOMPARE( model->frame()->layouts()[0].x0().in(), 0.6875 ); + QCOMPARE( model->frame()->layouts()[0].y0().in(), 0.583333 ); + QCOMPARE( model->frame()->layouts()[0].dx().in(), 3.75 ); + QCOMPARE( model->frame()->layouts()[0].dy().in(), 2.5 ); + + QCOMPARE( model->rotate(), false ); + + QCOMPARE( model->objectList().size(), 4 ); + + ModelTextObject* modelTextObject0 = dynamic_cast( model->objectList()[0] ); + QVERIFY( modelTextObject0 ); + QCOMPARE( modelTextObject0->x0().in(), 0.150603 ); + QCOMPARE( modelTextObject0->y0().in(), 0.2625 ); + // Width and height set to naturalSize() + QCOMPARE( modelTextObject0->lockAspectRatio(), false ); + QCOMPARE( modelTextObject0->matrix(), QMatrix( 1, 0, 0, 1, 0, 0 ) ); + QCOMPARE( modelTextObject0->shadow(), false ); + QCOMPARE( modelTextObject0->text(), QString( "Hello, my name is" ) ); + QCOMPARE( modelTextObject0->fontFamily(), QString( "Sans" ) ); + QCOMPARE( modelTextObject0->fontSize(), 16 * FONT_SCALE_FACTOR ); + QCOMPARE( modelTextObject0->fontWeight(), QFont::Bold ); + QCOMPARE( modelTextObject0->fontItalicFlag(), false ); + QCOMPARE( modelTextObject0->textLineSpacing(), 1.0 ); + QCOMPARE( modelTextObject0->textAutoShrink(), false ); + QCOMPARE( modelTextObject0->textColorNode().color(), QColor::fromRgba( 0xff3366ff ) ); // QColor uses ARGB + QCOMPARE( modelTextObject0->textHAlign(), Qt::AlignLeft ); + + ModelTextObject* modelTextObject1 = dynamic_cast( model->objectList()[1] ); + QVERIFY( modelTextObject1 ); + QCOMPARE( modelTextObject1->x0().in(), 0.150603 ); + QCOMPARE( modelTextObject1->y0().in(), 0.645 ); + // Width and height set to naturalSize() + QCOMPARE( modelTextObject1->lockAspectRatio(), false ); + QCOMPARE( modelTextObject1->matrix(), QMatrix( 1, 0, 0, 1, 0, 0 ) ); + QCOMPARE( modelTextObject1->shadow(), false ); + QCOMPARE( modelTextObject1->text(), QString( "${Name}" ) ); + QCOMPARE( modelTextObject1->fontFamily(), QString( "Sans" ) ); + QCOMPARE( modelTextObject1->fontSize(), 20 * FONT_SCALE_FACTOR ); + QCOMPARE( modelTextObject1->fontWeight(), QFont::Normal ); + QCOMPARE( modelTextObject1->fontItalicFlag(), false ); + QCOMPARE( modelTextObject1->textLineSpacing(), 1.0 ); + QCOMPARE( modelTextObject1->textAutoShrink(), false ); + QCOMPARE( modelTextObject1->textColorNode().color(), QColor::fromRgba( 0xff000000 ) ); + QCOMPARE( modelTextObject1->textHAlign(), Qt::AlignLeft ); + + ModelTextObject* modelTextObject2 = dynamic_cast( model->objectList()[2] ); + QVERIFY( modelTextObject2 ); + QCOMPARE( modelTextObject2->x0().in(), 0.150603 ); + QCOMPARE( modelTextObject2->y0().in(), 1.14 ); + // Width and height set to naturalSize() + QCOMPARE( modelTextObject2->lockAspectRatio(), false ); + QCOMPARE( modelTextObject2->matrix(), QMatrix( 1, 0, 0, 1, 0, 0 ) ); + QCOMPARE( modelTextObject2->shadow(), false ); + QCOMPARE( modelTextObject2->text(), QString( "Department: ${Department}" ) ); + QCOMPARE( modelTextObject2->fontFamily(), QString( "Sans" ) ); + QCOMPARE( modelTextObject2->fontSize(), 11 * FONT_SCALE_FACTOR ); + QCOMPARE( modelTextObject2->fontWeight(), QFont::Normal ); + QCOMPARE( modelTextObject2->fontItalicFlag(), false ); + QCOMPARE( modelTextObject2->textLineSpacing(), 1.0 ); + QCOMPARE( modelTextObject2->textAutoShrink(), false ); + QCOMPARE( modelTextObject2->textColorNode().color(), QColor::fromRgba( 0xff000000 ) ); + QCOMPARE( modelTextObject2->textHAlign(), Qt::AlignLeft ); + + ModelBarcodeObject* modelBarcodeObject3 = dynamic_cast( model->objectList()[3] ); + QVERIFY( modelBarcodeObject3 ); + QCOMPARE( modelBarcodeObject3->x0().in(), 0.150603 ); + QCOMPARE( modelBarcodeObject3->y0().in(), 1.395 ); + QCOMPARE( modelBarcodeObject3->w().in(), 3.06944 ); + QCOMPARE( modelBarcodeObject3->h().in(), 0.847222 ); + QCOMPARE( modelBarcodeObject3->lockAspectRatio(), false ); + QCOMPARE( modelBarcodeObject3->matrix(), QMatrix( 1, 0, 0, 1, 0, 0 ) ); + QCOMPARE( modelBarcodeObject3->shadow(), false ); + + QCOMPARE( modelBarcodeObject3->bcData(), QString( "${SN}" ) ); + QVERIFY( modelBarcodeObject3->bcTextFlag() ); + QVERIFY( modelBarcodeObject3->bcChecksumFlag() ); + QCOMPARE( modelBarcodeObject3->bcColorNode().color(), QColor::fromRgba( 0xff000000 ) ); + QCOMPARE( modelBarcodeObject3->bcStyle().fullId(), QString( "code39" ) ); + QCOMPARE( modelBarcodeObject3->bcFormatDigits(), 10 ); + + QVERIFY( model->merge() ); + QVERIFY( !model->merge()->source().isEmpty() ); // Merge source hacked to work relatively so not realistic + QCOMPARE( model->merge()->recordList().size(), 4 ); + + QCOMPARE( model->merge()->recordList()[0]->keys().size(), 3 ); + QList keys, values0, values1, values2, values3; + keys << "Department" << "Name" << "SN"; + values0 << "Management" << "Jim Kirk" << "SC937-0176 CEC"; + values1 << "Sciences" << "Mr. Spock" << "S179-276SP"; + values2 << "Medicine" << "Leonard McCoy" << "unknown"; + values3 << "Engineering" << "Montgomery Scott" << "SE-197-54T"; + + QCOMPARE( model->merge()->recordList()[0]->keys(), keys ); + QCOMPARE( model->merge()->recordList()[0]->values(), values0 ); + QCOMPARE( model->merge()->recordList()[1]->keys(), keys ); + QCOMPARE( model->merge()->recordList()[1]->values(), values1 ); + QCOMPARE( model->merge()->recordList()[2]->keys(), keys ); + QCOMPARE( model->merge()->recordList()[2]->values(), values2 ); + QCOMPARE( model->merge()->recordList()[3]->keys(), keys ); + QCOMPARE( model->merge()->recordList()[3]->values(), values3 ); + + delete model->merge(); + delete model->variables(); + delete model; +} + + +void TestXmlLabel::parser_3Barcode() +{ + QTemporaryFile glabels( "TestXmlLabel_XXXXXX.glabels" ); + glabels.open(); + glabels.write( "" ); + glabels.write( "" ); + glabels.write( "" ); + glabels.write( "" ); + glabels.write( "" ); + glabels.write( "" ); + glabels.write( "" ); + glabels.write( "" ); + glabels.write( "" ); + glabels.write( "" ); + glabels.close(); + + Model* model = XmlLabelParser::readFile( glabels.fileName() ); + QVERIFY( model ); + + QCOMPARE( model->objectList().size(), 5 ); + + ModelBarcodeObject* modelBarcodeObject; + + modelBarcodeObject = dynamic_cast( model->objectList()[0] ); + QVERIFY( modelBarcodeObject ); + QCOMPARE( modelBarcodeObject->bcStyle().fullId(), QString( "code39ext" ) ); + + modelBarcodeObject = dynamic_cast( model->objectList()[1] ); + QVERIFY( modelBarcodeObject ); + QCOMPARE( modelBarcodeObject->bcStyle().fullId(), QString( "datamatrix" ) ); + + modelBarcodeObject = dynamic_cast( model->objectList()[2] ); + QVERIFY( modelBarcodeObject ); + if ( Backends::style( "qrencode", "qrcode" ) != Backends::defaultStyle() ) + { + QCOMPARE( modelBarcodeObject->bcStyle().fullId(), QString( "qrencode::qrcode" ) ); + } + else if ( Backends::style( "zint", "qr" ) != Backends::defaultStyle() ) + { + QCOMPARE( modelBarcodeObject->bcStyle().fullId(), QString( "zint::qr" ) ); + } + else + { + QCOMPARE( modelBarcodeObject->bcStyle().fullId(), QString( "code39" ) ); + } + + modelBarcodeObject = dynamic_cast( model->objectList()[3] ); + QVERIFY( modelBarcodeObject ); + if ( Backends::style( "gnu-barcode", "upc-a+2" ) != Backends::defaultStyle() ) + { + QCOMPARE( modelBarcodeObject->bcStyle().fullId(), QString( "gnu-barcode::upc-a+2" ) ); + } + else + { + QCOMPARE( modelBarcodeObject->bcStyle().fullId(), QString( "code39" ) ); + } + + modelBarcodeObject = dynamic_cast( model->objectList()[4] ); + QVERIFY( modelBarcodeObject ); + if ( Backends::style( "zint", "gs1-128" ) != Backends::defaultStyle() ) + { + QCOMPARE( modelBarcodeObject->bcStyle().fullId(), QString( "zint::gs1-128" ) ); + } + else + { + QCOMPARE( modelBarcodeObject->bcStyle().fullId(), QString( "code39" ) ); + } + + delete model->merge(); + delete model->variables(); + delete model; +} diff --git a/model/unit_tests/TestXmlLabel.h b/model/unit_tests/TestXmlLabel.h new file mode 100644 index 00000000..57464ede --- /dev/null +++ b/model/unit_tests/TestXmlLabel.h @@ -0,0 +1,36 @@ +/* TestXmlLabel.h + * + * Copyright (C) 2018 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestXmlLabel : public QObject +{ + Q_OBJECT + +private slots: + void initTestCase(); + void serializeDeserialize(); + void writeReadFile(); + void parser_3ReadFile(); + void parser_3Barcode(); +}; + + diff --git a/model/unit_tests/TestXmlUtil.cpp b/model/unit_tests/TestXmlUtil.cpp new file mode 100644 index 00000000..dc64f7d5 --- /dev/null +++ b/model/unit_tests/TestXmlUtil.cpp @@ -0,0 +1,298 @@ +/* TestXmlUtil.cpp + * + * Copyright (C) 2018 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include "TestXmlUtil.h" + +#include "model/XmlUtil.h" + + +QTEST_MAIN(TestXmlUtil) + + +void TestXmlUtil::getStringAttr() +{ + using namespace glabels::model; + + // Test XML + QString xml = ""; + + // Setup document and extract root node + QDomDocument doc; + QCOMPARE( doc.setContent( xml, false ), true ); + QDomElement node = doc.documentElement(); + QCOMPARE( node.tagName(), QString("root") ); + + // + // Tests + // + QCOMPARE( XmlUtil::getStringAttr( node, "a", "Default" ), QString( "A test string" ) ); + + // non-existant attribute, use default + QCOMPARE( XmlUtil::getStringAttr( node, "b", "Default" ), QString( "Default" ) ); +} + + +void TestXmlUtil::getDoubleAttr() +{ + using namespace glabels::model; + + // Test XML + QString xml = ""; + + // Setup document and extract root node + QDomDocument doc; + QCOMPARE( doc.setContent( xml, false ), true ); + QDomElement node = doc.documentElement(); + QCOMPARE( node.tagName(), QString("root") ); + + // + // Tests + // + QCOMPARE( XmlUtil::getDoubleAttr( node, "a", 3.14 ), 0.0 ); + QCOMPARE( XmlUtil::getDoubleAttr( node, "b", 3.14 ), 0.0 ); + QCOMPARE( XmlUtil::getDoubleAttr( node, "c", 3.14 ), 1.0 ); + QCOMPARE( XmlUtil::getDoubleAttr( node, "d", 3.14 ), 1.5 ); + QCOMPARE( XmlUtil::getDoubleAttr( node, "e", 3.14 ), -0.15 ); + + // bad value, use default + QCOMPARE( XmlUtil::getDoubleAttr( node, "f", 3.14 ), 3.14 ); + + // non-existant attribute, use default + QCOMPARE( XmlUtil::getDoubleAttr( node, "g", 3.14 ), 3.14 ); +} + + +void TestXmlUtil::getBoolAttr() +{ + using namespace glabels::model; + + // Test XML + QString xml = ""; + + // Setup document and extract root node + QDomDocument doc; + QCOMPARE( doc.setContent( xml, false ), true ); + QDomElement node = doc.documentElement(); + QCOMPARE( node.tagName(), QString("root") ); + + // + // Tests + // + QCOMPARE( XmlUtil::getBoolAttr( node, "a", false ), false ); + QCOMPARE( XmlUtil::getBoolAttr( node, "a", true ), false ); + QCOMPARE( XmlUtil::getBoolAttr( node, "b", false ), true ); + QCOMPARE( XmlUtil::getBoolAttr( node, "b", true ), true ); + QCOMPARE( XmlUtil::getBoolAttr( node, "c", false ), true ); + QCOMPARE( XmlUtil::getBoolAttr( node, "c", true ), true ); + QCOMPARE( XmlUtil::getBoolAttr( node, "d", false ), false ); + QCOMPARE( XmlUtil::getBoolAttr( node, "d", true ), false ); + + // bad value, use default + QCOMPARE( XmlUtil::getBoolAttr( node, "e", false ), false ); + QCOMPARE( XmlUtil::getBoolAttr( node, "e", true ), true ); + + // non-existant attribute, use default + QCOMPARE( XmlUtil::getBoolAttr( node, "f", false ), false ); + QCOMPARE( XmlUtil::getBoolAttr( node, "f", true ), true ); +} + + +void TestXmlUtil::getIntAttr() +{ + using namespace glabels::model; + + // Test XML + QString xml = ""; + + // Setup document and extract root node + QDomDocument doc; + QCOMPARE( doc.setContent( xml, false ), true ); + QDomElement node = doc.documentElement(); + QCOMPARE( node.tagName(), QString("root") ); + + // + // Tests + // + QCOMPARE( XmlUtil::getIntAttr( node, "a", 456 ), 0 ); + QCOMPARE( XmlUtil::getIntAttr( node, "b", 456 ), 1 ); + QCOMPARE( XmlUtil::getIntAttr( node, "c", 456 ), -11 ); + QCOMPARE( XmlUtil::getIntAttr( node, "d", 456 ), 1234567890 ); + + // bad value, use default + QCOMPARE( XmlUtil::getIntAttr( node, "e", 456 ), 456 ); + + // non-existant attribute, use default + QCOMPARE( XmlUtil::getIntAttr( node, "f", 456 ), 456 ); +} + + +void TestXmlUtil::getUIntAttr() +{ + using namespace glabels::model; + + // Test XML + QString xml = ""; + + // Setup document and extract root node + QDomDocument doc; + QCOMPARE( doc.setContent( xml, false ), true ); + QDomElement node = doc.documentElement(); + QCOMPARE( node.tagName(), QString("root") ); + + // + // Tests + // + QCOMPARE( XmlUtil::getUIntAttr( node, "a", 0xdefa17 ), 0u ); + QCOMPARE( XmlUtil::getUIntAttr( node, "b", 0xdefa17 ), 1u ); + QCOMPARE( XmlUtil::getUIntAttr( node, "c", 0xdefa17 ), 1234567890u ); + QCOMPARE( XmlUtil::getUIntAttr( node, "d", 0xdefa17 ), 0xbaadbeef ); + QCOMPARE( XmlUtil::getUIntAttr( node, "e", 0xdefa17 ), 0xc001000f ); + + // bad value, use default + QCOMPARE( XmlUtil::getUIntAttr( node, "f", 0xdefa17 ), 0xdefa17u ); + + // non-existant attribute, use default + QCOMPARE( XmlUtil::getUIntAttr( node, "g", 0xdefa17 ), 0xdefa17u ); +} + + +void TestXmlUtil::getLengthAttr() +{ + using namespace glabels::model; + + // Test XML + QString xml = ""; + + // Setup document and extract root node + QDomDocument doc; + QCOMPARE( doc.setContent( xml, false ), true ); + QDomElement node = doc.documentElement(); + QCOMPARE( node.tagName(), QString("root") ); + + // + // Tests + // + QCOMPARE( XmlUtil::getLengthAttr( node, "a", Distance::pt(1234) ), Distance::pt(0.0) ); + QCOMPARE( XmlUtil::getLengthAttr( node, "b", Distance::pt(1234) ), Distance::pt(-1.0) ); + QCOMPARE( XmlUtil::getLengthAttr( node, "c", Distance::pt(1234) ), Distance::pt(720) ); + QCOMPARE( XmlUtil::getLengthAttr( node, "d", Distance::pt(1234) ), Distance::mm(1.5) ); + QCOMPARE( XmlUtil::getLengthAttr( node, "e", Distance::pt(1234) ), Distance::cm(-0.15) ); + QCOMPARE( XmlUtil::getLengthAttr( node, "f", Distance::pt(1234) ), Distance::pt(3) ); + + // bad value, use default + QCOMPARE( XmlUtil::getLengthAttr( node, "g", Distance::pt(1234) ), Distance::pt(1234) ); + QCOMPARE( XmlUtil::getLengthAttr( node, "h", Distance::pt(1234) ), Distance::pt(1234) ); + + // non-existant attribute, use default + QCOMPARE( XmlUtil::getLengthAttr( node, "i", Distance::pt(1234) ), Distance::pt(1234) ); +} + + +void TestXmlUtil::getWeightAttr() +{ + using namespace glabels::model; + + // Test XML + QString xml = ""; + + // Setup document and extract root node + QDomDocument doc; + QCOMPARE( doc.setContent( xml, false ), true ); + QDomElement node = doc.documentElement(); + QCOMPARE( node.tagName(), QString("root") ); + + // + // Tests + // + QCOMPARE( XmlUtil::getWeightAttr( node, "a", QFont::Normal ), QFont::Bold ); + QCOMPARE( XmlUtil::getWeightAttr( node, "a", QFont::Bold ), QFont::Bold ); + QCOMPARE( XmlUtil::getWeightAttr( node, "b", QFont::Normal ), QFont::Normal ); + QCOMPARE( XmlUtil::getWeightAttr( node, "b", QFont::Bold ), QFont::Normal ); + + // bad value, use default + QCOMPARE( XmlUtil::getWeightAttr( node, "c", QFont::Normal ), QFont::Normal ); + QCOMPARE( XmlUtil::getWeightAttr( node, "c", QFont::Bold ), QFont::Bold ); + + // non-existant attribute, use default + QCOMPARE( XmlUtil::getWeightAttr( node, "d", QFont::Normal ), QFont::Normal ); + QCOMPARE( XmlUtil::getWeightAttr( node, "d", QFont::Bold ), QFont::Bold ); +} + + +void TestXmlUtil::getAlignmentAttr() +{ + using namespace glabels::model; + + // Test XML + QString xml = ""; + + // Setup document and extract root node + QDomDocument doc; + QCOMPARE( doc.setContent( xml, false ), true ); + QDomElement node = doc.documentElement(); + QCOMPARE( node.tagName(), QString("root") ); + + // + // Tests + // + QCOMPARE( XmlUtil::getAlignmentAttr( node, "a", Qt::AlignRight ), Qt::AlignLeft ); + QCOMPARE( XmlUtil::getAlignmentAttr( node, "b", Qt::AlignRight ), Qt::AlignHCenter ); + QCOMPARE( XmlUtil::getAlignmentAttr( node, "c", Qt::AlignLeft ), Qt::AlignRight ); + QCOMPARE( XmlUtil::getAlignmentAttr( node, "d", Qt::AlignTop ), Qt::AlignBottom ); + QCOMPARE( XmlUtil::getAlignmentAttr( node, "e", Qt::AlignTop ), Qt::AlignVCenter ); + QCOMPARE( XmlUtil::getAlignmentAttr( node, "f", Qt::AlignBottom ), Qt::AlignTop ); + + // bad value, use default + QCOMPARE( XmlUtil::getAlignmentAttr( node, "g", Qt::AlignTop ), Qt::AlignTop ); + QCOMPARE( XmlUtil::getAlignmentAttr( node, "g", Qt::AlignLeft ), Qt::AlignLeft ); + + // non-existant attribute, use default + QCOMPARE( XmlUtil::getAlignmentAttr( node, "h", Qt::AlignBottom ), Qt::AlignBottom ); +} + + +void TestXmlUtil::getWrapModeAttr() +{ + using namespace glabels::model; + + // Test XML + QString xml = ""; + + // Setup document and extract root node + QDomDocument doc; + QCOMPARE( doc.setContent( xml, false ), true ); + QDomElement node = doc.documentElement(); + QCOMPARE( node.tagName(), QString("root") ); + + // + // Tests + // + QCOMPARE( XmlUtil::getWrapModeAttr( node, "a", QTextOption::NoWrap ), QTextOption::WordWrap ); + QCOMPARE( XmlUtil::getWrapModeAttr( node, "b", QTextOption::NoWrap ), QTextOption::WrapAnywhere ); + QCOMPARE( XmlUtil::getWrapModeAttr( node, "c", QTextOption::WordWrap ), QTextOption::NoWrap ); + + // bad value, use default + QCOMPARE( XmlUtil::getWrapModeAttr( node, "d", QTextOption::NoWrap ), QTextOption::NoWrap ); + QCOMPARE( XmlUtil::getWrapModeAttr( node, "d", QTextOption::WordWrap ), QTextOption::WordWrap ); + + // non-existant attribute, use default + QCOMPARE( XmlUtil::getWrapModeAttr( node, "e", QTextOption::NoWrap ), QTextOption::NoWrap ); + QCOMPARE( XmlUtil::getWrapModeAttr( node, "e", QTextOption::WrapAnywhere ), QTextOption::WrapAnywhere ); +} diff --git a/model/unit_tests/TestXmlUtil.h b/model/unit_tests/TestXmlUtil.h new file mode 100644 index 00000000..f104b53d --- /dev/null +++ b/model/unit_tests/TestXmlUtil.h @@ -0,0 +1,42 @@ +/* TestXmlUtil.h + * + * Copyright (C) 2018 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#include + + +class TestXmlUtil : public QObject +{ + Q_OBJECT + +private slots: + void getStringAttr(); + void getDoubleAttr(); + void getBoolAttr(); + void getIntAttr(); + void getUIntAttr(); + void getLengthAttr(); + void getWeightAttr(); + void getAlignmentAttr(); + void getWrapModeAttr(); + + // TODO: test setters +}; + + diff --git a/model/unit_tests/Test_Constants.h b/model/unit_tests/Test_Constants.h new file mode 100644 index 00000000..f57b640b --- /dev/null +++ b/model/unit_tests/Test_Constants.h @@ -0,0 +1,41 @@ +/* Test_Constants.h + * + * Copyright (C) 2019 Jim Evins + * + * This file is part of gLabels-qt. + * + * gLabels-qt 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 3 of the License, or + * (at your option) any later version. + * + * gLabels-qt 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 gLabels-qt. If not, see . + */ + +#ifndef test_Constants_h +#define test_Constants_h + + +namespace glabels +{ + namespace test + { + + const char* blue_8x8_png = "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw1AUhU9TpVIqDhYRcchQnSyIijhqFYpQIdQKrTqYvPQPmjQkKS6OgmvBwZ/FqoOLs64OroIg+APi4uqk6CIl3pcUWsR44fE+zrvn8N59gNCoMM3qGgc03TbTyYSYza2KoVeEEMYAAhBkZhlzkpSCb33dUx/VXZxn+ff9Wb1q3mJAQCSeZYZpE28QT2/aBud94igrySrxOfGYSRckfuS64vEb56LLAs+Mmpn0PHGUWCx2sNLBrGRqxFPEMVXTKV/Ieqxy3uKsVWqsdU/+wkheX1nmOq1hJLGIJUgQoaCGMiqwEaddJ8VCms4TPv4h1y+RSyFXGYwcC6hCg+z6wf/g92ytwuSElxRJAN0vjvMxAoR2gWbdcb6PHad5AgSfgSu97a82gJlP0uttLXYE9G0DF9dtTdkDLneAwSdDNmVXCtISCgXg/Yy+KQf03wLhNW9urXOcPgAZmlXqBjg4BEaLlL3u8+6ezrn929Oa3w/Q2XJm1/XlIwAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAuIwAALiMBeKU/dgAAAAd0SU1FB+MHChYzAoNXJCYAAAAWSURBVBjTY2Rg+P+fAQ9gYiAAhocCABBdAg7zMxsKAAAAAElFTkSuQmCC"; + const char* green_8x8_png = "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kTtIw1AUhv+mikUqDlYQcchQnSz4Qhy1CkWoEGqFVh1MbvqCJg1Jiouj4Fpw8LFYdXBx1tXBVRAEHyAurk6KLlLiuUmhRYwXDvfjv/f/OfdcQKiXmWZ1jAGabpupRFzMZFfFrleE0E81jpDMLGNOkpLwXV/3CPD9Lsaz/O/9uXrUnMWAgEg8ywzTJt4gnt60Dc77xBFWlFXic+JRkxokfuS64vEb54LLAs+MmOnUPHGEWCy0sdLGrGhqxFPEUVXTKV/IeKxy3uKslaus2Sd/YTinryxznWoICSxiCRJEKKiihDJsxGjXSbGQovO4j3/Q9UvkUshVAiPHAirQILt+8D/4PVsrPznhJYXjQOeL43wMA127QKPmON/HjtM4AYLPwJXe8lfqwMwn6bWWFj0CereBi+uWpuwBlzvAwJMhm7IrBamEfB54P6NvygJ9t0D3mje35jlOH4A0zSp5AxwcAiMFyl73eXeofW7/3mnO7wdSvnKatbS90wAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB+MIFQovFXnldrAAAAAWSURBVBjTY2T4z/CfAQ9gYiAAhocCABFcAg5KXrI7AAAAAElFTkSuQmCC"; + const char* yellow_8x8_png = "iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAABhGlDQ1BJQ0MgcHJvZmlsZQAAKJF9kT1Iw0AcxV9TpUUqIhYs4pChOlkQFXHUKhShQqgVWnUwufQLmjQkLS6OgmvBwY/FqoOLs64OroIg+AHi4uqk6CIl/i8ptIjx4Lgf7+497t4BQqPMNKtrHND0qplKxMVMdlUMvCKIQfjRj4jMLGNOkpLwHF/38PH1LsazvM/9OXrVnMUAn0g8ywyzSrxBPL1ZNTjvE4dZUVaJz4nHTLog8SPXFZffOBccFnhm2Eyn5onDxGKhg5UOZkVTI54ijqqaTvlCxmWV8xZnrVxjrXvyF4Zy+soy12kOI4FFLEGCCAU1lFBGFTFadVIspGg/7uEfcvwSuRRylcDIsYAKNMiOH/wPfndr5Scn3KRQHOh+se2PESCwCzTrtv19bNvNE8D/DFzpbX+lAcx8kl5va9EjoG8buLhua8oecLkDRJ4M2ZQdyU9TyOeB9zP6piwwcAv0rLm9tfZx+gCkqavkDXBwCIwWKHvd493Bzt7+PdPq7wcjL3KHuPu4MgAAAAZiS0dEAP8A/wD/oL2nkwAAAAlwSFlzAAAOxAAADsQBlSsOGwAAAAd0SU1FB+MIFwMyBT7m+cwAAAAWSURBVBjTY/z/n+E/Ax7AxEAADA8FABdkAw08uCaCAAAAAElFTkSuQmCC"; + const char* red_8x8_svg = ""; + const char* cyan_8x8_svg = ""; + const char* magenta_8x8_svg = ""; + + } +} + + +#endif // test_Constants_h diff --git a/model/unit_tests/data/glabels-3/crew-orientation-list.csv b/model/unit_tests/data/glabels-3/crew-orientation-list.csv new file mode 100644 index 00000000..1af4b38a --- /dev/null +++ b/model/unit_tests/data/glabels-3/crew-orientation-list.csv @@ -0,0 +1,5 @@ +Name,Department,SN +"Jim Kirk",Management,"SC937-0176 CEC" +"Mr. Spock",Sciences,S179-276SP +"Leonard McCoy",Medicine,unknown +"Montgomery Scott",Engineering,SE-197-54T diff --git a/model/unit_tests/data/glabels-3/crew-orientation-name-tags-7.glabels b/model/unit_tests/data/glabels-3/crew-orientation-name-tags-7.glabels new file mode 100644 index 00000000..29546fa3 --- /dev/null +++ b/model/unit_tests/data/glabels-3/crew-orientation-name-tags-7.glabels @@ -0,0 +1,25 @@ + + + + + + Hello, my name is + + + + + + + + Department: + + + + + + diff --git a/COPYING-TEMPLATES b/templates/LICENSE similarity index 100% rename from COPYING-TEMPLATES rename to templates/LICENSE diff --git a/templates/brother-other-templates.xml b/templates/brother-other-templates.xml index cc554e30..437538a7 100644 --- a/templates/brother-other-templates.xml +++ b/templates/brother-other-templates.xml @@ -7,49 +7,75 @@ ********************************************************************* Labels for the Brother QL-500/550/650 PC Label Printers - These templates are based on the exact media sizes and measurements - in version 1.1 of the 'Brother-QL-500-ptouch.ppd' file. - Currently only have templates for a few of the popular sizes. + REFERENCES: + + Brother Industries, Ltd. (October 3, 2011), "Brother QL Series + Command Reference (QL-500/550/560/570/580N/650TD/700/1050/1060N)", + http://download.brother.com/welcome/docp000678/cv_qlseries_eng_raster_600.pdf + ********************************************************************* ********************************************************************* --> -