├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── CMakeLists.txt ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Fixture.pro ├── LICENSE ├── PULL_REQUEST_TEMPLATE.md ├── README.MD ├── ROADMAP.md ├── resources ├── basic.qss ├── checkers.png ├── icons │ ├── angle-bottom.svg │ ├── angle-left.svg │ ├── angle-right.svg │ ├── angle-top.svg │ ├── check-tick.svg │ ├── close-white-dark.svg │ ├── close-white-lighter.svg │ └── close-white.svg ├── layermanager.qss ├── paintwidget.qss ├── resources.qrc ├── tool-menu.qss └── tools │ ├── paint.svg │ ├── pan.svg │ └── select.svg ├── src ├── dialogs │ ├── newdialog.cpp │ ├── newdialog.h │ └── newdialog.ui ├── layers │ ├── layer.cpp │ ├── layer.h │ ├── rasterlayer.cpp │ └── rasterlayer.h ├── main.cpp ├── mainwindow.cpp ├── mainwindow.h ├── mainwindow.ui ├── model │ ├── canvas.cpp │ ├── canvas.h │ ├── document.cpp │ ├── document.h │ ├── drawing.cpp │ └── drawing.h ├── tools │ ├── abstractperception.cpp │ ├── abstractperception.h │ ├── abstractselection.cpp │ ├── abstractselection.h │ ├── components │ │ ├── boundingrectitem.cpp │ │ └── boundingrectitem.h │ ├── pan.cpp │ ├── pan.h │ ├── tool.cpp │ ├── tool.h │ ├── tooloptions │ │ ├── pan_menu.cpp │ │ ├── pan_menu.h │ │ ├── pan_menu.ui │ │ ├── toolmenu.cpp │ │ ├── toolmenu.h │ │ ├── transform_menu.cpp │ │ └── transform_menu.h │ ├── transform.cpp │ └── transform.h └── widgets │ ├── layermanager.cpp │ ├── layermanager.h │ ├── paintwidget.cpp │ └── paintwidget.h └── uncrustify.cfg /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **Describe the bug** 8 | A clear and concise description of what the bug is. 9 | 10 | **To Reproduce** 11 | Steps to reproduce the behavior: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **Expected behavior** 18 | A clear and concise description of what you expected to happen. 19 | 20 | **Screenshots** 21 | If applicable, add screenshots to help explain your problem. 22 | 23 | **Desktop (please complete the following information):** 24 | - OS: [e.g. Ubuntu] 25 | - Version [e.g. 22] 26 | 27 | **Additional context** 28 | Add any other context about the problem here. 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | 5 | --- 6 | 7 | **Is your feature request related to a problem? Please describe.** 8 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 9 | 10 | **Describe the solution you'd like** 11 | A clear and concise description of what you want to happen. 12 | 13 | **Describe alternatives you've considered** 14 | A clear and concise description of any alternative solutions or features you've considered. 15 | 16 | **Additional context** 17 | Add any other context or screenshots about the feature request here. 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Fixture.pro.user 2 | *~ 3 | *# 4 | *.autosave 5 | *.orig 6 | .idea -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | set(VERSION_MAJOR "0") 4 | set(VERSION_MINOR "0") 5 | set(VERSION_PATCH "0") 6 | 7 | project(fixture) 8 | 9 | set( CMAKE_BUILD_TYPE Debug) 10 | set( CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib ) 11 | set( CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin ) 12 | set( CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib ) 13 | 14 | if( "${CMAKE_BUILD_TYPE}" STREQUAL "Debug") 15 | if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") 16 | # using clang 17 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Weverything") 18 | elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") 19 | # using gcc 20 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra") 21 | elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "Intel") 22 | # using intel c compiler 23 | elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") 24 | # using visual studio c compiler 25 | endif() 26 | endif() 27 | 28 | find_package(Qt5Widgets REQUIRED) 29 | find_package(Qt5Core REQUIRED) 30 | find_package(Qt5Gui REQUIRED) 31 | 32 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 33 | set(CMAKE_AUTOMOC ON) 34 | set(CMAKE_AUTOUIC ON) 35 | set(CMAKE_AUTORCC ON) 36 | 37 | set(SOURCES 38 | resources/resources.qrc 39 | src/dialogs/newdialog.cpp 40 | src/widgets/paintwidget.cpp 41 | src/main.cpp 42 | src/mainwindow.cpp 43 | src/widgets/layermanager.cpp 44 | src/model/canvas.cpp 45 | src/tools/tool.cpp 46 | src/tools/transform.cpp 47 | src/tools/pan.cpp 48 | src/layers/rasterlayer.cpp 49 | src/layers/layer.cpp 50 | src/model/drawing.cpp 51 | src/tools/abstractselection.cpp 52 | src/tools/abstractperception.cpp 53 | src/tools/components/boundingrectitem.cpp 54 | src/tools/tooloptions/transform_menu.cpp 55 | src/tools/tooloptions/toolmenu.cpp 56 | src/tools/tooloptions/pan_menu.cpp 57 | src/model/document.cpp 58 | src/mainwindow.ui 59 | src/dialogs/newdialog.ui 60 | src/tools/tooloptions/pan_menu.ui 61 | ) 62 | 63 | add_executable(fixture ${SOURCES}) 64 | target_link_libraries(fixture 65 | Qt5::Widgets 66 | Qt5::Gui 67 | Qt5::Core 68 | ) 69 | 70 | install(TARGETS fixture DESTINATION bin) 71 | 72 | set(CPACK_PACKAGE_VERSION_MAJOR ${VERSION_MAJOR}) 73 | set(CPACK_PACKAGE_VERSION_MINOR ${VERSION_MINOR}) 74 | set(CPACK_PACKAGE_VERSION_PATCH ${VERSION_PATCH}) 75 | set(CPACK_PACKAGE_CONTACT "eyeon@disroot.org") 76 | include(CPack) 77 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at eyeon@disroot.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish to make via issue, 4 | email, Telegram or any other method with the owners of this repository before making a change. 5 | 6 | Please check the [ROADMAP file](https://github.com/eyeon/Fixture/blob/master/README.MD) before raising an issue. There's a chance that the missing feature you 7 | are expecting is already in our TODO list. We will get there soon. 8 | 9 | Please note we have a [code of conduct](https://github.com/eyeon/Fixture/blob/master/CODE_OF_CONDUCT.md), please follow it in all your interactions with the project. 10 | 11 | Join the telegram channel at: https://t.me/eyeon_social if you have any queries, suggestions etc. 12 | 13 | ## General rules 14 | 15 | - Commits must be atomic. They should reflect a set of distinct changes as a single operation that solve one problem at a time. 16 | - Commit messages must be meaningful, clear and concise. Stress on your grammar, it's fine to google the correct way to say something, English isn't everyone's first language. 17 | - Commits should do exactly what their message says. If a commit affects quite a few files, and does a few related things, mention them in the commit description. Refrain from using `git commit -m` in such cases. 18 | - IDE configuration files/temp files are not meant to be a part of the repository and they should remain so. 19 | - Variable names like `a`, `x`, `foo` are unacceptable, use variable names that describes its purpose. 20 | - Format the file with `uncrustify` using the `uncrustify.cfg` provided with the project before submitting any patch. 21 | 22 | ## Pull Request Process 23 | 24 | - Check if there's already a PR assigned to someone that solves a similar problem to yours. 25 | - Ensure that the pull request is being sent from a hotfix/feature branch and not from `master`. 26 | - Check the issue page if the feature you are trying to add/bug you are trying to fix is already in the list. If it does, mention the issue number in parentheses with the pull request. 27 | - Provide a clear, concise description of why this pull request is necessary and what value it adds. Make sure that your PR doesn't contain any more or less than what the PR description says. 28 | - One PR should do only one thing only. No more, no less. If you want to implement two new features, open two PRs. 29 | -------------------------------------------------------------------------------- /Fixture.pro: -------------------------------------------------------------------------------- 1 | #------------------------------------------------- 2 | # 3 | # Project created by QtCreator 2018-05-07T20:37:49 4 | # 5 | #------------------------------------------------- 6 | 7 | QT += core gui 8 | 9 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 10 | 11 | TARGET = Fixture 12 | TEMPLATE = app 13 | 14 | # The following define makes your compiler emit warnings if you use 15 | # any feature of Qt which has been marked as deprecated (the exact warnings 16 | # depend on your compiler). Please consult the documentation of the 17 | # deprecated API in order to know how to port your code away from it. 18 | DEFINES += QT_DEPRECATED_WARNINGS 19 | #CONFIG += sanitizer sanitize_address | We don't need that yet 20 | 21 | # You can also make your code fail to compile if you use deprecated APIs. 22 | # In order to do so, uncomment the following line. 23 | # You can also select to disable deprecated APIs only up to a certain version of Qt. 24 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 25 | 26 | 27 | SOURCES += \ 28 | src/dialogs/newdialog.cpp \ 29 | src/widgets/paintwidget.cpp \ 30 | src/main.cpp \ 31 | src/mainwindow.cpp \ 32 | src/widgets/layermanager.cpp \ 33 | src/model/canvas.cpp \ 34 | src/tools/tool.cpp \ 35 | src/tools/transform.cpp \ 36 | src/tools/pan.cpp \ 37 | src/layers/rasterlayer.cpp \ 38 | src/layers/layer.cpp \ 39 | src/model/drawing.cpp \ 40 | src/tools/abstractselection.cpp \ 41 | src/tools/abstractperception.cpp \ 42 | src/tools/components/boundingrectitem.cpp \ 43 | src/tools/tooloptions/transform_menu.cpp \ 44 | src/tools/tooloptions/pan_menu.cpp \ 45 | src/tools/tooloptions/toolmenu.cpp \ 46 | src/model/document.cpp 47 | 48 | HEADERS += \ 49 | src/dialogs/newdialog.h \ 50 | src/widgets/paintwidget.h \ 51 | src/mainwindow.h \ 52 | src/widgets/layermanager.h \ 53 | src/model/canvas.h \ 54 | src/tools/tool.h \ 55 | src/tools/transform.h \ 56 | src/tools/pan.h \ 57 | src/layers/rasterlayer.h \ 58 | src/layers/layer.h \ 59 | src/model/drawing.h \ 60 | src/tools/abstractselection.h \ 61 | src/tools/abstractperception.h \ 62 | src/tools/tooloptions/transform_menu.h \ 63 | src/tools/tooloptions/pan_menu.h \ 64 | src/tools/components/boundingrectitem.h \ 65 | src/tools/tooloptions/toolmenu.h \ 66 | src/model/document.h 67 | 68 | FORMS += \ 69 | src/mainwindow.ui \ 70 | src/dialogs/newdialog.ui \ 71 | src/tools/tooloptions/pan_menu.ui 72 | 73 | RESOURCES += \ 74 | resources/resources.qrc 75 | 76 | DISTFILES += \ 77 | resources/checkers.png \ 78 | resources/icons/angle-bottom.svg \ 79 | resources/icons/angle-left.svg \ 80 | resources/icons/angle-right.svg \ 81 | resources/icons/angle-top.svg \ 82 | resources/icons/close-white-dark.svg \ 83 | resources/icons/close-white-lighter.svg \ 84 | resources/icons/close-white.svg \ 85 | resources/tools/select.svg \ 86 | resources/tools/pan.svg \ 87 | resources/basic.qss \ 88 | resources/layermanager.qss \ 89 | resources/paintwidget.qss \ 90 | uncrustify.cfg 91 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Fixture -- A no bullshit image editor 2 | 3 | Copyright (C) 2018 Dedipyaman Das and Kuntal Majumder 4 | 5 | This software is provided 'as-is', without any express or implied 6 | warranty. In no event will the authors be held liable for any damages 7 | arising from the use of this software. 8 | 9 | Permission is granted to anyone to use this software for any purpose, 10 | including commercial applications, and to alter it and redistribute it 11 | freely, subject to the following restrictions: 12 | 13 | 1. The origin of this software must not be misrepresented; you must not 14 | claim that you wrote the original software. If you use this software 15 | in a product, an acknowledgment in the product documentation is required. 16 | 2. Altered source versions must be plainly marked as such, and must not be 17 | misrepresented as being the original software. 18 | 3. This notice must not be removed or altered from any source distribution. 19 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Description 4 | 5 | 6 | ## Related Issue 7 | 8 | 9 | 10 | 11 | 12 | ## Motivation and Context 13 | 14 | 15 | ## How Has This Been Tested? 16 | 17 | 18 | 19 | 20 | ## Screenshots (if appropriate): 21 | 22 | ## Types of changes 23 | 24 | - [ ] Bug fix (non-breaking change which fixes an issue) 25 | - [ ] New feature (non-breaking change which adds functionality) 26 | - [ ] Breaking change (fix or feature that would cause existing functionality to change) 27 | 28 | ## Checklist: 29 | 30 | 31 | - [ ] My code follows the code style of this project. 32 | - [ ] My change requires a change to the documentation. 33 | - [ ] I have updated the documentation accordingly. 34 | - [ ] I have read the **CONTRIBUTING** document. 35 | -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # Fixture 2 | ![Fixture Logo](https://image.ibb.co/db15Lz/logo_small.png) 3 | 4 | **Fixture** is a no-bullshit, free and open source raster graphics editor. 5 | 6 | ## Motivation 7 | 8 | **Fixture** is an attempt to be a multiplatform, free and open source replacement for Adobe Photoshop. This aims to solve the need for having a proper raster graphics editor with a clean and focused UI, to help graphic artists and regular users morph or create images quickly, even on a Linux distribution. 9 | 10 | **Example window [WIP]** 11 | ![Fixture screenshot(WIP)](https://image.ibb.co/mL8Hdz/screenshot_20180727_153726.png) 12 | 13 | ## Installation 14 | 15 | Fixture, as of now is a work in progress. The master branch has the latest compilable but not necessary the most stable version. To grab a copy of Fixture, simply clone the repository. 16 | 17 | `git clone https://github.com/eyeon/Fixture.git ` 18 | 19 | Install the latest version of Qt Creator from [here](https://www.qt.io/download). 20 | You can then open the `Fixture.pro` file with Qt Creator and compile the program to an executable. You can find more info [here](https://doc.qt.io/qtcreator/creator-building-running.html). 21 | 22 | If you'd like to use `qmake`, make sure you are running it on or above Qt 5.10. 23 | 24 | We also plan to provide `appimages` and builds for Windows & Mac in the future. 25 | 26 | ## Support 27 | If you are having troubles figuring out how to make Fixture work, or have any queries/suggestions, join our telegram group: 28 | https://t.me/eyeon_social. You can mailto:eyeon@disroot.org. 29 | 30 | ## Roadmap 31 | 32 | The [Roadmap file](https://github.com/eyeon/Fixture/blob/master/ROADMAP.md) contains a high level description of our TODO list in terms of feature addition. You can help us extend it by adding features we might have missed. 33 | 34 | ## Contributors 35 | 36 | To contribute to Fixture, fork the repo, create a new branch and send us a pull request. Make sure you read the [CONTRIBUTING.md](https://github.com/eyeon/Fixture/blob/master/CONTRIBUTING.md) before sending us PRs. We welcome all sorts of contribution ranging from documentation to bug fixes. If you can find any bugs or want us to implement a feature, raise an issue. Keep in mind that it is a Work in Progress and what you could be asking for is already in our TODO list. 37 | 38 | ## License 39 | 40 | Fixture is licensed under Eyeon General License which is adapted from [zLib license](https://www.zlib.net/zlib_license.html). Refer to the LICENSE file for more information. 41 | -------------------------------------------------------------------------------- /ROADMAP.md: -------------------------------------------------------------------------------- 1 | # Fixture Roadmap 2 | 3 | This is the series of phases that we are on track to go about. Work in progress. 4 | 5 | ### TODOs: 6 | 7 | - [X] **Phase 1** 8 | 9 | 10 | - Have a minimal image viewer to open various formats. 11 | - Different Images can be stacked up onto each other and rearranged using Layers window. 12 | - Ability to create custom canvas with predefined sizes like A4, A3, Letter. 13 | 14 | 15 | 16 | - [ ] **Phase 2** 17 | 18 | 19 | - Community contribution support and basic documentation. 20 | - Document saving/loading to files. 21 | - Basic tool option menu to modify tool behavior. 22 | 23 | 24 | 25 | 26 | - [ ] **Phase 3** 27 | 28 | 29 | 30 | - Resizing and cropping options operating globally. 31 | - Adjustments like Brightness, Saturation, Contrast, Color implemented globally. 32 | - Start implementing previously mentioned features layerwise. 33 | 34 | 35 | 36 | 37 | - [ ] **Phase 4** 38 | 39 | 40 | 41 | - Add shape selection tools. 42 | - Add a Lasso tool. 43 | - Implement Phase 2 features with masks created with selection tools. 44 | - Control Alpha with the masks generated by the selection tools. 45 | 46 | 47 | 48 | 49 | - [ ] **Phase 5** 50 | 51 | 52 | 53 | - Add a Text Tool. 54 | - Text Generated with the text tool must be a vector image. 55 | - Implement various typographical features onto the text tool. 56 | 57 | 58 | 59 | 60 | - [ ] **Phase 6** 61 | 62 | 63 | 64 | - Add a Shapes tool for generating common shapes. 65 | - The shapes generated would be vector image. 66 | - Implement color masks. 67 | -------------------------------------------------------------------------------- /resources/basic.qss: -------------------------------------------------------------------------------- 1 | QMenu::item:selected { 2 | background-color: #0066ff; 3 | } 4 | 5 | QMainWindow { 6 | background-color: #1a1a1a; 7 | } 8 | 9 | #mainToolBar { 10 | background-color: #262626; 11 | } 12 | 13 | #toolMenuBar { 14 | background-color: rgb(75, 75, 75); 15 | border-top : 1px solid #252525; 16 | border-bottom : 1px solid #252525; 17 | } 18 | 19 | QToolButton{ 20 | padding: 10; 21 | border: none; 22 | } 23 | 24 | QToolButton:hover{ 25 | background-color: #333; 26 | } 27 | 28 | QToolButton:checked{ 29 | background-color: #111; 30 | } 31 | 32 | QTabBar { 33 | background-color: #1a1a1a; 34 | border-bottom: 1px solid #262626; 35 | margin: 0px; 36 | } 37 | 38 | QTabBar::tab { 39 | padding: 2px; 40 | min-width: 200px; 41 | max-width: 250px; 42 | height: 15px; 43 | font-size: 12px; 44 | background: #262626; 45 | color: #cccccc; 46 | border: 1px solid #1a1a1a; 47 | } 48 | QTabBar::close-button { 49 | subcontrol-position: right; 50 | } 51 | 52 | QTabBar::tab:hover { 53 | background: #333333 54 | } 55 | 56 | QTabBar::tab:selected { 57 | color: #d9d9d9; 58 | background: #004d99; 59 | } 60 | 61 | QTabBar::close-button { 62 | image: url(:/icons/close-white.svg); 63 | } 64 | 65 | QTabBar::close-button:hover { 66 | image: url(:/icons/close-white-lighter.svg); 67 | } 68 | 69 | QMdiSubWindow QScrollBar:horizontal { 70 | border-top: 1px solid #1a1a1a; 71 | background: #262626; 72 | height: 15px; 73 | margin: 0px 20px 0 20px; 74 | } 75 | 76 | QMdiSubWindow QScrollBar::handle:horizontal { 77 | background: #404040; 78 | min-width: 20px; 79 | border: 1px solid #333333; 80 | } 81 | 82 | QMdiSubWindow QScrollBar::add-line:horizontal { 83 | border: 1px solid #1a1a1a; 84 | background: #1a1a1a; 85 | width: 20px; 86 | subcontrol-position: right; 87 | subcontrol-origin: margin; 88 | } 89 | 90 | QMdiSubWindow QScrollBar::sub-line:horizontal { 91 | border: 1px solid #1a1a1a; 92 | background: #1a1a1a; 93 | width: 20px; 94 | subcontrol-position: left; 95 | subcontrol-origin: margin; 96 | } 97 | 98 | QMdiSubWindow QScrollBar:left-arrow:horizontal { 99 | height: 20px; 100 | width: 20px; 101 | margin-left: 5px; 102 | margin-top: 5px; 103 | image: url(:/icons/angle-left.svg); 104 | } 105 | 106 | QMdiSubWindow QScrollBar:right-arrow:horizontal { 107 | height: 20px; 108 | width: 20px; 109 | margin-left: 5px; 110 | margin-top: 5px; 111 | image: url(:/icons/angle-right.svg); 112 | } 113 | QMdiSubWindow QScrollBar:vertical { 114 | border-left: 1px solid #1a1a1a; 115 | background: #262626; 116 | width: 15px; 117 | margin: 22px 0 22px 0; 118 | } 119 | 120 | QMdiSubWindow QScrollBar::handle:vertical { 121 | background: #404040; 122 | min-height: 20px; 123 | border: 1px solid #333333; 124 | } 125 | 126 | QMdiSubWindow QScrollBar::add-line:vertical { 127 | border: 1px solid #1a1a1a; 128 | background: #1a1a1a; 129 | height: 20px; 130 | subcontrol-position: bottom; 131 | subcontrol-origin: margin; 132 | } 133 | 134 | QMdiSubWindow QScrollBar::sub-line:vertical { 135 | border: 1px solid #1a1a1a; 136 | background: #1a1a1a; 137 | height: 20px; 138 | subcontrol-position: top; 139 | subcontrol-origin: margin; 140 | } 141 | 142 | QMdiSubWindow QScrollBar:up-arrow:vertical { 143 | height: 20px; 144 | width: 20px; 145 | margin-left: 5px; 146 | margin-top: 5px; 147 | image: url(:/icons/angle-top.svg); 148 | } 149 | 150 | QMdiSubWindow QScrollBar:down-arrow:vertical { 151 | height: 20px; 152 | width: 20px; 153 | margin-left: 5px; 154 | margin-top: 5px; 155 | image: url(:/icons/angle-bottom.svg); 156 | } 157 | 158 | 159 | -------------------------------------------------------------------------------- /resources/checkers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eyeon/Fixture/5b8b7aefa500ff548b2054a714d363016adac46f/resources/checkers.png -------------------------------------------------------------------------------- /resources/icons/angle-bottom.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/icons/angle-left.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/icons/angle-right.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/icons/angle-top.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/icons/check-tick.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /resources/icons/close-white-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/icons/close-white-lighter.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/icons/close-white.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /resources/layermanager.qss: -------------------------------------------------------------------------------- 1 | QListWidget{ 2 | background-color: #262626; 3 | max-width: 300px; 4 | border: 1px solid #1a1a1a; 5 | } 6 | 7 | QListWidget::item{ 8 | background-color: #333; 9 | color : #aaa; 10 | border: 1px solid #262626; 11 | } 12 | 13 | QListWidget::item:selected{ 14 | background-color: #aaa; 15 | color : #333; 16 | } 17 | -------------------------------------------------------------------------------- /resources/paintwidget.qss: -------------------------------------------------------------------------------- 1 | PaintWidget { 2 | background-color: #1a1a1a; 3 | } 4 | -------------------------------------------------------------------------------- /resources/resources.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | checkers.png 4 | 5 | 6 | basic.qss 7 | paintwidget.qss 8 | layermanager.qss 9 | tool-menu.qss 10 | 11 | 12 | icons/close-white-lighter.svg 13 | icons/close-white.svg 14 | icons/close-white-dark.svg 15 | icons/angle-top.svg 16 | icons/angle-right.svg 17 | icons/angle-left.svg 18 | icons/angle-bottom.svg 19 | tools/select.svg 20 | tools/pan.svg 21 | icons/check-tick.svg 22 | tools/paint.svg 23 | 24 | 25 | -------------------------------------------------------------------------------- /resources/tool-menu.qss: -------------------------------------------------------------------------------- 1 | QFrame { 2 | padding: 7px; 3 | padding-left: 10px; 4 | font-size: 10px; 5 | } 6 | 7 | QCheckBox { 8 | color: #d9d9d9; 9 | font-size: 11px; 10 | } 11 | 12 | QCheckBox:disabled { 13 | color: #aaaaaa; 14 | } 15 | 16 | QCheckBox::indicator { 17 | width: 11px; 18 | height: 11px; 19 | background-color: #262626; 20 | border: 1px solid #1a1a1a; 21 | border-radius: 3px; 22 | } 23 | 24 | QCheckBox::indicator:checked { 25 | image: url(:/icons/check-tick.svg); 26 | } 27 | 28 | QPushButton { 29 | background-color: #262626; 30 | border : none; 31 | } 32 | 33 | -------------------------------------------------------------------------------- /resources/tools/paint.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 10 | 11 | -------------------------------------------------------------------------------- /resources/tools/pan.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 42 | 43 | 45 | image/svg+xml 46 | 48 | 49 | 50 | 51 | 52 | 57 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /resources/tools/select.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 40 | 42 | 43 | 45 | image/svg+xml 46 | 48 | 49 | 50 | 51 | 52 | 57 | 60 | 68 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /src/dialogs/newdialog.cpp: -------------------------------------------------------------------------------- 1 | #include "newdialog.h" 2 | #include "ui_newdialog.h" 3 | 4 | NewDialog::NewDialog(QWidget *parent) : 5 | QDialog(parent), ui(new Ui::NewDialog) 6 | { 7 | ui->setupUi(this); 8 | ui->actionBtnsContainer->setAlignment(Qt::AlignTop); 9 | 10 | initPresetCombo(); 11 | initDimensionCombos(); 12 | initSignalSlots(); 13 | } 14 | 15 | NewDialog::~NewDialog() 16 | { 17 | delete ui; 18 | } 19 | 20 | void NewDialog::initSignalSlots() 21 | { 22 | connect(ui->presetCombo, SIGNAL(currentIndexChanged(int)), 23 | this, SLOT(switchPreset(int))); 24 | 25 | connect(ui->widthUnitCombo, SIGNAL(currentIndexChanged(int)), 26 | ui->heightUnitCombo, SLOT(setCurrentIndex(int))); 27 | 28 | connect(ui->heightUnitCombo, SIGNAL(currentIndexChanged(int)), 29 | ui->widthUnitCombo, SLOT(setCurrentIndex(int))); 30 | 31 | connect(ui->sizeCombo, SIGNAL(currentIndexChanged(QString)), 32 | this, SLOT(displaySize(QString))); 33 | 34 | connect(ui->widthTxt, SIGNAL(textEdited(const QString&)), 35 | this, SLOT(switchToCustomPreset(const QString&))); 36 | 37 | connect(ui->heightTxt, SIGNAL(textEdited(const QString&)), 38 | this, SLOT(switchToCustomPreset(const QString&))); 39 | } 40 | 41 | void NewDialog::switchToCustomPreset(const QString &string) 42 | { 43 | ui->presetCombo->setCurrentIndex(Preset::CUSTOM); 44 | } 45 | 46 | void NewDialog::initPresetCombo() 47 | { 48 | QMap presets = getPresetList(); 49 | ui->presetCombo->addItems(QStringList(presets.values())); 50 | ui->presetCombo->setCurrentIndex(Preset::DEFAULT); 51 | } 52 | 53 | void NewDialog::initDimensionCombos() 54 | { 55 | QMap unitMap = getDimensionUnitList(); 56 | 57 | ui->widthUnitCombo->addItems(QStringList(unitMap.values())); 58 | ui->widthUnitCombo->setCurrentIndex(Canvas::DimensionUnit::PIXELS); 59 | 60 | ui->heightUnitCombo->addItems(QStringList(unitMap.values())); 61 | ui->heightUnitCombo->setCurrentIndex(Canvas::DimensionUnit::PIXELS); 62 | } 63 | 64 | QMap NewDialog::getDimensionUnitList() 65 | { 66 | QMap units; 67 | 68 | units.insert(Canvas::DimensionUnit::PIXELS, "Pixels"); 69 | units.insert(Canvas::DimensionUnit::INCHES, "Inches"); 70 | units.insert(Canvas::DimensionUnit::CENTIMETERS, "Centimeters"); 71 | units.insert(Canvas::DimensionUnit::MILLIMETERS, "Millimeters"); 72 | units.insert(Canvas::DimensionUnit::POINTS, "Points"); 73 | units.insert(Canvas::DimensionUnit::PICAS, "Picas"); 74 | 75 | return units; 76 | } 77 | 78 | QMap NewDialog::getPresetList() 79 | { 80 | QMap presets; 81 | 82 | presets.insert(Preset::DEFAULT, "Fixture Default"); 83 | presets.insert(Preset::INTERNATIONAL, "International"); 84 | presets.insert(Preset::US_PAPER, "US Paper"); 85 | presets.insert(Preset::CUSTOM, "Custom"); 86 | 87 | return presets; 88 | } 89 | 90 | QMap NewDialog::getInternationalList() 91 | { 92 | QMap stdSizes; 93 | 94 | stdSizes.insert("A1", { 23.39, 33.11, Canvas::INCHES }); 95 | stdSizes.insert("A2", { 16.54, 23.39, Canvas::INCHES }); 96 | stdSizes.insert("A3", { 11.69, 16.54, Canvas::INCHES }); 97 | stdSizes.insert("A4", { 8.27, 11.69, Canvas::INCHES }); 98 | stdSizes.insert("A5", { 5.83, 8.27, Canvas::INCHES }); 99 | 100 | return stdSizes; 101 | } 102 | 103 | QMap NewDialog::getUSPaperList() 104 | { 105 | QMap stdSizes; 106 | 107 | stdSizes.insert("Letter", { 8.5, 11, Canvas::INCHES }); 108 | stdSizes.insert("Legal", { 8.5, 14, Canvas::INCHES }); 109 | stdSizes.insert("Ledger", { 11, 17, Canvas::INCHES }); 110 | stdSizes.insert("Tabloid", { 17, 11, Canvas::INCHES }); 111 | stdSizes.insert("Executive", { 7.25, 10.55, Canvas::INCHES }); 112 | 113 | return stdSizes; 114 | } 115 | 116 | void NewDialog::switchPreset(int index) 117 | { 118 | ui->sizeCombo->setDisabled(false); 119 | 120 | switch (index) { 121 | case Preset::INTERNATIONAL: 122 | ui->sizeCombo->clear(); 123 | _currSize = getInternationalList(); 124 | ui->sizeCombo->addItems(QStringList(_currSize.keys())); 125 | break; 126 | 127 | case Preset::US_PAPER: 128 | ui->sizeCombo->clear(); 129 | _currSize = getUSPaperList(); 130 | ui->sizeCombo->addItems(QStringList(_currSize.keys())); 131 | break; 132 | 133 | case Preset::DEFAULT: 134 | ui->sizeCombo->clear(); 135 | ui->sizeCombo->setDisabled(true); 136 | displaySizeContents(NewDialog::Default); 137 | break; 138 | 139 | default: 140 | ui->sizeCombo->setDisabled(true); 141 | } 142 | } 143 | 144 | void NewDialog::displaySize(QString presetKey) 145 | { 146 | PageSize size = _currSize.value(presetKey); 147 | displaySizeContents(size); 148 | } 149 | 150 | void NewDialog::displaySizeContents(NewDialog::PageSize size) 151 | { 152 | ui->widthTxt->setText(QString::number(size.width)); 153 | ui->heightTxt->setText(QString::number(size.height)); 154 | ui->widthUnitCombo->setCurrentIndex(size.unit); 155 | ui->heightUnitCombo->setCurrentIndex(size.unit); 156 | } 157 | 158 | void NewDialog::on_actionOk_clicked() 159 | { 160 | // Needs a validation layer 161 | try { 162 | _dimensionUnit = 163 | (Canvas::DimensionUnit) ui->widthUnitCombo->currentIndex(); 164 | _resolution = (int) getDoubleValue(ui->resTxt); 165 | 166 | ui->heightTxt->setFocus(); 167 | int height = getPixelValue(ui->heightTxt); 168 | 169 | ui->widthTxt->setFocus(); 170 | int width = getPixelValue(ui->widthTxt); 171 | 172 | QString docName = ui->docNameVal->text(); 173 | 174 | QSharedDataPointer canvas = createCanvas(docName, width, height, 175 | _dimensionUnit, 176 | _resolution, 177 | Canvas::PPI); 178 | emit canvasAvailable(canvas); 179 | this->close(); 180 | } catch (const QString msg) { 181 | showZeroErrorMessage(msg); 182 | return; 183 | } 184 | } 185 | 186 | /** 187 | * @brief NewDialog::createCanvas Creates a new canvas with the given params 188 | * 189 | * Processes the dimensions with specified units 190 | * @param docName 191 | * @param width 192 | * @param height 193 | * @param dimUnit 194 | * @param resolution 195 | * @param resUnit 196 | * @return 197 | */ 198 | QSharedDataPointer NewDialog::createCanvas(QString docName, 199 | double width, double height, 200 | Canvas::DimensionUnit dimUnit, double resolution, 201 | Canvas::ResolutionUnit resUnit) const 202 | { 203 | return QSharedDataPointer(new Canvas(docName, width, height, 204 | dimUnit, resolution, resUnit)); 205 | } 206 | 207 | void NewDialog::on_actionCancel_clicked() 208 | { 209 | this->close(); 210 | } 211 | 212 | void NewDialog::on_widthTxt_editingFinished() 213 | { 214 | try { 215 | checkDimensionValidity(getDoubleValue(ui->widthTxt)); 216 | } 217 | catch (const QString msg) { 218 | ui->widthTxt->setFocus(); 219 | showZeroErrorMessage(msg); 220 | } 221 | } 222 | 223 | void NewDialog::showZeroErrorMessage(QString errMsg) 224 | { 225 | QMessageBox errDialogbox; 226 | errDialogbox.setText(errMsg); 227 | errDialogbox.exec(); 228 | } 229 | 230 | void NewDialog::on_heightTxt_editingFinished() 231 | { 232 | try { 233 | checkDimensionValidity(getDoubleValue(ui->heightTxt)); 234 | } 235 | catch (const QString msg) { 236 | ui->heightTxt->setFocus(); 237 | showZeroErrorMessage(msg); 238 | } 239 | } 240 | 241 | // This part needs to be heavily improved. 242 | void NewDialog::checkDimensionValidity(double fieldVal) 243 | { 244 | if (fieldVal < 1) { 245 | QString error = "Cannot set dimension value less than 1"; 246 | throw error; 247 | } 248 | } 249 | 250 | double NewDialog::getDoubleValue(QLineEdit *fieldVal) 251 | { 252 | QString fieldStr = fieldVal->text(); 253 | return fieldStr.toDouble(); 254 | } 255 | 256 | int NewDialog::getPixelValue(QLineEdit *field) 257 | { 258 | double val = getDoubleValue(field); 259 | 260 | switch (_dimensionUnit) { 261 | case Canvas::DimensionUnit::INCHES: 262 | return val * _resolution; 263 | 264 | case Canvas::DimensionUnit::CENTIMETERS: 265 | return val * _resolution / 2.54; 266 | 267 | case Canvas::DimensionUnit::MILLIMETERS: 268 | return val * _resolution / 25.4; 269 | 270 | case Canvas::DimensionUnit::POINTS: 271 | return val * _resolution / 72; 272 | 273 | case Canvas::DimensionUnit::PICAS: 274 | return val * _resolution / 16; 275 | 276 | default: 277 | return val; 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /src/dialogs/newdialog.h: -------------------------------------------------------------------------------- 1 | #ifndef NEWDIALOG_H 2 | #define NEWDIALOG_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "../model/canvas.h" 12 | 13 | namespace Ui { 14 | class NewDialog; 15 | } 16 | 17 | class NewDialog : public QDialog 18 | { 19 | Q_OBJECT 20 | 21 | public: 22 | explicit NewDialog(QWidget *parent = nullptr); 23 | ~NewDialog(); 24 | 25 | signals: 26 | void canvasAvailable(const QSharedDataPointer canvas); 27 | 28 | private slots: 29 | void on_actionOk_clicked(); 30 | void on_actionCancel_clicked(); 31 | void on_widthTxt_editingFinished(); 32 | void on_heightTxt_editingFinished(); 33 | 34 | void switchToCustomPreset(const QString &string); 35 | void switchPreset(int index); 36 | void displaySize(QString presetKey); 37 | private: 38 | struct PageSize { 39 | double width; 40 | double height; 41 | Canvas::DimensionUnit unit; 42 | } 43 | Default { 600, 600, Canvas::DimensionUnit::PIXELS }; 44 | 45 | enum Preset { 46 | DEFAULT, INTERNATIONAL, US_PAPER, CUSTOM 47 | }; 48 | QSharedDataPointer createCanvas(QString docName, double width, 49 | double height, 50 | Canvas::DimensionUnit dimUnit, double resolution, 51 | Canvas::ResolutionUnit resUnit) const; 52 | Ui::NewDialog *ui; 53 | int _resolution; 54 | Canvas::DimensionUnit _dimensionUnit; 55 | double getDoubleValue(QLineEdit *fieldVal); 56 | int getPixelValue(QLineEdit *field); 57 | void showZeroErrorMessage(QString fieldName); 58 | void checkDimensionValidity(double fieldVal); 59 | void displaySizeContents(NewDialog::PageSize size); 60 | QMap _currSize; 61 | 62 | void initPresetCombo(); 63 | void initDimensionCombos(); 64 | void initSignalSlots(); 65 | 66 | 67 | QMap getInternationalList(); 68 | QMap getUSPaperList(); 69 | QMap getDimensionUnitList(); 70 | QMap getPresetList(); 71 | }; 72 | 73 | #endif // NEWDIALOG_H 74 | -------------------------------------------------------------------------------- /src/dialogs/newdialog.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | NewDialog 4 | 5 | 6 | 7 | 0 8 | 0 9 | 535 10 | 331 11 | 12 | 13 | 14 | New 15 | 16 | 17 | 18 | 30 19 | 20 | 21 | 20 22 | 23 | 24 | 40 25 | 26 | 27 | 30 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | Name: 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | Document type: 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 0 60 | 0 61 | 62 | 63 | 64 | Qt::LeftToRight 65 | 66 | 67 | Canvas: 68 | 69 | 70 | true 71 | 72 | 73 | 74 | QFormLayout::WrapLongRows 75 | 76 | 77 | Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter 78 | 79 | 80 | Qt::AlignHCenter|Qt::AlignTop 81 | 82 | 83 | 7 84 | 85 | 86 | 9 87 | 88 | 89 | 9 90 | 91 | 92 | 93 | 94 | true 95 | 96 | 97 | 98 | 0 99 | 0 100 | 101 | 102 | 103 | Qt::LeftToRight 104 | 105 | 106 | Size: 107 | 108 | 109 | 110 | 111 | 112 | 113 | Width: 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 0 122 | 0 123 | 124 | 125 | 126 | QFrame::NoFrame 127 | 128 | 129 | QFrame::Plain 130 | 131 | 132 | 0 133 | 134 | 135 | 136 | 10 137 | 138 | 139 | 0 140 | 141 | 142 | 0 143 | 144 | 145 | 0 146 | 147 | 148 | 0 149 | 150 | 151 | 152 | 153 | 154 | 0 155 | 0 156 | 157 | 158 | 159 | 600 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 0 168 | 0 169 | 170 | 171 | 172 | -1 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | Height: 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 0 191 | 0 192 | 193 | 194 | 195 | QFrame::NoFrame 196 | 197 | 198 | QFrame::Plain 199 | 200 | 201 | 0 202 | 203 | 204 | 205 | 10 206 | 207 | 208 | 0 209 | 210 | 211 | 0 212 | 213 | 214 | 0 215 | 216 | 217 | 0 218 | 219 | 220 | 221 | 222 | 223 | 0 224 | 0 225 | 226 | 227 | 228 | 600 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 0 237 | 0 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | Resolution: 249 | 250 | 251 | 252 | 253 | 254 | 255 | Background: 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | White 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 0 273 | 0 274 | 275 | 276 | 277 | QFrame::NoFrame 278 | 279 | 280 | QFrame::Plain 281 | 282 | 283 | 0 284 | 285 | 286 | 287 | 10 288 | 289 | 290 | 0 291 | 292 | 293 | 0 294 | 295 | 296 | 0 297 | 298 | 299 | 0 300 | 301 | 302 | 303 | 304 | 305 | 0 306 | 0 307 | 308 | 309 | 310 | 72 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 0 319 | 0 320 | 321 | 322 | 323 | 324 | Pixels/Inch 325 | 326 | 327 | 328 | 329 | Dots/Inch 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | false 341 | 342 | 343 | false 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | Qt::Vertical 356 | 357 | 358 | 359 | 20 360 | 40 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | OK 371 | 372 | 373 | 374 | 375 | 376 | 377 | Cancel 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 0 386 | 100 387 | 388 | 389 | 390 | Save Preset 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | -------------------------------------------------------------------------------- /src/layers/layer.cpp: -------------------------------------------------------------------------------- 1 | #include "layer.h" 2 | #include "rasterlayer.h" 3 | 4 | /** 5 | * @brief Layer::layer Constructs a new Layer for storing Raster Images 6 | * @param name 7 | * @param image 8 | * @param x, y 9 | * @param height, width 10 | */ 11 | 12 | Layer::Layer(QString name, LayerType type) : 13 | _name(name), _type(type) 14 | { 15 | setText(name); 16 | setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter); 17 | setSizeHint(QSize(0, 40)); 18 | } 19 | 20 | Layer::~Layer() 21 | { } 22 | 23 | void Layer::write(QDataStream &ds) const 24 | { 25 | ds << getName() << getType(); 26 | } 27 | 28 | QDataStream &operator << (QDataStream &out, const Layer *layer) 29 | { 30 | layer->write(out); 31 | return out; 32 | } 33 | 34 | QDataStream& operator << (QDataStream& ds, const Layer &layer) 35 | { 36 | layer.write(ds); 37 | return ds; 38 | } 39 | 40 | QDataStream& operator >> (QDataStream& ds, Layer &layer) 41 | { 42 | layer.read(ds); 43 | return ds; 44 | } 45 | 46 | QDataStream& operator << (QDataStream& stream, const QList& l) 47 | { 48 | stream << l.size(); 49 | for (auto& a_ptr: l) { 50 | stream << *a_ptr; 51 | } 52 | return stream; 53 | } 54 | 55 | QDataStream& operator >> (QDataStream& stream, QList &layers) 56 | { 57 | layers.clear(); 58 | int size; 59 | int type; 60 | 61 | stream >> size; 62 | 63 | QString name; 64 | layers.reserve(size); 65 | 66 | for (int i = 0; i < size; ++i) { 67 | stream >> name >> type; 68 | Layer::LayerType val = static_cast(type); 69 | Layer *layer = Layer::create(name, val); 70 | 71 | stream >> *layer; 72 | layers.push_back(layer); 73 | } 74 | return stream; 75 | } 76 | 77 | Layer * Layer::create(const QString &name, Layer::LayerType type) 78 | { 79 | switch (type) { 80 | case Layer::RASTER: 81 | return new RasterLayer(name); 82 | 83 | break; 84 | default: 85 | Q_UNREACHABLE(); 86 | break; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/layers/layer.h: -------------------------------------------------------------------------------- 1 | #ifndef LAYER_H 2 | #define LAYER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | class Layer : public QListWidgetItem 12 | { 13 | public: 14 | enum LayerType { 15 | RASTER = QListWidgetItem::UserType, 16 | VECTOR, 17 | TEXT, 18 | ADJUSTMENT 19 | }; 20 | 21 | Layer(QString name, LayerType type); 22 | 23 | ~Layer(); 24 | inline void setName(QString &name){ _name = name; } 25 | inline QString getName() const { return _name; } 26 | inline LayerType getType() const { return _type; } 27 | inline void setType(LayerType type){ _type = type; } 28 | virtual void setSceneSelected(bool select) = 0; 29 | virtual void setLayerSelected(bool select) = 0; 30 | virtual void setZvalue(int z) = 0; 31 | virtual void setParent(QGraphicsItem *parent) = 0; 32 | virtual QPixmap getPixmap() const = 0; 33 | virtual QPointF getPos() const = 0; 34 | virtual void write(QDataStream&) const = 0; 35 | virtual void read(QDataStream&) = 0; 36 | friend QDataStream& operator >> (QDataStream& ds, Layer &layer); 37 | friend QDataStream& operator << (QDataStream& ds, const Layer &layer); 38 | 39 | friend QDataStream& operator << (QDataStream& stream, 40 | const QList & l); 41 | friend QDataStream& operator >> (QDataStream& stream, QList& l); 42 | static Layer * create(const QString &name, LayerType type); 43 | virtual Layer * clone() const = 0; 44 | protected: 45 | QString _name; 46 | LayerType _type; 47 | }; 48 | 49 | #endif // LAYER_H 50 | -------------------------------------------------------------------------------- /src/layers/rasterlayer.cpp: -------------------------------------------------------------------------------- 1 | #include "rasterlayer.h" 2 | 3 | 4 | RasterLayer::RasterLayer(const QString &name) : 5 | Layer(name, Layer::RASTER) 6 | { } 7 | 8 | RasterLayer::RasterLayer(const RasterLayer &other) : 9 | Layer(other.getName(), Layer::RASTER) 10 | { 11 | create(other.getPixmap()); 12 | } 13 | 14 | RasterLayer::RasterLayer(const QString &name, const QImage &image) : 15 | Layer(name, Layer::RASTER) 16 | { 17 | create(QPixmap::fromImage(image)); 18 | ; 19 | } 20 | 21 | RasterLayer::RasterLayer(const QString &name, const QPixmap &pixmap) : 22 | Layer(name, Layer::RASTER) 23 | 24 | { 25 | create(pixmap); 26 | } 27 | 28 | void RasterLayer::create(const QPixmap &pixmap) 29 | { 30 | QIcon icon(pixmap); 31 | setIcon(icon); 32 | setPixmap(pixmap); 33 | 34 | QGraphicsPixmapItem::setFlags(QGraphicsItem::ItemIsMovable 35 | | QGraphicsItem::ItemIsSelectable); 36 | } 37 | 38 | RasterLayer::~RasterLayer() 39 | { } 40 | 41 | void RasterLayer::setLocked(bool lock) 42 | { 43 | QGraphicsItem::setFlag(QGraphicsItem::ItemIsMovable, !lock); 44 | QGraphicsItem::setFlag(QGraphicsItem::ItemIsSelectable, !lock); 45 | } 46 | 47 | void RasterLayer::setSceneSelected(bool select) 48 | { 49 | QGraphicsItem::setSelected(select); 50 | } 51 | 52 | void RasterLayer::setLayerSelected(bool select) 53 | { 54 | QListWidgetItem::setSelected(select); 55 | } 56 | 57 | void RasterLayer::setZvalue(int z) 58 | { 59 | QGraphicsPixmapItem::setZValue(z); 60 | } 61 | 62 | void RasterLayer::setParent(QGraphicsItem *parent) 63 | { 64 | QGraphicsPixmapItem::setParentItem(parent); 65 | } 66 | 67 | void RasterLayer::paint(QPainter * painter, 68 | const QStyleOptionGraphicsItem *option, 69 | QWidget * widget) 70 | { 71 | QStyleOptionGraphicsItem tampered(*option); 72 | tampered.state &= ~QStyle::State_Selected; 73 | QGraphicsPixmapItem::paint(painter, &tampered, widget); 74 | } 75 | 76 | void RasterLayer::write(QDataStream &ds) const 77 | { 78 | Layer::write(ds); 79 | ds << getPixmap() << getPos(); 80 | } 81 | 82 | void RasterLayer::read(QDataStream &ds) 83 | { 84 | QPixmap pixmap; 85 | QPointF pos; 86 | ds >> pixmap >> pos; 87 | create(pixmap); 88 | setPos(pos); 89 | // To test if deserialization is working properly 90 | // Uncomment the following line 91 | // qDebug() << getName() << getPixmap() << getPos(); 92 | } 93 | 94 | Layer * RasterLayer::clone() const 95 | { 96 | return new RasterLayer(*this); 97 | } 98 | -------------------------------------------------------------------------------- /src/layers/rasterlayer.h: -------------------------------------------------------------------------------- 1 | #ifndef RASTERLAYER_H 2 | #define RASTERLAYER_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "layer.h" 9 | 10 | class RasterLayer : public Layer, public QGraphicsPixmapItem 11 | { 12 | public: 13 | RasterLayer(const QString &name); 14 | RasterLayer(const RasterLayer &other); 15 | RasterLayer(const QString &name, const QImage &image); 16 | RasterLayer(const QString &name, const QPixmap &pixmap); 17 | 18 | ~RasterLayer(); 19 | 20 | void setLocked(bool lock); 21 | void setSceneSelected(bool select); 22 | void setLayerSelected(bool select); 23 | void setZvalue(int z); 24 | void setParent(QGraphicsItem *parent); 25 | inline QPixmap getPixmap() const { return pixmap(); } 26 | inline QPointF getPos() const { return QGraphicsPixmapItem::pos(); } 27 | inline void setLayerPos(QPointF pos){ setPos(pos); } 28 | inline void setLayerPixmap(QPixmap pixmap){ setPixmap(pixmap); } 29 | 30 | void write(QDataStream&) const; 31 | void read(QDataStream&); 32 | virtual Layer * clone() const; 33 | 34 | 35 | protected: 36 | void paint(QPainter * painter, 37 | const QStyleOptionGraphicsItem *option, 38 | QWidget * widget); 39 | 40 | private: 41 | void create(const QPixmap &pixmap); 42 | }; 43 | 44 | #endif // RASTERLAYER_H 45 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | QApplication a(argc, argv); 7 | MainWindow w; 8 | 9 | w.showMaximized(); 10 | w.show(); 11 | 12 | QFile styleFile(":/styles/basic.qss"); 13 | styleFile.open(QFile::ReadOnly); 14 | 15 | QString style(styleFile.readAll() ); 16 | w.setStyleSheet(style); 17 | 18 | return a.exec(); 19 | } 20 | -------------------------------------------------------------------------------- /src/mainwindow.cpp: -------------------------------------------------------------------------------- 1 | #include "mainwindow.h" 2 | #include "ui_mainwindow.h" 3 | #include "tools/tooloptions/transform_menu.h" 4 | 5 | MainWindow::MainWindow(QWidget *parent) : 6 | QMainWindow(parent), 7 | ui(new Ui::MainWindow) 8 | { 9 | ui->setupUi(this); 10 | setupMdiArea(); 11 | initTools(); 12 | initSignalsAndSlots(); 13 | initSupportedFileMap(); 14 | initFilterListString(); 15 | Transform *transform = dynamic_cast(_toolsList.value(0)); 16 | setDefaultTool(transform); 17 | _lastFileLoc = 18 | QStandardPaths::writableLocation(QStandardPaths::HomeLocation); 19 | 20 | setAcceptDrops(true); 21 | } 22 | 23 | void MainWindow::initSupportedFileMap() 24 | { 25 | _supportedTypeMap.insert(FXD, "Fixture document (*.fxd *.fxt)"); 26 | _supportedTypeMap.insert(IMAGE, "Image Files (*.png *.jpg *.jpeg *.gif)"); 27 | _supportedTypeMap.insert(PNG, "PNG (*.png)"); 28 | _supportedTypeMap.insert(JPG, "JPEG (*.jpg *.jpeg)"); 29 | _supportedTypeMap.insert(GIF, "GIF (*.gif)"); 30 | _supportedTypeMap.insert(TIFF, "TIFF (*.tif *.tiff)"); 31 | _supportedTypeMap.insert(ICO, "ICO (*.ico)"); 32 | _supportedTypeMap.insert(ICO, "BMP (*.bmp)"); 33 | } 34 | 35 | void MainWindow::initFilterListString() 36 | { 37 | QMap::iterator i; 38 | 39 | for (i = _supportedTypeMap.begin(); i != _supportedTypeMap.end(); ++i) { 40 | _filterListString += i.value(); 41 | } 42 | } 43 | 44 | MainWindow::~MainWindow() 45 | { 46 | delete ui; 47 | } 48 | 49 | void MainWindow::setupMdiArea() 50 | { 51 | ui->mdiArea->setViewMode(QMdiArea::TabbedView); 52 | ui->mdiArea->setTabsClosable(true); 53 | ui->mdiArea->setTabsMovable(true); 54 | 55 | QTabBar *bar = ui->mdiArea->findChild(); 56 | setupTabBar(bar); 57 | } 58 | 59 | void MainWindow::setupTabBar(QTabBar *bar) 60 | { 61 | bar->setExpanding(false); 62 | bar->setDrawBase(false); 63 | bar->setElideMode(Qt::ElideLeft); 64 | } 65 | 66 | void MainWindow::initSignalsAndSlots() 67 | { 68 | connect(ui->mdiArea, SIGNAL(subWindowActivated(QMdiSubWindow *)), 69 | this, SLOT(updateWindow(QMdiSubWindow *))); 70 | 71 | connect(ui->mainToolBar, SIGNAL(actionTriggered(QAction *)), 72 | this, SLOT(changeTool(QAction *))); 73 | 74 | connect(ui->layerView, SIGNAL(itemSelectionChanged()), 75 | this, SLOT(onSelectionChange())); 76 | } 77 | 78 | void MainWindow::initTools() 79 | { 80 | _toolsGroup = new QActionGroup(ui->mainToolBar); 81 | 82 | addTool(new Transform(ui->toolMenuBar)); 83 | addTool(new Pan(ui->toolMenuBar)); 84 | 85 | ui->mainToolBar->addActions(_toolsList); 86 | } 87 | 88 | void MainWindow::addTool(Tool *tool) 89 | { 90 | _toolsGroup->addAction(tool); 91 | _toolsList.push_back(tool); 92 | } 93 | 94 | void MainWindow::setDefaultTool(Tool *tool) 95 | { 96 | tool->toggle(); 97 | 98 | QWidget *menuWidget = tool->getToolMenu(); 99 | activateMenu(menuWidget); 100 | _currentTool = tool; 101 | } 102 | 103 | void MainWindow::setCurrentTool(Tool *tool) 104 | { 105 | _currentTool = tool; 106 | 107 | QWidget *menuWidget = tool->getToolMenu(); 108 | 109 | if (_menu != nullptr) { 110 | _menu->setVisible(false); 111 | } 112 | updateMenuFromCache(menuWidget); 113 | 114 | // TODO: Remember preferences 115 | _menu->setVisible(true); 116 | } 117 | 118 | void MainWindow::updateMenuFromCache(QWidget *menuWidget) 119 | { 120 | if (_toolMenuCache.contains(menuWidget)) { 121 | _menu = _toolMenuCache.value(menuWidget); 122 | } else { 123 | activateMenu(menuWidget); 124 | } 125 | } 126 | 127 | void MainWindow::activateMenu(QWidget *menuWidget) 128 | { 129 | _menu = ui->toolMenuBar->addWidget(menuWidget); 130 | _toolMenuCache.insert(menuWidget, _menu); 131 | } 132 | 133 | void MainWindow::dragEnterEvent(QDragEnterEvent *e) 134 | { 135 | if (e->mimeData()->hasUrls()) { 136 | e->acceptProposedAction(); 137 | } 138 | } 139 | 140 | void MainWindow::dropEvent(QDropEvent *e) 141 | { 142 | foreach(const QUrl &url, e->mimeData()->urls()){ 143 | QString fileName = url.toLocalFile(); 144 | openNewImage(fileName); 145 | } 146 | } 147 | 148 | void MainWindow::openNewImage(const QString &fileName) 149 | { 150 | if (PaintWidget::isFileValid(fileName)) { 151 | rememberLastPath(fileName); 152 | addPaintWidget(new PaintWidget(fileName, _currentTool)); 153 | } 154 | } 155 | 156 | void MainWindow::addChildWindow(PaintWidget *widget, bool isNew) 157 | { 158 | ui->mdiArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded); 159 | ui->mdiArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded); 160 | 161 | QMdiSubWindow *mdiSubWindow = ui->mdiArea->addSubWindow(widget); 162 | 163 | setupSubWindowTitle(mdiSubWindow, widget->getImagePath(), isNew); 164 | if (widget->getImagePath() != "") { 165 | _windowCache.insert(widget->getImagePath(), mdiSubWindow); 166 | } 167 | 168 | mdiSubWindow->installEventFilter(this); 169 | mdiSubWindow->show(); 170 | } 171 | 172 | void MainWindow::setupSubWindowTitle(QMdiSubWindow *mdiSubWindow, 173 | const QString& filePath, bool isNew) 174 | { 175 | QString title = "Untitled[*]"; 176 | 177 | if (filePath != "") { 178 | QFileInfo info(filePath); 179 | title = info.fileName() + "[*]"; 180 | } 181 | 182 | mdiSubWindow->setWindowTitle(title); 183 | mdiSubWindow->setWindowModified(isNew); 184 | } 185 | 186 | bool MainWindow::eventFilter(QObject *watched, QEvent *event) 187 | { 188 | switch (event->type()) { 189 | case QEvent::Close: { 190 | QMdiSubWindow *subWindow = dynamic_cast(watched); 191 | Q_ASSERT(subWindow != NULL); 192 | 193 | _windowCache.remove(_windowCache.key(subWindow)); 194 | break; 195 | } 196 | default: 197 | qt_noop(); 198 | } 199 | return QObject::eventFilter(watched, event); 200 | } 201 | 202 | /** 203 | * @brief MainWindow::updateWindow 204 | * @param window 205 | */ 206 | void MainWindow::updateWindow(QMdiSubWindow *window) 207 | { 208 | QString title = "Fixture"; 209 | 210 | if (window != nullptr) { 211 | title = window->windowTitle() + " - " + title; 212 | PaintWidget *wid = qobject_cast(window->widget()); 213 | ui->layerView->updateItems(wid->getItems()); 214 | } else { 215 | ui->layerView->clear(); 216 | } 217 | 218 | updateActions(window != nullptr); 219 | setWindowTitle(title); 220 | } 221 | 222 | void MainWindow::updateActions(bool val) 223 | { 224 | ui->actionImport->setEnabled(val); 225 | ui->actionSave->setEnabled(val); 226 | ui->actionSaveAs->setEnabled(val); 227 | } 228 | 229 | void MainWindow::changeTool(QAction *action) 230 | { 231 | setCurrentTool(dynamic_cast(action)); 232 | 233 | QMdiSubWindow *currentWindow = ui->mdiArea->activeSubWindow(); 234 | if (currentWindow != nullptr) { 235 | updateToolForCurrentWindow(currentWindow); 236 | } 237 | } 238 | 239 | void MainWindow::updateToolForCurrentWindow(QMdiSubWindow *currentWindow) 240 | { 241 | PaintWidget *paintWidget = qobject_cast( 242 | currentWindow->widget()); 243 | paintWidget->setTool(_currentTool); 244 | updateDragMode(paintWidget); 245 | } 246 | 247 | void MainWindow::updateDragMode(PaintWidget *paintWidget) 248 | { 249 | if (_currentTool->getToolGroup() == Tool::PERCEPTION) { 250 | // The cursor needs to be fixed for zoom here. 251 | paintWidget->setDragMode(QGraphicsView::ScrollHandDrag); 252 | } else { 253 | paintWidget->setDragMode(QGraphicsView::NoDrag); 254 | } 255 | } 256 | 257 | void MainWindow::onSelectionChange() 258 | { 259 | QMdiSubWindow *currentWindow = ui->mdiArea->activeSubWindow(); 260 | 261 | if (currentWindow != nullptr) { 262 | updateLayerSelection(); 263 | } 264 | } 265 | 266 | void MainWindow::updateLayerSelection() 267 | { 268 | QList widgetSelectedItems = 269 | ui->layerView->selectedItems(); 270 | QList widgetItems = ui->layerView->getitems(); 271 | QList::iterator itr = widgetItems.begin(); 272 | 273 | for (; itr != widgetItems.end(); ++itr) { 274 | Layer *l = dynamic_cast(*itr); 275 | l->setSceneSelected(widgetSelectedItems.contains(*itr)); 276 | } 277 | } 278 | 279 | void MainWindow::addPaintWidget(PaintWidget *widget, bool isNew) 280 | { 281 | addChildWindow(widget, isNew); 282 | ui->mdiArea->setCursor(_currentTool->getToolCursor()); 283 | } 284 | 285 | PaintWidget * MainWindow::createPaintWidget(const QString &imagePath) const 286 | { 287 | return new PaintWidget(imagePath, _currentTool); 288 | } 289 | 290 | void MainWindow::on_actionOpen_triggered() 291 | { 292 | const QString fileName = chooseFileForOpening("Open"); 293 | 294 | if (_windowCache.contains(fileName)) { 295 | ui->mdiArea->setActiveSubWindow(_windowCache.value(fileName)); 296 | return; 297 | } 298 | openNewFile(fileName); 299 | } 300 | 301 | void MainWindow::openNewFile(const QString &fileName) 302 | { 303 | if (Document::isDocumentValid(fileName)) { 304 | Document document = readDocument(fileName); 305 | rememberLastPath(fileName); 306 | addPaintWidget(new PaintWidget(document, _currentTool)); 307 | return; 308 | } 309 | openNewImage(fileName); 310 | } 311 | 312 | Document MainWindow::readDocument(const QString &fileName) 313 | { 314 | QFile file(fileName); 315 | 316 | file.open(QIODevice::ReadOnly); 317 | QDataStream in(&file); 318 | 319 | Document document(fileName); 320 | in >> document; 321 | file.close(); 322 | 323 | return document; 324 | } 325 | 326 | void MainWindow::rememberLastPath(const QString &fileName) 327 | { 328 | QFileInfo info(fileName); 329 | _lastFileLoc = info.absolutePath(); 330 | } 331 | 332 | const QString MainWindow::chooseFileForOpening(const QString& prompt) 333 | { 334 | QFileDialog fileDialog(this, tr(qPrintable(prompt)), 335 | _lastFileLoc); 336 | fileDialog.setAcceptMode(QFileDialog::AcceptOpen); 337 | 338 | setFilters(fileDialog); 339 | 340 | if (fileDialog.exec() == QDialog::Accepted) { 341 | return fileDialog.selectedFiles()[0]; 342 | } 343 | 344 | return nullptr; 345 | } 346 | 347 | void MainWindow::setFilters(QFileDialog& fileDialog) 348 | { 349 | QStringList filters = QStringList(_supportedTypeMap.values()); 350 | fileDialog.setNameFilters(filters); 351 | } 352 | 353 | bool MainWindow::saveFileWithDialog(PaintWidget *paintWidget) 354 | { } 355 | 356 | void MainWindow::on_actionExit_triggered() 357 | { 358 | QApplication::exit(); 359 | } 360 | 361 | void MainWindow::on_actionNew_triggered() 362 | { 363 | NewDialog *newDialog = new NewDialog(); 364 | connect(newDialog, SIGNAL(canvasAvailable( 365 | const QSharedDataPointer )), 366 | this, SLOT(createNewDocument(const QSharedDataPointer ))); 367 | 368 | newDialog->show(); 369 | } 370 | 371 | void MainWindow::createNewDocument(const QSharedDataPointer canvas) 372 | { 373 | addPaintWidget(new PaintWidget(canvas, _currentTool), true); 374 | } 375 | 376 | void MainWindow::on_actionImport_triggered() 377 | { 378 | const QString fileName = chooseFileForOpening("Import File"); 379 | QMdiSubWindow *currentWindow = ui->mdiArea->activeSubWindow(); 380 | PaintWidget *paintWidget = qobject_cast( 381 | currentWindow->widget()); 382 | 383 | paintWidget->importPathToLayer(fileName); 384 | ui->layerView->updateItems(paintWidget->getItems()); 385 | } 386 | 387 | void MainWindow::on_actionSave_triggered() 388 | { } 389 | 390 | void MainWindow::on_actionSaveAs_triggered() 391 | { 392 | QMdiSubWindow *currentWindow = ui->mdiArea->activeSubWindow(); 393 | PaintWidget *paintWidget = qobject_cast( 394 | currentWindow->widget()); 395 | 396 | QFileDialog fileDialog(this, tr(qPrintable("Save as")), 397 | _lastFileLoc); 398 | // fileDialog.selectFile(fileName); 399 | fileDialog.setAcceptMode(QFileDialog::AcceptSave); 400 | setFilters(fileDialog); 401 | 402 | if (fileDialog.exec() == QDialog::Accepted) { 403 | persistFromDialog(fileDialog, paintWidget); 404 | updateStateChange(SAVED, fileDialog.selectedFiles()[0], currentWindow); 405 | } 406 | } 407 | 408 | void MainWindow::persistFromDialog(QFileDialog &fileDialog, 409 | PaintWidget * paintWidget) 410 | { 411 | QString filter = fileDialog.selectedNameFilter(); 412 | QString fileName = fileDialog.selectedFiles()[0]; 413 | serialize(fileName, _supportedTypeMap.key(filter), paintWidget); 414 | } 415 | 416 | void MainWindow::serialize(QString fileName, SupportedTypes type, 417 | PaintWidget *paintWidget) 418 | { 419 | switch (type) { 420 | case PNG: 421 | saveToPNG(fileName, paintWidget); 422 | break; 423 | case FXD: 424 | saveToFXD(getCorrectFileName(fileName), paintWidget); 425 | break; 426 | default: 427 | break; 428 | } 429 | } 430 | 431 | void MainWindow::saveToPNG(const QString &fileName, PaintWidget *paintWidget) 432 | { 433 | QImage image(paintWidget->sceneRect().size().toSize(), 434 | QImage::Format_ARGB32); 435 | QPainter painter(&image); 436 | paintWidget->render(&painter); 437 | image.save(fileName); 438 | } 439 | 440 | void MainWindow::saveToFXD(const QString &fileName, PaintWidget *paintWidget) 441 | { 442 | QList layers = paintWidget->getItems(); 443 | QSharedDataPointer canvas = paintWidget->getCanvas(); 444 | Document document(layers, canvas); 445 | 446 | storeDocument(fileName, document); 447 | } 448 | 449 | QString MainWindow::getCorrectFileName(QString &fileName) 450 | { 451 | if (!fileName.endsWith(".fxt") && !fileName.endsWith(".fxd")) { 452 | fileName = fileName + ".fxd"; 453 | } 454 | 455 | return fileName; 456 | } 457 | 458 | void MainWindow::storeDocument(const QString &fileName, Document document) 459 | { 460 | QFile file(fileName); 461 | 462 | file.open(QIODevice::WriteOnly); 463 | QDataStream out(&file); 464 | 465 | out.setVersion(QDataStream::Qt_5_1); 466 | out << document; 467 | file.close(); 468 | } 469 | 470 | void MainWindow::updateStateChange(State state, const QString &fileName, 471 | QMdiSubWindow *subWindow) 472 | { 473 | switch (state) { 474 | case SAVED: 475 | _lastFileLoc = fileName; 476 | if (!_windowCache.contains(fileName)) { 477 | _windowCache.insert(fileName, subWindow); 478 | } 479 | updateTitleOnSave(fileName, subWindow); 480 | break; 481 | default: 482 | break; 483 | } 484 | } 485 | 486 | void MainWindow::updateTitleOnSave(const QString& title, QMdiSubWindow *window) 487 | { 488 | QFileInfo info(title); 489 | window->setWindowTitle(info.fileName()); 490 | window->setWindowModified(false); 491 | } 492 | -------------------------------------------------------------------------------- /src/mainwindow.h: -------------------------------------------------------------------------------- 1 | #ifndef MAINWINDOW_H 2 | #define MAINWINDOW_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "dialogs/newdialog.h" 12 | #include "widgets/paintwidget.h" 13 | #include "tools/pan.h" 14 | #include "tools/transform.h" 15 | #include "tools/tool.h" 16 | #include "model/document.h" 17 | #include "tools/tooloptions/transform_menu.h" 18 | 19 | namespace Ui { 20 | class MainWindow; 21 | } 22 | 23 | class MainWindow : public QMainWindow 24 | { 25 | Q_OBJECT 26 | 27 | public: 28 | explicit MainWindow(QWidget *parent = nullptr); 29 | ~MainWindow(); 30 | 31 | 32 | public slots: 33 | void createNewDocument(const QSharedDataPointer canvas); 34 | 35 | private slots: 36 | void updateWindow(QMdiSubWindow *window); 37 | void changeTool(QAction *action); 38 | void onSelectionChange(); 39 | void on_actionNew_triggered(); 40 | void on_actionOpen_triggered(); 41 | void on_actionImport_triggered(); 42 | void on_actionSave_triggered(); 43 | void on_actionSaveAs_triggered(); 44 | void on_actionExit_triggered(); 45 | 46 | private: 47 | Ui::MainWindow *ui; 48 | QString _lastFileLoc; 49 | QActionGroup *_toolsGroup; 50 | QList _toolsList; 51 | Tool *_currentTool; 52 | QAction *_menu; 53 | QMap _windowCache; 54 | QMap _toolMenuCache; 55 | enum State { 56 | SAVED, MODIFIED 57 | }; 58 | enum SupportedTypes { 59 | FXD, IMAGE, PNG, JPG, GIF, TIFF, ICO, BMP 60 | }; 61 | QMap _supportedTypeMap; 62 | QString _filterListString; 63 | void initSupportedFileMap(); 64 | void initFilterListString(); 65 | void openNewImage(const QString &); 66 | void setupMdiArea(); 67 | void setupTabBar(QTabBar *bar); 68 | void updateActions(bool val); 69 | void initTools(); 70 | void initSignalsAndSlots(); 71 | void addTool(Tool *tool); 72 | void setDefaultTool(Tool *tool); 73 | void setCurrentTool(Tool *tool); 74 | void activateMenu(QWidget *menuWidget); 75 | void updateMenuFromCache(QWidget *widget); 76 | void setFilters(QFileDialog&); 77 | const QString chooseFileForOpening(const QString &prompt); 78 | bool saveFileWithDialog(PaintWidget *paintWidget); 79 | void persistFromDialog(QFileDialog &, PaintWidget *); 80 | void addChildWindow(PaintWidget *widget, bool isNew); 81 | void setupSubWindowTitle(QMdiSubWindow *, const QString&, bool isNew); 82 | void addPaintWidget(PaintWidget *widget, bool isNew = false); 83 | void updateLayerSelection(); 84 | void updateToolForCurrentWindow(QMdiSubWindow *); 85 | void updateDragMode(PaintWidget *); 86 | void openNewFile(const QString&); 87 | PaintWidget * createPaintWidget(const QString &imagePath) const; 88 | void rememberLastPath(const QString& fileName); 89 | QString getCorrectFileName(QString &name); 90 | void serialize(QString, SupportedTypes, PaintWidget *paintWidget); 91 | void saveToFXD(const QString&, PaintWidget *); 92 | void saveToPNG(const QString&, PaintWidget *); 93 | void storeDocument(const QString &fileName, Document document); 94 | Document readDocument(const QString&); 95 | void updateStateChange(State state, const QString &fileName, 96 | QMdiSubWindow *subWindow); 97 | void updateTitleOnSave(const QString&, QMdiSubWindow *window); 98 | 99 | void dragEnterEvent(QDragEnterEvent *e); 100 | void dropEvent(QDropEvent *e); 101 | bool eventFilter(QObject *watched, QEvent *event); 102 | }; 103 | 104 | #endif // MAINWINDOW_H 105 | -------------------------------------------------------------------------------- /src/mainwindow.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | MainWindow 4 | 5 | 6 | 7 | 0 8 | 0 9 | 997 10 | 600 11 | 12 | 13 | 14 | Fixture 15 | 16 | 17 | background-color: #1a1a1a; 18 | color:rgb(255,255,255); 19 | 20 | 21 | 22 | 23 | 0 24 | 25 | 26 | 0 27 | 28 | 29 | 0 30 | 31 | 32 | 0 33 | 34 | 35 | 0 36 | 37 | 38 | 39 | 40 | 41 | 0 42 | 0 43 | 44 | 45 | 46 | true 47 | 48 | 49 | 50 | 51 | 52 | QFrame::Raised 53 | 54 | 55 | 0 56 | 57 | 58 | Qt::ScrollBarAsNeeded 59 | 60 | 61 | QAbstractScrollArea::AdjustIgnored 62 | 63 | 64 | 65 | 66 | 26 67 | 26 68 | 26 69 | 70 | 71 | 72 | 73 | QMdiArea::TabbedView 74 | 75 | 76 | true 77 | 78 | 79 | true 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 0 88 | 0 89 | 90 | 91 | 92 | Qt::ScrollBarAsNeeded 93 | 94 | 95 | Qt::ScrollBarAlwaysOff 96 | 97 | 98 | true 99 | 100 | 101 | QAbstractItemView::InternalMove 102 | 103 | 104 | Qt::MoveAction 105 | 106 | 107 | QListView::Adjust 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 0 117 | 0 118 | 997 119 | 24 120 | 121 | 122 | 123 | background-color: rgb(75, 75, 75); 124 | color: rgb(255, 255, 255); 125 | padding: 2px; 126 | 127 | 128 | 129 | File 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | false 143 | 144 | 145 | 146 | 147 | 148 | Edit 149 | 150 | 151 | 152 | 153 | Image 154 | 155 | 156 | 157 | 158 | View 159 | 160 | 161 | 162 | 163 | Window 164 | 165 | 166 | 167 | 168 | Help 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 16777215 182 | 16777215 183 | 184 | 185 | 186 | 187 | Segoe UI 188 | 189 | 190 | 191 | false 192 | 193 | 194 | 195 | 196 | 197 | Qt::LeftToolBarArea|Qt::RightToolBarArea 198 | 199 | 200 | Qt::Vertical 201 | 202 | 203 | 204 | 18 205 | 18 206 | 207 | 208 | 209 | LeftToolBarArea 210 | 211 | 212 | false 213 | 214 | 215 | 216 | 217 | toolBar 218 | 219 | 220 | 221 | 222 | 223 | false 224 | 225 | 226 | TopToolBarArea 227 | 228 | 229 | false 230 | 231 | 232 | 233 | 234 | 235 | New 236 | 237 | 238 | Ctrl + N 239 | 240 | 241 | Ctrl+N 242 | 243 | 244 | 245 | 246 | Open 247 | 248 | 249 | Ctrl + O 250 | 251 | 252 | Ctrl+O 253 | 254 | 255 | 256 | 257 | Open recent 258 | 259 | 260 | 261 | 262 | false 263 | 264 | 265 | Save 266 | 267 | 268 | Ctrl + S 269 | 270 | 271 | Ctrl+S 272 | 273 | 274 | 275 | 276 | false 277 | 278 | 279 | Save as 280 | 281 | 282 | Ctrl + Shift + S 283 | 284 | 285 | Ctrl+Shift+S 286 | 287 | 288 | 289 | 290 | false 291 | 292 | 293 | Import 294 | 295 | 296 | Ctrl+I 297 | 298 | 299 | 300 | 301 | Exit 302 | 303 | 304 | Alt + F4 305 | 306 | 307 | Alt+F4 308 | 309 | 310 | 311 | 312 | 313 | 314 | LayerManager 315 | QListWidget 316 |
src/widgets/layermanager.h
317 |
318 |
319 | 320 | 321 |
322 | -------------------------------------------------------------------------------- /src/model/canvas.cpp: -------------------------------------------------------------------------------- 1 | #include "canvas.h" 2 | 3 | Canvas::Canvas(const QString &name) : 4 | _docName(name) 5 | { } 6 | 7 | Canvas::Canvas(QString docName, 8 | int width, int height, 9 | DimensionUnit dimensionUnit, 10 | int resolution, 11 | ResolutionUnit resUnit 12 | ) : 13 | _docName(docName), 14 | _width(width), _height(height), 15 | _dimensionUnit(dimensionUnit), 16 | _resolution(resolution), 17 | _resolutionUnit(resUnit) 18 | { } 19 | 20 | Canvas::Canvas(const Canvas &other) 21 | { 22 | _docName = other.getName(); 23 | _width = other.getWidth(); 24 | _height = other.getHeight(); 25 | _dimensionUnit = other.getDimensionUnit(); 26 | _resolution = other.getResolution(); 27 | _resolutionUnit = other.getResolutionUnit(); 28 | } 29 | 30 | Canvas * Canvas::clone() const 31 | { 32 | return new Canvas(*this); 33 | } 34 | 35 | QDataStream& operator << (QDataStream& ds, const Canvas &canvas) 36 | { 37 | canvas.write(ds); 38 | return ds; 39 | } 40 | 41 | QDataStream& operator >> (QDataStream& ds, Canvas &canvas) 42 | { 43 | canvas.read(ds); 44 | return ds; 45 | } 46 | 47 | void Canvas::write(QDataStream &out) const 48 | { 49 | out << _width << _height << (int) _dimensionUnit << _resolution 50 | << (int) _resolutionUnit; 51 | } 52 | 53 | void Canvas::read(QDataStream &in) 54 | { 55 | int width, height, dimensionUnit, resolution, resolutionUnit; 56 | 57 | in >> width >> height >> dimensionUnit >> resolution >> resolutionUnit; 58 | 59 | _width = width; 60 | _height = height; 61 | _dimensionUnit = static_cast(dimensionUnit); 62 | _resolution = resolution; 63 | _resolutionUnit = static_cast(resolutionUnit); 64 | } 65 | -------------------------------------------------------------------------------- /src/model/canvas.h: -------------------------------------------------------------------------------- 1 | #ifndef CANVAS_H 2 | #define CANVAS_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class Canvas : public QSharedData 9 | { 10 | public: 11 | enum DimensionUnit { 12 | PIXELS, INCHES, CENTIMETERS, MILLIMETERS, POINTS, PICAS 13 | }; 14 | 15 | enum ResolutionUnit { 16 | PPI, PPC 17 | }; 18 | 19 | Canvas(QString docName, 20 | int width, int height, 21 | DimensionUnit dimensionUnit, 22 | int resolution, 23 | ResolutionUnit resUnit); 24 | Canvas(const Canvas&); 25 | Canvas(const QString &name); 26 | 27 | Canvas * clone() const; 28 | inline void setName(const QString &name){ _docName = name; } 29 | inline int getHeight() const { return _height; } 30 | inline int getWidth() const { return _width; } 31 | inline int getResolution() const { return _resolution; } 32 | inline QString getName() const { return _docName; } 33 | inline DimensionUnit getDimensionUnit() const { return _dimensionUnit; } 34 | inline ResolutionUnit getResolutionUnit() const { return _resolutionUnit; } 35 | 36 | void write(QDataStream&) const; 37 | void read(QDataStream&); 38 | friend QDataStream& operator >> (QDataStream& ds, Canvas &canvas); 39 | friend QDataStream& operator << (QDataStream& ds, const Canvas &canvas); 40 | 41 | private: 42 | QString _docName; 43 | int _width; 44 | int _height; 45 | DimensionUnit _dimensionUnit; 46 | int _resolution; 47 | ResolutionUnit _resolutionUnit; 48 | }; 49 | 50 | #endif // CANVAS_H 51 | -------------------------------------------------------------------------------- /src/model/document.cpp: -------------------------------------------------------------------------------- 1 | #include "document.h" 2 | 3 | Document::Document(const QList layers, 4 | const QSharedDataPointer canvas) : 5 | _layers(layers), 6 | _canvas(canvas) 7 | { } 8 | 9 | Document::Document(const QString &name) 10 | { 11 | _name = name; 12 | } 13 | 14 | bool Document::isDocumentValid(const QString &fileName) 15 | { 16 | return fileName.endsWith(".fxt") || fileName.endsWith(".fxd"); 17 | } 18 | 19 | void Document::write(QDataStream &out) const 20 | { 21 | out << _magicNum << _version << *_canvas.constData() << _layers; 22 | } 23 | 24 | void Document::read(QDataStream &in) 25 | { 26 | QList layers; 27 | Canvas *canvas = new Canvas(_name); 28 | 29 | qint64 magicNum; 30 | qint32 version; 31 | 32 | in >> magicNum >> version; 33 | 34 | if (magicNum != _magicNum) { 35 | // TODO: Add an exception for failure due to file incompatibility 36 | return; 37 | } 38 | 39 | in >> *canvas >> layers; 40 | 41 | _layers = layers; 42 | _canvas = QSharedDataPointer(canvas); 43 | } 44 | 45 | QDataStream &operator << (QDataStream &out, const Document &document) 46 | { 47 | document.write(out); 48 | return out; 49 | } 50 | 51 | QDataStream &operator >> (QDataStream &in, Document &document) 52 | { 53 | document.read(in); 54 | return in; 55 | } 56 | -------------------------------------------------------------------------------- /src/model/document.h: -------------------------------------------------------------------------------- 1 | #ifndef DOCUMENT_H 2 | #define DOCUMENT_H 3 | 4 | #include "../layers/layer.h" 5 | #include "canvas.h" 6 | 7 | class Document 8 | { 9 | public: 10 | Document(const QList layers, 11 | const QSharedDataPointer canvas); 12 | Document(const QString &name); 13 | void write(QDataStream&) const; 14 | void read(QDataStream&); 15 | friend QDataStream& operator >> (QDataStream& ds, Document& document); 16 | friend QDataStream& operator << (QDataStream& ds, const Document &document); 17 | static bool isDocumentValid(const QString &fileName); 18 | inline QList getLayerList(){ return _layers; } 19 | inline QSharedDataPointer getCanvas(){ return _canvas; } 20 | 21 | private: 22 | QList _layers; 23 | QSharedDataPointer _canvas; 24 | 25 | qint64 _magicNum = 0x798F138BA; // Hex value of multiplication of the two authors' dob 26 | qint32 _version = 1; 27 | QString _name; 28 | }; 29 | 30 | #endif // DOCUMENT_H 31 | -------------------------------------------------------------------------------- /src/model/drawing.cpp: -------------------------------------------------------------------------------- 1 | #include "drawing.h" 2 | 3 | Drawing::Drawing(QWidget *widget, int width, int height) : 4 | _height(height), _width(width), 5 | QGraphicsScene(widget) 6 | 7 | { 8 | QImage surface = 9 | QImage(_width, _height, QImage::Format_ARGB32_Premultiplied); 10 | QPainter painter(&surface); 11 | QBrush brush; 12 | brush.setTextureImage(QImage(":/brushes/checkers.png")); 13 | painter.setBrush(brush); 14 | painter.setCompositionMode(QPainter::CompositionMode_Source); 15 | painter.fillRect(surface.rect(), brush); 16 | painter.end(); 17 | _parentItem = addPixmap(QPixmap::fromImage(surface)); 18 | _parentItem->setFlag(QGraphicsItem::ItemClipsChildrenToShape); 19 | _parentItem->setHandlesChildEvents(false); 20 | } 21 | 22 | Drawing::~Drawing() 23 | { } 24 | 25 | void Drawing::dragEnterEvent(QGraphicsSceneDragDropEvent *e) 26 | { 27 | if (e->mimeData()->hasUrls()) { 28 | e->acceptProposedAction(); 29 | } 30 | } 31 | 32 | void Drawing::dragMoveEvent(QGraphicsSceneDragDropEvent *e) 33 | { 34 | if (e->mimeData()->hasUrls()) { 35 | e->acceptProposedAction(); 36 | } 37 | } 38 | 39 | void Drawing::dropEvent(QGraphicsSceneDragDropEvent *e) 40 | { 41 | foreach(const QUrl &url, e->mimeData()->urls()){ 42 | QString fileName = url.toLocalFile(); 43 | emit importAvailable(fileName); 44 | } 45 | } 46 | 47 | void Drawing::mousePressEvent(QGraphicsSceneMouseEvent *event) 48 | { 49 | _tool->press(event); 50 | } 51 | 52 | void Drawing::mouseMoveEvent(QGraphicsSceneMouseEvent *event) 53 | { 54 | _tool->move(event); 55 | } 56 | 57 | void Drawing::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) 58 | { 59 | _tool->release(event); 60 | } 61 | 62 | void Drawing::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) 63 | { 64 | event->accept(); 65 | } 66 | -------------------------------------------------------------------------------- /src/model/drawing.h: -------------------------------------------------------------------------------- 1 | #ifndef DRAWING_H 2 | #define DRAWING_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "../layers/rasterlayer.h" 16 | #include "../tools/tool.h" 17 | 18 | class Drawing : public QGraphicsScene 19 | { 20 | Q_OBJECT 21 | 22 | public: 23 | Drawing(QWidget *widget, int width, int height); 24 | ~Drawing(); 25 | inline QGraphicsItem * getParentItem(){ return _parentItem; } 26 | inline void setTool(Tool *tool){ _tool = tool; } 27 | 28 | private: 29 | int _height, _width; 30 | QGraphicsItem *_parentItem; 31 | Tool *_tool; 32 | 33 | void dragEnterEvent(QGraphicsSceneDragDropEvent *e); 34 | void dragMoveEvent(QGraphicsSceneDragDropEvent *e); 35 | void dropEvent(QGraphicsSceneDragDropEvent *e); 36 | void mouseMoveEvent(QGraphicsSceneMouseEvent *event); 37 | void mousePressEvent(QGraphicsSceneMouseEvent *event); 38 | void mouseReleaseEvent(QGraphicsSceneMouseEvent *event); 39 | void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event); 40 | 41 | signals: 42 | void importAvailable(const QString &fileName); 43 | }; 44 | 45 | #endif // DRAWING_H 46 | -------------------------------------------------------------------------------- /src/tools/abstractperception.cpp: -------------------------------------------------------------------------------- 1 | #include "abstractperception.h" 2 | 3 | AbstractPerception::AbstractPerception() 4 | { } 5 | -------------------------------------------------------------------------------- /src/tools/abstractperception.h: -------------------------------------------------------------------------------- 1 | #ifndef ABSTRACTPERCEPTION_H 2 | #define ABSTRACTPERCEPTION_H 3 | 4 | #include "tool.h" 5 | 6 | class AbstractPerception : public virtual Tool 7 | { 8 | public: 9 | AbstractPerception(); 10 | }; 11 | 12 | #endif // ABSTRACTPERCEPTION_H 13 | -------------------------------------------------------------------------------- /src/tools/abstractselection.cpp: -------------------------------------------------------------------------------- 1 | #include "abstractselection.h" 2 | 3 | AbstractSelection::AbstractSelection(QIcon ico, const QString &name, 4 | QCursor cur, ToolGroup group, 5 | ToolType type, QWidget *parent) 6 | : Tool(ico, name, cur, group, type, parent), _scene(nullptr) 7 | { } 8 | -------------------------------------------------------------------------------- /src/tools/abstractselection.h: -------------------------------------------------------------------------------- 1 | #ifndef ABSTRACTSELECTION_H 2 | #define ABSTRACTSELECTION_H 3 | 4 | #include "tool.h" 5 | #include "../layers/layer.h" 6 | 7 | #include 8 | #include 9 | 10 | class AbstractSelection : public Tool 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | AbstractSelection(QIcon ico, const QString &name, QCursor cur, 16 | ToolGroup group, ToolType type, QWidget *parent = nullptr); 17 | 18 | public: 19 | inline void setScene(QGraphicsScene *scene){ _scene = scene; } 20 | 21 | protected: 22 | QGraphicsScene *_scene; 23 | }; 24 | 25 | #endif // ABSTRACTSELECTION_H 26 | -------------------------------------------------------------------------------- /src/tools/components/boundingrectitem.cpp: -------------------------------------------------------------------------------- 1 | #include "boundingrectitem.h" 2 | 3 | using namespace TransformTool; 4 | 5 | BoundingRectItem::BoundingRectItem() 6 | { 7 | setFlag(QGraphicsItem::ItemIsMovable); 8 | _transformMode = false; 9 | _width = 0; 10 | _height = 0; 11 | } 12 | 13 | void BoundingRectItem::setPoints(QPointF min, QPointF max) 14 | { 15 | _boundingRect.setTopLeft(min); 16 | _boundingRect.setBottomRight(max); 17 | 18 | _width = max.x() - min.x(); 19 | _height = max.y() - min.y(); 20 | } 21 | 22 | void BoundingRectItem::paint(QPainter *painter, 23 | const QStyleOptionGraphicsItem * option, 24 | QWidget * widget) 25 | { 26 | painter->setPen(QPen(Qt::gray, 1, Qt::DashLine)); 27 | if (_transformMode) { 28 | painter->setPen(QPen(Qt::black, 1, Qt::SolidLine)); 29 | painter->drawLine(_boundingRect.topLeft(), _boundingRect.bottomRight()); 30 | painter->drawLine(_boundingRect.bottomLeft(), _boundingRect.topRight()); 31 | } 32 | painter->drawRect(_boundingRect); 33 | QPointF topleft = _boundingRect.topLeft(); 34 | topleft -= QPointF(2, 2); 35 | QPointF bottomRight = _boundingRect.topLeft(); 36 | bottomRight += QPointF(2, 2); 37 | QRectF r(topleft, bottomRight); 38 | painter->setPen(QPen(Qt::gray, 1, Qt::SolidLine)); 39 | if (_transformMode) { 40 | painter->setPen(QPen(Qt::black, 1, Qt::SolidLine)); 41 | } 42 | painter->drawRect(r); 43 | r.translate(_width / 2, 0); 44 | painter->drawRect(r); 45 | r.translate(_width / 2 + 0.75, 0); 46 | painter->drawRect(r); 47 | r.translate(0, _height / 2); 48 | painter->drawRect(r); 49 | r.translate(0, _height / 2 + 0.75); 50 | painter->drawRect(r); 51 | r.translate(-1 * _width / 2, 0); 52 | painter->drawRect(r); 53 | r.translate(-1 * _width / 2 - 0.75, 0); 54 | painter->drawRect(r); 55 | r.translate(0, -1 * _height / 2 - 0.75); 56 | painter->drawRect(r); 57 | } // BoundingRectItem::paint 58 | 59 | QRectF BoundingRectItem::boundingRect() const 60 | { 61 | return QRectF(_boundingRect.topLeft() - QPointF(5, 5), 62 | _boundingRect.bottomRight() + QPointF(5, 5)); 63 | } 64 | 65 | BoundingRectItem::HotSpot BoundingRectItem::checkMouse( 66 | QGraphicsSceneMouseEvent *event) 67 | { 68 | QPointF pos = event->scenePos(); 69 | 70 | // Kind of an ugly hack, should be improved 71 | QPointF tl = _boundingRect.topLeft() - QPointF(5, 5); 72 | QPointF br = tl + QPointF(10, 10); 73 | QRectF rect(tl, br); 74 | 75 | if (rect.contains(pos)) { 76 | return BoundingRectItem::HotSpot::ScaleTopLeftCorner; 77 | } 78 | 79 | rect.translate(_width / 2, 0); 80 | if (rect.contains(pos)) { 81 | return BoundingRectItem::HotSpot::ScaleTopBoundary; 82 | } 83 | 84 | rect.translate(_width / 2, 0); 85 | if (rect.contains(pos)) { 86 | return BoundingRectItem::HotSpot::ScaleTopRightCorner; 87 | } 88 | 89 | rect.translate(0, _height / 2); 90 | if (rect.contains(pos)) { 91 | return BoundingRectItem::HotSpot::ScaleRightBoundary; 92 | } 93 | 94 | rect.translate(0, _height / 2); 95 | if (rect.contains(pos)) { 96 | return BoundingRectItem::HotSpot::ScaleBottomRightCorner; 97 | } 98 | 99 | rect.translate(-1 * _width / 2, 0); 100 | if (rect.contains(pos)) { 101 | return BoundingRectItem::HotSpot::ScaleBottomBoundary; 102 | } 103 | 104 | rect.translate(-1 * _width / 2, 0); 105 | if (rect.contains(pos)) { 106 | return BoundingRectItem::HotSpot::ScaleBottomLeftCorner; 107 | } 108 | 109 | rect.translate(0, -1 * _height / 2); 110 | if (rect.contains(pos)) { 111 | return BoundingRectItem::HotSpot::ScaleLeftBoundary; 112 | } 113 | 114 | return BoundingRectItem::HotSpot::Move; 115 | } // BoundingRectItem::checkMouse 116 | -------------------------------------------------------------------------------- /src/tools/components/boundingrectitem.h: -------------------------------------------------------------------------------- 1 | #ifndef BOUNDINGRECTITEM_H 2 | #define BOUNDINGRECTITEM_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace TransformTool { 13 | class BoundingRectItem; 14 | } 15 | 16 | class TransformTool::BoundingRectItem : public QGraphicsRectItem 17 | { 18 | public: 19 | 20 | enum HotSpot { 21 | None, 22 | ScaleTopLeftCorner, 23 | ScaleTopRightCorner, 24 | ScaleBottomLeftCorner, 25 | ScaleBottomRightCorner, 26 | ScaleTopBoundary, 27 | ScaleBottomBoundary, 28 | ScaleLeftBoundary, 29 | ScaleRightBoundary, 30 | RotateTopLeftCorner, 31 | RotateTopRightCorner, 32 | RotateBottomLeftCorner, 33 | RotateBottomRightCorner, 34 | Move 35 | }; 36 | 37 | BoundingRectItem(); 38 | 39 | inline void transformMode(bool set){ _transformMode = set; } 40 | void setPoints(QPointF min, QPointF max); 41 | HotSpot checkMouse(QGraphicsSceneMouseEvent *event); 42 | inline qreal width(){ return _boundingRect.width(); } 43 | inline qreal height(){ return _boundingRect.height(); } 44 | 45 | signals: 46 | void mouseIsHovering(HotSpot); 47 | 48 | protected: 49 | void paint(QPainter * painter, 50 | const QStyleOptionGraphicsItem *option, 51 | QWidget * widget); 52 | 53 | QRectF boundingRect() const; 54 | 55 | private: 56 | QRectF _boundingRect; 57 | qreal _width, _height; 58 | bool _transformMode; 59 | }; 60 | 61 | #endif // BOUNDINGRECTITEM_H 62 | -------------------------------------------------------------------------------- /src/tools/pan.cpp: -------------------------------------------------------------------------------- 1 | #include "pan.h" 2 | 3 | /** 4 | * @brief Pan::Pan Constructs a Pan Tool 5 | * @param parent 6 | */ 7 | Pan::Pan(QWidget *parent) : 8 | Tool(QIcon(":/tools/pan.svg"), "Pan Tool (H)", 9 | Qt::OpenHandCursor, Tool::PERCEPTION, Tool::PAN, parent) 10 | { 11 | setShortcut(Qt::Key_H); 12 | } 13 | 14 | /** 15 | * @brief Pan::Pan 16 | * @param other 17 | */ 18 | Pan::Pan(const Pan &other) : 19 | Tool(QIcon(":/tools/pan.svg"), "Pan Tool (H)", 20 | Qt::OpenHandCursor, Tool::PERCEPTION, Tool::PAN) 21 | { } 22 | 23 | /** 24 | * @brief Pan::~Pan 25 | */ 26 | Pan::~Pan() 27 | { } 28 | 29 | /** 30 | * @brief Pan::move dummy function for now, functionality handled by paintwidget itself 31 | * @param event 32 | */ 33 | void Pan::move(QGraphicsSceneMouseEvent *event) 34 | { 35 | event->accept(); 36 | } 37 | 38 | /** 39 | * @brief Pan::press dummy function for now, functionality handled by paintwidget itself 40 | * @param event 41 | */ 42 | void Pan::press(QGraphicsSceneMouseEvent *event) 43 | { 44 | event->accept(); 45 | } 46 | 47 | /** 48 | * @brief Pan::release dummy function for now, functionality handled by paintwidget itself 49 | * @param event 50 | */ 51 | void Pan::release(QGraphicsSceneMouseEvent *event) 52 | { 53 | event->accept(); 54 | } 55 | 56 | /** 57 | * @brief Pan::getToolMenu returns the tool menu associated with the Pan Tool 58 | * @return 59 | */ 60 | QWidget * Pan::getToolMenu() 61 | { 62 | PanMenu *menu = new PanMenu(); 63 | return menu; 64 | } 65 | 66 | /** 67 | * @brief Pan::connectMenu 68 | */ 69 | void Pan::connectMenu() 70 | { } 71 | 72 | /** 73 | * @brief Pan::clone 74 | * @return 75 | */ 76 | Tool * Pan::clone() const 77 | { 78 | return new Pan(*this); 79 | } 80 | -------------------------------------------------------------------------------- /src/tools/pan.h: -------------------------------------------------------------------------------- 1 | #ifndef PANTOOL_H 2 | #define PANTOOL_H 3 | 4 | #include "abstractperception.h" 5 | #include "tooloptions/pan_menu.h" 6 | 7 | /** 8 | * @brief class Pan responsible for different properties and functions of 9 | * the hand tool, like panning across a zoomed scene 10 | */ 11 | class Pan : public AbstractPerception 12 | { 13 | public: 14 | Pan(QWidget *parent = nullptr); 15 | Pan(const Pan &other); 16 | ~Pan(); 17 | void move(QGraphicsSceneMouseEvent *event); 18 | void press(QGraphicsSceneMouseEvent *event); 19 | void release(QGraphicsSceneMouseEvent *event); 20 | QWidget * getToolMenu(); 21 | void connectMenu(); 22 | Tool * clone() const; 23 | }; 24 | 25 | #endif // PANTOOL_H 26 | -------------------------------------------------------------------------------- /src/tools/tool.cpp: -------------------------------------------------------------------------------- 1 | #include "tool.h" 2 | 3 | Tool::Tool(QIcon ico, const QString &name, QCursor cur, ToolGroup group, 4 | ToolType type, QWidget *parent) : 5 | QAction(ico, name, parent), _cursor(cur), _name(name), _group(group), _type( 6 | type) 7 | { 8 | setCheckable(true); 9 | } 10 | 11 | Tool::~Tool() 12 | { } 13 | -------------------------------------------------------------------------------- /src/tools/tool.h: -------------------------------------------------------------------------------- 1 | #ifndef TOOL_H 2 | #define TOOL_H 3 | 4 | #include 5 | #include 6 | 7 | #include "../layers/layer.h" 8 | 9 | class Tool : public QAction 10 | { 11 | public: 12 | enum ToolGroup { 13 | SELECTION, 14 | CREATION, 15 | MODIFICATION, 16 | PERCEPTION 17 | }; 18 | 19 | enum ToolType { 20 | TRANSFORM, 21 | PAN 22 | }; 23 | 24 | Tool(QIcon ico, const QString &name, QCursor cur, ToolGroup group, 25 | ToolType type, QWidget *parent = nullptr); 26 | ~Tool(); 27 | 28 | inline const QCursor getToolCursor(){ return _cursor; } 29 | inline const QString getToolName(){ return _name; } 30 | inline ToolGroup getToolGroup(){ return _group; } 31 | inline ToolType getToolType(){ return _type; } 32 | 33 | public: 34 | virtual void move(QGraphicsSceneMouseEvent *event) = 0; 35 | virtual void press(QGraphicsSceneMouseEvent *event) = 0; 36 | virtual void release(QGraphicsSceneMouseEvent *event) = 0; 37 | virtual QWidget * getToolMenu() = 0; 38 | virtual Tool * clone() const = 0; 39 | 40 | private: 41 | QCursor _cursor; 42 | QString _name; 43 | ToolGroup _group; 44 | ToolType _type; 45 | void drawRectItem(); 46 | // virtual void connectMenu() = 0; 47 | 48 | protected: 49 | QWidget *_menu; 50 | bool _menuExists = false; 51 | }; 52 | template <> 53 | inline Tool *QSharedDataPointer::clone() 54 | { 55 | return d->clone(); 56 | } 57 | 58 | #endif // TOOL_H 59 | -------------------------------------------------------------------------------- /src/tools/tooloptions/pan_menu.cpp: -------------------------------------------------------------------------------- 1 | #include "pan_menu.h" 2 | #include "ui_pan_menu.h" 3 | 4 | PanMenu::PanMenu(QWidget *parent) : 5 | QFrame(parent), 6 | ui(new Ui::PanMenu) 7 | { 8 | ui->setupUi(this); 9 | } 10 | 11 | PanMenu::~PanMenu() 12 | { 13 | delete ui; 14 | } 15 | -------------------------------------------------------------------------------- /src/tools/tooloptions/pan_menu.h: -------------------------------------------------------------------------------- 1 | #ifndef PAN_MENU_H 2 | #define PAN_MENU_H 3 | 4 | #include 5 | 6 | namespace Ui { 7 | class PanMenu; 8 | } 9 | 10 | class PanMenu : public QFrame 11 | { 12 | Q_OBJECT 13 | 14 | public: 15 | explicit PanMenu(QWidget *parent = nullptr); 16 | ~PanMenu(); 17 | 18 | private: 19 | Ui::PanMenu *ui; 20 | }; 21 | 22 | #endif // PAN_MENU_H 23 | -------------------------------------------------------------------------------- /src/tools/tooloptions/pan_menu.ui: -------------------------------------------------------------------------------- 1 | 2 | 3 | PanMenu 4 | 5 | 6 | 7 | 0 8 | 0 9 | 400 10 | 41 11 | 12 | 13 | 14 | Frame 15 | 16 | 17 | 18 | 19 | 20 | Pan 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/tools/tooloptions/toolmenu.cpp: -------------------------------------------------------------------------------- 1 | #include "toolmenu.h" 2 | 3 | ToolMenu::ToolMenu(QWidget *parent) : 4 | QFrame(parent) 5 | { } 6 | 7 | void ToolMenu::addStyleSheet() 8 | { 9 | QFile styleFile(":/styles/tool-menu.qss"); 10 | styleFile.open(QFile::ReadOnly); 11 | 12 | QString style(styleFile.readAll() ); 13 | setStyleSheet(style); 14 | } 15 | -------------------------------------------------------------------------------- /src/tools/tooloptions/toolmenu.h: -------------------------------------------------------------------------------- 1 | #ifndef TOOLMENU_H 2 | #define TOOLMENU_H 3 | 4 | #include 5 | #include 6 | 7 | class ToolMenu : public QFrame 8 | { 9 | Q_OBJECT 10 | 11 | public: 12 | ToolMenu(QWidget *parent); 13 | 14 | protected: 15 | void addStyleSheet(); 16 | }; 17 | 18 | #endif // TOOLMENU_H 19 | -------------------------------------------------------------------------------- /src/tools/tooloptions/transform_menu.cpp: -------------------------------------------------------------------------------- 1 | #include "transform_menu.h" 2 | 3 | TransformMenu::TransformMenu(QWidget *parent) : 4 | ToolMenu(parent) 5 | { 6 | generateUI(parent); 7 | setLayout(_box); 8 | addStyleSheet(); 9 | connectSignals(); 10 | } 11 | 12 | TransformMenu::~TransformMenu() 13 | { } 14 | 15 | void TransformMenu::generateUI(QWidget *parent) 16 | { 17 | showTransformCheck = new QCheckBox(parent); 18 | autoSelectCheck = new QCheckBox(parent); 19 | showTransformCheck->setText("Show Transform Controls"); 20 | autoSelectCheck->setText("Auto Select"); 21 | 22 | acceptBtn = new QPushButton(parent); 23 | acceptBtn->setIcon(QIcon(":/icons/check-tick.svg")); 24 | rejectBtn = new QPushButton(parent); 25 | rejectBtn->setIcon(QIcon(":/icons/close-white.svg")); 26 | _box = new QHBoxLayout(parent); 27 | acceptBtn->setVisible(false); 28 | rejectBtn->setVisible(false); 29 | _box->addWidget(autoSelectCheck); 30 | _box->addWidget(showTransformCheck); 31 | _box->addWidget(acceptBtn); 32 | _box->addWidget(rejectBtn); 33 | } 34 | 35 | void TransformMenu::connectSignals() 36 | { 37 | connect(acceptBtn, SIGNAL(clicked()), this, SLOT(on_acceptBtn_clicked())); 38 | connect(rejectBtn, SIGNAL(clicked()), this, SLOT(on_rejectBtn_clicked())); 39 | connect(showTransformCheck, SIGNAL(stateChanged(int)), this, 40 | SLOT(on_showTransfromCheck_stateChanged(int))); 41 | connect(autoSelectCheck, SIGNAL(stateChanged(int)), this, 42 | SLOT(on_autoSelectCheck_stateChanged(int))); 43 | } 44 | 45 | void TransformMenu::on_showTransfromCheck_stateChanged(int arg1) 46 | { 47 | emit showTransform(arg1 == Qt::Checked); 48 | } 49 | 50 | void TransformMenu::on_autoSelectCheck_stateChanged(int arg1) 51 | { 52 | emit autoSelect(arg1 == Qt::Checked); 53 | } 54 | 55 | void TransformMenu::enterTransformMode(bool enter) 56 | { 57 | acceptBtn->setVisible(enter); 58 | rejectBtn->setVisible(enter); 59 | showTransformCheck->setEnabled(!enter); 60 | autoSelectCheck->setEnabled(!enter); 61 | } 62 | 63 | void TransformMenu::on_acceptBtn_clicked() 64 | { 65 | emit changesAccepted(true); 66 | emit autoSelect(autoSelectCheck->checkState() == Qt::Checked); 67 | } 68 | 69 | void TransformMenu::on_rejectBtn_clicked() 70 | { 71 | emit changesAccepted(false); 72 | emit autoSelect(autoSelectCheck->checkState() == Qt::Checked); 73 | } 74 | -------------------------------------------------------------------------------- /src/tools/tooloptions/transform_menu.h: -------------------------------------------------------------------------------- 1 | #ifndef TRANSFORMOPTIONS_H 2 | #define TRANSFORMOPTIONS_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "toolmenu.h" 10 | 11 | namespace Ui { 12 | class TransformOptions; 13 | } 14 | 15 | class TransformMenu : public ToolMenu 16 | { 17 | Q_OBJECT 18 | 19 | public: 20 | explicit TransformMenu(QWidget *parent = nullptr); 21 | ~TransformMenu(); 22 | 23 | public slots: 24 | void enterTransformMode(bool enter); 25 | 26 | private slots: 27 | void on_showTransfromCheck_stateChanged(int arg1); 28 | void on_autoSelectCheck_stateChanged(int arg1); 29 | void on_acceptBtn_clicked(); 30 | void on_rejectBtn_clicked(); 31 | 32 | signals: 33 | void autoSelect(bool check); 34 | void showTransform(bool show); 35 | void changesAccepted(bool accept); 36 | 37 | private: 38 | QCheckBox *showTransformCheck, *autoSelectCheck; 39 | QPushButton *acceptBtn, *rejectBtn; 40 | QLabel *selectlbl, *transformlbl; 41 | QHBoxLayout *_box; 42 | 43 | void connectSignals(); 44 | void generateUI(QWidget *parent); 45 | }; 46 | 47 | #endif // TRANSFORMOPTIONS_H 48 | -------------------------------------------------------------------------------- /src/tools/transform.cpp: -------------------------------------------------------------------------------- 1 | #include "transform.h" 2 | 3 | /** 4 | * @brief Transform::Transform Constructs a transform tool 5 | * @param parent 6 | */ 7 | Transform::Transform(QWidget *parent) : 8 | AbstractSelection(QIcon(":/tools/select.svg"), "Transform Tool (V)", 9 | QCursor(QIcon(":/tools/select.svg").pixmap(QSize(15, 15)), 10 | 0, 0), 11 | Tool::SELECTION, Tool::TRANSFORM, parent) 12 | { 13 | setShortcut(Qt::Key_V); 14 | _rect = new TransformTool::BoundingRectItem(); 15 | _boundsDrawn = false; 16 | _mouseButton = Qt::NoButton; 17 | _scalex = 1; 18 | _scaley = 1; 19 | _autoSelect = false; 20 | _totaldx = 0; 21 | _totaldy = 0; 22 | _handle = TransformTool::BoundingRectItem::None; 23 | 24 | _menu = new TransformMenu(parent); 25 | connectMenu(); 26 | } 27 | 28 | /** 29 | * @brief Transform::Transform 30 | * @param other 31 | */ 32 | Transform::Transform(const Transform &other) : 33 | AbstractSelection(QIcon(":/tools/select.svg"), "Transform Tool (V)", 34 | QCursor(QIcon(":/tools/select.svg").pixmap(QSize(15, 15)), 35 | 0, 0), 36 | Tool::SELECTION, Tool::TRANSFORM) 37 | { 38 | _rect = other._rect; 39 | _boundsDrawn = other._boundsDrawn; 40 | _mouseButton = other._mouseButton; 41 | _scalex = other._scalex; 42 | _scaley = other._scaley; 43 | _autoSelect = other._autoSelect; 44 | _totaldx = other._totaldx; 45 | _totaldy = other._totaldy; 46 | 47 | setShortcut(Qt::Key_V); 48 | } 49 | 50 | /** 51 | * @brief Transform::~Transform 52 | */ 53 | Transform::~Transform() 54 | { } 55 | 56 | /** 57 | * @brief Transform::press handles the mouse press event passed to it from the 58 | * QGraphicsScene 59 | * @param event 60 | */ 61 | void Transform::press(QGraphicsSceneMouseEvent *event) 62 | { 63 | _prevPos = event->scenePos(); 64 | QGraphicsItem *itm = _scene->itemAt(_prevPos, QTransform()); 65 | _mouseButton = event->button(); 66 | 67 | if (_boundsDrawn) { 68 | _handle = _rect->checkMouse(event); 69 | } 70 | 71 | if (_handle != TransformTool::BoundingRectItem::HotSpot::Move && 72 | _handle != TransformTool::BoundingRectItem::HotSpot::None) 73 | { 74 | setTransformMode(true); 75 | _autoSelect = false; 76 | _curItems = _scene->selectedItems(); 77 | foreach(QGraphicsItem * itm, _curItems){ 78 | _curState.push_back(itm->transform()); 79 | } 80 | _prevState = _curState; 81 | return; 82 | } 83 | 84 | if (_autoSelect) { 85 | if (itm != nullptr && itm->flags() & QGraphicsItem::ItemIsSelectable) { 86 | if (event->modifiers() & Qt::ControlModifier) { 87 | itm->setSelected(true); 88 | } else { 89 | if (_scene->selectedItems().length() < 2) 90 | _scene->clearSelection(); 91 | itm->setSelected(true); 92 | } 93 | } else { 94 | _scene->clearSelection(); 95 | } 96 | emit _scene->selectionChanged(); 97 | drawBounds(_boundsDrawn); 98 | return; 99 | } 100 | 101 | if (!(_scene->selectedItems().contains(itm))) { 102 | _mouseButton = Qt::NoButton; 103 | } 104 | } // Transform::press 105 | 106 | /** 107 | * @brief Transform::release handles the mouse release event passed to it from the 108 | * QGraphicsScene 109 | * @param event 110 | */ 111 | void Transform::release(QGraphicsSceneMouseEvent *event) 112 | { 113 | event->accept(); 114 | _mouseButton = Qt::NoButton; 115 | _curState.clear(); 116 | } 117 | 118 | /** 119 | * @brief Transform::move handles the mouse move event passed to it from the 120 | * QGraphicsScene 121 | * @param event 122 | */ 123 | void Transform::move(QGraphicsSceneMouseEvent *event) 124 | { 125 | _curPos = event->scenePos(); 126 | qreal dx = _curPos.x() - _prevPos.x(); 127 | qreal dy = _curPos.y() - _prevPos.y(); 128 | 129 | 130 | if (_mouseButton == Qt::LeftButton) { 131 | if (_handle != TransformTool::BoundingRectItem::HotSpot::Move && 132 | _handle != TransformTool::BoundingRectItem::HotSpot::None) 133 | { 134 | emit switchedToTransformMode(true); 135 | } 136 | 137 | switch (_handle) { 138 | case TransformTool::BoundingRectItem::HotSpot::Move: { 139 | foreach(QGraphicsItem * itm, _scene->selectedItems()){ 140 | itm->moveBy(dx, dy); 141 | _totaldx += dx; 142 | _totaldy += dy; 143 | } 144 | break; 145 | } 146 | 147 | case TransformTool::BoundingRectItem::HotSpot:: 148 | ScaleBottomRightCorner: { 149 | _scalex = (width + dx) / width; 150 | _scaley = (height + dy) / height; 151 | for (int i = 0; i < _curItems.length(); i++) { 152 | _curState[i].scale(_scalex, _scaley); 153 | _curItems[i]->setTransform(_curState[i], false); 154 | } 155 | break; 156 | } 157 | 158 | case TransformTool::BoundingRectItem::HotSpot::ScaleTopLeftCorner: { 159 | _scalex = (width - dx) / width; 160 | _scaley = (height - dy) / height; 161 | for (int i = 0; i < _curItems.length(); i++) { 162 | _curState[i].scale(_scalex, _scaley); 163 | _curItems[i]->setTransform(_curState[i], false); 164 | _curItems[i]->moveBy(dx, dy); 165 | _totaldx += dx; 166 | _totaldy += dy; 167 | } 168 | break; 169 | } 170 | 171 | case TransformTool::BoundingRectItem::HotSpot::ScaleTopRightCorner: 172 | { 173 | _scalex = (width + dx) / width; 174 | _scaley = (height - dy) / height; 175 | for (int i = 0; i < _curItems.length(); i++) { 176 | _curState[i].scale(_scalex, _scaley); 177 | _curItems[i]->setTransform(_curState[i], false); 178 | _curItems[i]->moveBy(0, dy); 179 | _totaldy += dy; 180 | } 181 | break; 182 | } 183 | 184 | case TransformTool::BoundingRectItem::HotSpot::ScaleBottomLeftCorner 185 | : { 186 | _scalex = (width - dx) / width; 187 | _scaley = (height + dy) / height; 188 | for (int i = 0; i < _curItems.length(); i++) { 189 | _curState[i].scale(_scalex, _scaley); 190 | _curItems[i]->setTransform(_curState[i], false); 191 | _curItems[i]->moveBy(dx, 0); 192 | _totaldx += dx; 193 | } 194 | break; 195 | } 196 | case TransformTool::BoundingRectItem::HotSpot::ScaleTopBoundary: { 197 | _scaley = (height - dy) / height; 198 | for (int i = 0; i < _curItems.length(); i++) { 199 | _curState[i].scale(_scalex, _scaley); 200 | _curItems[i]->setTransform(_curState[i], false); 201 | _curItems[i]->moveBy(0, dy); 202 | _totaldy += dy; 203 | } 204 | break; 205 | } 206 | 207 | case TransformTool::BoundingRectItem::HotSpot::ScaleBottomBoundary: 208 | { 209 | _scaley = (height + dy) / height; 210 | for (int i = 0; i < _curItems.length(); i++) { 211 | _curState[i].scale(_scalex, _scaley); 212 | _curItems[i]->setTransform(_curState[i], false); 213 | } 214 | break; 215 | } 216 | 217 | case TransformTool::BoundingRectItem::HotSpot::ScaleLeftBoundary: { 218 | _scalex = (width - dx) / width; 219 | for (int i = 0; i < _curItems.length(); i++) { 220 | _curState[i].scale(_scalex, _scaley); 221 | _curItems[i]->setTransform(_curState[i], false); 222 | _curItems[i]->moveBy(dx, 0); 223 | _totaldx += dx; 224 | } 225 | break; 226 | } 227 | 228 | case TransformTool::BoundingRectItem::HotSpot::ScaleRightBoundary: { 229 | _scalex = (width + dx) / width; 230 | for (int i = 0; i < _curItems.length(); i++) { 231 | _curState[i].scale(_scalex, _scaley); 232 | _curItems[i]->setTransform(_curState[i], false); 233 | } 234 | break; 235 | } 236 | 237 | case TransformTool::BoundingRectItem::HotSpot:: 238 | RotateBottomLeftCorner: { 239 | break; 240 | } 241 | 242 | case TransformTool::BoundingRectItem::HotSpot:: 243 | RotateBottomRightCorner: { 244 | break; 245 | } 246 | 247 | case TransformTool::BoundingRectItem::HotSpot::RotateTopLeftCorner: 248 | { 249 | break; 250 | } 251 | 252 | case TransformTool::BoundingRectItem::HotSpot::RotateTopRightCorner: 253 | { 254 | break; 255 | } 256 | 257 | case TransformTool::BoundingRectItem::HotSpot::None: { 258 | break; 259 | } 260 | } 261 | } 262 | 263 | _scalex = 1; 264 | _scaley = 1; 265 | _prevPos = _curPos; 266 | updateBounds(); 267 | } // Transform::move 268 | 269 | /** 270 | * @brief Transform::drawBounds determines whether to draw the bounding rectangle 271 | * of the selection 272 | * @param draw 273 | */ 274 | void Transform::drawBounds(bool draw) 275 | { 276 | if (draw) { 277 | drawBoundingRect(); 278 | } else { 279 | _rect->setVisible(false); 280 | } 281 | 282 | _boundsDrawn = draw; 283 | } 284 | 285 | /** 286 | * @brief Transform::setTransformMode sets the transform mode whenever there is 287 | * a change in scale or rotate with the selection. 288 | * @param set 289 | */ 290 | void Transform::setTransformMode(bool set) 291 | { 292 | _rect->transformMode(set); 293 | } 294 | 295 | /** 296 | * @brief Transform::updateBounds redraw the boundaries whenever there is a change 297 | * in the scene. 298 | */ 299 | void Transform::updateBounds() 300 | { 301 | if (_boundsDrawn) { 302 | drawBoundingRect(); 303 | _scene->update(_scene->sceneRect()); 304 | } 305 | } 306 | 307 | /** 308 | * @brief Transform::drawBoundingRect calculated the bounding rect of the given 309 | * selection 310 | */ 311 | void Transform::drawBoundingRect() 312 | { 313 | if (_scene == nullptr) { 314 | return; 315 | } 316 | 317 | QList selected = _scene->selectedItems(); 318 | QList::iterator itr = selected.begin(); 319 | QPointF max(INT_MIN, INT_MIN), min(INT_MAX, INT_MAX); 320 | 321 | for (; itr != selected.end(); ++itr) { 322 | QGraphicsItem *temp = *itr; 323 | QPointF itmTopLeft = temp->boundingRect().topLeft(); 324 | QPointF itmBottomRight = temp->boundingRect().bottomRight(); 325 | itmTopLeft = temp->mapToScene(itmTopLeft); 326 | itmBottomRight = temp->mapToScene(itmBottomRight); 327 | if (itmTopLeft.x() < min.x()) { 328 | min.setX(itmTopLeft.x()); 329 | } 330 | 331 | if (itmTopLeft.y() < min.y()) { 332 | min.setY(itmTopLeft.y()); 333 | } 334 | 335 | if (itmBottomRight.x() > max.x()) { 336 | max.setX(itmBottomRight.x()); 337 | } 338 | 339 | if (itmBottomRight.y() > max.y()) { 340 | max.setY(itmBottomRight.y()); 341 | } 342 | } 343 | 344 | if (max == QPointF(INT_MIN, INT_MIN) || min == QPointF(INT_MAX, INT_MAX)) { 345 | if (_scene->items().contains(_rect)) { 346 | _rect->setVisible(false); 347 | } 348 | return; 349 | } 350 | 351 | _rect->setPoints(min, max); 352 | width = max.x() - min.x(); 353 | height = max.y() - min.y(); 354 | 355 | if (_scene->items().contains(_rect)) { 356 | _rect->setVisible(true); 357 | } else { 358 | _scene->addItem(_rect); 359 | } 360 | } // Transform::drawBoundingRect 361 | 362 | /** 363 | * @brief Transform::actionTaken decides whether to keep the current transforms 364 | * made after enter the transform mode or not 365 | * @param accept 366 | */ 367 | void Transform::actionTaken(bool accept) 368 | { 369 | if (!accept) { 370 | for (int i = 0; i < _curItems.length(); i++) { 371 | _curItems[i]->setTransform(_prevState[i], false); 372 | _curItems[i]->moveBy(-1 * _totaldx, -1 * _totaldy); 373 | } 374 | } 375 | setTransformMode(false); 376 | _scene->update(); 377 | drawBoundingRect(); 378 | emit switchedToTransformMode(false); 379 | _totaldx = 0; 380 | _totaldy = 0; 381 | } 382 | 383 | /** 384 | * @brief Transform::getToolMenu returns the tool menu of the current tool 385 | * @return 386 | */ 387 | QWidget * Transform::getToolMenu() 388 | { 389 | return _menu; 390 | } 391 | 392 | /** 393 | * @brief Transform::connectMenu connects the signals of the menu to the respective 394 | * slots 395 | * @param menu 396 | */ 397 | void Transform::connectMenu() 398 | { 399 | connect(_menu, SIGNAL(autoSelect(bool)), this, SLOT(setAutoSelect(bool))); 400 | connect(_menu, SIGNAL(showTransform(bool)), this, SLOT(drawBounds(bool))); 401 | connect(this, SIGNAL(switchedToTransformMode(bool)), _menu, 402 | SLOT(enterTransformMode(bool))); 403 | connect(_menu, SIGNAL(changesAccepted(bool)), this, 404 | SLOT(actionTaken(bool))); 405 | } 406 | 407 | /** 408 | * @brief Transform::clone 409 | * @return 410 | */ 411 | Tool * Transform::clone() const 412 | { 413 | return new Transform(*this); 414 | } 415 | -------------------------------------------------------------------------------- /src/tools/transform.h: -------------------------------------------------------------------------------- 1 | #ifndef SELECTTOOL_H 2 | #define SELECTTOOL_H 3 | 4 | #include "abstractselection.h" 5 | #include "components/boundingrectitem.h" 6 | #include "tooloptions/transform_menu.h" 7 | 8 | /** 9 | * @brief class Transform responsible for different properties and functions of 10 | * the transform tool, which is being used for moving, scaling and rotating items 11 | */ 12 | class Transform : public AbstractSelection 13 | { 14 | Q_OBJECT 15 | 16 | public: 17 | Transform(QWidget *parent = nullptr); 18 | Transform(const Transform &other); 19 | ~Transform(); 20 | 21 | void move(QGraphicsSceneMouseEvent *event); 22 | void press(QGraphicsSceneMouseEvent *event); 23 | void release(QGraphicsSceneMouseEvent *event); 24 | 25 | void setTransformMode(bool set); 26 | QWidget * getToolMenu(); 27 | Tool * clone() const; 28 | void updateBounds(); 29 | 30 | public slots: 31 | void drawBounds(bool draw); 32 | inline void setAutoSelect(bool set){ _autoSelect = set; } 33 | void actionTaken(bool accept); 34 | 35 | private: 36 | void drawBoundingRect(); 37 | void connectMenu(); 38 | 39 | TransformTool::BoundingRectItem *_rect; 40 | TransformTool::BoundingRectItem::HotSpot _handle; 41 | bool _boundsDrawn, _autoSelect; 42 | QPointF _prevPos, _curPos; 43 | Qt::MouseButton _mouseButton; 44 | QList _curState, _prevState; 45 | QList _curItems; 46 | qreal _scalex, _scaley; 47 | qreal width, height; 48 | qreal _totaldx, _totaldy; 49 | 50 | signals: 51 | void switchedToTransformMode(bool enable); 52 | }; 53 | 54 | #endif // SELECTTOOL_H 55 | -------------------------------------------------------------------------------- /src/widgets/layermanager.cpp: -------------------------------------------------------------------------------- 1 | #include "layermanager.h" 2 | 3 | LayerManager::LayerManager(QWidget *parent) : 4 | QListWidget(parent) 5 | { 6 | QFile styleFile(":/styles/layermanager.qss"); 7 | styleFile.open(QFile::ReadOnly); 8 | 9 | QString style(styleFile.readAll() ); 10 | setStyleSheet(style); 11 | installEventFilter(this); 12 | setSelectionMode(QAbstractItemView::ExtendedSelection); 13 | } 14 | 15 | LayerManager::~LayerManager() 16 | { } 17 | 18 | bool LayerManager::eventFilter(QObject *obj, QEvent *event) 19 | { 20 | if (event->type() == QEvent::ChildRemoved) { 21 | int n = count(); 22 | 23 | for (int i = 0; i < n; i++) { 24 | QListWidgetItem *w = item(i); 25 | Layer *l = dynamic_cast(w); 26 | l->setZvalue(n - i); 27 | } 28 | return true; 29 | } 30 | 31 | return QListWidget::eventFilter(obj, event); 32 | } 33 | 34 | void LayerManager::updateItems(QList items) 35 | { 36 | int max = count(); 37 | for (int i = max - 1; i >= 0; i--) { 38 | takeItem(i); 39 | } 40 | 41 | _curItems = items; 42 | QList::reverse_iterator itr = _curItems.rbegin(); 43 | for (int i = 0; itr != _curItems.rend(); ++itr, i++) { 44 | insertItem(i, *itr); 45 | } 46 | } 47 | 48 | QList LayerManager::getitems() 49 | { 50 | QList l; 51 | 52 | int n = count(); 53 | 54 | for (int i = 0; i < n; i++) { 55 | l.push_back(item(i)); 56 | } 57 | 58 | return l; 59 | } 60 | -------------------------------------------------------------------------------- /src/widgets/layermanager.h: -------------------------------------------------------------------------------- 1 | #ifndef LAYERMANAGER_H 2 | #define LAYERMANAGER_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "../layers/layer.h" 10 | 11 | class LayerManager : public QListWidget 12 | { 13 | Q_OBJECT 14 | public: 15 | LayerManager(QWidget *parent = nullptr); 16 | ~LayerManager(); 17 | 18 | void updateItems(QList items); 19 | QList getitems(); 20 | 21 | signals: 22 | void itemschanged(); 23 | 24 | protected: 25 | bool eventFilter(QObject *, QEvent *); 26 | 27 | private: 28 | QList _curItems; 29 | }; 30 | 31 | #endif // LAYERMANAGER_H 32 | -------------------------------------------------------------------------------- /src/widgets/paintwidget.cpp: -------------------------------------------------------------------------------- 1 | #include "paintwidget.h" 2 | 3 | /** 4 | * @brief PaintWidget::PaintWidget Constructs a new PaintWidget for a new document 5 | * Creates a new canvas based on image path 6 | * @param imagePath 7 | * @param parent 8 | */ 9 | PaintWidget::PaintWidget(const QString &imagePath, Tool *tool, 10 | QWidget *parent) : 11 | QGraphicsView(parent) 12 | { 13 | setImagePath(imagePath); 14 | QImage image = getImageFromPath(imagePath); 15 | prepareDocument(tool, image.rect()); 16 | 17 | createBgLayer(image); 18 | 19 | int dpi = image.dotsPerMeterX() * 2.54 / 100; 20 | _canvas = 21 | QSharedDataPointer(new Canvas(imagePath, image.width(), 22 | image.height(), 23 | Canvas::PIXELS, 24 | dpi, Canvas::PPI)); 25 | } 26 | 27 | PaintWidget::PaintWidget(Document &document, Tool *tool, QWidget *parent) : 28 | QGraphicsView(parent) 29 | { 30 | _canvas = document.getCanvas(); 31 | setImagePath(_canvas->getName()); 32 | 33 | QImage image = drawEmptyImage(_canvas); 34 | prepareDocument(tool, image.rect()); 35 | 36 | for (auto &i : document.getLayerList()) { 37 | pushLayer(i); 38 | } 39 | } 40 | 41 | /** 42 | * @brief PaintWidget::PaintWidget Constructs a new PaintWidget for a new document 43 | * Creates a new canvas based on Document 44 | * @param document 45 | * @param parent 46 | */ 47 | PaintWidget::PaintWidget(const QSharedDataPointer canvas, Tool *tool, 48 | QWidget *parent) : 49 | QGraphicsView(parent) 50 | { 51 | _canvas = canvas; 52 | 53 | setImagePath(canvas->getName()); 54 | QImage image = drawEmptyImage(canvas); 55 | image.fill(Qt::white); 56 | prepareDocument(tool, image.rect()); 57 | 58 | createBgLayer(image); 59 | } 60 | 61 | void PaintWidget::createBgLayer(const QImage &image) 62 | { 63 | RasterLayer *layer = getLayerFromImage(image, "Background"); 64 | layer->setLocked(false); 65 | pushLayer(layer); 66 | } 67 | 68 | QImage PaintWidget::drawEmptyImage(const QSharedDataPointer canvas) 69 | { 70 | QSize imageSize(canvas->getWidth(), canvas->getHeight()); 71 | 72 | QImage image(imageSize, QImage::Format_ARGB32_Premultiplied); 73 | 74 | return image; 75 | } 76 | 77 | void PaintWidget::prepareDocument(Tool *tool, QRect rect) 78 | { 79 | addStyleSheet(); 80 | setupCanvas(rect); 81 | setTool(tool); 82 | 83 | connect(d, SIGNAL(importAvailable(QString)), 84 | this, SLOT(importPathToLayer(QString))); 85 | 86 | connect(d, SIGNAL(selectionChanged()), this, SLOT(setSelectedLayers())); 87 | 88 | setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing 89 | | QPainter::SmoothPixmapTransform); 90 | 91 | setViewportUpdateMode(QGraphicsView::BoundingRectViewportUpdate); 92 | } 93 | 94 | /** 95 | * @brief PaintWidget::add3NewLayer Adds a new layer based on an image 96 | * TODO: Support adding documents as layer 97 | * @param imagePath 98 | */ 99 | void PaintWidget::importPathToLayer(const QString &fileName) 100 | { 101 | if (isFileValid(fileName)) { 102 | QImage image = getImageFromPath(fileName); 103 | QFileInfo info(fileName); 104 | 105 | RasterLayer *layer = getLayerFromImage(image, info.fileName()); 106 | pushLayer(layer); 107 | } 108 | } 109 | 110 | QImage PaintWidget::getImageFromPath(const QString &imagePath) 111 | { 112 | if (isRaw(imagePath)) { 113 | QImageReader reader(imagePath); 114 | QSize size = reader.size(); 115 | 116 | int w = size.width() - 1; 117 | int h = size.height() - 1; 118 | 119 | QSize newSize = QSize(w, h); 120 | reader.setScaledSize(newSize); 121 | return reader.read(); 122 | } 123 | return QImage(imagePath); 124 | } 125 | 126 | bool PaintWidget::isRaw(const QString &imagePath) 127 | { 128 | QStringList list = imagePath.split("."); 129 | QString fileNameNoExt = list[1]; 130 | 131 | QString rawExtensions[] = { 132 | "ARW", 133 | "BAY", 134 | "CR2", 135 | "DCS", 136 | "MOS", 137 | "NEF", 138 | "RAW" 139 | }; 140 | 141 | return std::find(std::begin(rawExtensions), std::end(rawExtensions), 142 | fileNameNoExt.toUpper()) != std::end(rawExtensions); 143 | } 144 | 145 | bool PaintWidget::isFileValid(const QString& fileName) 146 | { 147 | return PaintWidget::isImageSupported(fileName); 148 | } 149 | 150 | bool PaintWidget::isImageSupported(const QString &fileName) 151 | { 152 | QImageReader reader(fileName); 153 | return reader.format() != ""; 154 | } 155 | 156 | void PaintWidget::addStyleSheet() 157 | { 158 | QFile styleFile(":/styles/paintwidget.qss"); 159 | styleFile.open(QFile::ReadOnly); 160 | 161 | QString style(styleFile.readAll() ); 162 | setStyleSheet(style); 163 | } 164 | 165 | /** 166 | * @brief PaintWidget::setupCanvas 167 | * Sets up the canvas and the drawing environment to place layers 168 | * @param image The target QImage layer to be placed. 169 | */ 170 | void PaintWidget::setupCanvas(QRect rect) 171 | { 172 | setSceneRect(rect); 173 | 174 | d = new Drawing(this, rect.width(), rect.height()); 175 | setScene(d); 176 | fitInView(d->sceneRect(), Qt::KeepAspectRatio); 177 | } 178 | 179 | /** 180 | * @brief PaintWidget::pushLayer 181 | * @param image 182 | * @param name 183 | */ 184 | void PaintWidget::pushLayer(Layer *layer) 185 | { 186 | // Needs smarter naming based on positions on the stack 187 | layer->setParent(d->getParentItem()); 188 | _items.push_back(layer); 189 | d->clearSelection(); 190 | layer->setLayerSelected(true); 191 | } 192 | 193 | RasterLayer * PaintWidget::getLayerFromImage(const QImage &image, 194 | const QString &name) 195 | { 196 | return new RasterLayer(name, image); 197 | } 198 | 199 | /** 200 | * @brief PaintWidget::wheelEvent Overrides the wheelEvent of QGraphicsView 201 | * Implements zooming in and out according to the position of the cursor 202 | * @param document 203 | * @param parent 204 | */ 205 | void PaintWidget::wheelEvent(QWheelEvent *event) 206 | { 207 | const QPointF p0scene = mapToScene(event->pos()); 208 | 209 | qreal factor = std::pow(1.001, event->delta()); 210 | scale(factor, factor); 211 | 212 | const QPointF p1mouse = mapFromScene(p0scene); 213 | const QPointF move = p1mouse - event->pos(); // The move 214 | horizontalScrollBar()->setValue(move.x() + horizontalScrollBar()->value()); 215 | verticalScrollBar()->setValue(move.y() + verticalScrollBar()->value()); 216 | } 217 | 218 | void PaintWidget::setSelectedLayers() 219 | { 220 | QList selectedItems = d->selectedItems(); 221 | QList allItems = d->getParentItem()->childItems(); 222 | QList::iterator itr = allItems.begin(); 223 | for (; itr != allItems.end(); ++itr) { 224 | Layer *l = dynamic_cast(*itr); 225 | if (selectedItems.contains(*itr)) { 226 | l->setLayerSelected(true); 227 | } else { 228 | l->setLayerSelected(false); 229 | } 230 | } 231 | 232 | if (_currentTool->getToolType() == Tool::TRANSFORM) { 233 | Transform *t = dynamic_cast(_currentTool); 234 | t->updateBounds(); 235 | } 236 | } 237 | 238 | /** 239 | * @brief PaintWidget::setTool Sets up the tool and passes on layers/selection areas if necessary 240 | * @param tool 241 | */ 242 | void PaintWidget::setTool(Tool *tool) 243 | { 244 | setCursor(tool->getToolCursor()); 245 | _currentTool = tool; 246 | d->setTool(tool); 247 | switch (_currentTool->getToolGroup()) { 248 | case Tool::SELECTION: { 249 | AbstractSelection *curTool = 250 | dynamic_cast(tool); 251 | if (d != nullptr) { 252 | curTool->setScene(d); 253 | } 254 | break; 255 | } 256 | default: 257 | break; 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /src/widgets/paintwidget.h: -------------------------------------------------------------------------------- 1 | #ifndef PAINTWIDGET_H 2 | #define PAINTWIDGET_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include 18 | #include 19 | 20 | #include "../model/drawing.h" 21 | #include "../widgets/paintwidget.h" 22 | #include "../model/canvas.h" 23 | #include "../tools/abstractselection.h" 24 | #include "../tools/tool.h" 25 | #include "../layers/rasterlayer.h" 26 | #include "../tools/transform.h" 27 | #include "../model/document.h" 28 | 29 | class PaintWidget : public QGraphicsView 30 | { 31 | Q_OBJECT 32 | public: 33 | 34 | PaintWidget(const QString &imagePath, Tool *tool, 35 | QWidget *parent = nullptr); 36 | PaintWidget(const QSharedDataPointer canvas, Tool *tool, 37 | QWidget *parent = nullptr); 38 | PaintWidget(Document &document, Tool *tool, QWidget *parent = nullptr); 39 | 40 | void pushLayer(Layer *layer); 41 | 42 | inline QSharedDataPointer getCanvas(){ return _canvas; } 43 | inline void setImagePath(QString path){ _imagePath = path; } 44 | inline QString getImagePath() const { return _imagePath; } 45 | inline QList getItems() const { return _items; } 46 | // This has to be extended to accomodate new documents 47 | void setSelectedLayers(QList layers); 48 | void setTool(Tool *tool); 49 | static bool isFileValid(const QString& fileName); 50 | static RasterLayer * getLayerFromImage(const QImage &image, 51 | const QString &name); 52 | 53 | public slots: 54 | void importPathToLayer(const QString &fileName); 55 | void setSelectedLayers(); 56 | 57 | signals: 58 | void layersSelected(QList layers); 59 | 60 | protected: 61 | void wheelEvent(QWheelEvent *event); 62 | 63 | private: 64 | Drawing *d; 65 | QString _imagePath; 66 | QList _items; 67 | 68 | 69 | QImage getImageFromPath(const QString &imagePath); 70 | void createBgLayer(const QImage &image); 71 | QImage drawEmptyImage(const QSharedDataPointer canvas); 72 | void prepareDocument(Tool *tool, QRect rect); 73 | void setupCanvas(QRect rect); 74 | bool isRaw(const QString &imagePath); 75 | void addStyleSheet(); 76 | 77 | static bool isImageSupported(const QString& fileName); 78 | Tool *_currentTool; 79 | QSharedDataPointer _canvas; 80 | }; 81 | 82 | #endif // PAINTWIDGET_H 83 | -------------------------------------------------------------------------------- /uncrustify.cfg: -------------------------------------------------------------------------------- 1 | # -------------------------------------------------------------------------------------------------# 2 | # # 3 | # _ _ _ _ __ ___ _____ _ _ __ _ # 4 | # | | | |_ _ __ _ _ _ _ __| |_(_)/ _|_ _ / __| / / __|| |_ _| |_ __ ___ _ _ / _(_)__ _ # 5 | # | |_| | ' \/ _| '_| || (_-< _| | _| || | | (__ / / (_|_ _|_ _| / _/ _ \ ' \| _| / _` | # 6 | # \___/|_||_\__|_| \_,_/__/\__|_|_| \_, | \___/_/ \___||_| |_| \__\___/_||_|_| |_\__, | # 7 | # |__/ |___/ # 8 | # # 9 | # -------------------------------------------------------------------------------------------------# 10 | # # 11 | # Style: rindeal # 12 | # # 13 | # -------------------------------------------------------------------------------------------------# 14 | # Boilerplate: https://github.com/bengardner/uncrustify/blob/master/etc/defaults.cfg # 15 | # -------------------------------------------------------------------------------------------------# 16 | 17 | 18 | ## General 19 | ## ------------------------------------------------------------------------------------------------- 20 | 21 | # The type of line endings 22 | newlines = lf # auto/lf/crlf/cr 23 | 24 | code_width = 80 25 | 26 | # empty_lines_max = nl_max - 1 27 | nl_max = 3 28 | 29 | 30 | ## UNICODE 31 | ## ------------------------------------------------------------------------------------------------- 32 | ## Ideally ASCII, UTF-8 otherwise 33 | 34 | # If the file contains bytes with values between 128 and 255, but is not UTF-8, then output as UTF-8 35 | utf8_byte = false 36 | 37 | # Force the output encoding to UTF-8 38 | utf8_force = true 39 | 40 | 41 | ## Tabs 42 | ## ------------------------------------------------------------------------------------------------- 43 | ## Always use 4 spaces 44 | 45 | input_tab_size = 4 46 | output_tab_size = 4 47 | 48 | indent_with_tabs = 0 49 | 50 | # Comments that are not a brace level are indented with tabs on a tabstop. 51 | # Requires indent_with_tabs = 2. If false, will use spaces. 52 | indent_cmt_with_tabs = false 53 | 54 | # Whether to use tabs for aligning 55 | align_with_tabs = false 56 | 57 | # Whether to keep non-indenting tabs 58 | align_keep_tabs = false 59 | 60 | # Whether to bump out to the next tab when aligning 61 | align_on_tabstop = false 62 | 63 | 64 | ## Indenting 65 | ## ------------------------------------------------------------------------------------------------- 66 | 67 | # True: indent_func_call_param will be used (default) 68 | # False: indent_func_call_param will NOT be used. 69 | use_indent_func_call_param = true # false/true 70 | 71 | 72 | # The continuation indent for func_*_param if they are true. 73 | # If non-zero, this overrides the indent. 74 | indent_param = 1 # unsigned number 75 | 76 | # The number of columns to indent per level. 77 | # Usually 2, 3, 4, or 8. 78 | indent_columns = 4 79 | 80 | # The continuation indent. If non-zero, this overrides the indent of '(' and '=' continuation indents. 81 | # For FreeBSD, this is set to 4. Negative value is absolute and not increased for each ( level 82 | indent_continue = 0 83 | 84 | 85 | ## Spacing 86 | ## ------------------------------------------------------------------------------------------------- 87 | 88 | # Whether to balance spaces inside nested parens 89 | sp_balance_nested_parens = false 90 | 91 | 92 | ## Parentheses 93 | ## ------------------------------------------------------------------------------------------------- 94 | 95 | # Controls the indent of a close paren after a newline. 96 | # 0: Indent to body level 97 | # 1: Align under the open paren 98 | # 2: Indent to the brace level 99 | indent_paren_close = 0 100 | 101 | 102 | ## Preprocessor 103 | ## ------------------------------------------------------------------------------------------------- 104 | 105 | # Control indent of preprocessors inside #if blocks at brace level 0 106 | pp_indent = remove # ignore/add/remove/force 107 | 108 | # indent by 1 space 109 | pp_space = add 110 | pp_space_count = 1 111 | 112 | # indent pp at code level 113 | pp_indent_at_level = true 114 | pp_define_at_level = true 115 | 116 | # Control whether to indent the code between #if, #else and #endif when not at file-level 117 | pp_if_indent_code = false 118 | 119 | # # Align macro functions and variables together 120 | align_pp_define_together = false 121 | 122 | # The minimum space between label and value of a preprocessor define 123 | align_pp_define_gap = 1 124 | 125 | # The span for aligning on '#define' bodies (0=don't align) 126 | align_pp_define_span = 2 127 | 128 | # Add or remove space around preprocessor '##' concatenation operator. Default=Add 129 | sp_pp_concat = add # ignore/add/remove/force 130 | 131 | # Add or remove space after preprocessor '#' stringify operator. Also affects the '#@' charizing operator. 132 | sp_pp_stringify = ignore # ignore/add/remove/force 133 | 134 | # Add or remove space before preprocessor '#' stringify operator as in '#define x(y) L#y'. 135 | sp_before_pp_stringify = ignore # ignore/add/remove/force 136 | 137 | 138 | # Template 139 | # -------------------------------------------------------------------------------------------------- 140 | 141 | # Add or remove space in 'template <' vs 'template<'. 142 | # If set to ignore, sp_before_angle is used. 143 | sp_template_angle = add # ignore/add/remove/force 144 | 145 | # Add or remove space before '<>' 146 | sp_before_angle = remove # ignore/add/remove/force 147 | 148 | # Add or remove space inside '<' and '>' 149 | sp_inside_angle = remove # ignore/add/remove/force 150 | 151 | # Add or remove space after '<>' 152 | sp_after_angle = add # ignore/add/remove/force 153 | 154 | # Add or remove space between '<>' and '(' as found in 'new List();' 155 | sp_angle_paren = remove # ignore/add/remove/force 156 | 157 | # Add or remove space between '<>' and a word as in 'List m;' 158 | sp_angle_word = add # ignore/add/remove/force 159 | 160 | # Add or remove space between '>' and '>' in '>>' (template stuff C++/C# only). Default=Add 161 | sp_angle_shift = add # ignore/add/remove/force 162 | 163 | 164 | 165 | 166 | 167 | indent_align_string = false 168 | 169 | # Whether braces are indented to the body level 170 | indent_braces = false 171 | # Disabled indenting function braces if indent_braces is true 172 | indent_braces_no_func = false 173 | # Disabled indenting class braces if indent_braces is true 174 | indent_braces_no_class = false 175 | # Disabled indenting struct braces if indent_braces is true 176 | indent_braces_no_struct = false 177 | # Indent based on the size of the brace parent, i.e. 'if' => 3 spaces, 'for' => 4 spaces, etc. 178 | indent_brace_parent = false 179 | 180 | indent_namespace = false 181 | indent_extern = false 182 | indent_class = true 183 | indent_class_colon = false 184 | indent_else_if = false 185 | indent_var_def_cont = true 186 | 187 | indent_func_call_param = false 188 | indent_func_def_param = true 189 | indent_func_proto_param = true 190 | indent_func_class_param = false 191 | indent_func_ctor_var_param = false 192 | indent_func_param_double = true 193 | 194 | indent_template_param = false 195 | indent_relative_single_line_comments = false 196 | indent_col1_comment = true 197 | indent_access_spec_body = false 198 | indent_paren_nl = false 199 | indent_comma_paren = false 200 | indent_bool_paren = false 201 | indent_first_bool_expr = false 202 | indent_square_nl = false 203 | indent_preserve_sql = false 204 | indent_align_assign = true 205 | 206 | #align_number_left = true 207 | align_func_params = true 208 | align_same_func_call_params = false 209 | align_var_def_colon = false 210 | align_var_def_attribute = true 211 | align_var_def_inline = true 212 | align_right_cmt_mix = false 213 | align_on_operator = false 214 | align_mix_var_proto = false 215 | align_single_line_func = false 216 | align_single_line_brace = false 217 | align_nl_cont = false 218 | align_left_shift = true 219 | align_oc_decl_colon = false 220 | 221 | nl_collapse_empty_body = true 222 | nl_assign_leave_one_liners = true 223 | nl_class_leave_one_liners = true 224 | nl_enum_leave_one_liners = true 225 | nl_getset_leave_one_liners = true 226 | nl_func_leave_one_liners = true 227 | nl_if_leave_one_liners = true 228 | nl_multi_line_cond = true 229 | nl_multi_line_define = true 230 | nl_before_case = false 231 | nl_after_case = false 232 | nl_after_return = true 233 | nl_after_semicolon = true 234 | nl_after_brace_open = false 235 | nl_after_brace_open_cmt = false 236 | nl_after_vbrace_open = false 237 | nl_after_vbrace_open_empty = false 238 | nl_after_brace_close = false 239 | nl_after_vbrace_close = false 240 | nl_define_macro = false 241 | nl_squeeze_ifdef = false 242 | nl_ds_struct_enum_cmt = false 243 | nl_ds_struct_enum_close_brace = false 244 | nl_create_if_one_liner = false 245 | nl_create_for_one_liner = false 246 | nl_create_while_one_liner = false 247 | ls_for_split_full = true 248 | ls_func_split_full = false 249 | nl_after_multiline_comment = true 250 | eat_blanks_after_open_brace = true 251 | eat_blanks_before_close_brace = true 252 | mod_full_brace_if_chain = true 253 | mod_pawn_semicolon = false 254 | mod_full_paren_if_bool = false 255 | mod_remove_extra_semicolon = false 256 | mod_sort_import = false 257 | mod_sort_using = false 258 | mod_sort_include = false 259 | mod_move_case_break = false 260 | mod_remove_empty_return = true 261 | cmt_indent_multi = true 262 | cmt_c_group = false 263 | cmt_c_nl_start = false 264 | cmt_c_nl_end = false 265 | cmt_cpp_group = false 266 | cmt_cpp_nl_start = false 267 | cmt_cpp_nl_end = false 268 | cmt_cpp_to_c = false 269 | cmt_star_cont = true 270 | cmt_multi_check_last = true 271 | cmt_insert_before_preproc = false 272 | indent_sing_line_comments = 0 273 | indent_switch_case = 4 274 | indent_case_shift = 0 275 | 276 | align_var_def_star_style = 0 277 | align_var_def_amp_style = 1 278 | align_assign_span = 1 279 | align_assign_thresh = 8 280 | align_enum_equ_span = 3 281 | align_var_struct_span = 3 282 | align_var_struct_gap = 1 283 | align_struct_init_span = 2 284 | align_right_cmt_span = 2 285 | align_right_cmt_gap = 1 286 | align_right_cmt_at_col = 2 287 | 288 | nl_end_of_file_min = 1 289 | nl_func_var_def_blk = 0 290 | nl_after_func_body = 2 291 | nl_after_func_body_one_liner = 1 292 | nl_before_block_comment = 2 293 | nl_after_struct = 1 294 | mod_full_brace_nl = 1 295 | mod_add_long_function_closebrace_comment = 32 296 | mod_add_long_ifdef_endif_comment = 10 297 | mod_add_long_ifdef_else_comment = 10 298 | sp_arith = force 299 | sp_assign = force 300 | sp_assign_default = add 301 | sp_enum_assign = force 302 | sp_bool = force 303 | sp_compare = force 304 | sp_before_ptr_star = add 305 | sp_before_unnamed_ptr_star = add 306 | sp_between_ptr_star = remove 307 | sp_after_ptr_star = remove 308 | sp_after_ptr_star_func = force 309 | sp_before_ptr_star_func = force 310 | sp_after_type = force 311 | sp_before_sparen = force 312 | sp_inside_sparen = remove 313 | sp_after_sparen = add 314 | sp_sparen_brace = add 315 | sp_special_semi = remove 316 | sp_before_semi = remove 317 | sp_before_semi_for_empty = remove 318 | sp_after_semi = add 319 | sp_after_semi_for_empty = remove 320 | sp_after_comma = force 321 | sp_before_comma = remove 322 | sp_before_case_colon = remove 323 | sp_after_operator = add 324 | sp_after_operator_sym = add 325 | sp_after_cast = add 326 | sp_inside_paren_cast = remove 327 | sp_sizeof_paren = remove 328 | sp_inside_braces_enum = add 329 | sp_inside_braces_struct = add 330 | sp_inside_braces = add 331 | sp_inside_braces_empty = add 332 | sp_func_proto_paren = remove 333 | sp_func_def_paren = remove 334 | sp_inside_fparens = remove 335 | sp_inside_fparen = remove 336 | sp_fparen_brace = remove 337 | sp_func_call_paren = remove 338 | sp_func_call_paren_empty = remove 339 | sp_func_call_user_paren = remove 340 | sp_return_paren = add 341 | sp_attribute_paren = remove 342 | sp_defined_paren = remove 343 | sp_macro = add 344 | sp_macro_func = add 345 | sp_else_brace = add 346 | sp_brace_else = add 347 | sp_brace_typedef = add 348 | sp_not = remove 349 | sp_inv = remove 350 | sp_addr = remove 351 | sp_member = remove 352 | sp_deref = remove 353 | sp_sign = remove 354 | sp_incdec = remove 355 | sp_before_nl_cont = add 356 | sp_cond_colon = force 357 | sp_cond_question = force 358 | sp_cmt_cpp_start = add 359 | nl_start_of_file = remove 360 | nl_end_of_file = force 361 | nl_assign_brace = remove 362 | nl_assign_square = remove 363 | nl_enum_brace = remove 364 | nl_struct_brace = remove 365 | nl_union_brace = remove 366 | nl_if_brace = remove 367 | nl_brace_else = remove 368 | nl_elseif_brace = remove 369 | nl_else_brace = remove 370 | nl_else_if = remove 371 | nl_for_brace = remove 372 | nl_while_brace = remove 373 | nl_do_brace = remove 374 | nl_brace_while = remove 375 | nl_switch_brace = remove 376 | nl_case_colon_brace = remove 377 | nl_func_type_name = remove 378 | nl_func_proto_type_name = remove 379 | nl_func_paren = remove 380 | nl_func_def_paren = remove 381 | nl_func_decl_empty = remove 382 | nl_func_def_empty = remove 383 | nl_fdef_brace = add 384 | nl_return_expr = remove 385 | pos_arith = lead 386 | pos_assign = trail 387 | pos_bool = trail 388 | pos_conditional = trail 389 | pos_comma = trail 390 | pos_class_comma = lead 391 | pos_class_colon = lead 392 | mod_full_brace_do = remove 393 | mod_full_brace_for = remove 394 | mod_full_brace_function = force 395 | mod_full_brace_while = remove 396 | mod_paren_on_return = ignore 397 | --------------------------------------------------------------------------------