pax_global_header00006660000000000000000000000064140621604650014516gustar00rootroot0000000000000052 comment=d4b20eb6aaaf8ea6d4a3c5782d72e9038f516e5f wiRedPanda-3.0.1/000077500000000000000000000000001406216046500135355ustar00rootroot00000000000000wiRedPanda-3.0.1/.gitattributes000066400000000000000000000005721406216046500164340ustar00rootroot00000000000000# Auto detect text files and perform LF normalization * text=auto # Custom for Visual Studio *.cs diff=csharp # Standard to msysgit *.doc diff=astextplain *.DOC diff=astextplain *.docx diff=astextplain *.DOCX diff=astextplain *.dot diff=astextplain *.DOT diff=astextplain *.pdf diff=astextplain *.PDF diff=astextplain *.rtf diff=astextplain *.RTF diff=astextplain wiRedPanda-3.0.1/.github/000077500000000000000000000000001406216046500150755ustar00rootroot00000000000000wiRedPanda-3.0.1/.github/workflows/000077500000000000000000000000001406216046500171325ustar00rootroot00000000000000wiRedPanda-3.0.1/.github/workflows/build.yml000066400000000000000000000120541406216046500207560ustar00rootroot00000000000000# Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors # SPDX-License-Identifier: GPL-3.0-or-later name: Build on: [push, pull_request] env: BUILD_TYPE: MinSizeRel jobs: # === Ubuntu 16.04 === ubuntu-16: runs-on: ubuntu-16.04 steps: - uses: actions/checkout@v2 with: submodules: recursive fetch-depth: 0 - name: Install Qt5 run: | sudo apt-get update sudo apt-get install build-essential libgl1-mesa-dev libxkbcommon-x11-0 libpulse-dev -y python3 -m pip install setuptools wheel python3 -m pip install py7zr==0.10.1 python3 -m pip install aqtinstall==0.9.7 python3 -m pip install importlib-metadata==2.0.0 python3 -m aqt install 5.15.1 linux desktop -m qtcharts qtmultimedia -O /home/runner/work/wiredpanda-testing/Qt ls -li /home/runner/work/wiredpanda-testing/Qt ls -li /home/runner/work/wiredpanda-testing/Qt/5.15.1 ls -li /home/runner/work/wiredpanda-testing/Qt/5.15.1/gcc_64 ls -li /home/runner/work/wiredpanda-testing/Qt/5.15.1/gcc_64/bin export Qt5_Dir=/home/runner/work/wiredpanda-testing/Qt/5.15.1 export Qt5_DIR=/home/runner/work/wiredpanda-testing/Qt/5.15.1 export QT_PLUGIN_PATH=/home/runner/work/wiredpanda-testing/Qt/5.15.1/gcc_64/plugins PATH=/home/runner/work/wiredpanda-testing/Qt/5.15.1/gcc_64/bin:$PATH export PATH - name: Install AppImage dependencies run: | sudo apt-get install libgstreamer-plugins-base1.0-0 libgstreamer-plugins-base1.0-dev sudo apt-get install libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-render-util0-dev libxcb-render-util0 libxcb-xinerama0 - name: Get linuxdeployqt run: | wget "https://github.com/probonopd/linuxdeployqt/releases/download/continuous/linuxdeployqt-continuous-x86_64.AppImage" -P ~/ chmod +x ~/linuxdeployqt-continuous-x86_64.AppImage - name: Build run: | PATH=/home/runner/work/wiredpanda-testing/Qt/5.15.1/gcc_64/bin:$PATH export PATH mkdir build cd build qmake ../WPanda.pro make -j8 - name: Deploy uses: actions/upload-artifact@v2 with: name: 'wiredpanda-ubuntu16.04-glibc' path: build/app/wpanda - name: Make WiredPanda AppImage run: | PATH=/home/runner/work/wiredpanda-testing/Qt/5.15.1/gcc_64/bin:$PATH export PATH cp installer/Linux/portable_files/wpanda.desktop build/app/wpanda.desktop cp app/resources/wpanda.png build/app/wpanda.png ~/linuxdeployqt-continuous-x86_64.AppImage build/app/wpanda -appimage - name: Upload WiredPanda AppImage uses: actions/upload-artifact@v2 with: name: WiredPanda Linux (64-bit) AppImage path: wiRED_Panda*.AppImage # === Ubuntu 18.04 === ubuntu-18: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 with: submodules: recursive fetch-depth: 0 - name: Install Qt5 uses: jurplel/install-qt-action@v2 with: version: 5.15.1 modules: qtcharts qtmultimedia - name: Build run: | mkdir build cd build qmake ../WPanda.pro make -j8 - name: Deploy uses: actions/upload-artifact@v2 with: name: 'wiredpanda-ubuntu18.04-glibc' path: build/app/wpanda # === Windows 10 === windows: runs-on: windows-latest steps: - uses: actions/checkout@v2 with: submodules: recursive fetch-depth: 0 - name: Set up Git for properly handling symlinks on Windows run: git config --global core.symlinks true - name: Install Qt uses: jurplel/install-qt-action@v2 with: arch: win64_mingw81 version: 5.15.0 modules: qtcharts qtmultimedia - name: Build run: | mkdir build cd build qmake ../WPanda.pro make -j8 - name: Deploy WiredPanda binary run : | cd build\app\release windeployqt --compiler-runtime wpanda.exe del *.o del *.cpp cp $env:Qt5_Dir\bin\libgcc_s_seh-1.dll . cp $env:Qt5_Dir\bin\libstdc++-6.dll . cp $env:Qt5_Dir\bin\libwinpthread-1.dll . cd .. ren release wpanda - name: Upload WiredPanda build continue-on-error: true uses: actions/upload-artifact@v2 with: name: WiredPanda Windows (64-bit) build path: build\app\wpanda\ # === MacOS 10.15 === macos: runs-on: macos-10.15 steps: - uses: actions/checkout@v2 with: submodules: recursive fetch-depth: 0 - name: Install run: brew install qt gdb - name: Build run: | QTDIR="/usr/local/opt/qt/" && PATH="${QTDIR}/bin:$PATH" && LDFLAGS=-L${QTDIR}/lib && CPPFLAGS=-I${QTDIR}/include; mkdir build cd build qmake ../WPanda.pro make -j8 - name: Deploy uses: actions/upload-artifact@v2 with: name: 'wiredpanda-macos-10_15' path: build/app/wpanda.app/Contents/MacOS/wpanda wiRedPanda-3.0.1/.github/workflows/commit-style.yml000066400000000000000000000004411406216046500223020ustar00rootroot00000000000000name: Test commit history on: pull_request jobs: message-check: name: Block Merge Commits runs-on: ubuntu-latest steps: - name: Block Merge Commits uses: z3by/block-merge-commits-action@master with: repo-token: ${{ secrets.GITHUB_TOKEN }} wiRedPanda-3.0.1/.gitignore000066400000000000000000000005641406216046500155320ustar00rootroot00000000000000# C++ objects and libs *.slo *.lo *.o *.a *.la *.lai *.so *.dll *.dylib # Qt-es /.qmake.cache /.qmake.stash *.pro.user *.pro.user.* *.moc moc_*.cpp qrc_*.cpp ui_*.h Makefile* *-build-* # QtCreator *.autosave #QtCtreator Qml *.qmlproject.user *.qmlproject.user.* /.project *.db .sass-cache _site /build* .vscode *~ *# *.deb *.rpm *.exe *.tar.gz *.lintian Deploy/*wiRedPanda-3.0.1/CMakeLists.txt000066400000000000000000000031071406216046500162760ustar00rootroot00000000000000cmake_minimum_required (VERSION 3.12 FATAL_ERROR) project(WiredPanda VERSION 1.0 DESCRIPTION "Logic Port teaching tool" LANGUAGES CXX ) # wiRedPanda version set(APP_VERSION "3.0") option(BUILD_TEST "BUILD_TEST" 0) set(CMAKE_CXX_STANDARD 17) set(CXX_STANDARD_REQUIRED ON) set(QT_MIN_VERSION "5.14.0") set(CMAKE_AUTOMOC ON) set(AUTOMOC_MOC_OPTIONS -Muri=org.GIBIS-UNIFESP.wiRedPanda) set(CMAKE_AUTOUIC ON) set(CMAKE_AUTORCC ON) set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) if(MSVC) set(COMPILE_WARNS /W4 /WX) else() set(COMPILE_WARNS -Wall -Wextra -pedantic) endif() add_compile_definitions( QT_USE_QSTRINGBUILDER QT_NO_CAST_TO_ASCII QT_STRICT_ITERATORS QT_NO_CAST_FROM_BYTEARRAY QT_USE_FAST_OPERATOR_PLUS ) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED Charts Core Gui Multimedia PrintSupport Widgets ) set( WPANDA_LIBS Qt5::Charts Qt5::Core Qt5::Gui Qt5::Multimedia Qt5::PrintSupport Qt5::Widgets ) find_package (ECM ${KF5_MIN_VERSION} NO_MODULE) set (CMAKE_MODULE_PATH ${ECM_MODULE_PATH}) include(cmake/Git.cmake) include(cmake/Format.cmake) include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ) include_directories(./app/element) include_directories(./app/nodes) include_directories(./app) add_subdirectory(app) if(BUILD_TEST) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED Test ) add_subdirectory(test) endif() wiRedPanda-3.0.1/LICENSE000066400000000000000000001037241406216046500145510ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . wiRedPanda-3.0.1/README.md000066400000000000000000000077301406216046500150230ustar00rootroot00000000000000# WiRed Panda ![GitHub All Releases](https://img.shields.io/github/downloads/gibis-unifesp/wiredpanda/total?style=flat-square) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/gibis-unifesp/wiredpanda?style=flat-square) ![GitHub](https://img.shields.io/github/license/gibis-unifesp/wiredpanda?style=flat-square) WiRed Panda is a free software designed to help students to learn about logic circuits and simulate them in an easy and friendly way. The main features of the software are: - Works on Windows, macOS and Linux; - Real time logic simulation; - User-friendly interface; - It's intuitive and easy to use; - Export your work as an image or a PDF. _Leia-me em [português](README_pt_BR.md). Léame en [español](README_es.md)._ ![Main screen](https://user-images.githubusercontent.com/36349314/97934063-532ed000-1d53-11eb-9667-73ea32f456ce.png) ## Downloads Compiled binaries for Windows and Linux are available [here](http://gibis-unifesp.github.io/wiRedPanda/downloads/). Dynamically-linked binaries for macOS are available [here](https://github.com/GIBIS-UNIFESP/wiRedPanda/releases). ## Building ### On Linux & macOS #### Dependencies Qt 5.15.0+ is needed for building, as well as QtCharts and QtMultimedia. On sufficiently well updated distros, such as Arch Linux, Gentoo, Manjaro, Debian Testing, etc., Qt 5.15 can be installed from the standard repos. * Debian Testing ```bash sudo apt install qtbase5-dev qt5-make qtbase5-dev-tools qtchooser libqt5charts5-dev libqt5multimedia5-dev ``` * Arch Linux-based ```bash sudo pacman -S qt5-base qt5-charts qt5-multimedia ``` * macOS ```bash brew install qt ``` * Others The Qt version that most non-rolling release distros have will not be enough to build this project (at the time of writing). Qt5 may be installed directly from the [Qt website](https://www.qt.io/download), from unofficial installers such as [aqtinstall](https://github.com/miurahr/aqtinstall), from community-maintained repositories or [built from source](https://wiki.qt.io/Building_Qt_5_from_Git). Here's how one could use aqtinstall to install Qt 5.15.1 (Python 3 needed) alongside with the necessary plugins on a Debian-based distro. ```bash sudo apt-get update sudo apt-get install build-essential libgl1-mesa-dev libxkbcommon-x11-0 libpulse-dev -y python3 -m pip install setuptools wheel python3 -m pip install py7zr==0.10.1 python3 -m pip install aqtinstall==0.9.7 python3 -m pip install importlib-metadata==2.0.0 python3 -m aqt install 5.15.1 linux desktop -m qtcharts qtmultimedia -O ~/Qt export Qt5_Dir=~/Qt/5.15.1 export Qt5_DIR=~/Qt/5.15.1 export QT_PLUGIN_PATH=~/Qt/5.15.1/gcc_64/plugins PATH=~/Qt/5.15.1/gcc_64/bin:$PATH export PATH ``` #### Build process ```bash git clone https://github.com/GIBIS-UNIFESP/wiredpanda.git mkdir wiredpanda/build cd wiredpanda/build qmake ../WPanda.pro make -j$(nproc) ``` This process could take a while. Once concluded, the binary will be located at `wiredpanda/build/app/wpanda`, on Linux, and at `wiredpanda/build/app/wpanda.app/Contents/MacOS/wpanda` on macOS. ## Licensing WiRed Panda is licensed under the [GNU General Public License, Version 3.0](http://www.gnu.org/licenses/). See [`LICENSE`](LICENSE) for the full license text. Copyright (C) 2020 - Davi Morales, Fábio Cappabianco, Lucas Lellis, Rodrigo Torres and Vinícius Miguel. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . wiRedPanda-3.0.1/README_es.md000066400000000000000000000073721406216046500155140ustar00rootroot00000000000000# WiRed Panda ![GitHub All Releases](https://img.shields.io/github/downloads/gibis-unifesp/wiredpanda/total?style=flat-square) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/gibis-unifesp/wiredpanda?style=flat-square) ![GitHub](https://img.shields.io/github/license/gibis-unifesp/wiredpanda?style=flat-square) WiRed Panda es [Software Libre](https://es.wikipedia.org/wiki/Software_libre) diseñado para ayudar a los estudiantes a aprender sobre la lógica detrás de los circuitos eléctricos, y simularlos de una forma fácil e intuitiva. Las principales características de WiRed Panda son: - Es multiplataforma, funciona en Windows, MacOs y Linux; - Simulación de lógica en tiempo real; - Interfaz amigable para el usuario; - Es intuitivo y fácil de usar; - Puede exportar su trabajo como una imagen o un PDF. _Leia-me em [português](README_pt_BR.md). Read me in [English](README.md)._ ![Pantalla principal](https://user-images.githubusercontent.com/36349314/97934063-532ed000-1d53-11eb-9667-73ea32f456ce.png) ## Descargas Binarios precompilados para **Windows** y **Linux** están disponibles [aquí](http://gibis-unifesp.github.io/wiRedPanda/downloads/). Binarios vinculados dinamicamente para **Linux** y **MacOs** están disponibles [aquí](https://github.com/GIBIS-UNIFESP/wiRedPanda/releases). ## Compilación ### En Linux y MacOs #### Dependencias * Distribuciones basadas en Debian Buster o Ubuntu 18.04 ```bash sudo apt install qt5-default qt5-qmake qtmultimedia5-dev libqt5charts5-dev ``` * Distribuciones basadas en Debian Stretch o Ubuntu 16.04 Algunas dependencias no están disponibles a travez de los repositorios estándar. ```bash sudo add-apt-repository ppa:beineri/opt-qt-5.10.1-xenial sudo apt update sudo apt install dh-make qt510base qt510charts-no-lgpl qt510multimedia source /opt/qt510/bin/qt510-env.sh ``` * Ubuntu 14.04+ y Debian Jessie ```bash sudo add-apt-repository ppa:beineri/opt-qt-5.10.1-trusty sudo apt update sudo apt install dh-make qt510base qt510charts-no-lgpl qt510multimedia source /opt/qt510/bin/qt510-env.sh ``` * Fedora ```bash sudo dnf install qt5 qt5-devel qt5-qtmultimedia-devel qt5-charts-devel ``` * MacOs Puede [compilar Qt5 a partir del código fuente](https://doc.qt.io/qt-5/macos-building.html), o puede instalarlo usando [Homebrew](https://brew.sh/). ```bash brew install qt ``` ### Proceso de compilación ```bash git clone https://github.com/GIBIS-UNIFESP/wiredpanda.git mkdir wiredpanda/build cd wiredpanda/build qmake ../WPanda.pro make -j$(nproc) ``` Este proceso puede tardar unos momentos. Una vez finalizado el proceso de compilación podrá encontrar el binario en '/wiredpanda/build/app/wpanda' **si esta usando Linux**, y en 'wiredpanda/build/app/wpanda.app' **si esta usando MacOs**. ## Licencia WiRed Panda esta bajo la 3ra versión de la Licencia Pública General GNU ([GPL3.0](http://www.gnu.org/licenses/)). Para más información consultar el archivo [`LICENSE`](LICENSE). Copyright (C) 2020 - Davi Morales, Fábio Cappabianco, Lucas Lellis, Rodrigo Torres and Vinícius Miguel. Este programa es software libre: puedes redistribuirlo y/o modificarlo bajo los términos de la Licencia Pública General GNU publicada por la Free Software Foundation, ya sea la versión 3 de la licencia, o (a su elección) cualquier versión posterior. Este programa se distribuye con la esperanza de que sea útil, pero SIN NINGUNA GARANTÍA; sin siquiera la garantía implícita de COMERCIALIDAD o APTITUD PARA UN PROPÓSITO PARTICULAR. Para más detalles consultar la Licencia Pública General GNU. Deberá haber recibido una copia de la Licencia Pública General GNU junto con este programa. De lo contrario, consulte wiRedPanda-3.0.1/README_pt_BR.md000066400000000000000000000071361406216046500161110ustar00rootroot00000000000000# WiRed Panda ![GitHub All Releases](https://img.shields.io/github/downloads/gibis-unifesp/wiredpanda/total?style=flat-square) ![GitHub release (latest by date)](https://img.shields.io/github/v/release/gibis-unifesp/wiredpanda?style=flat-square) ![GitHub](https://img.shields.io/github/license/gibis-unifesp/wiredpanda?style=flat-square) WiRed Panda é um software gratuito desenvolvido para ajudar os alunos a aprender sobre circuitos lógicos e simulá-los de forma fácil e amigável. Suas principais características são: - Funcionar em Windows, macOS e Linux; - Permite a simulação em tempo real de circuitos lógicos; - Interface amigável; - Intuitivo e fácil de se usar; - Exporta para imagens ou PDF. _Read me in [English](README.md). Léame en [español](README_es.md)._ ![Tela principal](https://user-images.githubusercontent.com/36349314/97934063-532ed000-1d53-11eb-9667-73ea32f456ce.png) ## Downloads Binário compilados para Windows e Linux podem ser encontrados [aqui](http://gibis-unifesp.github.io/wiRedPanda/downloads/). Binários _linkados_ dinâmicamente para Linux e macOS podem ser encontrados [aqui](https://github.com/GIBIS-UNIFESP/wiRedPanda/releases). ## Compilação ### Em Linux & macOS #### Dependências * Distros baseadas em Debian Buster 10+ ou Ubuntu 18.04+. ```bash sudo apt install qt5-default qt5-qmake qtmultimedia5-dev libqt5charts5-dev ``` * Debian Stretch 8+ or Ubuntu 16.04+ based distros Algumas das dependências necessárias não estão nos repositórios padrão. ```bash sudo add-apt-repository ppa:beineri/opt-qt-5.10.1-xenial sudo apt update sudo apt install dh-make qt510base qt510charts-no-lgpl qt510multimedia source /opt/qt510/bin/qt510-env.sh ``` * Ubuntu 14.04+ (ou Debian equivalentemente velho) based distros ```bash sudo add-apt-repository ppa:beineri/opt-qt-5.10.1-trusty sudo apt update sudo apt install dh-make qt510base qt510charts-no-lgpl qt510multimedia source /opt/qt510/bin/qt510-env.sh ``` * Fedora ```bash sudo dnf install qt5 qt5-devel qt5-qtmultimedia-devel qt5-charts-devel ``` * macOS Você pode ou [compilar o Qt5](https://doc.qt.io/qt-5/macos-building.html) ou baixá-lo através do [Homebrew](https://brew.sh/). ```bash brew install qt ``` #### Processo de compilação ```bash git clone https://github.com/GIBIS-UNIFESP/wiredpanda.git mkdir wiredpanda/build cd wiredpanda/build qmake ../WPanda.pro make -j$(nproc) ``` Note: este processo pode demorar vários minutos para ser concluído. Uma vez terminado, o executável se encontrará em `wiredpanda/build/app/wpanda`, quando usando Linux, e em `wiredpanda/build/app/wpanda.app/Contents/MacOS/wpanda` quando em macOS. ## Licença O WiRed Panda este software é licenciado sob a [Licença Pública Geral GNU, Versão 3.0](http://www.gnu.org/licenses/). Veja o arquivo [`LICENSE`](LICENSE) para o texto completo da licença. Copyright (C) 2020 - Davi Morales, Fábio Cappabianco, Lucas Lellis, Rodrigo Torres e Vinícius Miguel. Este programa é um software livre: você pode redistribuí-lo e / ou modificar sob os termos da Licença Pública Geral GNU conforme publicada pela Free Software Foundation, seja a versão 3 da Licença, ou (à sua escolha) qualquer versão posterior. Este programa é distribuído na esperança de que seja útil, mas SEM QUALQUER GARANTIA; sem mesmo a garantia implícita de COMERCIABILIDADE ou ADEQUAÇÃO A UM DETERMINADO FIM. Veja a GNU General Public License para mais detalhes. Você deve ter recebido uma cópia da Licença Pública Geral GNU junto com este programa. Caso contrário, consulte . wiRedPanda-3.0.1/TODO.md000066400000000000000000000024061406216046500146260ustar00rootroot00000000000000## TODO List - [x] Add `.panda` to files when saving. [link](https://github.com/lellisls/wiRedPanda/issues/10) - [x] Undo/Redo. [link](https://github.com/lellisls/wiRedPanda/issues/1) - [x] Boxes. [link](https://github.com/lellisls/wiRedPanda/issues/9) - [ ] Installer. [link](https://github.com/lellisls/wiRedPanda/issues/3) - [x] Add keyboard shortcuts to inputs. [link](https://github.com/lellisls/wiRedPanda/issues/11) - [x] Line Split. - [x] Zoom. - [x] Dynamic scene resizing - [x] Align to grid [link](https://github.com/lellisls/wiRedPanda/issues/14). - [ ] ~~Align lines to grid ()~~ - [x] Hide Lines, Wires and Ports. - [x] Show Lines with issues. - [x] Search. - [ ] Flip Elements in the latteral pannel. - [X] 16-seg Display. - [ ] ~~Allow gates with more than 8 in/out.~~ - [ ] ~~Labels always in horizontal.~~ - [ ] Verify if file changed outside. - [x] MUX and DEMUX. - [x] Ctrl + Drag copy. - [x] Lauch box in new instance. ## Features we have yet to implement - [ ] Karnaught Map or Truth Table. - [ ] Limited Clock Frequencies. - [ ] Images as buttons. - [ ] Labels as separate elements. - [ ] Verilog Script Generator. - [ ] Add support to custom image backgrounds.. - [ ] ~~Create separate modules for execution and edition.~~ - [ ] Bugs and Warnings highlights. wiRedPanda-3.0.1/WPanda.pro000066400000000000000000000000461406216046500154310ustar00rootroot00000000000000TEMPLATE = subdirs SUBDIRS = app test wiRedPanda-3.0.1/app/000077500000000000000000000000001406216046500143155ustar00rootroot00000000000000wiRedPanda-3.0.1/app/CMakeLists.txt000066400000000000000000000036611406216046500170630ustar00rootroot00000000000000 set( WPANDA_FILES bewaveddolphin.cpp clockDialog.cpp commands.cpp common.cpp editor.cpp elementeditor.cpp elementfactory.cpp elementmapping.cpp filehelper.cpp globalproperties.cpp graphicelement.cpp graphicsview.cpp graphicsviewzoom.cpp ic.cpp icmanager.cpp icmapping.cpp icnotfoundexception.cpp icprototype.cpp icprototypeimpl.cpp itemwithid.cpp label.cpp LengthDialog.cpp listitemwidget.cpp logicelement.cpp mainwindow.cpp recentfilescontroller.cpp scene.cpp scstop.cpp serializationfunctions.cpp simplewaveform.cpp simulationcontroller.cpp thememanager.cpp arduino/codegenerator.cpp nodes/qneconnection.cpp nodes/qneport.cpp ) add_library(wpandacommon ${WPANDA_FILES} ) target_link_libraries( wpandacommon PUBLIC ${WPANDA_LIBS} ) target_compile_definitions( wpandacommon PUBLIC -DCURRENTDIR="${CMAKE_CURRENT_SOURCE_DIR}" -DAPP_VERSION="${APP_VERSION}" ) target_compile_options( wpandacommon PUBLIC ${COMPILE_WARNS} ) add_subdirectory(element) add_subdirectory(logicelement) qt5_add_resources( WPANDA_RESOURCES resources/dolphin/dolphin.qrc resources/resources.qrc resources/basic/basic.qrc resources/input/input.qrc resources/output/output.qrc resources/toolbar/toolbar.qrc resources/memory/dark/memory_dark.qrc resources/memory/light/memory_light.qrc ) set ( WPANDA_FORMS bewaveddolphin.ui clockDialog.ui LengthDialog.ui mainwindow.ui elementeditor.ui simplewaveform.ui ) add_executable( wpanda main.cpp ${WPANDA_FORMS} ${WPANDA_RESOURCES} ) target_compile_definitions( wpanda PUBLIC -DAPP_VERSION="${APP_VERSION}" ) target_compile_options( wpanda PUBLIC ${COMPILE_WARNS} ) target_link_libraries( wpanda PUBLIC ${WPANDA_LIBS} wpandacommon wpandaelements wpandalogicelement ) wiRedPanda-3.0.1/app/LengthDialog.cpp000066400000000000000000000016031406216046500173620ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "LengthDialog.h" lengthDialog::lengthDialog(QWidget *parent) : QDialog(parent) , m_ui(new Ui::lengthDialog) { m_ui->setupUi(this); setWindowTitle("Simulation length Selection"); setWindowFlags(Qt::Window); setModal(true); connect(m_ui->cancelPushButton, &QPushButton::clicked, this, &lengthDialog::cancelClicked); connect(m_ui->okPushButton, &QPushButton::clicked, this, &lengthDialog::okClicked); } int lengthDialog::getFrequency() { m_canceled = false; exec(); if (m_canceled) { return -1; } return m_ui->lengthSpinBox->value(); } lengthDialog::~lengthDialog() { delete m_ui; } void lengthDialog::cancelClicked() { m_canceled = true; close(); } void lengthDialog::okClicked() { close(); } wiRedPanda-3.0.1/app/LengthDialog.h000066400000000000000000000011101406216046500170200ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LENGTHDIALOG_H #define LENGTHDIALOG_H #include #include "ui_LengthDialog.h" namespace Ui { class lengthDialog; } class lengthDialog : public QDialog { Q_OBJECT public: explicit lengthDialog(QWidget *parent = nullptr); int getFrequency(); ~lengthDialog() override; private slots: void cancelClicked(); void okClicked(); private: Ui::lengthDialog *m_ui; bool m_canceled; }; #endif /* LENGTHDIALOG_H */ wiRedPanda-3.0.1/app/LengthDialog.ui000066400000000000000000000071041406216046500172170ustar00rootroot00000000000000 lengthDialog 0 0 188 118 Dialog :/toolbar/wavyIcon.png:/toolbar/wavyIcon.png 2 Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 2 2048 1 64 Cancel OK Simulation Length 2048 Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing 2 2048 1 64 Qt::Horizontal lengthSlider valueChanged(int) lengthSpinBox setValue(int) 334 47 499 21 lengthSpinBox valueChanged(int) lengthSlider setValue(int) 499 21 334 47 wiRedPanda-3.0.1/app/app.pro000066400000000000000000000006721406216046500156240ustar00rootroot00000000000000#------------------------------------------------- # # Project created by QtCreator 2014-10-22T13:17:08 # #------------------------------------------------- TARGET = wpanda TEMPLATE = app include(../includes.pri) include(install.pri) win32{ RC_FILE = windows.rc DISTFILES += windows.rc } SOURCES += main.cpp DISTFILES += resources/postinst RESOURCES += FORMS += HEADERS += TRANSLATIONS = wpanda_en.ts \ wpanda_pt.ts wiRedPanda-3.0.1/app/arduino/000077500000000000000000000000001406216046500157565ustar00rootroot00000000000000wiRedPanda-3.0.1/app/arduino/codegenerator.cpp000066400000000000000000000510501406216046500213040ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "codegenerator.h" #include "clock.h" #include "common.h" #include "graphicelement.h" #include "qneconnection.h" #include "qneport.h" #include CodeGenerator::CodeGenerator(const QString &fileName, const QVector &elements) : file(fileName) , elements(elements) { if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { return; } globalCounter = 1; out.setDevice(&file); availablePins = {"A0", "A1", "A2", "A3", "A4", "A5", /*"0", "1",*/ "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13"}; } static inline QString highLow(int val) { return val == 1 ? "HIGH" : "LOW"; } QString clearString(const QString &input) { return input.toLower().trimmed().replace(" ", "_").replace("-", "_").replace(QRegExp("\\W"), ""); } QString CodeGenerator::otherPortName(QNEPort *port) { if (port) { if (port->connections().isEmpty()) { return highLow(port->defaultValue()); } QNEPort *other = port->connections().front()->otherPort(port); if (other) { return varMap[other]; } else { return highLow(port->defaultValue()); } } else { return "LOW"; } } bool CodeGenerator::generate() { out << "// ==================================================================== //" << Qt::endl; out << "// ======= This code was generated automatically by wiRED PANDA ======= //" << Qt::endl; out << "// ==================================================================== //" << Qt::endl; out << Qt::endl << Qt::endl; out << "#include " << Qt::endl; /* Declaring input and output pins; */ declareInputs(); declareOutputs(); declareAuxVariables(); /* Setup section */ setup(); /* Loop section */ loop(); return true; } void CodeGenerator::declareInputs() { int counter = 1; out << "/* ========= Inputs ========== */" << Qt::endl; for (GraphicElement *elm : elements) { if ((elm->elementType() == ElementType::BUTTON) || (elm->elementType() == ElementType::SWITCH)) { QString varName = elm->objectName() + QString::number(counter); QString label = elm->getLabel(); if (!label.isEmpty()) { varName = QString("%1_%2").arg(varName, label); } varName = clearString(varName); out << QString("const int %1 = %2;").arg(varName, availablePins.front()) << Qt::endl; inputMap.append(MappedPin(elm, availablePins.front(), varName, elm->output(0), 0)); availablePins.pop_front(); varMap[elm->output()] = varName + QString("_val"); counter++; } } out << Qt::endl; } void CodeGenerator::declareOutputs() { int counter = 1; out << "/* ========= Outputs ========== */" << Qt::endl; for (GraphicElement *elm : elements) { if (elm->elementGroup() == ElementGroup::OUTPUT) { QString label = elm->getLabel(); for (int i = 0; i < elm->inputs().size(); ++i) { QString varName = elm->objectName() + QString::number(counter); if (!label.isEmpty()) { varName = QString("%1_%2").arg(varName, label); } QNEPort *port = elm->input(i); if (!port->getName().isEmpty()) { varName = QString("%1_%2").arg(varName, port->getName()); } varName = clearString(varName); out << QString("const int %1 = %2;").arg(varName, availablePins.front()) << Qt::endl; outputMap.append(MappedPin(elm, availablePins.front(), varName, port, i)); availablePins.pop_front(); } } counter++; } out << Qt::endl; } void CodeGenerator::declareAuxVariablesRec(const QVector &elms, bool isBox) { for (GraphicElement *elm : elms) { if (elm->elementType() == ElementType::IC) { // Box *box = qgraphicsitem_cast< Box* >( elm ); // FIXME: Get code generator to work again // if( box ) { // out << "// " << box->getLabel( ) << Qt::endl; // declareAuxVariablesRec( box->getElements( ), true ); // out << "// END of " << box->getLabel( ) << Qt::endl; // for( int i = 0; i < box->outputSize( ); ++i ) { // QNEPort *port = box->outputMap.at( i ); // varMap[ box->output( i ) ] = otherPortName( port ); // } // } } else { QString varName = QString("aux_%1_%2").arg(clearString(elm->objectName())).arg(globalCounter++); auto const outputs = elm->outputs(); if (outputs.size() == 1) { QNEPort *port = outputs.first(); if (elm->elementType() == ElementType::VCC) { varMap[port] = "HIGH"; continue; } if (elm->elementType() == ElementType::GND) { varMap[port] = "LOW"; continue; } else if (varMap[port].isEmpty()) { varMap[port] = varName; } } else { int portCounter = 0; for (QNEPort *port : outputs) { QString portName = varName; portName.append(QString("_%1").arg(portCounter++)); if (!port->getName().isEmpty()) { portName.append(QString("_%1").arg(clearString(port->getName()))); } varMap[port] = portName; } } for (QNEPort *port : outputs) { QString varName2 = varMap[port]; out << "boolean " << varName2 << " = " << highLow(port->defaultValue()) << ";" << Qt::endl; switch (elm->elementType()) { case ElementType::CLOCK: { if (!isBox) { auto *clk = qgraphicsitem_cast(elm); out << "elapsedMillis " << varName2 << "_elapsed = 0;" << Qt::endl; out << "int " << varName2 << "_interval = " << 1000 / clk->getFrequency() << ";" << Qt::endl; } break; } case ElementType::DFLIPFLOP: { out << "boolean " << varName2 << "_inclk = LOW;" << Qt::endl; out << "boolean " << varName2 << "_last = LOW;" << Qt::endl; break; } case ElementType::TFLIPFLOP: case ElementType::SRFLIPFLOP: case ElementType::JKFLIPFLOP: { out << "boolean " << varName2 << "_inclk = LOW;" << Qt::endl; break; } default: break; } } } } } void CodeGenerator::declareAuxVariables() { out << "/* ====== Aux. Variables ====== */" << Qt::endl; declareAuxVariablesRec(elements); out << Qt::endl; } void CodeGenerator::setup() { out << "void setup( ) {" << Qt::endl; for (const MappedPin &pin : qAsConst(inputMap)) { out << " pinMode( " << pin.varName << ", INPUT );" << Qt::endl; } for (const MappedPin &pin : qAsConst(outputMap)) { out << " pinMode( " << pin.varName << ", OUTPUT );" << Qt::endl; } out << "}" << Qt::endl << Qt::endl; } void CodeGenerator::assignVariablesRec(const QVector &elms) { for (GraphicElement *elm : elms) { if (elm->elementType() == ElementType::IC) { throw std::runtime_error(QString("BOX element not supported : %1").arg(elm->objectName()).toStdString()); // TODO: CodeGenerator::assignVariablesRec for Box Element // Box *box = qgraphicsitem_cast< Box* >( elm ); // out << " // " << box->getLabel( ) << Qt::endl; // for( int i = 0; i < box->inputSize( ); ++i ) { // QNEPort *port = box->input( i ); // QNEPort *otherPort = port->connections( ).first( )->otherPort( port ); // QString value = highLow( port->defaultValue( ) ); // if( !varMap[ otherPort ].isEmpty( ) ) { // value = varMap[ otherPort ]; // } // out << " " << varMap[ box->inputMap.at( i ) ] << " = " << value << ";" << Qt::endl; // } // QVector< GraphicElement* > boxElms = box->getElements( ); // if( boxElms.isEmpty( ) ) { // continue; // } // boxElms = SimulationController::sortElements( boxElms ); // assignVariablesRec( boxElms ); // out << " // End of " << box->getLabel( ) << Qt::endl; } if (elm->inputs().isEmpty() || elm->outputs().isEmpty()) { continue; } else { QString firstOut = varMap[elm->output(0)]; switch (elm->elementType()) { case ElementType::DFLIPFLOP: { QString secondOut = varMap[elm->output(1)]; QString data = otherPortName(elm->input(0)); QString clk = otherPortName(elm->input(1)); QString inclk = firstOut + "_inclk"; QString last = firstOut + "_last"; out << QString(" //D FlipFlop") << Qt::endl; out << QString(" if( %1 && !%2) { ").arg(clk, inclk) << Qt::endl; out << QString(" %1 = %2;").arg(firstOut, last) << Qt::endl; out << QString(" %1 = !%2;").arg(secondOut, last) << Qt::endl; out << QString(" }") << Qt::endl; QString prst = otherPortName(elm->input(2)); QString clr = otherPortName(elm->input(3)); out << QString(" if( !%1 || !%2) { ").arg(prst, clr) << Qt::endl; out << QString(" %1 = !%2; //Preset").arg(firstOut, prst) << Qt::endl; out << QString(" %1 = !%2; //Clear").arg(secondOut, clr) << Qt::endl; out << QString(" }") << Qt::endl; /* Updating internal clock. */ out << " " << inclk << " = " << clk << ";" << Qt::endl; out << " " << last << " = " << data << ";" << Qt::endl; out << QString(" //End of D FlipFlop") << Qt::endl; break; } case ElementType::DLATCH: { QString secondOut = varMap[elm->output(1)]; QString data = otherPortName(elm->input(0)); QString clk = otherPortName(elm->input(1)); out << QString(" //D Latch") << Qt::endl; out << QString(" if( %1 ) { ").arg(clk) << Qt::endl; out << QString(" %1 = %2;").arg(firstOut, data) << Qt::endl; out << QString(" %1 = !%2;").arg(secondOut, data) << Qt::endl; out << QString(" }") << Qt::endl; out << QString(" //End of D Latch") << Qt::endl; break; } case ElementType::JKFLIPFLOP: { QString secondOut = varMap[elm->output(1)]; QString j = otherPortName(elm->input(0)); QString clk = otherPortName(elm->input(1)); QString k = otherPortName(elm->input(2)); QString inclk = firstOut + "_inclk"; out << QString(" //JK FlipFlop") << Qt::endl; out << QString(" if( %1 && !%2 ) { ").arg(clk, inclk) << Qt::endl; out << QString(" if( %1 && %2) { ").arg(j, k) << Qt::endl; out << QString(" boolean aux = %1;").arg(firstOut) << Qt::endl; out << QString(" %1 = %2;").arg(firstOut, secondOut) << Qt::endl; out << QString(" %1 = aux;").arg(secondOut) << Qt::endl; out << QString(" } else if ( %1 ) {").arg(j) << Qt::endl; out << QString(" %1 = 1;").arg(firstOut) << Qt::endl; out << QString(" %1 = 0;").arg(secondOut) << Qt::endl; out << QString(" } else if ( %1 ) {").arg(k) << Qt::endl; out << QString(" %1 = 0;").arg(firstOut) << Qt::endl; out << QString(" %1 = 1;").arg(secondOut) << Qt::endl; out << QString(" }") << Qt::endl; out << QString(" }") << Qt::endl; QString prst = otherPortName(elm->input(3)); QString clr = otherPortName(elm->input(4)); out << QString(" if( !%1 || !%2 ) { ").arg(prst, clr) << Qt::endl; out << QString(" %1 = !%2; //Preset").arg(firstOut, prst) << Qt::endl; out << QString(" %1 = !%2; //Clear").arg(secondOut, clr) << Qt::endl; out << QString(" }") << Qt::endl; /* Updating internal clock. */ out << " " << inclk << " = " << clk << ";" << Qt::endl; out << QString(" //End of JK FlipFlop") << Qt::endl; break; } case ElementType::SRFLIPFLOP: { QString secondOut = varMap[elm->output(1)]; QString s = otherPortName(elm->input(0)); QString clk = otherPortName(elm->input(1)); QString r = otherPortName(elm->input(2)); QString inclk = firstOut + "_inclk"; out << QString(" //SR FlipFlop") << Qt::endl; out << QString(" if( %1 && !%2 ) { ").arg(clk, inclk) << Qt::endl; out << QString(" if( %1 && %2) { ").arg(s, r) << Qt::endl; out << QString(" %1 = 1;").arg(firstOut) << Qt::endl; out << QString(" %1 = 1;").arg(secondOut) << Qt::endl; out << QString(" } else if ( %1 != %2) {").arg(s, r) << Qt::endl; out << QString(" %1 = %2;").arg(firstOut, s) << Qt::endl; out << QString(" %1 = %2;").arg(secondOut, r) << Qt::endl; out << QString(" }") << Qt::endl; out << QString(" }") << Qt::endl; QString prst = otherPortName(elm->input(3)); QString clr = otherPortName(elm->input(4)); out << QString(" if( !%1 || !%2 ) { ").arg(prst, clr) << Qt::endl; out << QString(" %1 = !%2; //Preset").arg(firstOut, prst) << Qt::endl; out << QString(" %1 = !%2; //Clear").arg(secondOut, clr) << Qt::endl; out << QString(" }") << Qt::endl; /* Updating internal clock. */ out << " " << inclk << " = " << clk << ";" << Qt::endl; out << QString(" //End of SR FlipFlop") << Qt::endl; break; } case ElementType::TFLIPFLOP: { QString secondOut = varMap[elm->output(1)]; QString t = otherPortName(elm->input(0)); QString clk = otherPortName(elm->input(1)); QString inclk = firstOut + "_inclk"; // QString last = firstOut + "_last"; out << QString(" //T FlipFlop") << Qt::endl; out << QString(" if( %1 && !%2) { ").arg(clk,inclk) << Qt::endl; out << QString(" if( %1 ) { ").arg(t) << Qt::endl; out << QString(" %1 = !%1;").arg(firstOut) << Qt::endl; out << QString(" %1 = !%2;").arg(secondOut, firstOut) << Qt::endl; out << QString(" }") << Qt::endl; out << QString(" }") << Qt::endl; QString prst = otherPortName(elm->input(2)); QString clr = otherPortName(elm->input(3)); out << QString(" if( !%1 || !%2) { ").arg(prst, clr) << Qt::endl; out << QString(" %1 = !%2; //Preset").arg(firstOut, prst) << Qt::endl; out << QString(" %1 = !%2; //Clear").arg(secondOut, clr) << Qt::endl; out << QString(" }") << Qt::endl; /* Updating internal clock. */ out << " " << inclk << " = " << clk << ";" << Qt::endl; // out << " " << last << " = " << data << ";" << Qt::endl; out << QString(" //End of T FlipFlop") << Qt::endl; break; } case ElementType::AND: case ElementType::OR: case ElementType::NAND: case ElementType::NOR: case ElementType::XOR: case ElementType::XNOR: case ElementType::NOT: case ElementType::NODE: assignLogicOperator(elm); break; default: throw std::runtime_error(ERRORMSG(QString("Element type not supported : %1").arg(elm->objectName()).toStdString())); } } } } void CodeGenerator::assignLogicOperator(GraphicElement *elm) { bool negate = false; bool parentheses = true; QString logicOperator; switch (elm->elementType()) { case ElementType::AND: { logicOperator = "&&"; break; } case ElementType::OR: { logicOperator = "||"; break; } case ElementType::NAND: { logicOperator = "&&"; negate = true; break; } case ElementType::NOR: { logicOperator = "||"; negate = true; break; } case ElementType::XOR: { logicOperator = "^"; break; } case ElementType::XNOR: { logicOperator = "^"; negate = true; break; } case ElementType::NOT: { negate = true; parentheses = false; break; } default: break; } if (elm->outputs().size() == 1) { QString varName = varMap[elm->output()]; QNEPort *inPort = elm->input(); out << " " << varName << " = "; if (negate) { out << "!"; } if (parentheses && negate) { out << "( "; } if (!inPort->connections().isEmpty()) { out << otherPortName(inPort); for (int i = 1; i < elm->inputs().size(); ++i) { const auto elmInputs = elm->inputs(); inPort = elmInputs[i]; out << " " << logicOperator << " "; out << otherPortName(inPort); } } if (parentheses && negate) { out << " )"; } out << ";" << Qt::endl; } else { /* ... */ } } void CodeGenerator::loop() { out << "void loop( ) {" << Qt::endl; out << " // Reading input data //." << Qt::endl; for (const MappedPin &pin : qAsConst(inputMap)) { out << QString(" %1_val = digitalRead( %1 );").arg(pin.varName) << Qt::endl; } out << Qt::endl; out << " // Updating clocks. //" << Qt::endl; for (GraphicElement *elm : elements) { if (elm->elementType() == ElementType::CLOCK) { const auto elm_outputs = elm->outputs(); QString varName = varMap[elm_outputs.first()]; out << QString(" if( %1_elapsed > %1_interval ){").arg(varName) << Qt::endl; out << QString(" %1_elapsed = 0;").arg(varName) << Qt::endl; out << QString(" %1 = ! %1;").arg(varName) << Qt::endl; out << QString(" }") << Qt::endl; } } /* Aux variables. */ out << Qt::endl; out << " // Assigning aux variables. //" << Qt::endl; assignVariablesRec(elements); out << "\n"; out << " // Writing output data. //\n"; for (const MappedPin& pin : qAsConst(outputMap)) { QString varName = otherPortName(pin.port); if (varName.isEmpty()) { varName = highLow(pin.port->defaultValue()); } out << QString(" digitalWrite( %1, %2 );").arg(pin.varName, varName) << Qt::endl; } out << "}" << Qt::endl; } wiRedPanda-3.0.1/app/arduino/codegenerator.h000066400000000000000000000027051406216046500207540ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef CODEGENERATOR_H #define CODEGENERATOR_H #include #include class GraphicElement; class QNEPort; class MappedPin { public: GraphicElement *elm; QString pin; QString varName; QNEPort *port; int portNbr; MappedPin(GraphicElement *elm, const QString &pin, const QString &varName, QNEPort *port, int portNbr = 0) : elm(elm) , pin(pin) , varName(varName) , port(port) , portNbr(portNbr) { } MappedPin() = default; }; class CodeGenerator { public: CodeGenerator(const QString &fileName, const QVector &aElements); bool generate(); private: void declareInputs(); void declareOutputs(); void declareAuxVariables(); QFile file; QTextStream out; const QVector elements; QVector inputMap, outputMap; QHash varMap; //! carmesim: fix typo in availablePins QVector availablePins; void setup(); void loop(); unsigned int globalCounter; void declareAuxVariablesRec(const QVector &elms, bool isBox = false); void assignVariablesRec(const QVector &elms); void assignLogicOperator(GraphicElement *elm); QString otherPortName(QNEPort *port); }; #endif /* CODEGENERATOR_H */wiRedPanda-3.0.1/app/bewaveddolphin.cpp000066400000000000000000001101311406216046500200110ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "bewaveddolphin.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "clockDialog.h" #include "common.h" #include "editor.h" #include "elementfactory.h" #include "elementmapping.h" #include "graphicelement.h" #include "graphicsview.h" #include "graphicsviewzoom.h" #include "input.h" #include "LengthDialog.h" #include "mainwindow.h" #include "qneport.h" #include "scstop.h" #include "simulationcontroller.h" #include "ui_bewaveddolphin.h" SignalModel::SignalModel(int rows, int inputs, int columns, QObject *parent) : QStandardItemModel(rows, columns, parent) , m_inputs(inputs) { } Qt::ItemFlags SignalModel::flags(const QModelIndex &index) const { Qt::ItemFlags flags; if (index.row() >= m_inputs) { flags = ~(Qt::ItemIsEditable | Qt::ItemIsSelectable); } else { flags = ~(Qt::ItemIsEditable | Qt::ItemIsSelectable) | Qt::ItemIsSelectable; } return flags; } SignalDelegate::SignalDelegate(int margin, QObject *parent) : QItemDelegate(parent) , m_margin(margin) { } void SignalDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { QStyleOptionViewItem itemOption(option); itemOption.rect.adjust(-m_margin, 0, 0, 0); QItemDelegate::paint(painter, itemOption, index); } BewavedDolphin::BewavedDolphin(Editor *editor, QWidget *parent) : QMainWindow(parent) , m_ui(new Ui::BewavedDolphin) , m_editor(editor) , m_mainWindow(dynamic_cast(parent)) , m_type(PlotType::line) { m_scale = 1.0; m_ui->setupUi(this); resize(800, 500); setWindowTitle("Bewaved Dolphin Simulator"); setWindowFlags(Qt::Window); QSettings settings(QSettings::IniFormat, QSettings::UserScope, QApplication::organizationName(), QApplication::applicationName()); settings.beginGroup("BewavedDolphin"); restoreGeometry(settings.value("geometry").toByteArray()); settings.endGroup(); m_gv = new GraphicsView(this); m_ui->verticalLayout->addWidget(m_gv); m_scene = new QGraphicsScene(this); // ui->graphicsView->setScene( scene ); m_gv->setScene(m_scene); m_signalTableView = new QTableView; m_scene->installEventFilter(m_signalTableView); auto *delegate = new SignalDelegate(4, this); m_signalTableView->setItemDelegate(delegate); m_scene->addWidget(m_signalTableView); QList zoom_in_shortcuts; zoom_in_shortcuts << QKeySequence("Ctrl++") << QKeySequence("Ctrl+="); m_ui->actionZoom_In->setShortcuts(zoom_in_shortcuts); // connect( gv->gvzoom( ), &GraphicsViewZoom::zoomed, this, &BewavedDolphin::zoomChanged ); m_gv->gvzoom()->setZoomFactorBase(m_SCALE_FACTOR); drawPixMaps(); m_edited = false; } BewavedDolphin::~BewavedDolphin() { QSettings settings(QSettings::IniFormat, QSettings::UserScope, QApplication::organizationName(), QApplication::applicationName()); settings.beginGroup("BewavedDolphin"); settings.setValue("geometry", saveGeometry()); settings.endGroup(); delete m_ui; } void BewavedDolphin::zoomChanged() { m_ui->actionZoom_In->setEnabled(m_gv->gvzoom()->canZoomIn()); m_ui->actionZoom_out->setEnabled(m_gv->gvzoom()->canZoomOut()); } void BewavedDolphin::drawPixMaps() { m_lowGreen = QPixmap(":/dolphin/low_green.png").scaled(100, 38); m_lowBlue = QPixmap(":/dolphin/low_blue.png").scaled(100, 38); m_highGreen = QPixmap(":/dolphin/high_green.png").scaled(100, 38); m_highBlue = QPixmap(":/dolphin/high_blue.png").scaled(100, 38); m_fallingGreen = QPixmap(":/dolphin/falling_green.png").scaled(100, 38); m_fallingBlue = QPixmap(":/dolphin/falling_blue.png").scaled(100, 38); m_risingGreen = QPixmap(":/dolphin/rising_green.png").scaled(100, 38); m_risingBlue = QPixmap(":/dolphin/rising_blue.png").scaled(100, 38); } void BewavedDolphin::closeEvent(QCloseEvent *e) { e->ignore(); if (checkSave()) { e->accept(); } } void BewavedDolphin::on_actionExit_triggered() { close(); } bool BewavedDolphin::checkSave() { if (m_edited) { QMessageBox::StandardButton reply; reply = QMessageBox::question(this, tr("Wired Panda - Bewaved Dolphin"), tr("Save simulation before closing?"), QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); if (reply == QMessageBox::Save) { on_actionSave_triggered(); return (!m_edited); } else if (reply == QMessageBox::Discard) { return true; } return false; } return true; } bool BewavedDolphin::loadElements() { QVector elements = m_editor->getScene()->getElements(); m_inputs.clear(); m_outputs.clear(); if (elements.isEmpty()) { return false; } elements = ElementMapping::sortGraphicElements(elements); for (GraphicElement *elm : qAsConst(elements)) { if (elm && (elm->type() == GraphicElement::Type)) { if (elm->elementGroup() == ElementGroup::INPUT) { m_inputs.append(elm); } else if (elm->elementGroup() == ElementGroup::OUTPUT) { m_outputs.append(elm); } } } std::stable_sort(m_inputs.begin(), m_inputs.end(), [](GraphicElement *elm1, GraphicElement *elm2) { return QString::compare(elm1->getLabel().toUtf8(), elm2->getLabel().toUtf8(), Qt::CaseInsensitive) <= 0; }); std::stable_sort(m_outputs.begin(), m_outputs.end(), [](GraphicElement *elm1, GraphicElement *elm2) { return QString::compare(elm1->getLabel().toUtf8(), elm2->getLabel().toUtf8(), Qt::CaseInsensitive) <= 0; }); return (!m_inputs.isEmpty() && !m_outputs.isEmpty()); } void BewavedDolphin::CreateElement(int row, int col, int value, bool isInput, bool changePrevious) { if (value == 0) { return CreateZeroElement(row, col, isInput, changePrevious); } return CreateOneElement(row, col, isInput, changePrevious); } void BewavedDolphin::CreateZeroElement(int row, int col, bool isInput, bool changePrevious) { COMMENT("Getting previous value to check previous cell refresh requirement.", 3); // QMessageBox::warning( this, "Zero: row, col", ( row + 48 ) + QString( " " ) + ( col + 48 ) ); int previous_value; if (m_model->item(row, col) == nullptr) { previous_value = -1; } else if (m_model->item(row, col)->text().toInt() == 0) { previous_value = 0; } else { previous_value = 1; } COMMENT("Changing current item.", 3); auto index = m_model->index(row, col); m_model->setData(index, "0", Qt::DisplayRole); m_model->setData(index, Qt::AlignCenter, Qt::TextAlignmentRole); if (m_type == PlotType::line) { m_model->setData(index, Qt::AlignLeft, Qt::TextAlignmentRole); if ((col != m_model->columnCount() - 1) && (m_model->item(row, col + 1) != nullptr) && (m_model->item(row, col + 1)->text().toInt() == 1)) { m_model->setData(index, m_risingGreen, Qt::DecorationRole); } else { m_model->setData(index, m_lowGreen, Qt::DecorationRole); } } else { m_model->setData(index, Qt::AlignCenter, Qt::TextAlignmentRole); } COMMENT("Changing previous item, if needed.", 3); if ((changePrevious) && (col != 0) && (previous_value == 1)) { CreateElement(row, col - 1, m_model->item(row, col - 1)->text().toInt(), isInput, false); } } void BewavedDolphin::CreateOneElement(int row, int col, bool isInput, bool changePrevious) { COMMENT("Getting previous value to check previous cell refresh requirement.", 3); // QMessageBox::warning( this, "One: row, col", ( row + 48 ) + QString( " " ) + ( col + 48 ) ); int previous_value; if (m_model->item(row, col) == nullptr) { previous_value = -1; } else if (m_model->item(row, col)->text().toInt() == 0) { previous_value = 0; } else { previous_value = 1; } COMMENT("Changing current item.", 3); auto index = m_model->index(row, col); m_model->setData(index, "1", Qt::DisplayRole); if (m_type == PlotType::line) { m_model->setData(index, Qt::AlignLeft, Qt::TextAlignmentRole); if ((col != m_model->columnCount() - 1) && (m_model->item(row, col + 1) != nullptr) && (m_model->item(row, col + 1)->text().toInt() == 0)) { m_model->setData(index, m_fallingGreen, Qt::DecorationRole); } else { m_model->setData(index, m_highGreen, Qt::DecorationRole); } } else { m_model->setData(index, Qt::AlignCenter, Qt::TextAlignmentRole); } COMMENT("Changing previous item, if needed.", 3); if ((changePrevious) && (col != 0) && (previous_value == 0)) { CreateElement(row, col - 1, m_model->item(row, col - 1)->text().toInt(), isInput, false); } } void BewavedDolphin::run() { for (int itr = 0; itr < m_model->columnCount(); ++itr) { COMMENT("itr:" << itr, 3); for (int in = 0; in < m_inputs.size(); ++in) { int val = m_model->item(in, itr)->text().toInt(); dynamic_cast(m_inputs[in])->setOn(val); } COMMENT("Updating the values of the circuit logic based on current input values.", 3); m_sc->update(); m_sc->updateAll(); COMMENT("Setting the computed output values to the waveform results.", 3); int counter = m_inputs.size(); for (int out = 0; out < m_outputs.size(); ++out) { int inSz = m_outputs[out]->inputSize(); for (int port = inSz - 1; port >= 0; --port) { int value = static_cast(m_outputs[out]->input(port)->value()); COMMENT("out value: " << value, 0); CreateElement(counter, itr, value, false); counter++; } } } m_signalTableView->viewport()->update(); } void BewavedDolphin::loadNewTable(QStringList &input_labels, QStringList &output_labels) { int iterations = 32; COMMENT("Num iter = " << iterations, 0); COMMENT("Update table.", 0); m_model = new SignalModel(m_inputs.size() + output_labels.size(), m_inputs.size(), iterations, this); m_signalTableView->setModel(m_model); m_model->setVerticalHeaderLabels(input_labels + output_labels); m_signalTableView->setAlternatingRowColors(true); m_signalTableView->verticalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::Fixed); m_signalTableView->horizontalHeader()->setSectionResizeMode(QHeaderView::ResizeMode::Fixed); COMMENT("Inputs: " << input_labels.size() << ", outputs: " << output_labels.size(), 0); m_edited = false; on_actionClear_triggered(); } QVector BewavedDolphin::loadSignals(QStringList &input_labels, QStringList &output_labels) { QVector oldValues(m_inputs.size()); for (int in = 0; in < m_inputs.size(); ++in) { QString label = m_inputs[in]->getLabel(); if (label.isEmpty()) { label = ElementFactory::translatedName(m_inputs[in]->elementType()); } input_labels.append(label); oldValues[in] = m_inputs[in]->output()->value(); } COMMENT( "Getting the name of the outputs. If no label is given, the element type is used as a name. Bug here? What if there are 2 outputs without name or two " "identical labels?", 0); for (int out = 0; out < m_outputs.size(); ++out) { QString label = m_outputs[out]->getLabel(); if (label.isEmpty()) { label = ElementFactory::translatedName(m_outputs[out]->elementType()); } for (int port = 0; port < m_outputs[out]->inputSize(); ++port) { if (m_outputs[out]->inputSize() > 1) { output_labels.append(QString("%1_%2").arg(label).arg(port)); } else { output_labels.append(label); } } } return oldValues; } bool BewavedDolphin::createWaveform(const QString& filename) { QSettings settings(QSettings::IniFormat, QSettings::UserScope, QApplication::organizationName(), QApplication::applicationName()); COMMENT("Getting digital circuit simulator.", 0); m_sc = m_editor->getSimulationController(); COMMENT("Creating class to pause main window simulator while creating waveform.", 0); SCStop scst(m_sc); COMMENT("Loading elements. All elements initially in elements vector. Then, inputs and outputs are extracted from it.", 0); if (!loadElements()) { QMessageBox::warning(parentWidget(), tr("Error"), tr("Could not load enough elements for the simulation.")); return false; } QStringList input_labels; QStringList output_labels; COMMENT( "Getting initial value from inputs and writing them to oldvalues. Used to save current state of inputs and restore it after simulation. Not saving " "memory states though...", 0); COMMENT( "Also getting the name of the inputs. If no label is given, the element type is used as a name. Bug here? What if there are 2 inputs without name or " "two identical labels?", 0); QVector oldValues = loadSignals(input_labels, output_labels); COMMENT("Loading initial data into the table.", 0); loadNewTable(input_labels, output_labels); if (filename != "none") { if (!load(filename)) { return false; } } COMMENT("Restoring the old values to the inputs, prior to simulaton.", 0); for (int in = 0; in < m_inputs.size(); ++in) { dynamic_cast(m_inputs[in])->setOn(oldValues[in]); } COMMENT("Resuming digital circuit main window after waveform simulation is finished.", 0); scst.release(); m_edited = false; return true; } void BewavedDolphin::show() { QMainWindow::show(); COMMENT("Getting table dimensions.", 0); int width = m_signalTableView->horizontalHeader()->length() + m_signalTableView->columnWidth(0); int height = m_signalTableView->verticalHeader()->length() + m_signalTableView->rowHeight(0); m_signalTableView->resize(width, height); } void BewavedDolphin::print() { std::cout << std::to_string(m_model->rowCount()).c_str() << ","; std::cout << std::to_string(m_model->columnCount()).c_str() << ",\n"; for (int row = 0; row < m_model->rowCount(); ++row) { std::cout << m_model->verticalHeaderItem(row)->text().toStdString() << ": "; for (int col = 0; col < m_model->columnCount(); ++col) { QString val = m_model->item(row, col)->text(); std::cout << val.toStdString().c_str() << ","; } std::cout << "\n"; } } void BewavedDolphin::on_actionSet_to_0_triggered() { COMMENT("Pressed 0!", 0); // auto itemList = signalTableView->selectedItems( ); auto itemList = m_signalTableView->selectionModel()->selectedIndexes(); for (auto item : qAsConst(itemList)) { int row = item.row(); int col = item.column(); COMMENT("Editing value.", 0); CreateZeroElement(row, col); } m_edited = true; COMMENT("Running simulation", 0); run(); } void BewavedDolphin::on_actionSet_to_1_triggered() { COMMENT("Pressed 0!", 0); auto itemList = m_signalTableView->selectionModel()->selectedIndexes(); for (auto item : qAsConst(itemList)) { int row = item.row(); int col = item.column(); COMMENT("Editing value.", 0); CreateOneElement(row, col); } m_edited = true; COMMENT("Running simulation", 0); run(); } void BewavedDolphin::on_actionInvert_triggered() { COMMENT("Pressed Not!", 0); auto itemList = m_signalTableView->selectionModel()->selectedIndexes(); for (auto item : qAsConst(itemList)) { int row = item.row(); int col = item.column(); int value = m_model->index(row, col, QModelIndex()).data().toInt(); value = (value + 1) % 2; COMMENT("Editing value.", 0); CreateElement(row, col, value); } m_edited = true; COMMENT("Running simulation", 0); run(); } int BewavedDolphin::sectionFirstColumn(const QItemSelection &ranges) { int first_col = m_model->columnCount() - 1; for (const auto &range : ranges) { if (range.left() < first_col) { first_col = range.left(); } } return first_col; } int BewavedDolphin::sectionFirstRow(const QItemSelection &ranges) { int first_row = m_model->rowCount() - 1; for (const auto &range : ranges) { if (range.top() < first_row) { first_row = range.top(); } } return first_row; } void BewavedDolphin::on_actionSet_clock_wave_triggered() { COMMENT("Getting first column.", 0); QItemSelection ranges = m_signalTableView->selectionModel()->selection(); int first_col = sectionFirstColumn(ranges); COMMENT("Setting the signal according it its column and clock period.", 0); clockDialog dialog(this); int clock_period = dialog.getFrequency(); if (clock_period < 0) { return; } int half_clock_period = clock_period / 2; auto itemList = m_signalTableView->selectionModel()->selectedIndexes(); for (auto item : qAsConst(itemList)) { int row = item.row(); int col = item.column(); int value = ((col - first_col) % clock_period < half_clock_period ? 0 : 1); COMMENT("Editing value.", 0) CreateElement(row, col, value); } m_edited = true; COMMENT("Running simulation", 0); run(); } void BewavedDolphin::on_actionCombinational_triggered() { COMMENT("Setting the signal according it its column and clock period.", 0); int clock_period = 2; int half_clock_period = 1; for (int row = 0; row < m_inputs.size(); ++row) { for (int col = 0; col < m_model->columnCount(); ++col) { int value = (col % clock_period < half_clock_period ? 0 : 1); CreateElement(row, col, value); } clock_period *= 2; half_clock_period *= 2; } m_edited = true; COMMENT("Running simulation", 0); run(); } void BewavedDolphin::on_actionSet_Length_triggered() { COMMENT("Setting the simulation length.", 0); lengthDialog dialog(this); int sim_length = dialog.getFrequency(); if (sim_length < 0) { return; } setLength(sim_length); } void BewavedDolphin::setLength(int sim_length, bool run_simulation) { if (sim_length <= m_model->columnCount()) { COMMENT("Reducing or keeping the simulation length.", 0); m_model->setColumnCount(sim_length); int width = m_signalTableView->horizontalHeader()->length() + m_signalTableView->columnWidth(0); int height = m_signalTableView->verticalHeader()->length() + m_signalTableView->rowHeight(0); m_signalTableView->resize(width, height); QRectF rect = m_scene->itemsBoundingRect(); m_scene->setSceneRect(rect); m_edited = true; return; } COMMENT("Increasing the simulation length.", 0); int old_length = m_model->columnCount(); m_model->setColumnCount(sim_length); for (int row = 0; row < m_inputs.size(); ++row) { for (int col = old_length; col < m_model->columnCount(); ++col) { CreateZeroElement(row, col, true, false); } } int width = m_signalTableView->horizontalHeader()->length() + m_signalTableView->columnWidth(0); int height = m_signalTableView->verticalHeader()->length() + m_signalTableView->rowHeight(0); m_signalTableView->resize(width, height); QRectF rect = m_scene->itemsBoundingRect(); m_scene->setSceneRect(rect); m_edited = true; COMMENT("Running simulation", 0); if (run_simulation) { run(); } } void BewavedDolphin::on_actionZoom_out_triggered() { // gv->gvzoom( )->zoomOut( ); m_scale *= m_SCALE_FACTOR; m_gv->scale(m_SCALE_FACTOR, m_SCALE_FACTOR); } void BewavedDolphin::on_actionZoom_In_triggered() { // gv->gvzoom( )->zoomIn( ); m_scale /= m_SCALE_FACTOR; m_gv->scale(1.0 / m_SCALE_FACTOR, 1.0 / m_SCALE_FACTOR); } void BewavedDolphin::on_actionReset_Zoom_triggered() { // gv->gvzoom( )->resetZoom( ); m_gv->scale(1.0 / m_scale, 1.0 / m_scale); m_scale = 1.0; } void BewavedDolphin::on_actionZoom_Range_triggered() { m_gv->scale(1.0 / m_scale, 1.0 / m_scale); double w_scale = static_cast(m_gv->width()) / (m_signalTableView->horizontalHeader()->length() + m_signalTableView->columnWidth(0)); double h_scale = static_cast(m_gv->height()) / (m_signalTableView->verticalHeader()->length() + m_signalTableView->rowHeight(0)); m_scale = std::min(w_scale, h_scale); m_gv->scale(1.0 * m_scale, 1.0 * m_scale); } void BewavedDolphin::on_actionClear_triggered() { for (int row = 0; row < m_inputs.size(); ++row) { for (int col = 0; col < m_model->columnCount(); ++col) { CreateZeroElement(row, col); } } COMMENT("Running simulation", 0); m_edited = true; run(); } void BewavedDolphin::on_actionCopy_triggered() { QItemSelection ranges = m_signalTableView->selectionModel()->selection(); if (ranges.isEmpty()) { QClipboard *clipboard = QApplication::clipboard(); clipboard->clear(); return; } QClipboard *clipboard = QApplication::clipboard(); auto *mimeData = new QMimeData; QByteArray itemData; QDataStream dataStream(&itemData, QIODevice::WriteOnly); copy(ranges, dataStream); mimeData->setData("bdolphin/copydata", itemData); clipboard->setMimeData(mimeData); } void BewavedDolphin::on_actionCut_triggered() { QItemSelection ranges = m_signalTableView->selectionModel()->selection(); if (ranges.isEmpty()) { QClipboard *clipboard = QApplication::clipboard(); clipboard->clear(); return; } QClipboard *clipboard = QApplication::clipboard(); auto *mimeData = new QMimeData(); QByteArray itemData; QDataStream dataStream(&itemData, QIODevice::WriteOnly); cut(ranges, dataStream); mimeData->setData("bdolphin/copydata", itemData); clipboard->setMimeData(mimeData); m_edited = true; } void BewavedDolphin::on_actionPaste_triggered() { QItemSelection ranges = m_signalTableView->selectionModel()->selection(); if (ranges.isEmpty()) { return; } const QClipboard *clipboard = QApplication::clipboard(); const QMimeData *mimeData = clipboard->mimeData(); if (mimeData->hasFormat("bdolphin/copydata")) { QByteArray itemData = mimeData->data("bdolphin/copydata"); QDataStream dataStream(&itemData, QIODevice::ReadOnly); paste(ranges, dataStream); } m_edited = true; } void BewavedDolphin::cut(const QItemSelection &ranges, QDataStream &ds) { copy(ranges, ds); on_actionSet_to_0_triggered(); } void BewavedDolphin::copy(const QItemSelection &ranges, QDataStream &ds) { COMMENT("Serializing data into data stream.", 0); int first_row = sectionFirstRow(ranges); int first_col = sectionFirstColumn(ranges); auto itemList = m_signalTableView->selectionModel()->selectedIndexes(); ds << static_cast(itemList.size()); for (auto item : qAsConst(itemList)) { int row = item.row(); int col = item.column(); int data = m_model->item(row, col)->text().toInt(); ds << static_cast(row - first_row); ds << static_cast(col - first_col); ds << static_cast(data); } } void BewavedDolphin::paste(QItemSelection &ranges, QDataStream &ds) { int first_col = sectionFirstColumn(ranges); int first_row = sectionFirstRow(ranges); quint64 itemListSize; ds >> itemListSize; for (int elm = 0; elm < static_cast(itemListSize); ++elm) { quint64 row; quint64 col; quint64 data; ds >> row; ds >> col; ds >> data; int new_row = static_cast(first_row + row); int new_col = static_cast(first_col + col); if ((new_row < m_inputs.size()) && (new_col < m_model->columnCount())) { CreateElement(new_row, new_col, data); } } run(); } void BewavedDolphin::on_actionSave_as_triggered() { QString fname = m_currentFile.absoluteFilePath(); QString path = m_mainWindow->getCurrentFile().dir().absolutePath(); if (!m_currentFile.fileName().isEmpty()) { path = m_currentFile.absoluteFilePath(); } auto *selected_filter = new QString(100); fname = QFileDialog::getSaveFileName(this, tr("Save File as ..."), path, tr("Dolphin files (*.dolphin);;CSV files (*.csv)"), selected_filter); if (fname.isEmpty()) { return; } if ((!fname.endsWith(".dolphin")) && (!fname.endsWith(".csv"))) { if (selected_filter->contains("dolphin")) { fname.append(".dolphin"); } else { fname.append(".csv"); } } if (save(fname)) { m_currentFile = fname; if (m_mainWindow->getDolphinFilename() != fname) { QMessageBox::StandardButton reply; reply = QMessageBox::question(this, tr("Wired Panda - Bewaved Dolphin"), tr("Do you want to link this Bewaved Dolphin file to your current Wired Panda file and save it?"), QMessageBox::Yes | QMessageBox::No); if (reply == QMessageBox::Yes) { m_mainWindow->setDolphinFilename(fname); m_mainWindow->save(); } } setWindowTitle("Bewaved Dolphin Simulator [" + m_currentFile.fileName() + "]"); m_ui->statusbar->showMessage(tr("Saved file successfully."), 2000); m_edited = false; } else { m_ui->statusbar->showMessage(tr("Could not save file: ") + fname + ".", 2000); } delete selected_filter; } void BewavedDolphin::on_actionSave_triggered() { if (m_currentFile.fileName().isEmpty()) { on_actionSave_as_triggered(); return; } QString fname = m_currentFile.absoluteFilePath(); if (save(fname)) { m_ui->statusbar->showMessage(tr("Saved file successfully."), 2000); m_edited = false; } else { m_ui->statusbar->showMessage(tr("Could not save file: ") + fname + ".", 2000); } } void BewavedDolphin::on_actionLoad_triggered() { QDir defaultDirectory; if (m_currentFile.exists()) { defaultDirectory = m_currentFile.dir(); } else { if (m_mainWindow->getCurrentFile().exists()) { m_mainWindow->getCurrentFile().dir(); } else { defaultDirectory = QDir::home(); } } QString fname = QFileDialog::getOpenFileName(this, tr("Open File"), defaultDirectory.absolutePath(), tr("All supported files (*.dolphin *.csv);;Dolphin files (*.dolphin);;CSV files (*.csv)")); if (fname.isEmpty()) { return; } load(fname); m_edited = false; m_ui->statusbar->showMessage(tr("File loaded successfully."), 2000); } bool BewavedDolphin::save(const QString &fname) { QSaveFile fl(fname); if (fl.open(QFile::WriteOnly)) { if (fname.endsWith(".dolphin")) { COMMENT("Saving dolphin file.", 0); QDataStream ds(&fl); try { save(ds); } catch (std::runtime_error &e) { m_ui->statusbar->showMessage(tr("Could not save file: ") + e.what(), 2000); return false; } } else { COMMENT("Saving CSV file.", 0); try { save(fl); } catch (std::runtime_error &e) { m_ui->statusbar->showMessage(tr("Could not save file: ") + e.what(), 2000); return false; } } } return fl.commit(); } void BewavedDolphin::save(QDataStream &ds) { ds << QString("Bewaved Dolphin 1.0"); COMMENT("Serializing data into data stream.", 0); ds << static_cast(m_inputs.size()); ds << static_cast(m_model->columnCount()); for (int itr = 0; itr < m_model->columnCount(); ++itr) { for (int in = 0; in < m_inputs.size(); ++in) { int val = m_model->item(in, itr)->text().toInt(); ds << static_cast(val); } } } void BewavedDolphin::save(QSaveFile &fl) { fl.write(std::to_string(m_model->rowCount()).c_str()); fl.write(","); fl.write(std::to_string(m_model->columnCount()).c_str()); fl.write(",\n"); for (int row = 0; row < m_model->rowCount(); ++row) { for (int col = 0; col < m_model->columnCount(); ++col) { QString val = m_model->item(row, col)->text(); fl.write(val.toStdString().c_str()); fl.write(","); } fl.write("\n"); } } bool BewavedDolphin::load(const QString &fname) { QFile fl(fname); if (!fl.exists()) { QMessageBox::warning(this, tr("Error!"), tr("File \"%1\" does not exists!").arg(fname), QMessageBox::Ok, QMessageBox::NoButton); return false; } COMMENT("File exists.", 0); if (fl.open(QFile::ReadOnly)) { if (fname.endsWith(".dolphin")) { COMMENT("Dolphin file opened.", 0); QDataStream ds(&fl); COMMENT("Current file set.", 0); try { COMMENT("Loading in editor.", 0); load(ds); COMMENT("Finished updating changed by signal.", 0); m_currentFile = QFileInfo(fname); } catch (std::runtime_error &e) { std::cerr << tr("Error loading project: ").toStdString() << e.what() << std::endl; QMessageBox::warning(this, tr("Error!"), tr("Could not open file.\nError: %1").arg(e.what()), QMessageBox::Ok, QMessageBox::NoButton); return false; } } else if (fname.endsWith(".csv")) { COMMENT("CSV file opened.", 0); try { COMMENT("Loading in editor.", 0); load(fl); COMMENT("Finished updating changed by signal.", 0); m_currentFile = QFileInfo(fname); } catch (std::runtime_error &e) { std::cerr << tr("Error loading project: ").toStdString() << e.what() << std::endl; QMessageBox::warning(this, tr("Error!"), tr("Could not open file.\nError: %1").arg(e.what()), QMessageBox::Ok, QMessageBox::NoButton); return false; } } else { std::cerr << tr("Format not supported. Could not open file : ").toStdString() << fname.toStdString() << "." << std::endl; return false; } } else { std::cerr << tr("Could not open file in ReadOnly mode : ").toStdString() << fname.toStdString() << "." << std::endl; QMessageBox::warning(this, tr("Error!"), tr("Could not open file in ReadOnly mode : ") + fname + ".", QMessageBox::Ok, QMessageBox::NoButton); return false; } COMMENT("Closing file.", 0); fl.close(); setWindowTitle("Bewaved Dolphin Simulator [" + m_currentFile.fileName() + "]"); return true; } void BewavedDolphin::load(QDataStream &ds) { QString str; ds >> str; if (!str.startsWith("Bewaved Dolphin")) { throw(std::runtime_error(ERRORMSG("Invalid file format. Starts with: " + str.toStdString()))); } qint64 rows; qint64 cols; ds >> rows; ds >> cols; if (rows > m_inputs.size()) { rows = m_inputs.size(); } if ((cols < 2) || (cols > 2048)) { throw(std::runtime_error(ERRORMSG("Invalid number of columns."))); } setLength(cols, false); COMMENT("Update table.", 0); for (int col = 0; col < cols; ++col) { for (int row = 0; row < rows; ++row) { qint64 value; ds >> value; CreateElement(row, col, value, true); } } run(); } void BewavedDolphin::load(QFile &fl) { QByteArray content = fl.readAll(); QList wordList(content.split(',')); int rows = wordList[0].toInt(); int cols = wordList[1].toInt(); if (rows > m_inputs.size()) { rows = m_inputs.size(); } if ((cols < 2) || (cols > 2048)) { throw(std::runtime_error(ERRORMSG("Invalid number of columns."))); } setLength(cols, false); COMMENT("Update table.", 0); for (int row = 0; row < rows; ++row) { for (int col = 0; col < cols; ++col) { int value = wordList[2 + col + row * cols].toInt(); CreateElement(row, col, value, true); } } run(); } void BewavedDolphin::on_actionShowValues_triggered() { m_type = PlotType::number; for (int row = 0; row < m_model->rowCount(); ++row) { for (int col = 0; col < m_model->columnCount(); ++col) { m_model->setData(m_model->index(row, col), "", Qt::DecorationRole); } } for (int row = 0; row < m_inputs.size(); ++row) { for (int col = 0; col < m_model->columnCount(); ++col) { CreateElement(row, col, m_model->item(row, col)->text().toInt()); } } COMMENT("Running simulation", 0); run(); } void BewavedDolphin::on_actionShowCurve_triggered() { m_type = PlotType::line; for (int row = 0; row < m_inputs.size(); ++row) { for (int col = 0; col < m_model->columnCount(); ++col) { CreateElement(row, col, m_model->item(row, col)->text().toInt()); } } COMMENT("Running simulation", 0); run(); } void BewavedDolphin::on_actionExport_to_PNG_triggered() { QString pngFile = QFileDialog::getSaveFileName(this, tr("Export to Image"), m_currentFile.absolutePath(), tr("PNG files (*.png)")); if (pngFile.isEmpty()) { return; } if (!pngFile.endsWith(".png", Qt::CaseInsensitive)) { pngFile.append(".png"); } QRectF s = m_scene->sceneRect(); QPixmap p(s.size().toSize()); QPainter painter; painter.begin(&p); painter.setRenderHint(QPainter::Antialiasing); m_scene->render(&painter, QRectF(), s); painter.end(); QImage img = p.toImage(); img.save(pngFile); } void BewavedDolphin::on_actionExport_to_PDF_triggered() { QString pdfFile = QFileDialog::getSaveFileName(this, tr("Export to PDF"), m_currentFile.absolutePath(), tr("PDF files (*.pdf)")); if (pdfFile.isEmpty()) { return; } if (!pdfFile.endsWith(".pdf", Qt::CaseInsensitive)) { pdfFile.append(".pdf"); } QPrinter printer(QPrinter::HighResolution); printer.setPageSize(QPageSize(QPageSize::A4)); printer.setPageOrientation(QPageLayout::Orientation::Landscape); printer.setOutputFormat(QPrinter::PdfFormat); printer.setOutputFileName(pdfFile); QPainter p; if (!p.begin(&printer)) { QMessageBox::warning(this, tr("ERROR"), tr("Could not print this circuit to PDF."), QMessageBox::Ok); return; } m_scene->render(&p, QRectF(), m_scene->sceneRect().adjusted(-64, -64, 64, 64)); p.end(); } void BewavedDolphin::on_actionAbout_triggered() { QMessageBox::about(this, "beWaved Dolphin", tr("

beWaved Dolphin is a waveform simulator for the weRed Panda software developed by the Federal University of São Paulo." " This project was created in order to help students to learn about logic circuits.

" "

Software version: %1

" "

Creators:

" "
    " "
  • Prof. Fábio Cappabianco, Ph.D.
  • " "
" "

beWaved Dolphin is currently maintained by Prof. Fábio Cappabianco, Ph.D. and Vinícius R. Miguel.

" "

Please file a report at our GitHub page if bugs are found or if you wish for a new functionality to be implemented.

" "

Visit our website!

") .arg(QApplication::applicationVersion())); } void BewavedDolphin::on_actionAbout_Qt_triggered() { QMessageBox::aboutQt(this); } wiRedPanda-3.0.1/app/bewaveddolphin.h000066400000000000000000000101011406216046500174520ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef BEWAVEDDOLPHIN_H #define BEWAVEDDOLPHIN_H #include #include #include #include #include #include class Editor; class GraphicsView; class MainWindow; class GraphicElement; class QGraphicsScene; class QPainter; class QTableView; class SimulationController; namespace Ui { class BewavedDolphin; } enum class PlotType { number, line }; class SignalModel : public QStandardItemModel { public: SignalModel(int rows, int inputs, int columns, QObject *parent = nullptr); Qt::ItemFlags flags(const QModelIndex &index) const override; private: int m_inputs; }; class SignalDelegate : public QItemDelegate { public: SignalDelegate(int margin, QObject *parent); void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override; private: int m_margin; }; class BewavedDolphin : public QMainWindow { Q_OBJECT public: explicit BewavedDolphin(Editor *editor, QWidget *parent = nullptr); ~BewavedDolphin() override; bool createWaveform(const QString& filename); void show(); void print(); private slots: void on_actionExit_triggered(); void on_actionSet_to_0_triggered(); void on_actionSet_to_1_triggered(); void on_actionInvert_triggered(); void on_actionSet_clock_wave_triggered(); void on_actionCombinational_triggered(); void on_actionSet_Length_triggered(); void on_actionZoom_out_triggered(); void on_actionZoom_In_triggered(); void on_actionReset_Zoom_triggered(); void on_actionZoom_Range_triggered(); void on_actionClear_triggered(); void on_actionCopy_triggered(); void on_actionPaste_triggered(); void on_actionCut_triggered(); void on_actionSave_as_triggered(); void on_actionSave_triggered(); void on_actionLoad_triggered(); void on_actionShowValues_triggered(); void on_actionShowCurve_triggered(); void on_actionExport_to_PNG_triggered(); void on_actionExport_to_PDF_triggered(); void on_actionAbout_triggered(); void on_actionAbout_Qt_triggered(); private: Ui::BewavedDolphin *m_ui; Editor *m_editor; MainWindow *m_mainWindow; SimulationController *m_sc; GraphicsView *m_gv; QFileInfo m_currentFile; QVector m_inputs; QVector m_outputs; QGraphicsScene *m_scene; QTableView *m_signalTableView; QStandardItemModel *m_model; PlotType m_type; bool m_edited; double m_scale; const double m_SCALE_FACTOR = 0.8; QPixmap m_lowGreen; QPixmap m_highGreen; QPixmap m_fallingGreen; QPixmap m_risingGreen; QPixmap m_lowBlue; QPixmap m_highBlue; QPixmap m_fallingBlue; QPixmap m_risingBlue; bool loadElements(); void loadNewTable(QStringList &input_labels, QStringList &output_labels); QVector loadSignals(QStringList &input_labels, QStringList &output_labels); void run(); void setLength(int sim_length, bool run_simulation = true); void cut(const QItemSelection &ranges, QDataStream &ds); void copy(const QItemSelection &ranges, QDataStream &ds); void paste(QItemSelection &ranges, QDataStream &ds); int sectionFirstColumn(const QItemSelection &ranges); int sectionFirstRow(const QItemSelection &ranges); bool save(const QString &fname); void save(QDataStream &ds); void save(QSaveFile &fl); bool load(const QString &fname); void load(QDataStream &ds); void load(QFile &fl); void drawPixMaps(); void CreateElement(int row, int col, int value, bool isInput = true, bool changePrevious = true); void CreateZeroElement(int row, int col, bool isInput = true, bool changePrevious = true); void CreateOneElement(int row, int col, bool isInput = true, bool changePrevious = true); void zoomChanged(); bool checkSave(); protected: void closeEvent(QCloseEvent *e) override; }; #endif // BEWAVEDDOLPHIN_H wiRedPanda-3.0.1/app/bewaveddolphin.ui000066400000000000000000000306361406216046500176570ustar00rootroot00000000000000 BewavedDolphin 0 0 800 600 MainWindow 0 0 toolBar TopToolBarArea false 0 0 800 21 File Help Edit View :/dolphin/help.png:/dolphin/help.png About Ctrl+H :/dolphin/folder.png:/dolphin/folder.png Load Ctrl+L :/dolphin/exit.png:/dolphin/exit.png Exit Ctrl+W :/toolbar/wavyIcon.png:/toolbar/wavyIcon.png Combinational Alt+C :/dolphin/pdf.png:/dolphin/pdf.png Export to PDF Ctrl+P :/dolphin/save.png:/dolphin/save.png Save Ctrl+S :/dolphin/save.png:/dolphin/save.png Save As... Ctrl+Shift+S :/toolbar/copy.png:/toolbar/copy.png Copy Ctrl+C :/dolphin/paste.png:/dolphin/paste.png Paste Ctrl+V :/input/0.png:/input/0.png Set to 0 Alt+0, 0 :/input/1.png:/input/1.png Set to 1 Alt+1, 1 :/input/clock1.png:/input/clock1.png Set clock wave Alt+W :/dolphin/not.png:/dolphin/not.png Invert Alt+N false Merge false Split :/dolphin/png.png:/dolphin/png.png Export to PNG Ctrl+Shift+P :/dolphin/range.png:/dolphin/range.png Set Length Alt+L :/toolbar/helpQt.png:/toolbar/helpQt.png About Qt Ctrl+Shift+H :/dolphin/zoomIn.png:/dolphin/zoomIn.png Zoom In Ctrl+Shift+=, Ctrl+= :/dolphin/zoomOut.png:/dolphin/zoomOut.png Zoom Out Ctrl+- :/dolphin/zoomRange.png:/dolphin/zoomRange.png Zoom Range Ctrl+Shift+R :/dolphin/zoomReset.png:/dolphin/zoomReset.png Reset Zoom Ctrl+Home :/toolbar/reloadFile.png:/toolbar/reloadFile.png Clear Alt+X :/dolphin/cut.png:/dolphin/cut.png Cut Ctrl+X Show Numeric Show Waves wiRedPanda-3.0.1/app/clockDialog.cpp000066400000000000000000000016371406216046500172430ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "clockDialog.h" #include "ui_clockDialog.h" clockDialog::clockDialog(QWidget *parent) : QDialog(parent) , m_ui(new Ui::clockDialog) { m_ui->setupUi(this); setWindowTitle("Clock Frequency Selection"); setWindowFlags(Qt::Window); setModal(true); connect(m_ui->cancelPushButton, &QPushButton::clicked, this, &clockDialog::cancelRequested); connect(m_ui->okPushButton, &QPushButton::clicked, this, &clockDialog::okRequested); } int clockDialog::getFrequency() { m_canceled = false; exec(); if (m_canceled) { return -1; } return m_ui->frequencySpinBox->value(); } clockDialog::~clockDialog() { delete m_ui; } void clockDialog::cancelRequested() { m_canceled = true; close(); } void clockDialog::okRequested() { close(); } wiRedPanda-3.0.1/app/clockDialog.h000066400000000000000000000012741406216046500167050ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef CLOCKDIALOG_H #define CLOCKDIALOG_H #include namespace Ui { class clockDialog; } //! //! \brief The clockDialog class handles dialogs for setting the frequency at which a clock ticks //! class clockDialog : public QDialog { Q_OBJECT public: explicit clockDialog(QWidget *parent = nullptr); //! Returns the clock frequency (in Hz) int getFrequency(); ~clockDialog() override; private slots: void cancelRequested(); void okRequested(); private: Ui::clockDialog * m_ui; bool m_canceled; }; #endif /* CLOCKDIALOG_H */ wiRedPanda-3.0.1/app/clockDialog.ui000066400000000000000000000067021406216046500170740ustar00rootroot00000000000000 clockDialog 0 0 186 118 Dialog :/toolbar/wavyIcon.png:/toolbar/wavyIcon.png 2 Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop Clock Frequency 1024 Qt::AlignRight|Qt::AlignTop|Qt::AlignTrailing Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 2 1024 2 2 1024 2 Qt::Horizontal Cancel OK frequencySlider valueChanged(int) frequencySpinBox setValue(int) 334 47 499 21 frequencySpinBox valueChanged(int) frequencySlider setValue(int) 499 21 334 47 wiRedPanda-3.0.1/app/commands.cpp000066400000000000000000000622161406216046500166310ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "commands.h" #include #include #include #include #include "common.h" #include "editor.h" #include "elementfactory.h" #include "graphicelement.h" #include "qneconnection.h" #include "qneport.h" #include "scene.h" #include "serializationfunctions.h" #include "simulationcontroller.h" void storeIds(const QList &items, QVector &ids) { ids.reserve(items.size()); for (QGraphicsItem *item : items) { auto *iwid = dynamic_cast(item); if (iwid) { ids.append(iwid->id()); } } } void storeOtherIds(const QList &connections, const QVector &ids, QVector &otherIds) { for (QGraphicsItem *item : connections) { auto *conn = qgraphicsitem_cast(item); if ((item->type() == QNEConnection::Type) && conn) { QNEOutputPort *p1 = conn->start(); if (p1 && p1->graphicElement() && !ids.contains(p1->graphicElement()->id())) { otherIds.append(p1->graphicElement()->id()); } QNEInputPort *p2 = conn->end(); if (p2 && p2->graphicElement() && !ids.contains(p2->graphicElement()->id())) { otherIds.append(p2->graphicElement()->id()); } } } } QList loadList(const QList &aItems, QVector &ids, QVector &otherIds) { QList elements; /* Stores selected graphicElements */ for (QGraphicsItem *item : aItems) { if (item->type() == GraphicElement::Type) { if (!elements.contains(item)) { elements.append(item); } } } QList connections; /* Stores all the wires linked to these elements */ for (QGraphicsItem *item : elements) { auto *elm = qgraphicsitem_cast(item); if (elm) { auto const inputs = elm->inputs(); for (QNEInputPort *port : inputs) { for (QNEConnection *conn : port->connections()) { if (!connections.contains(conn)) { connections.append(conn); } } } auto const outputs = elm->outputs(); for (QNEOutputPort *port : outputs) { for (QNEConnection *conn : port->connections()) { if (!connections.contains(conn)) { connections.append(conn); } } } } } /* Stores the other wires selected */ for (QGraphicsItem *item : aItems) { if (item->type() == QNEConnection::Type) { if (!connections.contains(item)) { connections.append(item); } } } /* Stores the ids of all elements listed in items; */ storeIds(elements + connections, ids); /* Stores all the elements linked to each connection that will not be deleted. */ storeOtherIds(connections, ids, otherIds); return elements + connections; } QList findItems(const QVector &ids) { QList items; for (int id : ids) { auto *item = dynamic_cast(ElementFactory::getItemById(id)); if (item) { items.append(item); } } if (items.size() != ids.size()) { throw std::runtime_error(ERRORMSG("One or more items was not found on the scene.")); } return items; } QList findElements(const QVector &ids) { QList items; for (int id : ids) { auto *item = dynamic_cast(ElementFactory::getItemById(id)); if (item) { items.append(item); } } if (items.size() != ids.size()) { throw std::runtime_error(ERRORMSG("One or more elements was not found on the scene.")); } return items; } void saveItems(QByteArray &itemData, const QList &items, const QVector &otherIds) { itemData.clear(); QDataStream dataStream(&itemData, QIODevice::WriteOnly); QList others = findElements(otherIds); for (GraphicElement *elm : qAsConst(others)) { elm->save(dataStream); } SerializationFunctions::serialize(items, dataStream); } void addItems(Editor *editor, const QList& items) { editor->getScene()->clearSelection(); for (QGraphicsItem *item : items) { if (item->scene() != editor->getScene()) { editor->getScene()->addItem(item); } if (item->type() == GraphicElement::Type) { item->setSelected(true); } } } QList loadItems(QByteArray &itemData, const QVector &ids, Editor *editor, QVector &otherIds) { if (itemData.isEmpty()) { return QList(); } QVector otherElms = findElements(otherIds).toVector(); QDataStream dataStream(&itemData, QIODevice::ReadOnly); double version = GlobalProperties::version; QMap portMap; for (GraphicElement *elm : qAsConst(otherElms)) { elm->load(dataStream, portMap, version); } /* * Assuming that all connections are stored after the elements, we will deserialize the elements first. * We will store one additional information: The element IDs! */ QList items = SerializationFunctions::deserialize(dataStream, version, GlobalProperties::currentFile, portMap); if (items.size() != ids.size()) { QString msg("One or more elements were not found on scene. Expected %1, found %2."); msg = msg.arg(ids.size()).arg(items.size()); throw std::runtime_error(ERRORMSG(msg.toStdString())); } for (int i = 0; i < items.size(); ++i) { auto *iwid = dynamic_cast(items[i]); if (iwid) { ElementFactory::updateItemId(iwid, ids[i]); } } addItems(editor, items); return items; } void deleteItems(const QList &items, Editor *editor) { QVector itemsVec = items.toVector(); /* Delete items on reverse order */ for (int i = itemsVec.size() - 1; i >= 0; --i) { editor->getScene()->removeItem(itemsVec[i]); delete itemsVec[i]; } } AddItemsCommand::AddItemsCommand(GraphicElement *aItem, Editor *aEditor, QUndoCommand *parent) : QUndoCommand(parent) { QList items({aItem}); items = loadList(items, m_ids, m_otherIds); m_editor = aEditor; addItems(m_editor, items); setText(tr("Add %1 element").arg(aItem->objectName())); } AddItemsCommand::AddItemsCommand(QNEConnection *aItem, Editor *aEditor, QUndoCommand *parent) : QUndoCommand(parent) { QList items({aItem}); items = loadList(items, m_ids, m_otherIds); m_editor = aEditor; addItems(m_editor, items); setText(tr("Add connection")); } AddItemsCommand::AddItemsCommand(const QList &aItems, Editor *aEditor, QUndoCommand *parent) : QUndoCommand(parent) { QList items = loadList(aItems, m_ids, m_otherIds); m_editor = aEditor; addItems(m_editor, items); setText(tr("Add %1 elements").arg(items.size())); } DeleteItemsCommand::DeleteItemsCommand(const QList &aItems, Editor *aEditor, QUndoCommand *parent) : QUndoCommand(parent) { QList items = loadList(aItems, m_ids, m_otherIds); m_editor = aEditor; setText(tr("Delete %1 elements").arg(items.size())); } DeleteItemsCommand::DeleteItemsCommand(QGraphicsItem *item, Editor *aEditor, QUndoCommand *parent) : DeleteItemsCommand(QList({item}), aEditor, parent) { } void AddItemsCommand::undo() { COMMENT("UNDO " + text().toStdString(), 0); QList items = findItems(m_ids); SimulationController *sc = m_editor->getSimulationController(); // We need to restart the simulation controller when deleting through the Undo command to // guarantee that no crashes occur when deleting input elements (clocks, input buttons, etc.) sc->setRestart(); saveItems(m_itemData, items, m_otherIds); deleteItems(items, m_editor); emit m_editor->circuitHasChanged(); } void AddItemsCommand::redo() { COMMENT("REDO " + text().toStdString(), 0); // TODO: items seems unused QList items = loadItems(m_itemData, m_ids, m_editor, m_otherIds); emit m_editor->circuitHasChanged(); } void DeleteItemsCommand::undo() { COMMENT("UNDO " + text().toStdString(), 0); loadItems(m_itemData, m_ids, m_editor, m_otherIds); emit m_editor->circuitHasChanged(); } void DeleteItemsCommand::redo() { COMMENT("REDO " + text().toStdString(), 0); QList items = findItems(m_ids); saveItems(m_itemData, items, m_otherIds); deleteItems(items, m_editor); emit m_editor->circuitHasChanged(); } RotateCommand::RotateCommand(const QList &aItems, int aAngle, QUndoCommand *parent) : QUndoCommand(parent) { m_angle = aAngle; setText(tr("Rotate %1 degrees").arg(m_angle)); m_ids.reserve(aItems.size()); m_positions.reserve(aItems.size()); for (GraphicElement *item : aItems) { m_positions.append(item->pos()); item->setPos(item->pos()); m_ids.append(item->id()); } } void RotateCommand::undo() { COMMENT("UNDO " + text().toStdString(), 0); QList list = findElements(m_ids); QGraphicsScene *scn = list[0]->scene(); scn->clearSelection(); for (int i = 0; i < list.size(); ++i) { GraphicElement *elm = list[i]; if (elm->rotatable()) { elm->setRotation(elm->rotation() - m_angle); } elm->setPos(m_positions[i]); elm->update(); elm->setSelected(true); } } void RotateCommand::redo() { COMMENT("REDO " + text().toStdString(), 0); QList list = findElements(m_ids); QGraphicsScene *scn = list[0]->scene(); scn->clearSelection(); double cx = 0, cy = 0; int sz = 0; for (GraphicElement *item : qAsConst(list)) { cx += item->pos().x(); cy += item->pos().y(); sz++; } cx /= sz; cy /= sz; for (GraphicElement *elm : qAsConst(list)) { QTransform transform; transform.translate(cx, cy); transform.rotate(m_angle); transform.translate(-cx, -cy); if (elm->rotatable()) { elm->setRotation(elm->rotation() + m_angle); } elm->setPos(transform.map(elm->pos())); elm->update(); elm->setSelected(true); } } bool RotateCommand::mergeWith(const QUndoCommand *command) { const auto *rotateCommand = static_cast(command); if (m_ids.size() != rotateCommand->m_ids.size()) { return false; } QVector list = findElements(m_ids).toVector(); QVector list2 = findElements(rotateCommand->m_ids).toVector(); for (int i = 0; i < list.size(); ++i) { if (list[i] != list2[i]) { return false; } } m_angle = (m_angle + rotateCommand->m_angle) % 360; setText(tr("Rotate %1 degrees").arg(m_angle)); undo(); redo(); return true; } int RotateCommand::id() const { return Id; } MoveCommand::MoveCommand(const QList &list, const QList &oldPositions, QUndoCommand *parent) : QUndoCommand(parent) { m_oldPositions = oldPositions; m_newPositions.reserve(list.size()); m_ids.reserve(list.size()); for (GraphicElement *elm : list) { m_ids.append(elm->id()); m_newPositions.append(elm->pos()); } setText(tr("Move elements")); } void MoveCommand::undo() { COMMENT("UNDO " + text().toStdString(), 0); QVector elms = findElements(m_ids).toVector(); for (int i = 0; i < elms.size(); ++i) { elms[i]->setPos(m_oldPositions[i]); } } void MoveCommand::redo() { COMMENT("REDO " + text().toStdString(), 0); QVector elms = findElements(m_ids).toVector(); for (int i = 0; i < elms.size(); ++i) { elms[i]->setPos(m_newPositions[i]); } } UpdateCommand::UpdateCommand(const QVector &elements, const QByteArray &oldData, Editor *editor, QUndoCommand *parent) : QUndoCommand(parent) , m_editor(editor) { m_oldData = oldData; ids.reserve(elements.size()); QDataStream dataStream(&m_newData, QIODevice::WriteOnly); for (GraphicElement *elm : elements) { elm->save(dataStream); ids.append(elm->id()); } setText(tr("Update %1 elements").arg(elements.size())); } void UpdateCommand::undo() { COMMENT("UNDO " + text().toStdString(), 0); loadData(m_oldData); emit m_editor->circuitHasChanged(); } void UpdateCommand::redo() { COMMENT("REDO " + text().toStdString(), 0); loadData(m_newData); emit m_editor->circuitHasChanged(); } void UpdateCommand::loadData(QByteArray itemData) { QVector elements = findElements(ids).toVector(); QDataStream dataStream(&itemData, QIODevice::ReadOnly); QMap portMap; if (!elements.isEmpty() && elements.front()->scene()) { elements.front()->scene()->clearSelection(); } double version = GlobalProperties::version; if (!elements.isEmpty()) { for (GraphicElement *elm : qAsConst(elements)) { elm->load(dataStream, portMap, version); elm->setSelected(true); } } } SplitCommand::SplitCommand(QNEConnection *conn, QPointF point, Editor *editor, QUndoCommand *parent) : QUndoCommand(parent) , m_editor(editor) , m_scene(editor->getScene()) { GraphicElement *node = ElementFactory::buildElement(ElementType::NODE); QNEConnection *conn2 = ElementFactory::instance->buildConnection(); /* Align node to Grid */ m_nodePos = point - QPointF(node->boundingRect().center()); if (m_scene) { int gridSize = m_scene->gridSize(); qreal xV = qRound(m_nodePos.x() / gridSize) * gridSize; qreal yV = qRound(m_nodePos.y() / gridSize) * gridSize; m_nodePos = QPointF(xV, yV); } /* Rotate line according to angle between p1 and p2 */ m_nodeAngle = conn->angle(); m_nodeAngle = 360 - 90 * (std::round(m_nodeAngle / 90.0)); /* Assingning class attributes */ m_elm1_id = conn->start()->graphicElement()->id(); m_elm2_id = conn->end()->graphicElement()->id(); m_c1_id = conn->id(); m_c2_id = conn2->id(); m_node_id = node->id(); setText(tr("Wire split")); } QNEConnection *findConn(int id) { return dynamic_cast(ElementFactory::getItemById(id)); } GraphicElement *findElm(int id) { return dynamic_cast(ElementFactory::getItemById(id)); } void SplitCommand::redo() { COMMENT("REDO " + text().toStdString(), 0); QNEConnection *c1 = findConn(m_c1_id); QNEConnection *c2 = findConn(m_c2_id); GraphicElement *node = findElm(m_node_id); GraphicElement *elm1 = findElm(m_elm1_id); GraphicElement *elm2 = findElm(m_elm2_id); if (!c2) { c2 = ElementFactory::buildConnection(); ElementFactory::updateItemId(c2, m_c2_id); } if (!node) { node = ElementFactory::buildElement(ElementType::NODE); ElementFactory::updateItemId(node, m_node_id); } if (c1 && c2 && elm1 && elm2 && node) { node->setPos(m_nodePos); node->setRotation(m_nodeAngle); /* QNEOutputPort *startPort = c1->start( ); */ QNEInputPort *endPort = c1->end(); c2->setStart(node->output()); c2->setEnd(endPort); c1->setEnd(node->input()); m_scene->addItem(node); m_scene->addItem(c2); c1->updatePosFromPorts(); c1->updatePath(); c2->updatePosFromPorts(); c2->updatePath(); } else { throw std::runtime_error(ERRORMSG(QString("Error tryng to redo %1").arg(text()).toStdString())); } emit m_editor->circuitHasChanged(); } void SplitCommand::undo() { COMMENT("UNDO " + text().toStdString(), 0); QNEConnection *c1 = findConn(m_c1_id); QNEConnection *c2 = findConn(m_c2_id); GraphicElement *node = findElm(m_node_id); GraphicElement *elm1 = findElm(m_elm1_id); GraphicElement *elm2 = findElm(m_elm2_id); if (c1 && c2 && elm1 && elm2 && node) { c1->setEnd(c2->end()); c1->updatePosFromPorts(); c1->updatePath(); m_editor->getScene()->removeItem(c2); m_editor->getScene()->removeItem(node); delete c2; delete node; } else { throw std::runtime_error(ERRORMSG(QString("Error tryng to undo %1").arg(text()).toStdString())); } emit m_editor->circuitHasChanged(); } MorphCommand::MorphCommand(const QVector &elements, ElementType aType, Editor *aEditor, QUndoCommand *parent) : QUndoCommand(parent) { m_newtype = aType; m_editor = aEditor; m_ids.reserve(elements.size()); m_types.reserve(elements.size()); for (GraphicElement *oldElm : elements) { m_ids.append(oldElm->id()); m_types.append(oldElm->elementType()); } setText(tr("Morph %1 elements to %2").arg(elements.size()).arg(elements.front()->objectName())); } void MorphCommand::undo() { COMMENT("UNDO " + text().toStdString(), 0); QVector newElms = findElements(m_ids).toVector(); QVector oldElms(newElms.size()); for (int i = 0; i < m_ids.size(); ++i) { oldElms[i] = ElementFactory::buildElement(m_types[i]); } transferConnections(newElms, oldElms); emit m_editor->circuitHasChanged(); } void MorphCommand::redo() { COMMENT("REDO " + text().toStdString(), 0); QVector oldElms = findElements(m_ids).toVector(); QVector newElms(oldElms.size()); for (int i = 0; i < m_ids.size(); ++i) { newElms[i] = ElementFactory::buildElement(m_newtype); } transferConnections(oldElms, newElms); emit m_editor->circuitHasChanged(); } void MorphCommand::transferConnections(QVector from, QVector to) { for (int elm = 0; elm < from.size(); ++elm) { GraphicElement *oldElm = from[elm]; GraphicElement *newElm = to[elm]; newElm->setInputSize(oldElm->inputSize()); newElm->setPos(oldElm->pos()); if (newElm->rotatable() && oldElm->rotatable()) { newElm->setRotation(oldElm->rotation()); } if ((newElm->elementType() == ElementType::NOT) && (oldElm->elementType() == ElementType::NODE)) { newElm->setRotation(oldElm->rotation() + 90.0); } else if ((newElm->elementType() == ElementType::NODE) && (oldElm->elementType() == ElementType::NOT)) { newElm->setRotation(oldElm->rotation() - 90.0); } if (newElm->hasLabel() && oldElm->hasLabel()) { newElm->setLabel(oldElm->getLabel()); } if (newElm->hasColors() && oldElm->hasColors()) { newElm->setColor(oldElm->getColor()); } if (newElm->hasFrequency() && oldElm->hasFrequency()) { newElm->setFrequency(oldElm->getFrequency()); } if (newElm->hasTrigger() && oldElm->hasTrigger()) { newElm->setTrigger(oldElm->getTrigger()); } for (int in = 0; in < oldElm->inputSize(); ++in) { while (!oldElm->input(in)->connections().isEmpty()) { QNEConnection *conn = oldElm->input(in)->connections().first(); if (conn->end() == oldElm->input(in)) { conn->setEnd(newElm->input(in)); } } } for (int out = 0; out < oldElm->outputSize(); ++out) { while (!oldElm->output(out)->connections().isEmpty()) { QNEConnection *conn = oldElm->output(out)->connections().first(); if (conn->start() == oldElm->output(out)) { conn->setStart(newElm->output(out)); } } } int oldId = oldElm->id(); m_editor->getScene()->removeItem(oldElm); delete oldElm; ElementFactory::updateItemId(newElm, oldId); m_editor->getScene()->addItem(newElm); newElm->updatePorts(); } } ChangeInputSZCommand::ChangeInputSZCommand(const QVector &elements, int newInputSize, Editor *editor, QUndoCommand *parent) : QUndoCommand(parent) , m_editor(editor) { for (GraphicElement *elm : elements) { m_elms.append(elm->id()); } m_newInputSize = newInputSize; if (!elements.isEmpty()) { m_scene = elements.front()->scene(); } setText(tr("Change input size to %1").arg(newInputSize)); } void ChangeInputSZCommand::redo() { COMMENT("REDO " + text().toStdString(), 0); const QVector m_elements = findElements(m_elms).toVector(); if (!m_elements.isEmpty() && m_elements.front()->scene()) { m_scene->clearSelection(); } QVector serializationOrder; m_oldData.clear(); QDataStream dataStream(&m_oldData, QIODevice::WriteOnly); for (int i = 0; i < m_elements.size(); ++i) { GraphicElement *elm = m_elements[i]; elm->save(dataStream); serializationOrder.append(elm); for (int in = m_newInputSize; in < elm->inputSize(); ++in) { for (QNEConnection *conn : elm->input(in)->connections()) { QNEPort *otherPort = conn->otherPort(elm->input(in)); otherPort->graphicElement()->save(dataStream); serializationOrder.append(otherPort->graphicElement()); } } } for (int i = 0; i < m_elements.size(); ++i) { GraphicElement *elm = m_elements[i]; for (int in = m_newInputSize; in < elm->inputSize(); ++in) { while (!elm->input(in)->connections().isEmpty()) { QNEConnection *conn = elm->input(in)->connections().front(); conn->save(dataStream); m_scene->removeItem(conn); QNEPort *otherPort = conn->otherPort(elm->input(in)); elm->input(in)->disconnect(conn); otherPort->disconnect(conn); } } elm->setInputSize(m_newInputSize); elm->setSelected(true); } m_order.clear(); for (GraphicElement *elm : serializationOrder) { m_order.append(elm->id()); } emit m_editor->circuitHasChanged(); } void ChangeInputSZCommand::undo() { COMMENT("UNDO " + text().toStdString(), 0); const QVector m_elements = findElements(m_elms).toVector(); const QVector serializationOrder = findElements(m_order).toVector(); if (!m_elements.isEmpty() && m_elements.front()->scene()) { m_scene->clearSelection(); } QDataStream dataStream(&m_oldData, QIODevice::ReadOnly); double version = GlobalProperties::version; QMap portMap; for (GraphicElement *elm : serializationOrder) { elm->load(dataStream, portMap, version); } for (int i = 0; i < m_elements.size(); ++i) { GraphicElement *elm = m_elements[i]; for (int in = m_newInputSize; in < elm->inputSize(); ++in) { QNEConnection *conn = ElementFactory::buildConnection(); conn->load(dataStream, portMap); m_scene->addItem(conn); } elm->setSelected(true); } emit m_editor->circuitHasChanged(); } FlipCommand::FlipCommand(const QList &aItems, int aAxis, QUndoCommand *parent) : QUndoCommand(parent) { m_axis = aAxis; setText(tr("Flip %1 elements in axis %2").arg(aItems.size()).arg(aAxis)); m_ids.reserve(aItems.size()); m_positions.reserve(aItems.size()); double xmin, xmax, ymin, ymax; if (aItems.size() > 0) { xmin = xmax = aItems.first()->pos().rx(); ymin = ymax = aItems.first()->pos().ry(); for (GraphicElement *item : aItems) { m_positions.append(item->pos()); item->setPos(item->pos()); m_ids.append(item->id()); xmin = qMin(xmin, item->pos().rx()); xmax = qMax(xmax, item->pos().rx()); ymin = qMin(ymin, item->pos().ry()); ymax = qMax(ymax, item->pos().ry()); } m_minPos = QPointF(xmin, ymin); m_maxPos = QPointF(xmax, ymax); } } void FlipCommand::undo() { COMMENT("UNDO " + text().toStdString(), 0); redo(); } void FlipCommand::redo() { COMMENT("REDO " + text().toStdString(), 0); // TODO: this might detach the QList QList list = findElements(m_ids); QGraphicsScene *scn = list[0]->scene(); scn->clearSelection(); for (GraphicElement *elm : qAsConst(list)) { QPointF pos = elm->pos(); if (m_axis == 0) { pos.setX(m_minPos.rx() + (m_maxPos.rx() - pos.rx())); } else { pos.setY(m_minPos.ry() + (m_maxPos.ry() - pos.ry())); } elm->setPos(pos); elm->update(); elm->setSelected(true); if (elm->rotatable()) { elm->setRotation(elm->rotation() + 180); } } } wiRedPanda-3.0.1/app/commands.h000066400000000000000000000204621406216046500162730ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef COMMANDS_H #define COMMANDS_H #include #include #include #include #include #include "elementtype.h" #include "globalproperties.h" class Scene; class Editor; class GraphicElement; class QGraphicsItem; class QNEConnection; void storeIds( const QList< QGraphicsItem* > &items, QVector< int > &ids ); void storeOtherIds( const QList< QGraphicsItem* > &connections, const QVector< int > &ids, QVector< int > &otherIds ); QList< QGraphicsItem* > loadList( const QList< QGraphicsItem* > &aItems, QVector< int > &ids, QVector< int > &otherIds ); QList< QGraphicsItem* > findItems( const QVector< int > &ids ); QList< GraphicElement* > findElements( const QVector< int > &ids ); void saveItems( QByteArray &itemData, const QList< QGraphicsItem* > &items, const QVector< int > &otherIds ); void addItems( Editor *editor, const QList< QGraphicsItem* >& items ); QList< QGraphicsItem* > loadItems( QByteArray &itemData, const QVector< int > &ids, Editor *editor, QVector< int > &otherIds ); void deleteItems( const QList< QGraphicsItem* > &items, Editor *editor ); class AddItemsCommand : public QUndoCommand { Q_DECLARE_TR_FUNCTIONS(AddItemsCommand) enum { Id = 101 }; public: //! //! \brief AddItemsCommand ctor taking a GraphicElement as its item //! \param aItem An item in the form of a GraphicElement (an IO elem., a gate or an IC) //! \param aEditor The editor to which the command will be added to //! explicit AddItemsCommand(GraphicElement *aItem, Editor *aEditor, QUndoCommand *parent = nullptr); //! //! \brief AddItemsCommand ctor taking a QNEConnection as its item //! \param aItem An item in the form of a QNEConnection (a node or a connection between nodes) //! \param aEditor The editor to which the command will be added to //! explicit AddItemsCommand(QNEConnection *aItem, Editor *aEditor, QUndoCommand *parent = nullptr); //! //! \brief AddItemsCommand ctor taking a several QGraphicsItems as its items //! \param aItem A list of items in the form of GraphicElements (an IO elem., a gate or an IC) //! \param aEditor The editor to which the command will be added to //! explicit AddItemsCommand(const QList &aItems, Editor *aEditor, QUndoCommand *parent = nullptr); //! //! \brief undo reverts a change on the editor made by AddItemsCommand::redo //! void undo() Q_DECL_OVERRIDE; //! //! \brief redo applies the change on the editor //! void redo() Q_DECL_OVERRIDE; private: QByteArray m_itemData; Editor * m_editor; QVector m_ids, m_otherIds; }; //! //! \brief The DeleteItemsCommand class represents a single action of removing a list of elements on the editor //! class DeleteItemsCommand : public QUndoCommand { Q_DECLARE_TR_FUNCTIONS(DeleteItemsCommand) enum { Id = 102 }; public: //! //! \brief DeleteItemsCommand //! \param aItems A list of QGraphicsItems to be removed from the editor //! \param aEditor The editor from where the items will be removed //! explicit DeleteItemsCommand(const QList &aItems, Editor *aEditor, QUndoCommand *parent = nullptr); //! //! \brief DeleteItemsCommand //! \param aItems A QGraphicsItem to be removed from the editor //! \param aEditor The editor from where the items will be removed from //! explicit DeleteItemsCommand(QGraphicsItem *item, Editor *aEditor, QUndoCommand *parent = nullptr); //! //! \brief undo reverts a change on the editor made by DeleteItemsCommand::redo. //! void undo() Q_DECL_OVERRIDE; //! //! \brief redo applies the change on the editor //! void redo() Q_DECL_OVERRIDE; private: QByteArray m_itemData; Editor * m_editor; QVector m_ids, m_otherIds; }; //! //! \brief The RotateCommand class represents a single action of rotating a list of elements on the editor //! class RotateCommand : public QUndoCommand { Q_DECLARE_TR_FUNCTIONS(RotateCommand) enum { Id = 103 }; public: //! //! \brief RotateCommand //! \param aItems are the items to be rotated //! \param angle defines how many degrees will be rotated, in clockwise direction, by this command. //! explicit RotateCommand(const QList &aItems, int angle, QUndoCommand *parent = nullptr); //! //! \brief undo reverts a change on the editor made by RotateCommand::redo //! void undo() Q_DECL_OVERRIDE; //! //! \brief redo applies the change on the editor //! void redo() Q_DECL_OVERRIDE; // TODO: mergeWith is unused! bool mergeWith(const QUndoCommand *command) Q_DECL_OVERRIDE; int id() const Q_DECL_OVERRIDE; private: //! //! \brief m_angle defines how many degrees will be rotated, in clockwise direction, in this command. //! int m_angle; QVector m_ids; QVector m_positions; }; //! //! \brief The MoveCommand class represents a single action of moving a list of actions on the editor //! class MoveCommand : public QUndoCommand { Q_DECLARE_TR_FUNCTIONS(MoveCommand) public: enum { Id = 104 }; explicit MoveCommand(const QList &list, const QList &aOldPositions, QUndoCommand *parent = nullptr); void undo() Q_DECL_OVERRIDE; void redo() Q_DECL_OVERRIDE; int id() const Q_DECL_OVERRIDE { return Id; } private: QVector m_ids; QList m_oldPositions; QList m_newPositions; QPointF m_offset; }; //! //! \brief The UpdateCommand class //! class UpdateCommand : public QUndoCommand { Q_DECLARE_TR_FUNCTIONS(UpdateCommand) public: enum { Id = 105 }; explicit UpdateCommand(const QVector &elements, const QByteArray &oldData, Editor *editor, QUndoCommand *parent = nullptr); void undo() Q_DECL_OVERRIDE; void redo() Q_DECL_OVERRIDE; int id() const Q_DECL_OVERRIDE { return Id; } private: QVector ids; QByteArray m_oldData; QByteArray m_newData; Editor *m_editor; void loadData(QByteArray itemData); }; class SplitCommand : public QUndoCommand { Q_DECLARE_TR_FUNCTIONS(SplitCommand) enum { Id = 106 }; public: explicit SplitCommand(QNEConnection *conn, QPointF point, Editor *editor, QUndoCommand *parent = nullptr); void undo() Q_DECL_OVERRIDE; void redo() Q_DECL_OVERRIDE; private: Editor *m_editor; Scene *m_scene; QPointF m_nodePos; int m_nodeAngle; int m_elm1_id, m_elm2_id; int m_c1_id, m_c2_id; int m_node_id; }; class MorphCommand : public QUndoCommand { Q_DECLARE_TR_FUNCTIONS(MorphCommand) public: enum { Id = 107 }; explicit MorphCommand(const QVector &elements, ElementType aType, Editor *aEditor, QUndoCommand *parent = nullptr); void undo() Q_DECL_OVERRIDE; void redo() Q_DECL_OVERRIDE; int id() const Q_DECL_OVERRIDE { return Id; } private: QVector m_ids; QVector m_types; ElementType m_newtype; Editor *m_editor; void transferConnections(QVector from, QVector to); }; class ChangeInputSZCommand : public QUndoCommand { Q_DECLARE_TR_FUNCTIONS(ChangeInputSZCommand) public: enum { Id = 108 }; explicit ChangeInputSZCommand(const QVector &elements, int newInputSize, Editor *editor, QUndoCommand *parent = nullptr); void undo() Q_DECL_OVERRIDE; void redo() Q_DECL_OVERRIDE; int id() const Q_DECL_OVERRIDE { return Id; } private: QVector m_elms; QVector m_order; Editor *m_editor; QGraphicsScene *m_scene; QByteArray m_oldData; int m_newInputSize; }; class FlipCommand : public QUndoCommand { Q_DECLARE_TR_FUNCTIONS(FlipHCommand) enum { Id = 109 }; public: explicit FlipCommand(const QList &aItems, int aAxis, QUndoCommand *parent = nullptr); void undo() Q_DECL_OVERRIDE; void redo() Q_DECL_OVERRIDE; int id() const Q_DECL_OVERRIDE { return Id; } private: int m_axis; QVector m_ids; QVector m_positions; QPointF m_minPos, m_maxPos; }; #endif /* COMMANDS_H */ wiRedPanda-3.0.1/app/common.cpp000066400000000000000000000003411406216046500163070ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "common.h" #ifdef DEBUG int Comment::verbosity = DEBUG; #else int Comment::verbosity = -1; #endifwiRedPanda-3.0.1/app/common.h000066400000000000000000000017531406216046500157640ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef COMMON_H #define COMMON_H #include class Comment { public: static int verbosity; static void setVerbosity(int vb) { verbosity = vb; } }; #ifdef DEBUG #define COMMENT(exp, num) \ if (Comment::verbosity > num) { \ std::cerr << __FILE__ << ": " << __LINE__ << ": " << __FUNCTION__ << " => " << exp << std::endl; \ } #else #define COMMENT(exp, num) #endif #define ERRORMSG(exp) std::string(__FILE__) + ": " + std::to_string(__LINE__) + ": " + std::string(__FUNCTION__) + ": Error: " + exp #endif // COMMON_HwiRedPanda-3.0.1/app/editor.cpp000066400000000000000000001116241406216046500163140ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "editor.h" #include "buzzer.h" #include "commands.h" #include "common.h" #include "elementeditor.h" #include "elementfactory.h" #include "globalproperties.h" #include "graphicelement.h" #include "ic.h" #include "icmanager.h" #include "icprototype.h" #include "input.h" #include "mainwindow.h" #include "nodes/qneconnection.h" #include "qneport.h" #include "serializationfunctions.h" #include "simulationcontroller.h" #include "thememanager.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include Editor *Editor::globalEditor = nullptr; Editor::Editor(QObject *parent) : QObject(parent) , m_scene(nullptr) { if (!globalEditor) { globalEditor = this; } m_mainWindow = qobject_cast(parent); m_markingSelectionIC = false; m_editedConn_id = 0; m_undoStack = new QUndoStack(this); m_scene = new Scene(this); m_icManager = new ICManager(m_mainWindow, this); install(m_scene); m_draggingElement = false; clear(); m_timer.start(); m_showWires = true; m_showGates = true; connect(this, &Editor::circuitHasChanged, m_simulationController, &SimulationController::reSortElms); connect(m_icManager, &ICManager::updatedIC, this, &Editor::redoSimulationController); } Editor::~Editor() = default; //! CARMESIM //#ifdef Q_OS_WIN //#include // for Sleep //#endif // void _sleep(int ms) //{ // if (ms <= 0) { return; } //#ifdef Q_OS_WIN // Sleep(uint(ms)); //#else // struct timespec ts = { ms / 1000, (ms % 1000) * 1000 * 1000 }; // nanosleep(&ts, NULL); //#endif //} void Editor::updateTheme() { COMMENT("Update theme.", 0); if (ThemeManager::globalMngr) { const ThemeAttrs attrs = ThemeManager::globalMngr->getAttrs(); if (!m_scene) { //! scene could be NULL here return; } m_scene->setBackgroundBrush(attrs.scene_bgBrush); m_scene->setDots(QPen(attrs.scene_bgDots)); m_selectionRect->setBrush(QBrush(attrs.selectionBrush)); m_selectionRect->setPen(QPen(attrs.selectionPen, 1, Qt::SolidLine)); auto const scene_elements = m_scene->getElements(); for (GraphicElement *elm : scene_elements) { elm->updateTheme(); } auto const scene_connections = m_scene->getConnections(); for (QNEConnection *conn : scene_connections) { conn->updateTheme(); } } COMMENT("Finished updating theme. ", 0); } void Editor::mute(bool _mute) { auto const scene_elems = m_scene->getElements(); for (GraphicElement *elm : scene_elems) { auto *bz = dynamic_cast(elm); if (bz) { bz->mute(_mute); } } } void Editor::install(Scene *s) { s->installEventFilter(this); m_simulationController = new SimulationController(s); m_simulationController->start(); clear(); } QNEConnection *Editor::getEditedConn() const { return dynamic_cast(ElementFactory::getItemById(m_editedConn_id)); } void Editor::setEditedConn(QNEConnection *editedConn) { if (editedConn) { editedConn->setFocus(); m_editedConn_id = editedConn->id(); } else { m_editedConn_id = 0; } } void Editor::buildSelectionRect() { COMMENT("Build Selection Rect.", 0); m_selectionRect = new QGraphicsRectItem(); m_selectionRect->setFlag(QGraphicsItem::ItemIsSelectable, false); if (m_scene) { m_scene->addItem(m_selectionRect); } COMMENT("Finished building rect.", 0); } void Editor::clear() { COMMENT("Clearing editor.", 0); // fprintf(stderr, "Clearing editor\n"); m_simulationController->stop(); m_simulationController->clear(); m_icManager->clear(); ElementFactory::instance->clear(); m_undoStack->clear(); if (m_scene) { m_scene->clear(); } COMMENT("Building rect.", 0); buildSelectionRect(); if (m_scene) { auto const scene_views = m_scene->views(); if (!scene_views.isEmpty()) { m_scene->setSceneRect(scene_views.front()->rect()); } } COMMENT("Updating theme.", 0); updateTheme(); m_simulationController->start(); COMMENT("Emitting circuitHasChanged.", 0); emit circuitHasChanged(); COMMENT("Finished clear.", 0); } //! CARMESIM: reset scene upon deletion in order to avoid SIGSEGV void Editor::deleteAction() { const QList &items = m_scene->selectedItems(); m_scene->clearSelection(); if (!items.isEmpty()) { receiveCommand(new DeleteItemsCommand(items, this)); redoSimulationController(); } } void Editor::redoSimulationController() { //! Guarantees that the simulation keeps running, if it was running upon the //! element deletion. bool simulationWasRunning = m_simulationController->isRunning(); //! Clear the simulation controller. //! This is needed to avoid a SIGSEGV caused by lack of synch. between an element's graphics code and logical code, //! that is, its sprite could get deleted but its logical implementation not know about it. //! Also, it is required whenever the contents of a box is updated. m_simulationController->clear(); if (simulationWasRunning) { getSimulationController()->start(); } } void Editor::showWires(bool checked) { m_showWires = checked; auto const scene_items = m_scene->items(); for (QGraphicsItem *item : scene_items) { auto *elm = qgraphicsitem_cast(item); if ((item->type() == QNEConnection::Type)) { item->setVisible(checked); } else if ((item->type() == GraphicElement::Type) && elm) { if (elm->elementType() == ElementType::NODE) { elm->setVisible(checked); } else { auto const elm_inputs = elm->inputs(); for (QNEPort *in : elm_inputs) { in->setVisible(checked); } auto const elm_outputs = elm->outputs(); for (QNEPort *out : elm_outputs) { out->setVisible(checked); } } } } } void Editor::showGates(bool checked) { m_showGates = checked; auto const scene_items = m_scene->items(); for (QGraphicsItem *item : scene_items) { auto *elm = qgraphicsitem_cast(item); if ((item->type() == GraphicElement::Type) && elm) { if ((elm->elementGroup() != ElementGroup::INPUT) && (elm->elementGroup() != ElementGroup::OUTPUT)) { item->setVisible(checked); } } } } void Editor::rotate(bool rotateRight) { double angle = 90.0; if (!rotateRight) { angle = -angle; } QList list = m_scene->selectedItems(); QList elms; for (QGraphicsItem *item : qAsConst(list)) { auto *elm = qgraphicsitem_cast(item); if (elm && (elm->type() == GraphicElement::Type)) { elms.append(elm); } } if ((elms.size() > 1) || ((elms.size() == 1) && elms.front()->rotatable())) { receiveCommand(new RotateCommand(elms, angle)); } } void Editor::flipH() { QList list = m_scene->selectedItems(); QList elms; for (QGraphicsItem *item : qAsConst(list)) { auto *elm = qgraphicsitem_cast(item); if (elm && (elm->type() == GraphicElement::Type)) { elms.append(elm); } } if ((elms.size() > 1) || ((elms.size() == 1))) { receiveCommand(new FlipCommand(elms, 0)); } } void Editor::flipV() { QList list = m_scene->selectedItems(); QList elms; for (QGraphicsItem *item : qAsConst(list)) { auto *elm = qgraphicsitem_cast(item); if (elm && (elm->type() == GraphicElement::Type)) { elms.append(elm); } } if ((elms.size() > 1) || ((elms.size() == 1))) { receiveCommand(new FlipCommand(elms, 1)); } } QList Editor::itemsAt(QPointF pos) { QRectF rect(pos - QPointF(4, 4), QSize(9, 9)); return m_scene->items(rect.normalized()); } QGraphicsItem *Editor::itemAt(QPointF pos) { QList items = m_scene->items(pos); items.append(itemsAt(pos)); for (QGraphicsItem *item : qAsConst(items)) { if (item->type() == QNEPort::Type) { return item; } } for (QGraphicsItem *item : qAsConst(items)) { if (item->type() > QGraphicsItem::UserType) { return item; } } return nullptr; } ElementEditor *Editor::getElementEditor() const { return m_elementEditor; } QPointF Editor::getMousePos() const { return m_mousePos; } SimulationController *Editor::getSimulationController() const { return m_simulationController; } void Editor::addItem(QGraphicsItem *item) { m_scene->addItem(item); } void Editor::deleteEditedConn() { QNEConnection *editedConn = getEditedConn(); if (editedConn) { m_scene->removeItem(editedConn); delete editedConn; } setEditedConn(nullptr); } void Editor::startNewConnection(QNEOutputPort *startPort) { QNEConnection *editedConn = ElementFactory::buildConnection(); editedConn->setStart(startPort); editedConn->setEndPos(m_mousePos); addItem(editedConn); setEditedConn(editedConn); editedConn->updatePath(); } void Editor::startNewConnection(QNEInputPort *endPort) { QNEConnection *editedConn = ElementFactory::buildConnection(); editedConn->setEnd(endPort); editedConn->setStartPos(m_mousePos); addItem(editedConn); setEditedConn(editedConn); editedConn->updatePath(); } void Editor::detachConnection(QNEInputPort *endPort) { QNEConnection *editedConn = endPort->connections().last(); QNEOutputPort *startPort = editedConn->start(); if (startPort) { receiveCommand(new DeleteItemsCommand(editedConn, this)); startNewConnection(startPort); } } void Editor::startSelectionRect() { m_selectionStartPoint = m_mousePos; m_markingSelectionIC = true; m_selectionRect->setRect(QRectF(m_selectionStartPoint, m_selectionStartPoint)); m_selectionRect->show(); m_selectionRect->update(); } bool Editor::mousePressEvt(QGraphicsSceneMouseEvent *mouseEvt) { QGraphicsItem *item = itemAt(m_mousePos); if (item && (item->type() == QNEPort::Type)) { /* When the mouse pressed over an connected input port, the line * is disconnected and can be connected in an other port. */ auto *pressedPort = qgraphicsitem_cast(item); QNEConnection *editedConn = getEditedConn(); if (editedConn) { makeConnection(editedConn); } else { if (pressedPort->isOutput()) { auto *startPort = dynamic_cast(pressedPort); if (startPort) { startNewConnection(startPort); } } else { auto *endPort = dynamic_cast(pressedPort); if (endPort) { if (endPort->connections().size() > 0) { detachConnection(endPort); } else { startNewConnection(endPort); } } } } return true; } if (getEditedConn()) { deleteEditedConn(); } else if (!item && (mouseEvt->button() == Qt::LeftButton)) { /* Mouse pressed over board (Selection box). */ startSelectionRect(); } return false; } void Editor::resizeScene() { QVector elms = m_scene->getElements(); if (!elms.isEmpty()) { QRectF rect = m_scene->sceneRect(); for (GraphicElement *elm : qAsConst(elms)) { QRectF itemRect = elm->boundingRect().translated(elm->pos()); rect = rect.united(itemRect.adjusted(-10, -10, 10, 10)); } m_scene->setSceneRect(rect); } QGraphicsItem *item = itemAt(m_mousePos); if (item && (m_timer.elapsed() > 100) && m_draggingElement) { if (!m_scene->views().isEmpty()) { auto const scene_views = m_scene->views(); QGraphicsView *view = scene_views.front(); view->ensureVisible(QRectF(m_mousePos - QPointF(4, 4), QSize(9, 9)).normalized()); } m_timer.restart(); } } bool Editor::mouseMoveEvt(QGraphicsSceneMouseEvent *mouseEvt) { Q_UNUSED(mouseEvt) QNEConnection *editedConn = getEditedConn(); if (editedConn) { /* If a connection is being created, the ending coordinate follows the mouse position. */ if (editedConn->start()) { editedConn->setEndPos(m_mousePos); editedConn->updatePath(); } else if (editedConn->end()) { editedConn->setStartPos(m_mousePos); editedConn->updatePath(); } else { deleteEditedConn(); } return true; } if (m_markingSelectionIC) { /* If is marking the selectionBox, the last coordinate follows the mouse position. */ QRectF rect = QRectF(m_selectionStartPoint, m_mousePos).normalized(); m_selectionRect->setRect(rect); QPainterPath selectionBox; selectionBox.addRect(rect); m_scene->setSelectionArea(selectionBox); } else if (!m_markingSelectionIC) { /* Else, the selectionRect is hidden. */ m_selectionRect->hide(); } return false; } void Editor::makeConnection(QNEConnection *editedConn) { auto *port = dynamic_cast(itemAt(m_mousePos)); if (port && editedConn) { /* The mouse is released over a QNEPort. */ QNEOutputPort *startPort = nullptr; QNEInputPort *endPort = nullptr; if (editedConn->start() != nullptr) { startPort = editedConn->start(); endPort = dynamic_cast(port); } else if (editedConn->end() != nullptr) { startPort = dynamic_cast(port); endPort = editedConn->end(); } if (!startPort || !endPort) { return; } /* Verifying if the connection is valid. */ if ((startPort->graphicElement() != endPort->graphicElement()) && !startPort->isConnected(endPort)) { /* Making connection. */ editedConn->setStart(startPort); editedConn->setEnd(endPort); receiveCommand(new AddItemsCommand(editedConn, this)); setEditedConn(nullptr); } else { deleteEditedConn(); } } } bool Editor::mouseReleaseEvt(QGraphicsSceneMouseEvent *mouseEvt) { if (!mouseEvt) { return false; } /* When mouse is released the selection rect is hidden. */ m_selectionRect->hide(); m_markingSelectionIC = false; if (QApplication::overrideCursor()) { QApplication::setOverrideCursor(Qt::ArrowCursor); } QNEConnection *editedConn = getEditedConn(); if (editedConn && !(mouseEvt->buttons() & Qt::LeftButton)) { /* A connection is being created, and left button was released. */ makeConnection(editedConn); return true; } return false; } void Editor::handleHoverPort() { auto *port = dynamic_cast(itemAt(m_mousePos)); QNEPort *hoverPort = getHoverPort(); if (hoverPort && (port != hoverPort)) { releaseHoverPort(); } if (port && (port->type() == QNEPort::Type)) { QNEConnection *editedConn = getEditedConn(); releaseHoverPort(); setHoverPort(port); if (editedConn && editedConn->start() && (editedConn->start()->isOutput() == port->isOutput())) { QApplication::setOverrideCursor(QCursor(Qt::ForbiddenCursor)); } } } void Editor::releaseHoverPort() { QNEPort *hoverPort = getHoverPort(); if (hoverPort) { hoverPort->hoverLeave(); setHoverPort(nullptr); QApplication::setOverrideCursor(QCursor()); } } void Editor::setHoverPort(QNEPort *port) { if (port) { GraphicElement *hoverElm = port->graphicElement(); port->hoverEnter(); if (hoverElm && ElementFactory::contains(hoverElm->id())) { m_hoverPortElm_id = hoverElm->id(); for (int i = 0; i < (hoverElm->inputSize() + hoverElm->outputSize()); ++i) { if (i < hoverElm->inputSize()) { if (port == hoverElm->input(i)) { m_hoverPort_nbr = i; } } else if (port == hoverElm->output(i - hoverElm->inputSize())) { m_hoverPort_nbr = i; } } } } else { m_hoverPortElm_id = 0; m_hoverPort_nbr = 0; } } QNEPort *Editor::getHoverPort() { auto *hoverElm = dynamic_cast(ElementFactory::getItemById(m_hoverPortElm_id)); QNEPort *hoverPort = nullptr; if (hoverElm) { if (m_hoverPort_nbr < hoverElm->inputSize()) { hoverPort = hoverElm->input(m_hoverPort_nbr); } else if (((m_hoverPort_nbr - hoverElm->inputSize()) < hoverElm->outputSize())) { hoverPort = hoverElm->output(m_hoverPort_nbr - hoverElm->inputSize()); } } if (!hoverPort) { setHoverPort(nullptr); } return hoverPort; } bool Editor::dropEvt(QGraphicsSceneDragDropEvent *dde) { /* Verify if mimetype is compatible. */ if (dde->mimeData()->hasFormat("application/x-dnditemdata")) { /* Extracting mimedata from drop event. */ QByteArray itemData = dde->mimeData()->data("application/x-dnditemdata"); QDataStream dataStream(&itemData, QIODevice::ReadOnly); QPointF offset; QString label_auxData; qint32 type; dataStream >> offset >> type >> label_auxData; QPointF pos = dde->scenePos() - offset; dde->accept(); GraphicElement *elm = ElementFactory::buildElement(static_cast(type)); /* If element type is unknown, a default element is created with the pixmap received from mimedata */ if (!elm) { return false; } if (elm->elementType() == ElementType::IC) { try { IC *box = dynamic_cast(elm); if (box) { const QString& fname = label_auxData; if (!m_icManager->loadIC(box, fname, GlobalProperties::currentFile)) { return false; } } } catch (std::runtime_error &err) { QMessageBox::warning(m_mainWindow, tr("Error"), QString::fromStdString(err.what())); return false; } } int wdtOffset = (64 - elm->boundingRect().width()) / 2; if (wdtOffset > 0) { pos = pos + QPointF(wdtOffset, wdtOffset); } /* * TODO: Rotate all element icons, remake the port position logic, and remove the code below. * Rotating element in 90 degrees. */ if (elm->rotatable() && (elm->elementType() != ElementType::NODE)) { elm->setRotation(90); } /* Adding the element to the scene. */ receiveCommand(new AddItemsCommand(elm, this)); /* Cleaning the selection. */ m_scene->clearSelection(); /* Setting created element as selected. */ elm->setSelected(true); /* Adjusting the position of the element. */ elm->setPos(pos); return true; } if (dde->mimeData()->hasFormat("application/ctrlDragData")) { QByteArray itemData = dde->mimeData()->data("application/ctrlDragData"); QDataStream ds(&itemData, QIODevice::ReadOnly); QPointF offset; ds >> offset; offset = dde->scenePos() - offset; dde->accept(); QPointF ctr; ds >> ctr; double version = GlobalProperties::version; QList itemList = SerializationFunctions::deserialize(ds, version, GlobalProperties::currentFile); receiveCommand(new AddItemsCommand(itemList, this)); m_scene->clearSelection(); for (QGraphicsItem *item : qAsConst(itemList)) { if (item->type() == GraphicElement::Type) { item->setPos((item->pos() + offset)); item->setSelected(true); item->update(); } } resizeScene(); return true; } return false; } bool Editor::dragMoveEvt(QGraphicsSceneDragDropEvent *dde) { /* Accepting drag/drop event of the following mimedata format. */ if (dde->mimeData()->hasFormat("application/x-dnditemdata")) { return true; } if (dde->mimeData()->hasFormat("application/ctrlDragData")) { return true; } return false; } bool Editor::wheelEvt(QWheelEvent *wEvt) { if (wEvt) { QPoint numDegrees = wEvt->angleDelta() / 8; QPoint numSteps = numDegrees / 15; emit scroll(numSteps.x(), numSteps.y()); wEvt->accept(); return true; } return false; } void Editor::ctrlDrag(QPointF pos) { COMMENT("Ctrl + Drag action triggered.", 0); QVector selectedElms = m_scene->selectedElements(); if (!selectedElms.isEmpty()) { QRectF rect; for (GraphicElement *elm : qAsConst(selectedElms)) { rect = rect.united(elm->boundingRect().translated(elm->pos())); } rect = rect.adjusted(-8, -8, 8, 8); QImage image(rect.size().toSize(), QImage::Format_ARGB32); image.fill(Qt::transparent); QPainter painter(&image); painter.setOpacity(0.25); m_scene->render(&painter, image.rect(), rect); QByteArray itemData; QDataStream dataStream(&itemData, QIODevice::WriteOnly); QPointF offset = pos - rect.topLeft(); dataStream << pos; copy(m_scene->selectedItems(), dataStream); auto *mimeData = new QMimeData; mimeData->setData("application/ctrlDragData", itemData); auto *drag = new QDrag(this); drag->setMimeData(mimeData); drag->setPixmap(QPixmap::fromImage(image)); drag->setHotSpot(offset.toPoint()); drag->exec(Qt::CopyAction, Qt::CopyAction); } } QUndoStack *Editor::getUndoStack() const { return m_undoStack; } Scene *Editor::getScene() const { return m_scene; } void Editor::cut(const QList &items, QDataStream &ds) { copy(items, ds); deleteAction(); } void Editor::copy(const QList &items, QDataStream &ds) { QPointF center(static_cast(0.0f), static_cast(0.0f)); float elm = 0; for (QGraphicsItem *item : items) { if (item->type() == GraphicElement::Type) { center += item->pos(); elm++; } } ds << center / static_cast(elm); SerializationFunctions::serialize(m_scene->selectedItems(), ds); } void Editor::paste(QDataStream &ds) { m_scene->clearSelection(); QPointF ctr; ds >> ctr; QPointF offset = m_mousePos - ctr - QPointF(static_cast(32.0f), static_cast(32.0f)); double version = GlobalProperties::version; QList itemList = SerializationFunctions::deserialize(ds, version, GlobalProperties::currentFile); receiveCommand(new AddItemsCommand(itemList, this)); for (QGraphicsItem *item : qAsConst(itemList)) { if (item->type() == GraphicElement::Type) { item->setPos((item->pos() + offset)); item->update(); item->setSelected(true); // If input or output, set label // Parei aqui... // if( ( item->elementGroup( ) == ElementGroup::INPUT ) || ( item->elementGroup( ) == ElementGroup::OUTPUT ) ) { // } } } resizeScene(); } void Editor::selectAll() { auto const scene_items = m_scene->items(); for (QGraphicsItem *item : scene_items) { item->setSelected(true); } } bool Editor::saveLocalIC(IC *ic, const QString& newICPath) { try { if (ic) { COMMENT("Getting new paths for the ics.", 0) QString fname = ic->getFile(); COMMENT("IC file name: " << fname.toStdString(), 0); auto icPrototype = m_icManager->getPrototype(fname); QString newFilePath = newICPath + "/boxes/" + QFileInfo(fname).fileName(); COMMENT("newFilePath: " << newFilePath.toStdString(), 0); QFile fl(newFilePath); if (!fl.exists()) { COMMENT("Copying file to local dir. File does not exist yet.", 0); QFile::copy(fname, newFilePath); if (icPrototype->updateLocalIC(newFilePath, newICPath)) { if (!ic->setFile(newFilePath)) { std::cerr << "Error changing boxes name." << std::endl; return false; } } else { std::cerr << "Error while saving boxes at the editor." << std::endl; return false; } } else { if (!ic->setFile(newFilePath)) { std::cerr << "Error changing boxes name." << std::endl; return false; } } } return true; } catch (std::runtime_error &err) { QMessageBox::warning(m_mainWindow, tr("Error"), QString::fromStdString(err.what())); return false; } } bool Editor::saveLocal(const QString& newPath) { if (!m_scene) { return true; } auto const scene_elements = m_scene->getElements(); COMMENT("new path: " << newPath.toStdString(), 0); for (GraphicElement *elm : scene_elements) { elm->updateSkinsPath(newPath + "/skins/"); if (elm->elementType() == ElementType::IC) { if (!saveLocalIC(dynamic_cast(elm), newPath)) { return false; } } } return true; } void Editor::save(QDataStream &ds, const QString &dolphinFilename) { SerializationFunctions::saveHeader(ds, dolphinFilename, m_scene->sceneRect()); SerializationFunctions::serialize(m_scene->items(), ds); } void Editor::load(QDataStream &ds) { COMMENT("Loading file.", 0); clear(); COMMENT("Clear!", 0); m_simulationController->stop(); COMMENT("Stopped simulation.", 0); double version = SerializationFunctions::loadVersion(ds); COMMENT("Version: " << version, 0); QString dolphinFilename(SerializationFunctions::loadDolphinFilename(ds, version)); if (m_mainWindow) { m_mainWindow->setDolphinFilename(dolphinFilename); } COMMENT("Dolphin name: " << dolphinFilename.toStdString(), 0); QRectF rect(SerializationFunctions::loadRect(ds, version)); COMMENT("Header Ok. Version: " << version, 0); QList items = SerializationFunctions::deserialize(ds, version, GlobalProperties::currentFile); COMMENT("Finished loading items.", 0); if (m_scene) { for (QGraphicsItem *item : qAsConst(items)) { m_scene->addItem(item); } m_scene->setSceneRect(m_scene->itemsBoundingRect()); if (!m_scene->views().empty()) { auto const scene_views = m_scene->views(); QGraphicsView *view = scene_views.first(); rect = rect.united(view->rect()); rect.moveCenter(QPointF(0, 0)); m_scene->setSceneRect(m_scene->sceneRect().united(rect)); view->centerOn(m_scene->itemsBoundingRect().center()); } } // SerializationFunctions::load( ds, GlobalProperties::currentFile, scene ); m_simulationController->start(); if (m_scene) { m_scene->clearSelection(); } COMMENT("Emitting circuit has changed.", 0); emit circuitHasChanged(); COMMENT("Finished loading file.", 0); } void Editor::setElementEditor(ElementEditor *value) { m_elementEditor = value; m_elementEditor->setScene(m_scene); m_elementEditor->setEditor(this); connect(m_elementEditor, &ElementEditor::sendCommand, this, &Editor::receiveCommand); } QPointF roundTo(QPointF point, int multiple) { int x = static_cast(point.x()); int y = static_cast(point.y()); int nx = multiple * std::floor(x / multiple); int ny = multiple * std::floor(y / multiple); return QPointF(nx, ny); } void Editor::contextMenu(QPoint screenPos) { QGraphicsItem *item = itemAt(m_mousePos); if (item) { if (m_scene->selectedItems().contains(item)) { m_elementEditor->contextMenu(screenPos); } else if ((item->type() == GraphicElement::Type)) { m_scene->clearSelection(); item->setSelected(true); m_elementEditor->contextMenu(screenPos); } } else { QMenu menu; QAction *pasteAction = menu.addAction(QIcon(QPixmap(":/toolbar/paste.png")), tr("Paste")); const QClipboard *clipboard = QApplication::clipboard(); const QMimeData *mimeData = clipboard->mimeData(); if (mimeData->hasFormat("wpanda/copydata")) { connect(pasteAction, &QAction::triggered, this, &Editor::pasteAction); } else { pasteAction->setEnabled(false); } menu.exec(screenPos); } } void Editor::updateVisibility() { showGates(m_showGates); showWires(m_showWires); } void Editor::receiveCommand(QUndoCommand *cmd) { m_undoStack->push(cmd); } void Editor::copyAction() { QVector elms = m_scene->selectedElements(); if (elms.empty()) { QClipboard *clipboard = QApplication::clipboard(); clipboard->clear(); } else { QClipboard *clipboard = QApplication::clipboard(); auto *mimeData = new QMimeData; QByteArray itemData; QDataStream dataStream(&itemData, QIODevice::WriteOnly); copy(m_scene->selectedItems(), dataStream); mimeData->setData("wpanda/copydata", itemData); clipboard->setMimeData(mimeData); } } void Editor::cutAction() { QClipboard *clipboard = QApplication::clipboard(); auto *mimeData = new QMimeData(); QByteArray itemData; QDataStream dataStream(&itemData, QIODevice::WriteOnly); cut(m_scene->selectedItems(), dataStream); mimeData->setData("wpanda/copydata", itemData); clipboard->setMimeData(mimeData); } void Editor::pasteAction() { const QClipboard *clipboard = QApplication::clipboard(); const QMimeData *mimeData = clipboard->mimeData(); if (mimeData->hasFormat("wpanda/copydata")) { QByteArray itemData = mimeData->data("wpanda/copydata"); QDataStream dataStream(&itemData, QIODevice::ReadOnly); paste(dataStream); } } bool Editor::eventFilter(QObject *obj, QEvent *evt) { if (!evt) { return false; } if (obj == m_scene) { auto *dde = dynamic_cast(evt); auto *mouseEvt = dynamic_cast(evt); auto *wEvt = dynamic_cast(evt); auto *keyEvt = dynamic_cast(evt); if (mouseEvt) { m_mousePos = mouseEvt->scenePos(); resizeScene(); handleHoverPort(); if (mouseEvt->modifiers() & Qt::ShiftModifier) { mouseEvt->setModifiers(Qt::ControlModifier); return QObject::eventFilter(obj, evt); } } bool ret = false; if (mouseEvt && ((evt->type() == QEvent::GraphicsSceneMousePress) || (evt->type() == QEvent::GraphicsSceneMouseDoubleClick))) { QGraphicsItem *item = itemAt(m_mousePos); if (item && (mouseEvt->button() == Qt::LeftButton)) { if ((mouseEvt->modifiers() & Qt::ControlModifier) && (item->type() == GraphicElement::Type)) { item->setSelected(true); ctrlDrag(mouseEvt->scenePos()); return true; } m_draggingElement = true; /* STARTING MOVING ELEMENT */ /* qDebug() << "IN"; */ QList list = m_scene->selectedItems(); list.append(itemsAt(m_mousePos)); m_movedElements.clear(); m_oldPositions.clear(); for (QGraphicsItem *it : qAsConst(list)) { auto *elm = qgraphicsitem_cast(it); if (elm) { m_movedElements.append(elm); m_oldPositions.append(elm->pos()); } } } else if (mouseEvt->button() == Qt::RightButton) { contextMenu(mouseEvt->screenPos()); } } if (evt->type() == QEvent::GraphicsSceneMouseRelease) { if (m_draggingElement && (mouseEvt->button() == Qt::LeftButton)) { if (!m_movedElements.empty()) { /* * if( movedElements.size( ) != oldPositions.size( ) ) { * throw std::runtime_error( ERRORMSG(tr( "Invalid coordinates." ).toStdString( ) )); * } * qDebug() << "OUT"; */ bool valid = false; for (int elm = 0; elm < m_movedElements.size(); ++elm) { if (m_movedElements[elm]->pos() != m_oldPositions[elm]) { valid = true; break; } } if (valid) { receiveCommand(new MoveCommand(m_movedElements, m_oldPositions)); } } m_draggingElement = false; m_movedElements.clear(); } } switch (static_cast(evt->type())) { case QEvent::GraphicsSceneMousePress: { ret = mousePressEvt(mouseEvt); break; } case QEvent::GraphicsSceneMouseMove: { ret = mouseMoveEvt(mouseEvt); break; } case QEvent::GraphicsSceneMouseRelease: { ret = mouseReleaseEvt(mouseEvt); break; } case QEvent::GraphicsSceneDrop: { ret = dropEvt(dde); break; } case QEvent::GraphicsSceneDragMove: case QEvent::GraphicsSceneDragEnter: { ret = dragMoveEvt(dde); break; } case QEvent::GraphicsSceneWheel: { ret = wheelEvt(wEvt); break; } case QEvent::GraphicsSceneMouseDoubleClick: { auto *connection = dynamic_cast(itemAt(m_mousePos)); if (connection && (connection->type() == QNEConnection::Type)) { /* Mouse pressed over a connection. */ if (connection) { if (connection->start() && connection->end()) { receiveCommand(new SplitCommand(connection, m_mousePos, this)); } } evt->accept(); return true; } break; } case QEvent::KeyPress: { if (keyEvt && !(keyEvt->modifiers() & Qt::ControlModifier)) { auto const scene_elems = m_scene->getElements(); for (GraphicElement *elm : scene_elems) { if (elm->hasTrigger() && !elm->getTrigger().isEmpty()) { auto *in = dynamic_cast(elm); if (in && elm->getTrigger().matches(keyEvt->key())) { if (elm->elementType() == ElementType::SWITCH) { in->setOn(!in->getOn()); } else { in->setOn(true); } } } } } break; } case QEvent::KeyRelease: { if (keyEvt && !(keyEvt->modifiers() & Qt::ControlModifier)) { auto const scene_elems = m_scene->getElements(); for (GraphicElement *elm : scene_elems) { if (elm->hasTrigger() && !elm->getTrigger().isEmpty()) { auto *in = dynamic_cast(elm); if (in && (elm->getTrigger().matches(keyEvt->key()) == QKeySequence::ExactMatch)) { if (elm->elementType() != ElementType::SWITCH) { in->setOn(false); } } } } } break; } } if (ret) { return ret; } } return QObject::eventFilter(obj, evt); } wiRedPanda-3.0.1/app/editor.h000066400000000000000000000105611406216046500157570ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef EDITOR_H #define EDITOR_H #include #include #include "scene.h" class ElementEditor; class IC; class ICManager; class MainWindow; class QNEConnection; class QNEInputPort; class QNEOutputPort; class QNEPort; class QUndoCommand; class QUndoStack; class QWheelEvent; class SimulationController; class Editor : public QObject { Q_OBJECT public: explicit Editor(QObject *parent = nullptr); ~Editor() override; /** * @brief saveLocalIC: saves an IC and its internal ICs recursively into the same local project subfolder. */ bool saveLocalIC(IC *ic, const QString& newICPath); /** * @brief saveLocal: saves a project locally in a given directory so that all ICs and skins are also saved in local subfolders. * This is very useful to export projects that use ICs from other folders or projects. * The function saves all ICs and skins into local folders to the given project path of the main .panda file. * It works recursively, saving eventual ICs inside used ICs and updates their reference paths. * The function does NOT save the main project file, which should be done by save() function. */ bool saveLocal(const QString& newPath); /** * @brief save: saves the project through a binary data stream. */ void save(QDataStream &ds, const QString &dolphinFilename); /** * @brief load: loads the project through a binary data stream. */ void load(QDataStream &ds); void cut(const QList &items, QDataStream &ds); void copy(const QList &items, QDataStream &ds); void paste(QDataStream &ds); void selectAll(); bool eventFilter(QObject *obj, QEvent *evt) override; void setElementEditor(ElementEditor *value); QUndoStack *getUndoStack() const; Scene *getScene() const; void buildSelectionRect(); void handleHoverPort(); void releaseHoverPort(); void setHoverPort(QNEPort *port); QNEPort *getHoverPort(); void resizeScene(); SimulationController *getSimulationController() const; void contextMenu(QPoint screenPos); void updateVisibility(); QPointF getMousePos() const; ElementEditor *getElementEditor() const; static Editor *globalEditor; void deleteEditedConn(); void flipH(); void flipV(); signals: void scroll(int x, int y); void circuitHasChanged(); public slots: void clear(); void showWires(bool checked); void showGates(bool checked); void rotate(bool rotateRight); void receiveCommand(QUndoCommand *cmd); void copyAction(); void cutAction(); void pasteAction(); void deleteAction(); void updateTheme(); void mute(bool _mute = true); private: QElapsedTimer m_timer; QUndoStack *m_undoStack; Scene *m_scene; QList itemsAt(QPointF pos); QGraphicsItem *itemAt(QPointF pos); int m_editedConn_id; int m_hoverPortElm_id; int m_hoverPort_nbr; ElementEditor *m_elementEditor; ICManager *m_icManager; bool m_markingSelectionIC; QGraphicsRectItem *m_selectionRect; QPointF m_selectionStartPoint; SimulationController *m_simulationController; QPointF m_mousePos, m_lastPos; void addItem(QGraphicsItem *item); bool m_draggingElement; QList m_movedElements; QList m_oldPositions; MainWindow *m_mainWindow; /* bool mControlKeyPressed; */ bool m_showWires; bool m_showGates; bool mousePressEvt(QGraphicsSceneMouseEvent *mouseEvt); bool mouseMoveEvt(QGraphicsSceneMouseEvent *mouseEvt); bool mouseReleaseEvt(QGraphicsSceneMouseEvent *mouseEvt); bool dropEvt(QGraphicsSceneDragDropEvent *dde); bool dragMoveEvt(QGraphicsSceneDragDropEvent *dde); bool wheelEvt(QWheelEvent *wEvt); void ctrlDrag(QPointF pos); void install(Scene *s); QNEConnection *getEditedConn() const; void setEditedConn(QNEConnection *editedConn); /* QObject interface */ void detachConnection(QNEInputPort *endPort); void startNewConnection(QNEOutputPort *startPort); void startNewConnection(QNEInputPort *endPort); void startSelectionRect(); void makeConnection(QNEConnection *editedConn); void redoSimulationController(); }; #endif /* EDITOR_H */ wiRedPanda-3.0.1/app/element/000077500000000000000000000000001406216046500157465ustar00rootroot00000000000000wiRedPanda-3.0.1/app/element/CMakeLists.txt000066400000000000000000000011251406216046500205050ustar00rootroot00000000000000 set ( WPANDA_ELEMENT and.cpp clock.cpp demux.cpp dflipflop.cpp display.cpp display_14.cpp dlatch.cpp inputbutton.cpp inputgnd.cpp inputswitch.cpp inputvcc.cpp jkflipflop.cpp jklatch.cpp led.cpp mux.cpp nand.cpp node.cpp nor.cpp not.cpp or.cpp srflipflop.cpp tflipflop.cpp xnor.cpp xor.cpp buzzer.cpp ) # tlatch.cpp add_library(wpandaelements ${WPANDA_ELEMENT} ) target_link_libraries(wpandaelements PUBLIC ${WPANDA_LIBS} ) target_compile_options(wpandaelements PRIVATE ${COMPILE_WARNS} ) wiRedPanda-3.0.1/app/element/and.cpp000066400000000000000000000011611406216046500172130ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "and.h" And::And(QGraphicsItem *parent) : GraphicElement(ElementType::AND, ElementGroup::GATE, 2, 8, 1, 1, parent) { m_pixmapSkinName = {":/basic/and.png"}; setOutputsOnTop(true); setPixmap(m_pixmapSkinName[0]); updatePorts(); setCanChangeSkin(true); setPortName("AND"); setToolTip("AND"); } void And::setSkin(bool defaultSkin, const QString &filename) { m_pixmapSkinName[0] = defaultSkin ? ":/basic/and.png" : filename; setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/element/and.h000066400000000000000000000006231406216046500166620ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef AND_H #define AND_H #include "graphicelement.h" class And : public GraphicElement { public: explicit And(QGraphicsItem *parent = nullptr); ~And() override = default; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* AND_H */ wiRedPanda-3.0.1/app/element/buzzer.cpp000066400000000000000000000061531406216046500200000ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "buzzer.h" #include #include "qneport.h" #include #include int Buzzer::current_id_number = 0; // macOS doesn't want to compile a constexpr std::array for some reason #ifdef Q_OS_MACOS static const std::array defaultSkins { #else static constexpr std::array defaultSkins { #endif ":/output/BuzzerOff.png", ":/output/BuzzerOn.png" }; Buzzer::Buzzer(QGraphicsItem *parent) : GraphicElement(ElementType::BUZZER, ElementGroup::OUTPUT, 1, 1, 0, 0, parent) { // m_pixmapSkinName.append( ":/output/BuzzerOff.png" ); // m_pixmapSkinName.append( ":/output/BuzzerOn.png" ); setOutputsOnTop(true); setRotatable(false); setHasAudio(true); // setPixmap( m_pixmapSkinName[ 0 ] ); setPixmap(defaultSkins[0]); m_alternativeSkins = QVector({defaultSkins[0], defaultSkins[1]}); updatePorts(); setCanChangeSkin(true); setHasLabel(true); // Let's reserve space for alternativeSkins :D // All is well, rite? m_alternativeSkins.resize(2); setPortName("Buzzer"); setLabel(objectName() + "_" + QString::number(Buzzer::current_id_number)); ++Buzzer::current_id_number; m_usingDefaultSkin = true; setAudio("C6"); m_play = 0; } void Buzzer::refresh() { if (!isValid()) { stopbuzzer(); return; } const bool value = m_inputs.first()->value(); if (value == 1) { playbuzzer(); } else if (m_play == 1) { stopbuzzer(); } } void Buzzer::setAudio(const QString ¬e) { m_audio.setSource(QUrl::fromLocalFile(QString(":output/audio/%1.wav").arg(note))); m_audio.setVolume(0.35); m_audio.setLoopCount(QSoundEffect::Infinite); m_note = note; } QString Buzzer::getAudio() const { return m_note; } void Buzzer::mute(bool _mute) { m_audio.setMuted(_mute); } void Buzzer::playbuzzer() { if (m_play != 0) { return; } m_usingDefaultSkin ? setPixmap(defaultSkins[1]) : setPixmap(m_alternativeSkins[1]); m_audio.play(); m_play = 1; } void Buzzer::stopbuzzer() { m_usingDefaultSkin ? setPixmap(defaultSkins[0]) : setPixmap(m_alternativeSkins[0]); m_play = 0; m_audio.stop(); } void Buzzer::save(QDataStream &ds) const { GraphicElement::save(ds); ds << getAudio(); } void Buzzer::load(QDataStream &ds, QMap &portMap, double version) { GraphicElement::load(ds, portMap, version); if (version < 2.4) { return; } QString note; ds >> note; setAudio(note); } void Buzzer::setSkin(bool defaultSkin, const QString &filename) { if (m_play > 0) { m_play = 1; } if (defaultSkin) { m_usingDefaultSkin = true; setPixmap(defaultSkins[m_play]); } else { m_usingDefaultSkin = false; m_alternativeSkins[m_play] = filename; setPixmap(m_alternativeSkins[m_play]); // std::cerr << "Filename: " << alternativeSkins[ play ].toStdString() << '\n'; } } wiRedPanda-3.0.1/app/element/buzzer.h000066400000000000000000000020241406216046500174360ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef BUZZER_H #define BUZZER_H #include "graphicelement.h" #include class Buzzer : public GraphicElement { public: explicit Buzzer(QGraphicsItem *parent = nullptr); ~Buzzer() override = default; static int current_id_number; // Number used to create distinct labels for each instance of this element. void refresh() override; void setAudio(const QString ¬e) override; QString getAudio() const override; void mute(bool _mute = true); void save(QDataStream &ds) const override; void load(QDataStream &ds, QMap &portMap, double version) override; void setSkin(bool defaultSkin, const QString &filename) override; private: void playbuzzer(); void stopbuzzer(); QVector m_alternativeSkins; // TODO: this could just be a bool int m_play; QSoundEffect m_audio; QString m_note; }; #endif // BUZZER_H wiRedPanda-3.0.1/app/element/clock.cpp000066400000000000000000000052651406216046500175550ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "clock.h" #include "globalproperties.h" #include "qneport.h" #include bool Clock::reset = false; int Clock::current_id_number = 0; Clock::~Clock() = default; Clock::Clock(QGraphicsItem *parent) : GraphicElement(ElementType::CLOCK, ElementGroup::INPUT, 0, 0, 1, 1, parent) { m_pixmapSkinName = { ":/input/clock0.png", ":/input/clock1.png" }; setOutputsOnTop(false); setRotatable(false); setCanChangeSkin(true); /* connect(&timer,&QTimer::timeout,this,&Clock::updateClock); */ setFrequency(1.0); // TODO: call to virtual function during construction setHasFrequency(true); m_isOn = false; Clock::reset = true; setHasLabel(true); setPortName("Clock"); setOn(false); setPixmap(m_pixmapSkinName[0]); } void Clock::updateClock() { if (!disabled()) { m_elapsed++; if ((m_elapsed % m_interval) == 0) { setOn(!m_isOn); } } setOn(m_isOn); } bool Clock::getOn() const { return m_isOn; } void Clock::setOn(bool value) { m_isOn = value; setPixmap(m_pixmapSkinName[m_isOn ? 1 : 0]); m_outputs.first()->setValue(m_isOn); } void Clock::save(QDataStream &ds) const { GraphicElement::save(ds); ds << getFrequency(); } void Clock::load(QDataStream &ds, QMap &portMap, double version) { GraphicElement::load(ds, portMap, version); if (version < 1.1) { return; } float freq; ds >> freq; setFrequency(freq); } float Clock::getFrequency() const { return static_cast(m_frequency); } void Clock::setFrequency(float freq) { /* qDebug() << "Clock frequency set to " << freq; */ if (qFuzzyIsNull(freq)) { return; } int auxinterval = 1000 / (freq * GLOBALCLK); if (auxinterval <= 0) { return; } m_interval = auxinterval; m_frequency = static_cast(freq); m_elapsed = 0; Clock::reset = true; // qDebug() << "Freq = " << freq << " interval = " << interval; /* timer.start( static_cast< int >(1000.0/freq) ); */ } void Clock::resetClock() { setOn(true); m_elapsed = 0; } QString Clock::genericProperties() { return QString("%1 Hz").arg(static_cast(getFrequency())); } void Clock::setSkin(bool defaultSkin, const QString &filename) { if (!m_isOn) { m_pixmapSkinName[0] = defaultSkin ? ":/input/clock0.png" : filename; setPixmap(m_pixmapSkinName[0]); } else { m_pixmapSkinName[1] = defaultSkin ? ":/input/clock1.png" : filename; setPixmap(m_pixmapSkinName[1]); } } wiRedPanda-3.0.1/app/element/clock.h000066400000000000000000000020411406216046500172070ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef CLOCK_H #define CLOCK_H #include "graphicelement.h" #include "input.h" class Clock : public GraphicElement, public Input { private: int m_interval; int m_elapsed; bool m_isOn; double m_frequency; public: explicit Clock(QGraphicsItem *parent = nullptr); ~Clock() override; static int current_id_number; // Number used to create distinct labels for each instance of this element. static bool reset; public: void save(QDataStream &ds) const override; void load(QDataStream &ds, QMap &portMap, double version) override; float getFrequency() const override; void setFrequency(float freq) override; void updateClock(); void resetClock(); QString genericProperties() override; public: bool getOn() const override; void setOn(bool value) override; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif // CLOCK_H wiRedPanda-3.0.1/app/element/demux.cpp000066400000000000000000000020221406216046500175700ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "demux.h" #include "qneport.h" Demux::Demux(QGraphicsItem *parent) : GraphicElement(ElementType::DEMUX, ElementGroup::MUX, 2, 2, 2, 2, parent) { m_pixmapSkinName = {":/basic/demux.png"}; setPixmap(m_pixmapSkinName[0]); setRotatable(true); setCanChangeSkin(true); updatePorts(); setPortName("DEMUX"); setRotation(180.0); input(0)->setName("in"); input(1)->setName("S"); output(0)->setName("out0"); output(1)->setName("out1"); } void Demux::updatePorts() { input(0)->setPos(32, 48); /* 0 */ input(1)->setPos(58, 32); /* S */ output(0)->setPos(32 - 12, 16); /* Out */ output(1)->setPos(32 + 12, 16); /* Out */ } void Demux::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { m_pixmapSkinName[0] = ":/basic/demux.png"; } else { m_pixmapSkinName[0] = filename; } setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/element/demux.h000066400000000000000000000007241406216046500172440ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef DEMUX_H #define DEMUX_H #include "graphicelement.h" #include class Demux : public GraphicElement { public: explicit Demux(QGraphicsItem *parent = nullptr); ~Demux() override = default; void updatePorts() override; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* DEMUX_H */ wiRedPanda-3.0.1/app/element/dflipflop.cpp000066400000000000000000000030001406216046500204220ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "dflipflop.h" #include "qneport.h" DFlipFlop::DFlipFlop(QGraphicsItem *parent) : GraphicElement(ElementType::DFLIPFLOP, ElementGroup::MEMORY, 4, 4, 2, 2, parent) { m_pixmapSkinName = {":/memory/D-flipflop.png"}; setPixmap(m_pixmapSkinName[0]); setRotatable(false); setCanChangeSkin(true); updatePorts(); setPortName("FlipFlop D"); input(0)->setName("Data"); input(1)->setName("Clock"); input(2)->setName("~Preset"); input(3)->setName("~Clear"); output(0)->setName("Q"); output(1)->setName("~Q"); output(0)->setDefaultValue(0); output(1)->setDefaultValue(1); input(2)->setRequired(false); input(3)->setRequired(false); input(2)->setDefaultValue(1); input(3)->setDefaultValue(1); lastValue = false; lastClk = false; } void DFlipFlop::updatePorts() { input(0)->setPos(topPosition(), 13); /* Data */ input(1)->setPos(topPosition(), 45); /* Clock */ input(2)->setPos(32, topPosition()); /* Preset */ input(3)->setPos(32, bottomPosition()); /* Clear */ output(0)->setPos(bottomPosition(), 15); /* Q */ output(1)->setPos(bottomPosition(), 45); /* ~Q */ } void DFlipFlop::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { m_pixmapSkinName[0] = ":/memory/D-flipflop.png"; } else { m_pixmapSkinName[0] = filename; } setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/element/dflipflop.h000066400000000000000000000007741406216046500201060ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef DFLIPFLOP_H #define DFLIPFLOP_H #include "graphicelement.h" class DFlipFlop : public GraphicElement { bool lastClk; bool lastValue; public: explicit DFlipFlop(QGraphicsItem *parent = nullptr); ~DFlipFlop() override = default; void updatePorts() override; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif // DFLIPFLOP_H wiRedPanda-3.0.1/app/element/display.cpp000066400000000000000000000104131406216046500201160ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "common.h" #include "display.h" #include "qneport.h" #include #include int Display::current_id_number = 0; Display::Display(QGraphicsItem *parent) : GraphicElement(ElementType::DISPLAY, ElementGroup::OUTPUT, 8, 8, 0, 0, parent) { m_pixmapSkinName = { ":/output/counter/counter_off.png", ":/output/counter/counter_a.png", ":/output/counter/counter_b.png", ":/output/counter/counter_c.png", ":/output/counter/counter_d.png", ":/output/counter/counter_e.png", ":/output/counter/counter_f.png", ":/output/counter/counter_g.png", ":/output/counter/counter_dp.png", }; setRotatable(false); setCanChangeSkin(true); setOutputsOnTop(true); updatePorts(); setBottomPosition(58); setTopPosition(6); setHasLabel(true); setPixmap(m_pixmapSkinName[0]); a = QPixmap(m_pixmapSkinName[1]); b = QPixmap(m_pixmapSkinName[2]); c = QPixmap(m_pixmapSkinName[3]); d = QPixmap(m_pixmapSkinName[4]); e = QPixmap(m_pixmapSkinName[5]); f = QPixmap(m_pixmapSkinName[6]); g = QPixmap(m_pixmapSkinName[7]); dp = QPixmap(m_pixmapSkinName[8]); setPortName("Display"); for (QNEPort *in : qAsConst(m_inputs)) { in->setRequired(false); in->setDefaultValue(0); } } void Display::refresh() { update(); } void Display::updatePorts() { input(0)->setPos(topPosition(), 10); /* G */ input(1)->setPos(topPosition(), 25); /* F */ input(2)->setPos(topPosition(), 39); /* E */ input(3)->setPos(topPosition(), 54); /* D */ input(4)->setPos(bottomPosition(), 10); /* A */ input(5)->setPos(bottomPosition(), 25); /* B */ input(6)->setPos(bottomPosition(), 39); /* DP */ input(7)->setPos(bottomPosition(), 54); /* C */ input(0)->setName("G (mid)"); input(1)->setName("F (upper left)"); input(2)->setName("E (lower left)"); input(3)->setName("D (bottom)"); input(4)->setName("A (top)"); input(5)->setName("B (upper right)"); input(6)->setName("DP (dot)"); input(7)->setName("C (lower right)"); } void Display::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { GraphicElement::paint(painter, option, widget); if (input(0)->value() == true) { /* G */ painter->drawPixmap(QPoint(0, 0), g); } if (input(1)->value() == true) { /* F */ painter->drawPixmap(QPoint(0, 0), f); } if (input(2)->value() == true) { /* E */ painter->drawPixmap(QPoint(0, 0), e); } if (input(3)->value() == true) { /* D */ painter->drawPixmap(QPoint(0, 0), d); } if (input(4)->value() == true) { /* A */ painter->drawPixmap(QPoint(0, 0), a); } if (input(5)->value() == true) { /* B */ painter->drawPixmap(QPoint(0, 0), b); } if (input(6)->value() == true) { /* DP */ painter->drawPixmap(QPoint(0, 0), dp); } if (input(7)->value() == true) { /* C */ painter->drawPixmap(QPoint(0, 0), c); } } void Display::load(QDataStream &ds, QMap &portMap, double version) { GraphicElement::load(ds, portMap, version); // qDebug( ) << "Version: " << version; /* * 0,7,2,1,3,4,5,6 * 7,5,4,2,1,4,6,3,0 * 4,5,7,3,2,1,0,6 * 2,1,4,5,0,7,3,6 */ if (version < 1.6) { COMMENT("Remapping inputs", 0); QVector order = {2, 1, 4, 5, 0, 7, 3, 6}; QVector aux = inputs(); for (int i = 0; i < aux.size(); ++i) { aux[order[i]] = m_inputs[i]; } setInputs(aux); updatePorts(); } if (version < 1.7) { COMMENT("Remapping inputs", 0); QVector order = {2, 5, 4, 0, 7, 3, 6, 1}; QVector aux = inputs(); for (int i = 0; i < aux.size(); ++i) { aux[order[i]] = m_inputs[i]; } setInputs(aux); updatePorts(); } } void Display::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { m_pixmapSkinName[0] = ":/output/counter/counter_off.png"; } else { m_pixmapSkinName[0] = filename; } setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/element/display.h000066400000000000000000000016501406216046500175660ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef DISPLAY_H #define DISPLAY_H #include "graphicelement.h" class QNEPort; class Display : public GraphicElement { public: explicit Display(QGraphicsItem *parent = nullptr); ~Display() override = default; static int current_id_number; // Number used to create distinct labels for each instance of this element. void refresh() override; void updatePorts() override; QPixmap bkg, a, b, c, d, e, f, g, dp; /* QGraphicsItem interface */ public: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; /* GraphicElement interface */ public: void load(QDataStream &ds, QMap &portMap, double version) override; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* DISPLAY_H */ wiRedPanda-3.0.1/app/element/display_14.cpp000066400000000000000000000126131406216046500204260ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "display_14.h" #include "qneport.h" #include #include int Display14::current_id_number = 0; Display14::Display14(QGraphicsItem *parent) : GraphicElement(ElementType::DISPLAY14, ElementGroup::OUTPUT, 15, 15, 0, 0, parent) { m_pixmapSkinName = { ":/output/counter/counter_14_off.png", ":/output/counter/counter_a.png", ":/output/counter/counter_b.png", ":/output/counter/counter_c.png", ":/output/counter/counter_d.png", ":/output/counter/counter_e.png", ":/output/counter/counter_f.png", ":/output/counter/counter_g1.png", ":/output/counter/counter_g2.png", ":/output/counter/counter_h.png", ":/output/counter/counter_j.png", ":/output/counter/counter_k.png", ":/output/counter/counter_l.png", ":/output/counter/counter_m.png", ":/output/counter/counter_n.png", ":/output/counter/counter_dp.png", }; setRotatable(false); setCanChangeSkin(true); setOutputsOnTop(true); updatePorts(); setBottomPosition(58); setTopPosition(6); setHasLabel(true); setPixmap(m_pixmapSkinName[0]); a = QPixmap(m_pixmapSkinName[1]); b = QPixmap(m_pixmapSkinName[2]); c = QPixmap(m_pixmapSkinName[3]); d = QPixmap(m_pixmapSkinName[4]); e = QPixmap(m_pixmapSkinName[5]); f = QPixmap(m_pixmapSkinName[6]); g1 = QPixmap(m_pixmapSkinName[7]); g2 = QPixmap(m_pixmapSkinName[8]); h = QPixmap(m_pixmapSkinName[9]); j = QPixmap(m_pixmapSkinName[10]); k = QPixmap(m_pixmapSkinName[11]); l = QPixmap(m_pixmapSkinName[12]); m = QPixmap(m_pixmapSkinName[13]); n = QPixmap(m_pixmapSkinName[14]); dp = QPixmap(m_pixmapSkinName[15]); setPortName("Display14"); for (QNEPort *in : qAsConst(m_inputs)) { in->setRequired(false); in->setDefaultValue(0); } } void Display14::refresh() { update(); } void Display14::updatePorts() { input(0)->setPos(topPosition(), -4); /* G1 */ input(1)->setPos(topPosition(), 8); /* F */ input(2)->setPos(topPosition(), 20); /* E */ input(3)->setPos(topPosition(), 32); /* D */ input(4)->setPos(bottomPosition(), -10); /* A */ input(5)->setPos(bottomPosition(), 2); /* B */ input(6)->setPos(bottomPosition(), 14); /* DP */ input(7)->setPos(bottomPosition(), 26); /* C */ input(8)->setPos(topPosition(), 44); /* G2 */ input(9)->setPos(topPosition(), 56); /* H */ input(10)->setPos(topPosition(), 68); /* J */ input(11)->setPos(bottomPosition(), 38); /* K */ input(12)->setPos(bottomPosition(), 50); /* L */ input(13)->setPos(bottomPosition(), 62); /* M */ input(14)->setPos(bottomPosition(), 74); /* N */ input(0)->setName("G1 (mid left)"); input(1)->setName("F (upper left)"); input(2)->setName("E (lower left)"); input(3)->setName("D (bottom)"); input(4)->setName("A (top)"); input(5)->setName("B (upper right)"); input(6)->setName("DP (dot)"); input(7)->setName("C (lower right)"); input(8)->setName("G2 (mid right)"); input(9)->setName("H (mid upper left)"); input(10)->setName("J (mid top)"); input(11)->setName("K (mid upper right)"); input(12)->setName("L (mid lower right)"); input(13)->setName("M (mid bottom)"); input(14)->setName("N (mid lower left)"); } void Display14::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { GraphicElement::paint(painter, option, widget); if (input(0)->value() == true) { /* G1 */ painter->drawPixmap(QPoint(0, 0), g1); } if (input(1)->value() == true) { /* F */ painter->drawPixmap(QPoint(0, 0), f); } if (input(2)->value() == true) { /* E */ painter->drawPixmap(QPoint(0, 0), e); } if (input(3)->value() == true) { /* D */ painter->drawPixmap(QPoint(0, 0), d); } if (input(4)->value() == true) { /* A */ painter->drawPixmap(QPoint(0, 0), a); } if (input(5)->value() == true) { /* B */ painter->drawPixmap(QPoint(0, 0), b); } if (input(6)->value() == true) { /* DP */ painter->drawPixmap(QPoint(0, 0), dp); } if (input(7)->value() == true) { /* C */ painter->drawPixmap(QPoint(0, 0), c); } if (input(8)->value() == true) { /* G2 */ painter->drawPixmap(QPoint(0, 0), g2); } if (input(9)->value() == true) { /* H */ painter->drawPixmap(QPoint(0, 0), h); } if (input(10)->value() == true) { /* J */ painter->drawPixmap(QPoint(0, 0), j); } if (input(11)->value() == true) { /* K */ painter->drawPixmap(QPoint(0, 0), k); } if (input(12)->value() == true) { /* L */ painter->drawPixmap(QPoint(0, 0), l); } if (input(13)->value() == true) { /* M */ painter->drawPixmap(QPoint(0, 0), m); } if (input(14)->value() == true) { /* N */ painter->drawPixmap(QPoint(0, 0), n); } } void Display14::load(QDataStream &ds, QMap &portMap, double version) { GraphicElement::load(ds, portMap, version); } void Display14::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { m_pixmapSkinName[0] = ":/output/counter/counter_14_off.png"; } else { m_pixmapSkinName[0] = filename; } setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/element/display_14.h000066400000000000000000000016731406216046500200770ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef DISPLAY14_H #define DISPLAY14_H #include "graphicelement.h" class Display14 : public GraphicElement { public: explicit Display14(QGraphicsItem *parent = nullptr); ~Display14() override = default; static int current_id_number; // Number used to create distinct labels for each instance of this element. void refresh() override; void updatePorts() override; QPixmap bkg, a, b, c, d, e, f, g1, g2, h, j, k, l, m, n, dp; /* QGraphicsItem interface */ public: void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; /* GraphicElement interface */ public: void load(QDataStream &ds, QMap &portMap, double version) override; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* DISPLAY14_H */ wiRedPanda-3.0.1/app/element/dlatch.cpp000066400000000000000000000022021406216046500177050ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "dlatch.h" #include "qneport.h" DLatch::DLatch(QGraphicsItem *parent) : GraphicElement(ElementType::DLATCH, ElementGroup::MEMORY, 2, 2, 2, 2, parent) { m_pixmapSkinName = {":/memory/D-latch.png"}; setPixmap(m_pixmapSkinName[0]); setRotatable(false); setCanChangeSkin(true); updatePorts(); setPortName("D Latch"); input(0)->setName("Data"); input(1)->setName("Enable"); output(0)->setName("Q"); output(1)->setName("~Q"); output(0)->setDefaultValue(0); output(1)->setDefaultValue(1); } void DLatch::updatePorts() { input(0)->setPos(topPosition(), 13); /* Data */ input(1)->setPos(topPosition(), 45); /* Enable */ output(0)->setPos(bottomPosition(), 15); /* Q */ output(1)->setPos(bottomPosition(), 45); /* ~Q */ } void DLatch::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { m_pixmapSkinName[0] = ":/memory/D-latch.png"; } else { m_pixmapSkinName[0] = filename; } setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/element/dlatch.h000066400000000000000000000007061406216046500173610ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef DLATCH_H #define DLATCH_H #include "graphicelement.h" class DLatch : public GraphicElement { public: explicit DLatch(QGraphicsItem *parent = nullptr); ~DLatch() override = default; void updatePorts() override; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* DLATCH_H */ wiRedPanda-3.0.1/app/element/element.pri000066400000000000000000000022571406216046500201210ustar00rootroot00000000000000SOURCES += \ $$PWD/and.cpp \ $$PWD/clock.cpp \ $$PWD/demux.cpp \ $$PWD/dflipflop.cpp \ $$PWD/display.cpp \ $$PWD/display_14.cpp \ $$PWD/dlatch.cpp \ $$PWD/inputbutton.cpp \ $$PWD/inputgnd.cpp \ $$PWD/inputswitch.cpp \ $$PWD/inputvcc.cpp \ $$PWD/jkflipflop.cpp \ $$PWD/jklatch.cpp \ $$PWD/led.cpp \ $$PWD/mux.cpp \ $$PWD/nand.cpp \ $$PWD/node.cpp \ $$PWD/nor.cpp \ $$PWD/not.cpp \ $$PWD/or.cpp \ $$PWD/srflipflop.cpp \ $$PWD/tflipflop.cpp \ # $$PWD/tlatch.cpp \ $$PWD/xnor.cpp \ $$PWD/xor.cpp \ $$PWD/buzzer.cpp HEADERS += \ $$PWD/and.h \ $$PWD/clock.h \ $$PWD/demux.h \ $$PWD/dflipflop.h \ $$PWD/display.h \ $$PWD/display_14.h \ $$PWD/dlatch.h \ $$PWD/inputbutton.h \ $$PWD/inputgnd.h \ $$PWD/input.h \ $$PWD/inputswitch.h \ $$PWD/inputvcc.h \ $$PWD/jkflipflop.h \ $$PWD/jklatch.h \ $$PWD/led.h \ $$PWD/mux.h \ $$PWD/nand.h \ $$PWD/node.h \ $$PWD/nor.h \ $$PWD/not.h \ $$PWD/or.h \ $$PWD/srflipflop.h \ $$PWD/tflipflop.h \ # $$PWD/tlatch.h \ $$PWD/xnor.h \ $$PWD/xor.h \ $$PWD/buzzer.h wiRedPanda-3.0.1/app/element/input.h000066400000000000000000000004311406216046500172540ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef INPUT_H #define INPUT_H class Input { public: virtual bool getOn() const = 0; virtual void setOn(bool value) = 0; }; #endif /* INPUT_H */ wiRedPanda-3.0.1/app/element/inputbutton.cpp000066400000000000000000000036771406216046500210620ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "inputbutton.h" #include "qneport.h" #include int InputButton::current_id_number = 0; InputButton::InputButton(QGraphicsItem *parent) : GraphicElement(ElementType::BUTTON, ElementGroup::INPUT, 0, 0, 1, 1, parent) { m_pixmapSkinName = { ":/input/buttonOff.png", ":/input/buttonOn.png", }; setOutputsOnTop(false); setCanChangeSkin(true); setPixmap(m_pixmapSkinName[0]); setRotatable(false); m_outputs.first()->setValue(0); setOn(false); setHasLabel(true); setHasTrigger(true); setPortName("Button"); } void InputButton::mousePressEvent(QGraphicsSceneMouseEvent *event) { if (event->button() == Qt::LeftButton) { setOn(true); event->accept(); } QGraphicsItem::mousePressEvent(event); } void InputButton::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { if (event->button() == Qt::LeftButton) { setOn(false); event->accept(); } GraphicElement::mouseReleaseEvent(event); } bool InputButton::getOn() const { return on; } void InputButton::setOn(const bool value) { on = value; setPixmap(on ? m_pixmapSkinName[1] : m_pixmapSkinName[0]); if (!disabled()) { output()->setValue(on); } } void InputButton::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { if (!on) { m_pixmapSkinName[0] = ":/input/buttonOff.png"; setPixmap(m_pixmapSkinName[0]); } else { m_pixmapSkinName[1] = ":/input/buttonOn.png"; setPixmap(m_pixmapSkinName[1]); } } else { if (!on) { m_pixmapSkinName[0] = filename; setPixmap(m_pixmapSkinName[0]); } else { m_pixmapSkinName[1] = filename; setPixmap(m_pixmapSkinName[1]); } } } wiRedPanda-3.0.1/app/element/inputbutton.h000066400000000000000000000015111406216046500205100ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef INPUTBUTTON_H #define INPUTBUTTON_H #include "graphicelement.h" #include "input.h" class InputButton : public GraphicElement, public Input { public: explicit InputButton(QGraphicsItem *parent = nullptr); ~InputButton() override = default; static int current_id_number; // Number used to create distinct labels for each instance of this element. bool on; protected: void mousePressEvent(QGraphicsSceneMouseEvent *event) override; void mouseReleaseEvent(QGraphicsSceneMouseEvent *event) override; public: bool getOn() const override; void setOn(const bool value) override; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* INPUTBUTTON_H */ wiRedPanda-3.0.1/app/element/inputgnd.cpp000066400000000000000000000013631406216046500203050ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "inputgnd.h" #include "qneport.h" InputGnd::InputGnd(QGraphicsItem *parent) : GraphicElement(ElementType::GND, ElementGroup::STATICINPUT, 0, 0, 1, 1, parent) { m_pixmapSkinName = {":/input/0.png"}; setOutputsOnTop(false); setCanChangeSkin(true); setPixmap(m_pixmapSkinName[0]); setRotatable(false); setPortName("GND"); m_outputs.first()->setValue(false); } void InputGnd::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { m_pixmapSkinName[0] = ":/input/0.png"; } else { m_pixmapSkinName[0] = filename; } setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/element/inputgnd.h000066400000000000000000000006701406216046500177520ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef INPUTGND_H #define INPUTGND_H #include "graphicelement.h" class InputGnd : public GraphicElement { public: explicit InputGnd(QGraphicsItem *parent = nullptr); ~InputGnd() override = default; public: void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* INPUTGND_H */ wiRedPanda-3.0.1/app/element/inputswitch.cpp000066400000000000000000000040211406216046500210300ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "inputswitch.h" #include "qneport.h" #include int InputSwitch::current_id_number = 0; InputSwitch::InputSwitch(QGraphicsItem *parent) : GraphicElement(ElementType::SWITCH, ElementGroup::INPUT, 0, 0, 1, 1, parent) { m_pixmapSkinName = { ":/input/switchOff.png", ":/input/switchOn.png", }; setOutputsOnTop(false); setCanChangeSkin(true); setRotatable(false); setPixmap(m_pixmapSkinName[0]); on = false; setHasLabel(true); setHasTrigger(true); setPortName("Switch"); } bool InputSwitch::getOn() const { return on; } void InputSwitch::setOn(bool value) { on = value; if (!disabled()) { output()->setValue(on); } if (on) { setPixmap(m_pixmapSkinName[1]); } else { setPixmap(m_pixmapSkinName[0]); } } void InputSwitch::mousePressEvent(QGraphicsSceneMouseEvent *event) { if (event->button() == Qt::LeftButton) { setOn(!on); event->accept(); } QGraphicsItem::mousePressEvent(event); } void InputSwitch::save(QDataStream &ds) const { GraphicElement::save(ds); ds << on; } void InputSwitch::load(QDataStream &ds, QMap &portMap, double version) { GraphicElement::load(ds, portMap, version); ds >> on; setOn(on); output()->setValue(on); } void InputSwitch::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { if (!on) { m_pixmapSkinName[0] = ":/input/switchOff.png"; setPixmap(m_pixmapSkinName[0]); } else { m_pixmapSkinName[1] = ":/input/switchOn.png"; setPixmap(m_pixmapSkinName[1]); } } else { if (!on) { m_pixmapSkinName[0] = filename; setPixmap(m_pixmapSkinName[0]); } else { m_pixmapSkinName[1] = filename; setPixmap(m_pixmapSkinName[1]); } } } wiRedPanda-3.0.1/app/element/inputswitch.h000066400000000000000000000017151406216046500205040ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef INPUTSWITCH_H #define INPUTSWITCH_H #include "graphicelement.h" #include "input.h" class InputSwitch : public GraphicElement, public Input { public: explicit InputSwitch(QGraphicsItem *parent = nullptr); ~InputSwitch() override = default; static int current_id_number; // Number used to create distinct labels for each instance of this element. bool on; /* QGraphicsItem interface */ protected: void mousePressEvent(QGraphicsSceneMouseEvent *event) override; /* GraphicElement interface */ public: void save(QDataStream &ds) const override; void load(QDataStream &ds, QMap &portMap, double version) override; bool getOn() const override; void setOn(bool value) override; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* INPUTSWITCH_H */ wiRedPanda-3.0.1/app/element/inputvcc.cpp000066400000000000000000000013621406216046500203070ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "inputvcc.h" #include "qneport.h" InputVcc::InputVcc(QGraphicsItem *parent) : GraphicElement(ElementType::VCC, ElementGroup::STATICINPUT, 0, 0, 1, 1, parent) { m_pixmapSkinName = {":/input/1.png"}; setOutputsOnTop(false); setCanChangeSkin(true); setPixmap(m_pixmapSkinName[0]); setRotatable(false); setPortName("VCC"); m_outputs.first()->setValue(true); } void InputVcc::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { m_pixmapSkinName[0] = ":/input/1.png"; } else { m_pixmapSkinName[0] = filename; } setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/element/inputvcc.h000066400000000000000000000006601406216046500177540ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef INPUTVCC_H #define INPUTVCC_H #include "graphicelement.h" class InputVcc : public GraphicElement { public: explicit InputVcc(QGraphicsItem *parent = nullptr); ~InputVcc() override = default; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* INPUTVCC_H */ wiRedPanda-3.0.1/app/element/jkflipflop.cpp000066400000000000000000000033401406216046500206120ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "jkflipflop.h" #include "qneport.h" JKFlipFlop::JKFlipFlop(QGraphicsItem *parent) : GraphicElement(ElementType::JKFLIPFLOP, ElementGroup::MEMORY, 5, 5, 2, 2, parent) { m_pixmapSkinName = {":/memory/JK-flipflop.png"}; setPixmap(m_pixmapSkinName[0]); setRotatable(false); setCanChangeSkin(true); updatePorts(); lastClk = false; setPortName("FlipFlop JK"); input(0)->setName("J"); input(1)->setName("Clock"); input(2)->setName("K"); input(3)->setName("~Preset"); input(4)->setName("~Clear"); output(0)->setName("Q"); output(1)->setName("~Q"); output(0)->setDefaultValue(0); output(1)->setDefaultValue(1); input(0)->setRequired(false); /* J */ input(2)->setRequired(false); /* K */ input(3)->setRequired(false); /* p */ input(4)->setRequired(false); /* c */ input(0)->setDefaultValue(1); input(2)->setDefaultValue(1); input(3)->setDefaultValue(1); input(4)->setDefaultValue(1); } void JKFlipFlop::updatePorts() { input(0)->setPos(topPosition(), 13); /* J */ input(1)->setPos(topPosition(), 29); /* Clk */ input(2)->setPos(topPosition(), 45); /* K */ input(3)->setPos(32, topPosition()); /* Preset */ input(4)->setPos(32, bottomPosition()); /* Clear */ output(0)->setPos(bottomPosition(), 15); /* Q */ output(1)->setPos(bottomPosition(), 45); /* ~Q */ } void JKFlipFlop::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { m_pixmapSkinName[0] = ":/memory/JK-flipflop.png"; } else { m_pixmapSkinName[0] = filename; } setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/element/jkflipflop.h000066400000000000000000000007611406216046500202630ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef JKFLIPFLOP_H #define JKFLIPFLOP_H #include "graphicelement.h" class JKFlipFlop : public GraphicElement { bool lastClk; public: explicit JKFlipFlop(QGraphicsItem *parent = nullptr); ~JKFlipFlop() override = default; void updatePorts() override; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* JKFLIPFLOP_H */ wiRedPanda-3.0.1/app/element/jklatch.cpp000066400000000000000000000012321406216046500200700ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "jklatch.h" #include "qneport.h" JKLatch::JKLatch(QGraphicsItem *parent) : GraphicElement(ElementType::JKLATCH, ElementGroup::MEMORY, 2, 2, 2, 2, parent) { setPixmap(":/memory/JK-latch.png"); setRotatable(false); setCanChangeSkin(true); updatePorts(); setCanChangeSkin(true); setPortName("JK Latch"); } void JKLatch::updatePorts() { input(0)->setPos(topPosition(), 13); input(1)->setPos(topPosition(), 45); output(0)->setPos(bottomPosition(), 15); output(1)->setPos(bottomPosition(), 45); } wiRedPanda-3.0.1/app/element/jklatch.h000066400000000000000000000006611406216046500175420ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef JKLATCH_H #define JKLATCH_H #include "graphicelement.h" class JKLatch : public GraphicElement { public: explicit JKLatch(QGraphicsItem *parent = nullptr); ~JKLatch() override = default; /* GraphicElement interface */ public: void updatePorts() override; }; #endif /* JKLATCH_H */ wiRedPanda-3.0.1/app/element/led.cpp000066400000000000000000000172101406216046500172170ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "led.h" #include "qneport.h" #include int Led::current_id_number = 0; /* Color pallets: * 2 bit - 00: dark gray, 01: light blue, 10: light green, 11: light red * 3 bit - 000: dark gray, 001: light blue, 010: light green, 011: yellow, 100: light red, 101: magenta, 110: cyan, 111: white * 4 bit - 0000: black, 0001: blue, 0010: green, 0011: teal, 0100: red, 0101: magenta, 0110: orange, 0111: light gray * 1000: dark gray, 1001: light blue, 1010: light green, 1011: cyan, 1100: light red, 1101: pink, 1110: yellow, 1111: white */ Led::Led(QGraphicsItem *parent) : GraphicElement(ElementType::LED, ElementGroup::OUTPUT, 1, 4, 0, 0, parent) { m_pixmapSkinName = { ":/output/WhiteLedOff.png", // Single input values: 0 ":/output/WhiteLedOn.png", // 1 ":/output/RedLedOff.png", // 2 ":/output/RedLedOn.png", // 3 ":/output/GreenLedOff.png", // 4 ":/output/GreenLedOn.png", // 5 ":/output/BlueLedOff.png", // 6 ":/output/BlueLedOn.png", // 7 ":/output/PurpleLedOff.png", // 8 ":/output/PurpleLedOn.png", // 9 ":/output/16colors/BlackLedOn.png", // Multiple input values: 10 ":/output/16colors/NavyBlueLedOn.png", // 11 ":/output/16colors/GreenLedOn.png", // 12 ":/output/16colors/TealLedOn.png", // 13 ":/output/16colors/DarkRedLedOn.png", // 14 ":/output/16colors/MagentaLedOn.png", // 15 ":/output/16colors/OrangeLedOn.png", // 16 ":/output/16colors/LightGrayLedOn.png", // 17 ":/output/16colors/DarkGrayLedOn.png", // 18 ":/output/16colors/RoyalLedOn.png", // 19 ":/output/16colors/LimeGreenLedOn.png", // 20 ":/output/16colors/AquaLightLedOn.png", // 21 ":/output/16colors/RedLedOn.png", // 22 ":/output/16colors/HotPinkLedOn.png", // 23 ":/output/16colors/YellowLedOn.png", // 24 ":/output/16colors/WhiteLedOn.png", // 25 }; setOutputsOnTop(true); setRotatable(false); setHasColors(true); setColor("White"); setPixmap(m_pixmapSkinName[0]); updatePorts(); setHasLabel(true); setCanChangeSkin(true); setPortName("Led"); } void Led::refresh() { int idx = 0; if (isValid()) { std::bitset<4> index; for (int i = 0; i < inputSize(); ++i) { index[i] = input(inputSize() - i - 1)->value(); } idx = index.to_ulong(); } switch (inputSize()) { case 1: { /* 1 bit */ setPixmap(m_pixmapSkinName[m_colorNumber + idx]); break; } case 2: { /* 2 bits */ // TODO: add option to select dark/light colors according to the theme. if (idx == 3) { setPixmap(m_pixmapSkinName[22]); } else { setPixmap(m_pixmapSkinName[18 + idx]); } break; } case 3: { /* 3 bits */ // TODO: add option to select dark/light colors according to the theme. setPixmap(m_pixmapSkinName[18 + idx]); break; } case 4: { /* 4 bits */ setPixmap(m_pixmapSkinName[10 + idx]); break; } } } void Led::setColor(const QString &color) { m_color = color; if (color == "White") { m_colorNumber = 0; } else if (color == "Red") { m_colorNumber = 2; } else if (color == "Green") { m_colorNumber = 4; } else if (color == "Blue") { m_colorNumber = 6; } else if (color == "Purple") { m_colorNumber = 8; } refresh(); } QString Led::getColor() const { return m_color; } void Led::save(QDataStream &ds) const { GraphicElement::save(ds); ds << getColor(); } void Led::load(QDataStream &ds, QMap &portMap, double version) { GraphicElement::load(ds, portMap, version); if (version >= 1.1) { QString clr; ds >> clr; setColor(clr); } } QString Led::genericProperties() { return getColor(); } void Led::updatePorts() { setHasColors(inputSize() == 1); GraphicElement::updatePorts(); } void Led::setSkin(bool defaultSkin, const QString &filename) { int idx = 0; if (isValid()) { std::bitset<4> index; for (int i = 0; i < inputSize(); ++i) { index[i] = input(inputSize() - i - 1)->value(); } idx = index.to_ulong(); } int value_idx = 0; switch (inputSize()) { case 1: /* 1 bit */ value_idx = m_colorNumber + idx; break; case 2: /* 2 bits */ // TODO: add option to select dark/light colors according to the theme. value_idx = idx == 3 ? 22 : 18 + idx; break; case 3: /* 3 bits */ // TODO: add option to select dark/light colors according to the theme. value_idx = 18 + idx; break; case 4: /* 4 bits */ value_idx = 10 + idx; break; } if (defaultSkin) { resetLedPixmapName(value_idx); } else { m_pixmapSkinName[value_idx] = filename; } setPixmap(m_pixmapSkinName[value_idx]); } void Led::resetLedPixmapName(int ledNumber) { switch (ledNumber) { case 0: m_pixmapSkinName[0] = ":/output/WhiteLedOff.png"; break; case 1: m_pixmapSkinName[1] = ":/output/WhiteLedOn.png"; break; case 2: m_pixmapSkinName[2] = ":/output/RedLedOff.png"; break; case 3: m_pixmapSkinName[3] = ":/output/RedLedOn.png"; break; case 4: m_pixmapSkinName[4] = ":/output/GreenLedOff.png"; break; case 5: m_pixmapSkinName[5] = ":/output/GreenLedOn.png"; break; case 6: m_pixmapSkinName[6] = ":/output/BlueLedOff.png"; break; case 7: m_pixmapSkinName[7] = ":/output/BlueLedOn.png"; break; case 8: m_pixmapSkinName[8] = ":/output/PurpleLedOff.png"; break; case 9: m_pixmapSkinName[9] = ":/output/PurpleLedOn.png"; break; case 10: m_pixmapSkinName[10] = ":/output/16colors/BlackLedOn.png"; break; case 11: m_pixmapSkinName[11] = ":/output/16colors/NavyBlueLedOn.png"; break; case 12: m_pixmapSkinName[12] = ":/output/16colors/GreenLedOn.png"; break; case 13: m_pixmapSkinName[13] = ":/output/16colors/TealLedOn.png"; break; case 14: m_pixmapSkinName[14] = ":/output/16colors/DarkRedLedOn.png"; break; case 15: m_pixmapSkinName[15] = ":/output/16colors/MagentaLedOn.png"; break; case 16: m_pixmapSkinName[16] = ":/output/16colors/OrangeLedOn.png"; break; case 17: m_pixmapSkinName[17] = ":/output/16colors/LightGrayLedOn.png"; break; case 18: m_pixmapSkinName[18] = ":/output/16colors/DarkGrayLedOn.png"; break; case 19: m_pixmapSkinName[19] = ":/output/16colors/RoyalLedOn.png"; break; case 20: m_pixmapSkinName[20] = ":/output/16colors/LimeGreenLedOn.png"; break; case 21: m_pixmapSkinName[21] = ":/output/16colors/AquaLightLedOn.png"; break; case 22: m_pixmapSkinName[22] = ":/output/16colors/RedLedOn.png"; break; case 23: m_pixmapSkinName[23] = ":/output/16colors/HotPinkLedOn.png"; break; case 24: m_pixmapSkinName[24] = ":/output/16colors/YellowLedOn.png"; break; case 25: m_pixmapSkinName[25] = ":/output/16colors/WhiteLedOn.png"; break; } } wiRedPanda-3.0.1/app/element/led.h000066400000000000000000000021021406216046500166560ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LED_H #define LED_H #include "graphicelement.h" class Led : public GraphicElement { public: explicit Led(QGraphicsItem *parent = nullptr); ~Led() override = default; static int current_id_number; // Number used to create distinct labels for each instance of this element. void refresh() override; void setColor(const QString &getColor) override; QString getColor() const override; private: QString m_color; int m_colorNumber; /* white = 0, red = 2, green = 4, blue = 6, purple = 8 */ /* GraphicElement interface */ void resetLedPixmapName(int ledNumber); public: void save(QDataStream &ds) const override; void load(QDataStream &ds, QMap &portMap, double version) override; QString genericProperties() override; // GraphicElement interface public: void updatePorts() override; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* LED_H */ wiRedPanda-3.0.1/app/element/mux.cpp000066400000000000000000000016771406216046500172760ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "mux.h" #include "qneport.h" Mux::Mux(QGraphicsItem *parent) : GraphicElement(ElementType::MUX, ElementGroup::MUX, 3, 3, 1, 1, parent) { m_pixmapSkinName = {":/basic/mux.png"}; setPixmap(m_pixmapSkinName[0]); setRotatable(true); updatePorts(); setPortName("MUX"); setCanChangeSkin(true); input(0)->setName("0"); input(1)->setName("1"); input(2)->setName("S"); } void Mux::updatePorts() { input(0)->setPos(32 - 12, 48); /* 0 */ input(1)->setPos(32 + 12, 48); /* 1 */ input(2)->setPos(58, 32); /* S */ output(0)->setPos(32, 16); /* Out */ } void Mux::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { m_pixmapSkinName[0] = ":/basic/mux.png"; } else { m_pixmapSkinName[0] = filename; } setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/element/mux.h000066400000000000000000000007631406216046500167360ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef MUX_H #define MUX_H #include "graphicelement.h" #include class Mux : public GraphicElement { public: explicit Mux(QGraphicsItem *parent = nullptr); ~Mux() override = default; /* GraphicElement interface */ public: void updatePorts() override; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* MUX_H */ wiRedPanda-3.0.1/app/element/nand.cpp000066400000000000000000000012731406216046500173750ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "nand.h" Nand::Nand(QGraphicsItem *parent) : GraphicElement(ElementType::NAND, ElementGroup::GATE, 2, 8, 1, 1, parent) { m_pixmapSkinName = {":/basic/nand.png"}; setOutputsOnTop(true); setPixmap(m_pixmapSkinName[0]); setCanChangeSkin(true); updatePorts(); setCanChangeSkin(true); setPortName("NAND"); } void Nand::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { m_pixmapSkinName[0] = ":/basic/nand.png"; } else { m_pixmapSkinName[0] = filename; } setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/element/nand.h000066400000000000000000000006311406216046500170370ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef NAND_H #define NAND_H #include "graphicelement.h" class Nand : public GraphicElement { public: explicit Nand(QGraphicsItem *parent = nullptr); ~Nand() override = default; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* NAND_H */ wiRedPanda-3.0.1/app/element/node.cpp000066400000000000000000000014711406216046500174020ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "node.h" #include "qneport.h" Node::Node(QGraphicsItem *parent) : GraphicElement(ElementType::NODE, ElementGroup::GATE, 1, 1, 1, 1, parent) { m_pixmapSkinName = {":/basic/node.png"}; setPixmap(m_pixmapSkinName[0], QRect(QPoint(16, 16), QPoint(48, 48))); updatePorts(); setCanChangeSkin(true); setPortName("NODE"); input()->setRequired(true); } void Node::updatePorts() { input()->setPos(0, 16); output()->setPos(32, 16); } void Node::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { m_pixmapSkinName[0] = ":/basic/node.png"; } else { m_pixmapSkinName[0] = filename; } setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/element/node.h000066400000000000000000000006721406216046500170510ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef NODE_H #define NODE_H #include "graphicelement.h" class Node : public GraphicElement { public: explicit Node(QGraphicsItem *parent = nullptr); ~Node() override = default; void updatePorts() override; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* NODE_H */ wiRedPanda-3.0.1/app/element/nor.cpp000066400000000000000000000012261406216046500172510ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "nor.h" Nor::Nor(QGraphicsItem *parent) : GraphicElement(ElementType::NOR, ElementGroup::GATE, 2, 8, 1, 1, parent) { m_pixmapSkinName = {":/basic/nor.png"}; setOutputsOnTop(true); setPixmap(m_pixmapSkinName[0]); setCanChangeSkin(true); updatePorts(); setPortName("NOR"); } void Nor::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { m_pixmapSkinName[0] = ":/basic/nor.png"; } else { m_pixmapSkinName[0] = filename; } setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/element/nor.h000066400000000000000000000006231406216046500167160ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef NOR_H #define NOR_H #include "graphicelement.h" class Nor : public GraphicElement { public: explicit Nor(QGraphicsItem *parent = nullptr); ~Nor() override = default; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* NOR_H */ wiRedPanda-3.0.1/app/element/not.cpp000066400000000000000000000012261406216046500172530ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "not.h" Not::Not(QGraphicsItem *parent) : GraphicElement(ElementType::NOT, ElementGroup::GATE, 1, 1, 1, 1, parent) { m_pixmapSkinName = {":/basic/not.png"}; setOutputsOnTop(true); setPixmap(m_pixmapSkinName[0]); setCanChangeSkin(true); updatePorts(); setPortName("NOT"); } void Not::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { m_pixmapSkinName[0] = ":/basic/not.png"; } else { m_pixmapSkinName[0] = filename; } setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/element/not.h000066400000000000000000000006221406216046500167170ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef NOT_H #define NOT_H #include "graphicelement.h" class Not : public GraphicElement { public: explicit Not(QGraphicsItem *parent = nullptr); ~Not() override = default; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* NOT_H */ wiRedPanda-3.0.1/app/element/or.cpp000066400000000000000000000012171406216046500170730ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "or.h" Or::Or(QGraphicsItem *parent) : GraphicElement(ElementType::OR, ElementGroup::GATE, 2, 8, 1, 1, parent) { m_pixmapSkinName = {":/basic/or.png"}; setOutputsOnTop(true); setPixmap(m_pixmapSkinName[0]); setCanChangeSkin(true); updatePorts(); setPortName("OR"); } void Or::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { m_pixmapSkinName[0] = ":/basic/or.png"; } else { m_pixmapSkinName[0] = filename; } setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/element/or.h000066400000000000000000000006151406216046500165410ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef OR_H #define OR_H #include "graphicelement.h" class Or : public GraphicElement { public: explicit Or(QGraphicsItem *parent = nullptr); ~Or() override = default; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* OR_H */ wiRedPanda-3.0.1/app/element/srflipflop.cpp000066400000000000000000000032341406216046500206340ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "srflipflop.h" #include "qneport.h" SRFlipFlop::SRFlipFlop(QGraphicsItem *parent) : GraphicElement(ElementType::SRFLIPFLOP, ElementGroup::MEMORY, 5, 5, 2, 2, parent) { m_pixmapSkinName = {":/memory/SR-flipflop.png"}; setPixmap(m_pixmapSkinName[0]); setRotatable(false); setCanChangeSkin(true); updatePorts(); lastClk = false; setPortName("FlipFlop SR"); input(0)->setName("S"); input(1)->setName("Clock"); input(2)->setName("R"); input(3)->setName("~Preset"); input(4)->setName("~Clear"); output(0)->setName("Q"); output(1)->setName("~Q"); output(0)->setDefaultValue(0); output(1)->setDefaultValue(1); input(0)->setRequired(false); /* S */ input(2)->setRequired(false); /* R */ input(3)->setRequired(false); /* p */ input(4)->setRequired(false); /* c */ input(3)->setDefaultValue(1); input(4)->setDefaultValue(1); } void SRFlipFlop::updatePorts() { input(0)->setPos(topPosition(), 13); /* S */ input(1)->setPos(topPosition(), 29); /* Clk */ input(2)->setPos(topPosition(), 45); /* R */ input(3)->setPos(32, topPosition()); /* Preset */ input(4)->setPos(32, bottomPosition()); /* Clear */ output(0)->setPos(bottomPosition(), 15); /* Q */ output(1)->setPos(bottomPosition(), 45); /* ~Q */ } void SRFlipFlop::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { m_pixmapSkinName[0] = ":/memory/SR-flipflop.png"; } else { m_pixmapSkinName[0] = filename; } setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/element/srflipflop.h000066400000000000000000000010241406216046500202740ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef SRFLIPFLOP_H #define SRFLIPFLOP_H #include "graphicelement.h" class SRFlipFlop : public GraphicElement { bool lastClk; public: explicit SRFlipFlop(QGraphicsItem *parent = nullptr); ~SRFlipFlop() override = default; /* GraphicElement interface */ void updatePorts() override; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* SRFLIPFLOP_H */ wiRedPanda-3.0.1/app/element/tflipflop.cpp000066400000000000000000000030451406216046500204530ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "tflipflop.h" #include "qneport.h" TFlipFlop::TFlipFlop(QGraphicsItem *parent) : GraphicElement(ElementType::TFLIPFLOP, ElementGroup::MEMORY, 4, 4, 2, 2, parent) { m_pixmapSkinName = {":/memory/T-flipflop.png"}; setPixmap(m_pixmapSkinName[0]); setRotatable(false); setCanChangeSkin(true); updatePorts(); lastClk = false; lastT = 0; lastQ = 0; setPortName("FlipFlop T"); input(0)->setName("T"); input(1)->setName("Clock"); input(2)->setName("~Preset"); input(3)->setName("~Clear"); output(0)->setName("Q"); output(1)->setName("~Q"); output(0)->setDefaultValue(0); output(1)->setDefaultValue(1); input(0)->setRequired(false); input(2)->setRequired(false); input(3)->setRequired(false); input(2)->setDefaultValue(1); input(3)->setDefaultValue(1); } void TFlipFlop::updatePorts() { input(0)->setPos(topPosition(), 13); /* T */ input(1)->setPos(topPosition(), 45); /* Clock */ input(2)->setPos(32, topPosition()); /* Preset */ input(3)->setPos(32, bottomPosition()); /* Clear */ output(0)->setPos(bottomPosition(), 15); /* Q */ output(1)->setPos(bottomPosition(), 45); /* ~Q */ } void TFlipFlop::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { m_pixmapSkinName[0] = ":/memory/T-flipflop.png"; } else { m_pixmapSkinName[0] = filename; } setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/element/tflipflop.h000066400000000000000000000010301406216046500201100ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef TFLIPFLOP_H #define TFLIPFLOP_H #include "graphicelement.h" class TFlipFlop : public GraphicElement { bool lastClk; signed char lastT; signed char lastQ; public: explicit TFlipFlop(QGraphicsItem *parent = nullptr); ~TFlipFlop() override = default; void updatePorts() override; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* TFLIPFLOP_H */ wiRedPanda-3.0.1/app/element/xnor.cpp000066400000000000000000000012371406216046500174430ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "xnor.h" Xnor::Xnor(QGraphicsItem *parent) : GraphicElement(ElementType::XNOR, ElementGroup::GATE, 2, 8, 1, 1, parent) { m_pixmapSkinName = {":/basic/xnor.png"}; setOutputsOnTop(true); setCanChangeSkin(true); setPixmap(m_pixmapSkinName[0]); updatePorts(); setPortName("XNOR"); } void Xnor::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { m_pixmapSkinName[0] = ":/basic/xnor.png"; } else { m_pixmapSkinName[0] = filename; } setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/element/xnor.h000066400000000000000000000006301406216046500171040ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef XNOR_H #define XNOR_H #include "graphicelement.h" class Xnor : public GraphicElement { public: explicit Xnor(QGraphicsItem *parent = nullptr); ~Xnor() override = default; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* Xnor_H */ wiRedPanda-3.0.1/app/element/xor.cpp000066400000000000000000000012321406216046500172600ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "xor.h" Xor::Xor(QGraphicsItem *parent) : GraphicElement(ElementType::XOR, ElementGroup::GATE, 2, 8, 1, 1, parent) { m_pixmapSkinName.append(":/basic/xor.png"); setOutputsOnTop(true); setCanChangeSkin(true); setPixmap(m_pixmapSkinName[0]); updatePorts(); setPortName("XOR"); } void Xor::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { m_pixmapSkinName[0] = ":/basic/xor.png"; } else { m_pixmapSkinName[0] = filename; } setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/element/xor.h000066400000000000000000000006231406216046500167300ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef XOR_H #define XOR_H #include "graphicelement.h" class Xor : public GraphicElement { public: explicit Xor(QGraphicsItem *parent = nullptr); ~Xor() override = default; void setSkin(bool defaultSkin, const QString &filename) override; }; #endif /* XOR_H */ wiRedPanda-3.0.1/app/elementeditor.cpp000066400000000000000000000517511406216046500176720ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "elementeditor.h" #include #include #include #include #include #include "commands.h" #include "editor.h" #include "elementfactory.h" #include "ui_elementeditor.h" #include "scene.h" ElementEditor::ElementEditor(QWidget *parent) : QWidget(parent) , m_ui(new Ui::ElementEditor) { m_manyLabels = tr(""); m_manyColors = tr(""); m_manyIS = tr(""); m_manyFreq = tr(""); m_manyTriggers = tr(""); m_manyAudios = tr(""); m_defaultSkin = true; m_updatingSkin = false; m_ui->setupUi(this); setEnabled(false); setVisible(false); m_ui->lineEditTrigger->setValidator(new QRegExpValidator(QRegExp("[a-z]| |[A-Z]|[0-9]"), this)); fillColorComboBox(); m_ui->lineEditElementLabel->installEventFilter(this); m_ui->lineEditTrigger->installEventFilter(this); m_ui->comboBoxColor->installEventFilter(this); m_ui->comboBoxInputSz->installEventFilter(this); m_ui->doubleSpinBoxFrequency->installEventFilter(this); m_ui->comboBoxAudio->installEventFilter(this); connect(m_ui->lineEditElementLabel, &QLineEdit::editingFinished, this, &ElementEditor::apply); connect(m_ui->doubleSpinBoxFrequency, &QDoubleSpinBox::editingFinished, this, &ElementEditor::apply); connect(m_ui->lineEditTrigger, &QLineEdit::editingFinished, this, &ElementEditor::apply); connect(m_ui->lineEditTrigger, &QLineEdit::textChanged, this, &ElementEditor::triggerChanged); connect(m_ui->pushButtonChangeSkin, &QPushButton::clicked, this, &ElementEditor::updateElementSkin); connect(m_ui->pushButtonDefaultSkin, &QPushButton::clicked, this, &ElementEditor::defaultSkin); connect(m_ui->comboBoxColor, QOverload::of(&QComboBox::currentIndexChanged), this, &ElementEditor::apply); connect(m_ui->comboBoxAudio, QOverload::of(&QComboBox::currentIndexChanged), this, &ElementEditor::apply); connect(m_ui->comboBoxInputSz, QOverload::of(&QComboBox::currentIndexChanged), this, &ElementEditor::inputIndexChanged); } ElementEditor::~ElementEditor() { delete m_ui; } void ElementEditor::setScene(Scene *s) { m_scene = s; connect(s, &QGraphicsScene::selectionChanged, this, &ElementEditor::selectionChanged); } QAction *addElementAction(QMenu *menu, GraphicElement *firstElm, ElementType type, bool hasSameType) { if (!hasSameType || (firstElm->elementType() != type)) { QAction *action = menu->addAction(QIcon(ElementFactory::getPixmap(type)), ElementFactory::translatedName(type)); action->setData(static_cast(type)); return action; } return nullptr; } void ElementEditor::contextMenu(QPoint screenPos) { QMenu menu; QString renameActionText(tr("Rename")); QString rotateActionText(tr("Rotate")); QString freqActionText(tr("Change frequency")); QString colorMenuText(tr("Change color to...")); QString changeSkinText(tr("Change skin to ...")); QString revertSkinText(tr("Set skin to default")); QString triggerActionText(tr("Change trigger")); QString morphMenuText(tr("Morph to...")); if (m_hasLabel) { menu.addAction(QIcon(QPixmap(":/toolbar/rename.png")), renameActionText)->setData(renameActionText); } if (m_hasTrigger) { menu.addAction(QIcon(ElementFactory::getPixmap(ElementType::BUTTON)), triggerActionText)->setData(triggerActionText); } if (m_canChangeSkin) { menu.addAction(changeSkinText); menu.addAction(revertSkinText); } if (m_hasRotation) { menu.addAction(QIcon(QPixmap(":/toolbar/rotateR.png")), rotateActionText)->setData(rotateActionText); } if (m_hasFrequency) { menu.addAction(QIcon(ElementFactory::getPixmap(ElementType::CLOCK)), freqActionText)->setData(freqActionText); } QMenu *submenucolors = nullptr; if (m_hasColors) { submenucolors = menu.addMenu(colorMenuText); for (int i = 0; i < m_ui->comboBoxColor->count(); ++i) { if (m_ui->comboBoxColor->currentIndex() != i) { submenucolors->addAction(m_ui->comboBoxColor->itemIcon(i), m_ui->comboBoxColor->itemText(i)); } } } QMenu *submenumorph = nullptr; if (m_canMorph) { submenumorph = menu.addMenu(morphMenuText); GraphicElement *firstElm = m_elements.front(); switch (firstElm->elementGroup()) { case ElementGroup::GATE: { if (firstElm->inputSize() == 1) { addElementAction(submenumorph, firstElm, ElementType::NOT, m_hasSameType); addElementAction(submenumorph, firstElm, ElementType::NODE, m_hasSameType); } else { addElementAction(submenumorph, firstElm, ElementType::AND, m_hasSameType); addElementAction(submenumorph, firstElm, ElementType::OR, m_hasSameType); addElementAction(submenumorph, firstElm, ElementType::NAND, m_hasSameType); addElementAction(submenumorph, firstElm, ElementType::NOR, m_hasSameType); addElementAction(submenumorph, firstElm, ElementType::XOR, m_hasSameType); addElementAction(submenumorph, firstElm, ElementType::XNOR, m_hasSameType); } break; } case ElementGroup::STATICINPUT: case ElementGroup::INPUT: { addElementAction(submenumorph, firstElm, ElementType::BUTTON, m_hasSameType); addElementAction(submenumorph, firstElm, ElementType::SWITCH, m_hasSameType); addElementAction(submenumorph, firstElm, ElementType::CLOCK, m_hasSameType); addElementAction(submenumorph, firstElm, ElementType::VCC, m_hasSameType); addElementAction(submenumorph, firstElm, ElementType::GND, m_hasSameType); break; } case ElementGroup::MEMORY: { if (firstElm->inputSize() == 2) { // addElementAction( submenumorph, firstElm, ElementType::TLATCH, hasSameType ); addElementAction(submenumorph, firstElm, ElementType::DLATCH, m_hasSameType); addElementAction(submenumorph, firstElm, ElementType::JKLATCH, m_hasSameType); } else if (firstElm->inputSize() == 4) { addElementAction(submenumorph, firstElm, ElementType::DFLIPFLOP, m_hasSameType); addElementAction(submenumorph, firstElm, ElementType::TFLIPFLOP, m_hasSameType); } break; } case ElementGroup::OUTPUT: { addElementAction(submenumorph, firstElm, ElementType::LED, m_hasSameType); addElementAction(submenumorph, firstElm, ElementType::BUZZER, m_hasSameType); break; } case ElementGroup::IC: case ElementGroup::MUX: case ElementGroup::OTHER: case ElementGroup::UNKNOWN: break; } if (submenumorph->actions().size() == 0) { menu.removeAction(submenumorph->menuAction()); } } menu.addSeparator(); if (m_hasElements) { QAction *copyAction = menu.addAction(QIcon(QPixmap(":/toolbar/copy.png")), tr("Copy")); QAction *cutAction = menu.addAction(QIcon(QPixmap(":/toolbar/cut.png")), tr("Cut")); connect(copyAction, &QAction::triggered, m_editor, &Editor::copyAction); connect(cutAction, &QAction::triggered, m_editor, &Editor::cutAction); } QAction *deleteAction = menu.addAction(QIcon(QPixmap(":/toolbar/delete.png")), tr("Delete")); connect(deleteAction, &QAction::triggered, m_editor, &Editor::deleteAction); QAction *a = menu.exec(screenPos); if (a) { if (a->data().toString() == renameActionText) { renameAction(); } else if (a->data().toString() == rotateActionText) { emit sendCommand(new RotateCommand(m_elements.toList(), 90.0)); } else if (a->data().toString() == triggerActionText) { changeTriggerAction(); } else if (a->text() == changeSkinText) { // Reads a new sprite and applies it to the element updateElementSkin(); } else if (a->text() == revertSkinText) { // Reset the icon to its default m_defaultSkin = true; m_updatingSkin = true; apply(); } else if (a->data().toString() == freqActionText) { m_ui->doubleSpinBoxFrequency->setFocus(); } else if (submenumorph && submenumorph->actions().contains(a)) { ElementType type = static_cast(a->data().toInt()); if (type != ElementType::UNKNOWN) { emit sendCommand(new MorphCommand(m_elements, type, m_editor)); } } else if (submenucolors && submenucolors->actions().contains(a)) { m_ui->comboBoxColor->setCurrentText(a->text()); } else { fprintf(stderr, "In elementeditor: uncaught text \"%s\"\n", a->text().toStdString().c_str()); } } } void ElementEditor::renameAction() { m_ui->lineEditElementLabel->setFocus(); m_ui->lineEditElementLabel->selectAll(); } void ElementEditor::changeTriggerAction() { m_ui->lineEditTrigger->setFocus(); m_ui->lineEditTrigger->selectAll(); } void ElementEditor::updateElementSkin() { const QString homeDir = QDir::homePath(); QString fname = QFileDialog::getOpenFileName(this, tr("Open File"), homeDir, tr("Images (*.png *.gif *.jpg)")); if (fname.isEmpty()) { return; } m_updatingSkin = true; m_skinName = fname; m_defaultSkin = false; apply(); } void ElementEditor::fillColorComboBox() { m_ui->comboBoxColor->clear(); m_ui->comboBoxColor->addItem(QIcon(QPixmap(":/output/GreenLedOn.png")), tr("Green"), "Green"); m_ui->comboBoxColor->addItem(QIcon(QPixmap(":/output/BlueLedOn.png")), tr("Blue"), "Blue"); m_ui->comboBoxColor->addItem(QIcon(QPixmap(":/output/PurpleLedOn.png")), tr("Purple"), "Purple"); m_ui->comboBoxColor->addItem(QIcon(QPixmap(":/output/RedLedOn.png")), tr("Red"), "Red"); m_ui->comboBoxColor->addItem(QIcon(QPixmap(":/output/WhiteLedOn.png")), tr("White"), "White"); } void ElementEditor::retranslateUi() { m_ui->retranslateUi(this); fillColorComboBox(); } void ElementEditor::setCurrentElements(const QVector &elms) { m_elements = elms; m_hasLabel = m_hasColors = m_hasFrequency = m_canChangeInputSize = m_hasTrigger = m_hasAudio = false; m_hasRotation = m_hasSameLabel = m_hasSameColors = m_hasSameFrequency = m_hasSameAudio = false; m_hasSameInputSize = m_hasSameTrigger = m_canMorph = m_hasSameType = false; m_hasElements = false; if (!elms.isEmpty()) { m_hasLabel = m_hasColors = m_hasAudio = m_hasFrequency = m_canChangeInputSize = m_hasTrigger = true; m_hasRotation = m_canChangeSkin = true; setVisible(true); setEnabled(false); int minimum = 0, maximum = 100000000; m_hasSameLabel = m_hasSameColors = m_hasSameFrequency = true; m_hasSameInputSize = m_hasSameTrigger = m_canMorph = true; m_hasSameAudio = true; m_hasSameType = true; m_hasElements = true; GraphicElement *firstElement = m_elements.front(); for (GraphicElement *elm : qAsConst(m_elements)) { m_hasLabel &= elm->hasLabel(); m_canChangeSkin &= elm->canChangeSkin(); m_hasColors &= elm->hasColors(); m_hasAudio &= elm->hasAudio(); m_hasFrequency &= elm->hasFrequency(); minimum = std::max(minimum, elm->minInputSz()); maximum = std::min(maximum, elm->maxInputSz()); m_hasTrigger &= elm->hasTrigger(); m_hasRotation &= elm->rotatable(); m_hasSameLabel &= elm->getLabel() == firstElement->getLabel(); m_hasSameColors &= elm->getColor() == firstElement->getColor(); m_hasSameFrequency &= qFuzzyCompare(elm->getFrequency(), firstElement->getFrequency()); m_hasSameInputSize &= elm->inputSize() == firstElement->inputSize(); m_hasSameTrigger &= elm->getTrigger() == firstElement->getTrigger(); m_hasSameType &= elm->elementType() == firstElement->elementType(); m_hasSameAudio &= elm->getAudio() == firstElement->getAudio(); m_canMorph &= elm->inputSize() == firstElement->inputSize(); m_canMorph &= elm->outputSize() == firstElement->outputSize(); bool sameElementGroup = elm->elementGroup() == firstElement->elementGroup(); sameElementGroup |= (elm->elementGroup() == ElementGroup::INPUT && firstElement->elementGroup() == ElementGroup::STATICINPUT); sameElementGroup |= (elm->elementGroup() == ElementGroup::STATICINPUT && firstElement->elementGroup() == ElementGroup::INPUT); m_canMorph &= sameElementGroup; } m_canChangeInputSize = (minimum < maximum); /* Labels */ m_ui->lineEditElementLabel->setVisible(m_hasLabel); m_ui->lineEditElementLabel->setEnabled(m_hasLabel); m_ui->label_labels->setVisible(m_hasLabel); if (m_hasLabel) { if (m_hasSameLabel) { m_ui->lineEditElementLabel->setText(firstElement->getLabel()); } else { m_ui->lineEditElementLabel->setText(m_manyLabels); } } /* Color */ m_ui->label_color->setVisible(m_hasColors); m_ui->comboBoxColor->setVisible(m_hasColors); m_ui->comboBoxColor->setEnabled(m_hasColors); if (m_ui->comboBoxColor->findText(m_manyColors) == -1) { m_ui->comboBoxColor->addItem(m_manyColors); } if (m_hasColors) { if (m_hasSameColors) { m_ui->comboBoxColor->removeItem(m_ui->comboBoxColor->findText(m_manyColors)); m_ui->comboBoxColor->setCurrentIndex(m_ui->comboBoxColor->findData(firstElement->getColor())); } else { m_ui->comboBoxColor->setCurrentText(m_manyColors); } } /* Sound */ m_ui->label_audio->setVisible(m_hasAudio); m_ui->comboBoxAudio->setVisible(m_hasAudio); m_ui->comboBoxAudio->setEnabled(m_hasAudio); if (m_ui->comboBoxAudio->findText(m_manyAudios) == -1) { m_ui->comboBoxAudio->addItem(m_manyAudios); } if (m_hasAudio) { if (m_hasSameAudio) { m_ui->comboBoxAudio->removeItem(m_ui->comboBoxAudio->findText(m_manyAudios)); m_ui->comboBoxAudio->setCurrentText(firstElement->getAudio()); } else { m_ui->comboBoxAudio->setCurrentText(m_manyAudios); } } /* Frequency */ m_ui->doubleSpinBoxFrequency->setVisible(m_hasFrequency); m_ui->doubleSpinBoxFrequency->setEnabled(m_hasFrequency); m_ui->label_frequency->setVisible(m_hasFrequency); if (m_hasFrequency) { if (m_hasSameFrequency) { m_ui->doubleSpinBoxFrequency->setMinimum(0.5); m_ui->doubleSpinBoxFrequency->setSpecialValueText(QString()); m_ui->doubleSpinBoxFrequency->setValue(static_cast(firstElement->getFrequency())); } else { m_ui->doubleSpinBoxFrequency->setMinimum(0.0); m_ui->doubleSpinBoxFrequency->setSpecialValueText(m_manyFreq); m_ui->doubleSpinBoxFrequency->setValue(0.0); } } /* Input size */ m_ui->comboBoxInputSz->clear(); m_ui->label_inputs->setVisible(m_canChangeInputSize); m_ui->comboBoxInputSz->setVisible(m_canChangeInputSize); m_ui->comboBoxInputSz->setEnabled(m_canChangeInputSize); for (int port = minimum; port <= maximum; ++port) { m_ui->comboBoxInputSz->addItem(QString::number(port), port); } if (m_ui->comboBoxInputSz->findText(m_manyIS) == -1) { m_ui->comboBoxInputSz->addItem(m_manyIS); } if (m_canChangeInputSize) { if (m_hasSameInputSize) { QString inputSz = QString::number(firstElement->inputSize()); m_ui->comboBoxInputSz->removeItem(m_ui->comboBoxInputSz->findText(m_manyIS)); m_ui->comboBoxInputSz->setCurrentText(inputSz); } else { m_ui->comboBoxInputSz->setCurrentText(m_manyIS); } } /* Trigger */ m_ui->lineEditTrigger->setVisible(m_hasTrigger); m_ui->lineEditTrigger->setEnabled(m_hasTrigger); m_ui->label_trigger->setVisible(m_hasTrigger); if (m_hasTrigger) { if (m_hasSameTrigger) { m_ui->lineEditTrigger->setText(firstElement->getTrigger().toString()); } else { m_ui->lineEditTrigger->setText(m_manyTriggers); } } setEnabled(true); setVisible(true); } else { m_hasElements = false; setVisible(false); m_ui->lineEditElementLabel->setText(""); } } void ElementEditor::selectionChanged() { QVector elms = m_scene->selectedElements(); setCurrentElements(elms); } void ElementEditor::apply() { if ((m_elements.isEmpty()) || (!isEnabled())) { return; } QByteArray itemData; QDataStream dataStream(&itemData, QIODevice::WriteOnly); for (GraphicElement *elm : qAsConst(m_elements)) { elm->save(dataStream); if (elm->hasColors() && (m_ui->comboBoxColor->currentData().isValid())) { elm->setColor(m_ui->comboBoxColor->currentData().toString()); } if (elm->hasAudio() && (m_ui->comboBoxAudio->currentText() != m_manyAudios)) { elm->setAudio(m_ui->comboBoxAudio->currentText()); } if (elm->hasLabel() && (m_ui->lineEditElementLabel->text() != m_manyLabels)) { elm->setLabel(m_ui->lineEditElementLabel->text()); } if (elm->hasFrequency() && (m_ui->doubleSpinBoxFrequency->text() != m_manyFreq)) { elm->setFrequency(m_ui->doubleSpinBoxFrequency->value()); } if (elm->hasTrigger() && (m_ui->lineEditTrigger->text() != m_manyTriggers)) { if (m_ui->lineEditTrigger->text().size() <= 1) { elm->setTrigger(QKeySequence(m_ui->lineEditTrigger->text())); } } if (m_updatingSkin) { elm->setSkin(m_defaultSkin, m_skinName); } } if (m_updatingSkin) { m_updatingSkin = false; } emit sendCommand(new UpdateCommand(m_elements, itemData, m_editor)); } void ElementEditor::setEditor(Editor *value) { m_editor = value; } void ElementEditor::inputIndexChanged(int idx) { Q_UNUSED(idx) if ((m_elements.isEmpty()) || (!isEnabled())) { return; } if (m_canChangeInputSize && (m_ui->comboBoxInputSz->currentText() != m_manyIS)) { emit sendCommand(new ChangeInputSZCommand(m_elements, m_ui->comboBoxInputSz->currentData().toInt(), m_editor)); } } void ElementEditor::triggerChanged(const QString &cmd) { m_ui->lineEditTrigger->setText(cmd.toUpper()); } bool ElementEditor::eventFilter(QObject *obj, QEvent *event) { auto *wgt = dynamic_cast(obj); auto *keyEvent = dynamic_cast(event); if ((event->type() == QEvent::KeyPress) && keyEvent && (m_elements.size() == 1)) { bool move_fwd = keyEvent->key() == Qt::Key_Tab; bool move_back = keyEvent->key() == Qt::Key_Backtab; if (move_back || move_fwd) { GraphicElement *elm = m_elements.first(); QVector elms = m_scene->getVisibleElements(); std::stable_sort(elms.begin(), elms.end(), [](GraphicElement *elm1, GraphicElement *elm2) { return elm1->pos().ry() < elm2->pos().ry(); }); std::stable_sort(elms.begin(), elms.end(), [](GraphicElement *elm1, GraphicElement *elm2) { return elm1->pos().rx() < elm2->pos().rx(); }); apply(); int elmPos = elms.indexOf(elm); qDebug() << "Pos = " << elmPos << " from " << elms.size(); int step = 1; if (move_back) { step = -1; } int pos = (elms.size() + elmPos + step) % elms.size(); for (; pos != elmPos; pos = ((elms.size() + pos + step) % elms.size())) { qDebug() << "Pos = " << pos; elm = elms[pos]; setCurrentElements(QVector({elm})); if (wgt->isEnabled()) { break; } } m_scene->clearSelection(); if (!wgt->isEnabled()) { elm = elms[elmPos]; } elm->setSelected(true); elm->ensureVisible(); wgt->setFocus(); event->accept(); return true; } } /* pass the event on to the parent class */ return QWidget::eventFilter(obj, event); } void ElementEditor::defaultSkin() { m_updatingSkin = true; m_defaultSkin = true; apply(); } wiRedPanda-3.0.1/app/elementeditor.h000066400000000000000000000036731406216046500173370ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef ELEMENTEDITOR_H #define ELEMENTEDITOR_H #include #include "graphicelement.h" class QUndoCommand; class Scene; namespace Ui { class ElementEditor; } class Editor; class ElementEditor : public QWidget { Q_OBJECT public: explicit ElementEditor(QWidget *parent = nullptr); ~ElementEditor() override; void setScene(Scene *s); void contextMenu(QPoint screenPos); void renameAction(); void changeTriggerAction(); void retranslateUi(); /* * void renameAction( const QVector< GraphicElement *> &element ); * void changeColorAction( const QVector &element ); */ void fillColorComboBox(); void updateElementSkin(); void setEditor(Editor *value); bool eventFilter(QObject *obj, QEvent *event) override; signals: void sendCommand(QUndoCommand *cmd); private slots: void selectionChanged(); void inputIndexChanged(int index); void triggerChanged(const QString &arg1); void defaultSkin(); private: void setCurrentElements(const QVector &element); void updateSkins(); void apply(); Ui::ElementEditor * m_ui; QVector m_elements; Scene *m_scene; Editor *m_editor; bool m_hasAnyProperty, m_hasLabel, m_hasColors, m_hasFrequency, m_hasAudio; bool m_canChangeInputSize, m_hasTrigger, m_hasRotation, m_canChangeSkin; bool m_hasSameLabel, m_hasSameColors, m_hasSameFrequency; bool m_hasSameInputSize, m_hasSameTrigger, m_canMorph, m_hasSameType; bool m_hasSameAudio; bool m_hasElements; QString m_manyLabels; QString m_manyColors; QString m_manyIS; QString m_manyFreq; QString m_manyTriggers; QString m_manyAudios; QString m_skinName; bool m_updatingSkin; bool m_defaultSkin; }; #endif /* ELEMENTEDITOR_H */ wiRedPanda-3.0.1/app/elementeditor.ui000066400000000000000000000123711406216046500175200ustar00rootroot00000000000000 ElementEditor 0 0 202 409 Form QLayout::SetNoConstraint 150 0 Change skin to ... 24 24 24 24 Default :/default.svg:/default.svg 24 24 Skin Color: Audio Input Ports: Trigger Frequency: Hz 1 0.000000000000000 50.000000000000000 0.100000000000000 C6 D6 E6 F6 G6 A7 B7 C7 Label: lineEditElementLabel comboBoxInputSz doubleSpinBoxFrequency comboBoxColor lineEditTrigger wiRedPanda-3.0.1/app/elementfactory.cpp000066400000000000000000000260601406216046500200460ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include #include "element/and.h" #include "element/buzzer.h" #include "element/clock.h" #include "element/demux.h" #include "element/dflipflop.h" #include "element/display.h" #include "element/display_14.h" #include "element/dlatch.h" #include "element/inputbutton.h" #include "element/inputgnd.h" #include "element/inputswitch.h" #include "element/inputvcc.h" #include "element/jkflipflop.h" #include "element/jklatch.h" #include "element/led.h" #include "element/nand.h" #include "element/nor.h" #include "element/not.h" #include "element/or.h" #include "element/srflipflop.h" #include "element/tflipflop.h" #include "ic.h" #include "itemwithid.h" #include "element/mux.h" #include "element/node.h" #include "element/xnor.h" #include "element/xor.h" #include "common.h" #include "elementfactory.h" #include "graphicelement.h" #include "qneconnection.h" ElementFactory *ElementFactory::instance = new ElementFactory(); size_t ElementFactory::getLastId() const { return m_lastId; } ElementType ElementFactory::textToType(QString text) { text = text.toUpper(); ElementType type; type = text == "BUTTON" ? ElementType::BUTTON : text == "LED" ? ElementType::LED : text == "AND" ? ElementType::AND : text == "OR" ? ElementType::OR : text == "CLOCK" ? ElementType::CLOCK : text == "SWITCH" ? ElementType::SWITCH : text == "NOT" ? ElementType::NOT : text == "NAND" ? ElementType::NAND : text == "NOR" ? ElementType::NOR : text == "XOR" ? ElementType::XOR : text == "XNOR" ? ElementType::XNOR : text == "VCC" ? ElementType::VCC : text == "GND" ? ElementType::GND : text == "DFLIPFLOP" ? ElementType::DFLIPFLOP : text == "DLATCH" ? ElementType::DLATCH : text == "JKFLIPFLOP" ? ElementType::JKFLIPFLOP : text == "JKLATCH" ? ElementType::JKLATCH : text == "SRFLIPFLOP" ? ElementType::SRFLIPFLOP : // text == "TLATCH" ? ElementType::TLATCH : text == "TFLIPFLOP" ? ElementType::TFLIPFLOP : text == "DISPLAY" ? ElementType::DISPLAY : text == "DISPLAY14" ? ElementType::DISPLAY14 : text == "IC" ? ElementType::IC : text == "MUX" ? ElementType::MUX : text == "DEMUX" ? ElementType::DEMUX : text == "NODE" ? ElementType::NODE : text == "BUZZER" ? ElementType::BUZZER : ElementType::UNKNOWN; return type; } QString ElementFactory::typeToText(ElementType type) { switch (type) { case ElementType::BUTTON: return "BUTTON"; case ElementType::LED: return "LED"; case ElementType::AND: return "AND"; case ElementType::OR: return "OR"; case ElementType::CLOCK: return "CLOCK"; case ElementType::SWITCH: return "SWITCH"; case ElementType::NOT: return "NOT"; case ElementType::NAND: return "NAND"; case ElementType::NOR: return "NOR"; case ElementType::XOR: return "XOR"; case ElementType::XNOR: return "XNOR"; case ElementType::VCC: return "VCC"; case ElementType::GND: return "GND"; case ElementType::DFLIPFLOP: return "DFLIPFLOP"; case ElementType::DLATCH: return "DLATCH"; case ElementType::JKFLIPFLOP: return "JKFLIPFLOP"; case ElementType::JKLATCH: return "JKLATCH"; case ElementType::SRFLIPFLOP: return "SRFLIPFLOP"; // case ElementType::TLATCH: return "TLATCH" ; case ElementType::TFLIPFLOP: return "TFLIPFLOP"; case ElementType::DISPLAY: return "DISPLAY"; case ElementType::DISPLAY14: return "DISPLAY14"; case ElementType::IC: return "IC"; case ElementType::MUX: return "MUX"; case ElementType::DEMUX: return "DEMUX"; case ElementType::NODE: return "NODE"; case ElementType::BUZZER: return "BUZZER"; case ElementType::UNKNOWN: default: return "UNKNOWN"; } } QString ElementFactory::translatedName(ElementType type) { switch (type) { case ElementType::BUTTON: return tr("Button"); case ElementType::LED: return tr("Led"); case ElementType::AND: return tr("And"); case ElementType::OR: return tr("Or"); case ElementType::CLOCK: return tr("Clock"); case ElementType::SWITCH: return tr("Switch"); case ElementType::NOT: return tr("Not"); case ElementType::NAND: return tr("Nand"); case ElementType::NOR: return tr("Nor"); case ElementType::XOR: return tr("Xor"); case ElementType::XNOR: return tr("Xnor"); case ElementType::VCC: return tr("VCC"); case ElementType::GND: return tr("GND"); case ElementType::DFLIPFLOP: return tr("D-flipflop"); case ElementType::DLATCH: return tr("D-latch"); case ElementType::JKFLIPFLOP: return tr("JK-flipflop"); case ElementType::JKLATCH: return tr("JK-latch"); case ElementType::SRFLIPFLOP: return tr("SR-flipflop"); // case ElementType::TLATCH: return tr( "T-latch" ) ; case ElementType::TFLIPFLOP: return tr("T-flipflop"); case ElementType::DISPLAY: return tr("Display"); case ElementType::DISPLAY14: return tr("Display14"); case ElementType::IC: return tr("IC"); case ElementType::MUX: return tr("Mux"); case ElementType::DEMUX: return tr("Demux"); case ElementType::NODE: return tr("Node"); case ElementType::BUZZER: return tr("Buzzer"); case ElementType::UNKNOWN: default: return tr("Unknown"); } } QPixmap ElementFactory::getPixmap(ElementType type) { switch (type) { case ElementType::BUTTON: return QPixmap(":/input/buttonOff.png"); case ElementType::LED: return QPixmap(":/output/WhiteLedOff.png"); case ElementType::AND: return QPixmap(":/basic/and.png"); case ElementType::OR: return QPixmap(":/basic/or.png"); case ElementType::CLOCK: return QPixmap(":/input/clock1.png"); case ElementType::SWITCH: return QPixmap(":/input/switchOn.png"); case ElementType::NOT: return QPixmap(":/basic/not.png"); case ElementType::NAND: return QPixmap(":/basic/nand.png"); case ElementType::NOR: return QPixmap(":/basic/nor.png"); case ElementType::XOR: return QPixmap(":/basic/xor.png"); case ElementType::XNOR: return QPixmap(":/basic/xnor.png"); case ElementType::VCC: return QPixmap(":/input/1.png"); case ElementType::GND: return QPixmap(":/input/0.png"); case ElementType::DFLIPFLOP: return QPixmap(":/memory/light/D-flipflop.png"); case ElementType::DLATCH: return QPixmap(":/memory/light/D-latch.png"); case ElementType::JKFLIPFLOP: return QPixmap(":/memory/light/JK-flipflop.png"); case ElementType::JKLATCH: return QPixmap(":/memory/light/JK-latch.png"); case ElementType::SRFLIPFLOP: return QPixmap(":/memory/light/SR-flipflop.png"); case ElementType::UNUSED: return QPixmap(":/memory/light/T-flipflop.png"); case ElementType::TFLIPFLOP: return QPixmap(":/memory/light/T-flipflop.png"); case ElementType::DISPLAY: return QPixmap(":/output/counter/counter_on.png"); case ElementType::DISPLAY14: return QPixmap(":/output/counter/counter_14_on.png"); case ElementType::IC: return QPixmap(":/basic/box.png"); case ElementType::MUX: return QPixmap(":/basic/mux.png"); case ElementType::DEMUX: return QPixmap(":/basic/demux.png"); case ElementType::NODE: return QPixmap(":/basic/node.png"); case ElementType::BUZZER: return QPixmap(":/output/BuzzerOff.png"); case ElementType::UNKNOWN: return QPixmap(); } return QPixmap(); } ElementFactory::ElementFactory() { clear(); } GraphicElement *ElementFactory::buildElement(ElementType type, QGraphicsItem *parent) { GraphicElement *elm; elm = type == ElementType::BUTTON ? new InputButton(parent) : type == ElementType::SWITCH ? new InputSwitch(parent) : type == ElementType::LED ? new Led(parent) : type == ElementType::NOT ? new Not(parent) : type == ElementType::AND ? new And(parent) : type == ElementType::OR ? new Or(parent) : type == ElementType::NAND ? new Nand(parent) : type == ElementType::NOR ? new Nor(parent) : type == ElementType::CLOCK ? new Clock(parent) : type == ElementType::XOR ? new Xor(parent) : type == ElementType::XNOR ? new Xnor(parent) : type == ElementType::VCC ? new InputVcc(parent) : type == ElementType::GND ? new InputGnd(parent) : type == ElementType::DLATCH ? new DLatch(parent) : type == ElementType::DFLIPFLOP ? new DFlipFlop(parent) : type == ElementType::JKLATCH ? new JKLatch(parent) : type == ElementType::JKFLIPFLOP ? new JKFlipFlop(parent) : type == ElementType::SRFLIPFLOP ? new SRFlipFlop(parent) : type == ElementType::TFLIPFLOP ? new TFlipFlop(parent) : type == ElementType::UNUSED ? new TFlipFlop(parent) : type == ElementType::DISPLAY ? new Display(parent) : type == ElementType::DISPLAY14 ? new Display14(parent) : type == ElementType::IC ? new IC(parent) : type == ElementType::NODE ? new Node(parent) : type == ElementType::MUX ? new Mux(parent) : type == ElementType::DEMUX ? new Demux(parent) : type == ElementType::BUZZER ? new Buzzer(parent) : static_cast(nullptr); return elm; } QNEConnection *ElementFactory::buildConnection(QGraphicsItem *parent) { return new QNEConnection(parent); } ItemWithId *ElementFactory::getItemById(size_t id) { if (instance->m_map.contains(id)) { return instance->m_map[id]; } return nullptr; } bool ElementFactory::contains(size_t id) { return instance->m_map.contains(id); } void ElementFactory::addItem(ItemWithId *item) { if (item) { int newId = instance->next_id(); instance->m_map[newId] = item; item->setId(newId); } } void ElementFactory::removeItem(ItemWithId *item) { instance->m_map.remove(item->id()); } void ElementFactory::updateItemId(ItemWithId *item, size_t newId) { instance->m_map.remove(item->id()); instance->m_map[newId] = item; item->setId(newId); } size_t ElementFactory::next_id() { return m_lastId++; } void ElementFactory::clear() { COMMENT("Element Factory clear.", 0); m_map.clear(); m_lastId = 1; } wiRedPanda-3.0.1/app/elementfactory.h000066400000000000000000000023041406216046500175060ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef ELEMENTFACTORY_H #define ELEMENTFACTORY_H #include #include #include "elementtype.h" class Editor; class GraphicElement; class ItemWithId; class QNEConnection; class ElementFactory : public QObject { Q_OBJECT public: static ElementFactory *instance; static ElementType textToType(QString text); static QString typeToText(ElementType type); static QString translatedName(ElementType type); static QPixmap getPixmap(ElementType type); static GraphicElement *buildElement(ElementType type, QGraphicsItem *parent = nullptr); static QNEConnection *buildConnection(QGraphicsItem *parent = nullptr); static ItemWithId *getItemById(size_t id); static bool contains(size_t id); static void updateItemId(ItemWithId *item, size_t newId); static void removeItem(ItemWithId *item); static void addItem(ItemWithId *item); size_t getLastId() const; size_t next_id(); void clear(); private: QMap m_map; size_t m_lastId; ElementFactory(); }; #endif /* ELEMENTFACTORY_H */wiRedPanda-3.0.1/app/elementmapping.cpp000066400000000000000000000235461406216046500200400ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "elementmapping.h" #include "clock.h" #include "graphicelement.h" #include "ic.h" #include "icmanager.h" #include "icmapping.h" #include "icprototype.h" #include "input.h" #include "logicelement.h" #include "qneconnection.h" #include "qneport.h" #include "logicelement/logicand.h" #include "logicelement/logicdemux.h" #include "logicelement/logicdflipflop.h" #include "logicelement/logicdlatch.h" #include "logicelement/logicjkflipflop.h" #include "logicelement/logicmux.h" #include "logicelement/logicnand.h" #include "logicelement/logicnode.h" #include "logicelement/logicnor.h" #include "logicelement/logicnot.h" #include "logicelement/logicor.h" #include "logicelement/logicoutput.h" #include "logicelement/logicsrflipflop.h" #include "logicelement/logictflipflop.h" #include "logicelement/logicxnor.h" #include "logicelement/logicxor.h" ElementMapping::ElementMapping(const QVector &elms, const QString &file) : m_currentFile(file) , m_initialized(false) , m_elements(elms) , m_globalGND(false) , m_globalVCC(true) { } ElementMapping::~ElementMapping() { clear(); } void ElementMapping::clear() { m_initialized = false; m_globalGND.clearSucessors(); m_globalVCC.clearSucessors(); qDeleteAll(m_deletableElements); m_deletableElements.clear(); qDeleteAll(m_icMappings); m_icMappings.clear(); m_elementMap.clear(); m_inputMap.clear(); m_clocks.clear(); m_logicElms.clear(); } QVector ElementMapping::sortGraphicElements(QVector elms) { //! Clazy warning: Use QHash instead of QMap when K is a pointer QHash beingvisited; QHash priority; for (GraphicElement *elm : elms) { calculatePriority(elm, beingvisited, priority); } std::sort(elms.begin(), elms.end(), [priority](GraphicElement *e1, GraphicElement *e2) { return priority[e2] < priority[e1]; }); return elms; } void ElementMapping::insertElement(GraphicElement *elm) { LogicElement *logicElm = buildLogicElement(elm); elm->type(); m_deletableElements.append(logicElm); m_logicElms.append(logicElm); m_elementMap.insert(elm, logicElm); auto *in = dynamic_cast(elm); if (in) { m_inputMap[in] = logicElm; } } void ElementMapping::insertIC(IC *ic) { Q_ASSERT(ic); Q_ASSERT(!m_icMappings.contains(ic)); ICPrototype *proto = ic->getPrototype(); if (proto) { ICMapping *icMap = proto->generateMapping(); Q_ASSERT(icMap); icMap->initialize(); m_icMappings.insert(ic, icMap); m_logicElms.append(icMap->m_logicElms); } } void ElementMapping::generateMap() { for (GraphicElement *elm : qAsConst(m_elements)) { if (elm->elementType() == ElementType::CLOCK) { auto *clk = dynamic_cast(elm); Q_ASSERT(clk != nullptr); m_clocks.append(clk); } if (elm->elementType() == ElementType::IC) { IC *ic = dynamic_cast(elm); insertIC(ic); } else { insertElement(elm); } } } LogicElement *ElementMapping::buildLogicElement(GraphicElement *elm) { switch (elm->elementType()) { case ElementType::SWITCH: case ElementType::BUTTON: case ElementType::CLOCK: return new LogicInput(); case ElementType::LED: case ElementType::BUZZER: case ElementType::DISPLAY: case ElementType::DISPLAY14: return new LogicOutput(elm->inputSize()); case ElementType::NODE: return new LogicNode(); case ElementType::VCC: return new LogicInput(true); case ElementType::GND: return new LogicInput(false); case ElementType::AND: return new LogicAnd(elm->inputSize()); case ElementType::OR: return new LogicOr(elm->inputSize()); case ElementType::NAND: return new LogicNand(elm->inputSize()); case ElementType::NOR: return new LogicNor(elm->inputSize()); case ElementType::XOR: return new LogicXor(elm->inputSize()); case ElementType::XNOR: return new LogicXnor(elm->inputSize()); case ElementType::NOT: return new LogicNot(); case ElementType::JKFLIPFLOP: return new LogicJKFlipFlop(); case ElementType::SRFLIPFLOP: return new LogicSRFlipFlop(); case ElementType::TFLIPFLOP: return new LogicTFlipFlop(); case ElementType::DFLIPFLOP: return new LogicDFlipFlop(); case ElementType::DLATCH: return new LogicDLatch(); case ElementType::MUX: return new LogicMux(); case ElementType::DEMUX: return new LogicDemux(); // case ElementType::TLATCH: case ElementType::JKLATCH: //! TODO: TLATCH not yet implemented. return new LogicDLatch(); default: throw std::runtime_error("Not implemented yet: " + elm->objectName().toStdString()); } } void ElementMapping::initialize() { clear(); generateMap(); connectElements(); m_initialized = true; } void ElementMapping::sort() { sortLogicElements(); validateElements(); } // TODO: This function can easily cause crashes when using the Undo command to delete elements void ElementMapping::update() { // bool resetSimulationController = false; if (canRun()) { for (Clock *clk : qAsConst(m_clocks)) { if (!clk) { continue; }; if (Clock::reset) { clk->resetClock(); } else { clk->updateClock(); } } Clock::reset = false; for (auto iter = m_inputMap.begin(); iter != m_inputMap.end(); ++iter) { if (!iter.key()) { continue; } if (!iter.value()) { continue; } iter.value()->setOutputValue(iter.key()->getOn()); } for (LogicElement *elm : qAsConst(m_logicElms)) { elm->updateLogic(); } } // return resetSimulationController; } ICMapping *ElementMapping::getICMapping(IC *ic) const { Q_ASSERT(ic); return m_icMappings[ic]; } LogicElement *ElementMapping::getLogicElement(GraphicElement *elm) const { Q_ASSERT(elm); return m_elementMap[elm]; } bool ElementMapping::canRun() const { return m_initialized; } bool ElementMapping::canInitialize() const { for (GraphicElement *elm : m_elements) { if (elm->elementType() == ElementType::IC) { IC *ic = dynamic_cast(elm); ICPrototype *prototype = ICManager::instance()->getPrototype(ic->getFile()); if (!ic || !prototype) { return false; } } } return true; } void ElementMapping::applyConnection(GraphicElement *elm, QNEPort *in) { LogicElement *currentLogElm; int inputIndex = 0; if (elm->elementType() == ElementType::IC) { IC *ic = dynamic_cast(elm); Q_ASSERT(ic); currentLogElm = m_icMappings[ic]->getInput(in->index()); } else { currentLogElm = m_elementMap[elm]; inputIndex = in->index(); } Q_ASSERT(currentLogElm); int connections = in->connections().size(); bool connection_required = in->isRequired(); if (connections == 1) { QNEPort *other_out = in->connections().first()->otherPort(in); if (other_out) { GraphicElement *predecessor = other_out->graphicElement(); if (predecessor) { int predOutIndex = 0; LogicElement *predOutElm; if (predecessor->elementType() == ElementType::IC) { IC *ic = dynamic_cast(predecessor); Q_ASSERT(ic); Q_ASSERT(m_icMappings.contains(ic)); predOutElm = m_icMappings[ic]->getOutput(other_out->index()); } else { predOutElm = m_elementMap[predecessor]; predOutIndex = other_out->index(); } currentLogElm->connectPredecessor(inputIndex, predOutElm, predOutIndex); } } } else if ((connections == 0) && (!connection_required)) { LogicElement *pred = in->defaultValue() ? &m_globalVCC : &m_globalGND; currentLogElm->connectPredecessor(inputIndex, pred, 0); } } void ElementMapping::connectElements() { for (GraphicElement *elm : qAsConst(m_elements)) { const auto elm_inputs = elm->inputs(); for (QNEPort *in : elm_inputs) { applyConnection(elm, in); } } } void ElementMapping::validateElements() { for (LogicElement *elm : qAsConst(m_logicElms)) { elm->validate(); } } void ElementMapping::sortLogicElements() { for (LogicElement *elm : qAsConst(m_logicElms)) { elm->calculatePriority(); } std::sort(m_logicElms.begin(), m_logicElms.end(), [](LogicElement *e1, LogicElement *e2) { return *e2 < *e1; }); } int ElementMapping::calculatePriority(GraphicElement *elm, QHash &beingvisited, QHash &priority) { if (!elm) { return 0; } if (beingvisited.contains(elm) && (beingvisited[elm])) { return 0; } if (priority.contains(elm)) { return priority[elm]; } beingvisited[elm] = true; int max = 0; auto const elm_outputs = elm->outputs(); for (QNEPort *port : elm_outputs) { for (QNEConnection *conn : port->connections()) { QNEPort *successor = conn->otherPort(port); if (successor) { max = qMax(calculatePriority(successor->graphicElement(), beingvisited, priority), max); } } } int p = max + 1; priority[elm] = p; beingvisited[elm] = false; return p; } wiRedPanda-3.0.1/app/elementmapping.h000066400000000000000000000036161406216046500175010ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef ELEMENTMAPPING_H #define ELEMENTMAPPING_H #include #include #include "logicelement/logicinput.h" class Clock; class GraphicElement; class IC; class Input; class LogicElement; class QNEPort; class ElementMapping; class ICMapping; typedef QMap ElementMap; typedef QMap InputMap; class ElementMapping { public: ElementMapping(const QVector &elms, const QString &file = QString()); virtual ~ElementMapping(); void clear(); static QVector sortGraphicElements(QVector elms); virtual void initialize(); void sort(); void update(); ICMapping *getICMapping(IC *ic) const; LogicElement *getLogicElement(GraphicElement *elm) const; bool canRun() const; bool canInitialize() const; protected: // Attributes QString m_currentFile; bool m_initialized; ElementMap m_elementMap; InputMap m_inputMap; QVector m_clocks; QVector m_elements; QMap m_icMappings; QVector m_logicElms; LogicInput m_globalGND; LogicInput m_globalVCC; QVector m_deletableElements; // Methods LogicElement *buildLogicElement(GraphicElement *elm); void setDefaultValue(GraphicElement *elm, QNEPort *in); void applyConnection(GraphicElement *elm, QNEPort *in); void generateMap(); void connectElements(); void validateElements(); void sortLogicElements(); static int calculatePriority(GraphicElement *elm, QHash &beingvisited, QHash &priority); void insertElement(GraphicElement *elm); void insertIC(IC *ic); }; #endif // ELEMENTMAPPING_H wiRedPanda-3.0.1/app/elementtype.h000066400000000000000000000006561406216046500170300ustar00rootroot00000000000000#ifndef ELEMENTTYPE_H #define ELEMENTTYPE_H #include enum class ElementType : uint_fast8_t { UNKNOWN, BUTTON, SWITCH, LED, NOT, AND, OR, NAND, NOR, CLOCK, XOR, XNOR, VCC, GND, DISPLAY, DLATCH, JKLATCH, DFLIPFLOP, JKFLIPFLOP, SRFLIPFLOP, TFLIPFLOP, UNUSED, IC, NODE, MUX, DEMUX, BUZZER, DISPLAY14 }; #endifwiRedPanda-3.0.1/app/filehelper.cpp000066400000000000000000000056761406216046500171560ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include "common.h" #include "filehelper.h" #include "globalproperties.h" #include "icnotfoundexception.h" QFileInfo FileHelper::findICFile(const QString &fname, const QString &parentFile) { qDebug() << "Loading file: " << fname << ", parentFile: " << parentFile; QFileInfo currentFile(GlobalProperties::currentFile); QFileInfo fileInfo(fname); QString myFile = fileInfo.fileName(); auto setFileInfo = [&fileInfo](const QDir &dir, const QString &file) { fileInfo.setFile(dir, file); return fileInfo.isFile(); }; QDir subdir(currentFile.absolutePath()); subdir.cdUp(); if (!fileInfo.isFile() && !setFileInfo(QDir::current(), fileInfo.fileName()) && !setFileInfo(QFileInfo(parentFile).absoluteDir(), myFile) && !setFileInfo(currentFile.absoluteDir(), myFile) && !setFileInfo(QDir(currentFile.absolutePath() + "/boxes"), myFile) && !setFileInfo(QDir(subdir.absolutePath() + "/boxes"), myFile)) { std::cerr << "Error: This file does not exists: " << fname.toStdString() << std::endl; throw(ICNotFoundException(QString(tr("IC linked file \"%1\" could not be found!\n" "Do you want to find this file?")) .arg(fname) .toStdString(), nullptr)); } return fileInfo; } QFileInfo FileHelper::findSkinFile(const QString &fname) { qDebug() << "Loading file: " << fname; QFileInfo currentFile(GlobalProperties::currentFile); QFileInfo fileInfo(fname); QString myFile = fileInfo.fileName(); auto setFileInfo = [&fileInfo](const QDir &dir, const QString &file) { fileInfo.setFile(dir, file); return fileInfo.isFile(); }; if (!fileInfo.isFile() && !setFileInfo(QDir::current(), fileInfo.fileName()) && !setFileInfo(currentFile.absoluteDir(), myFile) && !setFileInfo(QDir(currentFile.absolutePath() + "/skins"), myFile)) { std::cerr << "Error: This file does not exists: " << fname.toStdString() << std::endl; } COMMENT("FileInfo found: " << fileInfo.absoluteFilePath().toStdString(), 0); return fileInfo; } void FileHelper::verifyRecursion(const QString &fname) { Q_UNUSED(fname); // TODO: BoxFileHelper::verifyRecursion // std::string msg = "Oh no! I'm my own parent.\nSomething is not ok..."; // if( !parentFile.isEmpty( ) && ( fname == parentFile ) ) { // throw( std::runtime_error( msg ) ); // } // for( Box *box = parentBox; box != nullptr; box = box->getParentBox( ) ) { //// qDebug( ) << "File: " << box->getFile( ); // if( box->getFile( ) == fname ) { // throw( std::runtime_error( msg ) ); // } // } } wiRedPanda-3.0.1/app/filehelper.h000066400000000000000000000011021406216046500165770ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef FILEHELPER_H #define FILEHELPER_H #include #include class FileHelper : public QObject { Q_OBJECT public: static QFileInfo findICFile(const QString &fname, const QString &parentFile); static QFileInfo findSkinFile(const QString &fname); static void verifyRecursion(const QString &fname); private: FileHelper(QObject *parent = nullptr) : QObject(parent) { } }; #endif // FILEHELPER_H wiRedPanda-3.0.1/app/globalproperties.cpp000066400000000000000000000016561406216046500204060ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include #include "common.h" #include "globalproperties.h" double GlobalProperties::toDouble(const QString &txtVersion, bool *ok) { QString value = txtVersion; if (value.contains("-")) { value = value.split("-").first(); } if (value.contains(".")) { value = value.split(".")[0] + "." + value.split(".")[1]; } return value.toDouble(ok); } double loadVersion() { QString txtVersion(APP_VERSION); bool ok; double version = GlobalProperties::toDouble(txtVersion, &ok); if (!ok || (qFuzzyIsNull(version))) { throw std::runtime_error(ERRORMSG("INVALID VERSION NUMBER!")); } return version; } QString GlobalProperties::currentFile = QString(); double GlobalProperties::version = loadVersion(); bool GlobalProperties::soundEnabled = true; wiRedPanda-3.0.1/app/globalproperties.h000066400000000000000000000006721406216046500200500ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef GLOBALPROPERTIES_H #define GLOBALPROPERTIES_H #include #define GLOBALCLK 10 class GlobalProperties { public: static QString currentFile; static double version; static bool soundEnabled; static double toDouble(const QString &txtVersion, bool *ok); }; #endif // GLOBALPROPERTIES_HwiRedPanda-3.0.1/app/graphicelement.cpp000066400000000000000000000543631406216046500200230ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include #include #include #include #include #include #include #include "common.h" #include "graphicelement.h" #include "nodes/qneconnection.h" #include "nodes/qneport.h" #include "scene.h" #include "thememanager.h" // TODO - WARNING: non-POD static static QMap loadedPixmaps; GraphicElement::GraphicElement( ElementType type, ElementGroup group, int minInputSz, int maxInputSz, int minOutputSz, int maxOutputSz, QGraphicsItem *parent): QGraphicsObject(parent) , m_pixmap(nullptr) , m_label(new QGraphicsTextItem(this)) , m_topPosition(0) , m_bottomPosition(64) , m_maxInputSz(maxInputSz) , m_maxOutputSz(maxOutputSz) , m_minInputSz(minInputSz) , m_minOutputSz(minOutputSz) , m_outputsOnTop(true) , m_rotatable(true) , m_hasLabel(false) , m_canChangeSkin(false) , m_hasFrequency(false) , m_hasColors(false) , m_hasTrigger(false) , m_hasAudio(false) , m_disabled(false) , m_elementType(type) , m_elementGroup(group) { COMMENT("Setting flags of elements. ", 4); setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsGeometryChanges); COMMENT("Setting attributes. ", 4); m_label->hide(); QFont font("SansSerif"); font.setBold(true); m_label->setFont(font); m_label->setPos(64, 30); m_label->setParentItem(this); m_label->setDefaultTextColor(Qt::black); COMMENT("Including input and output ports.", 4); for (int i = 0; i < minInputSz; i++) { addInputPort(); } for (int i = 0; i < minOutputSz; i++) { addOutputPort(); } updateTheme(); } QPixmap GraphicElement::getPixmap() const { if (m_pixmap) { return *m_pixmap; } return {}; } ElementType GraphicElement::elementType() const { return m_elementType; } ElementGroup GraphicElement::elementGroup() const { return m_elementGroup; } void GraphicElement::disable() { m_disabled = true; } void GraphicElement::enable() { m_disabled = false; } void GraphicElement::setPixmap(const QString &pixmapName) { QString pixmapPath = pixmapName; if (ThemeManager::globalMngr) { // TODO: This has to be changed. Not a good way to do it. Probably better inside the class. if (pixmapPath.contains("memory")) { switch (ThemeManager::globalMngr->theme()) { case Theme::Panda_Light: pixmapPath.replace("memory", "memory/light"); break; case Theme::Panda_Dark: pixmapPath.replace("memory", "memory/dark"); break; } } } if (pixmapPath != m_currentPixmapName) { if (!loadedPixmaps.contains(pixmapPath)) { // TODO: use QPixmap::loadFromData() here if (!loadedPixmaps[pixmapPath].load(pixmapPath)) { std::cerr << "Problem loading pixmapPath = " << pixmapPath.toStdString() << '\n'; throw std::runtime_error(ERRORMSG("Couldn't load pixmap.")); } } m_pixmap = &loadedPixmaps[pixmapPath]; setTransformOriginPoint(m_pixmap->rect().center()); update(boundingRect()); } m_currentPixmapName = pixmapName; } void GraphicElement::setPixmap(const QString &pixmapName, QRect size) { QString pixmapPath = pixmapName; if (ThemeManager::globalMngr) { // TODO: This has to be changed. Not a good way to do it. Probably better inside the class. if (pixmapPath.contains("memory")) { switch (ThemeManager::globalMngr->theme()) { case Theme::Panda_Light: pixmapPath.replace("memory", "memory/light"); break; case Theme::Panda_Dark: pixmapPath.replace("memory", "memory/dark"); break; } } } if (pixmapPath != m_currentPixmapName) { if (!loadedPixmaps.contains(pixmapPath)) { // TODO: use QPixmap::loadFromData() here QPixmap pixmap; if (!pixmap.load(pixmapPath)) { throw std::runtime_error(ERRORMSG("Couldn't load pixmap.")); } loadedPixmaps[pixmapPath] = pixmap.copy(size); } m_pixmap = &loadedPixmaps[pixmapPath]; setTransformOriginPoint(m_pixmap->rect().center()); update(boundingRect()); } m_currentPixmapName = pixmapName; } QVector GraphicElement::outputs() const { return m_outputs; } const QNEInputPort *GraphicElement::input(int pos) const { return m_inputs.at(pos); } const QNEOutputPort *GraphicElement::output(int pos) const { return m_outputs.at(pos); } QNEInputPort *GraphicElement::input(int pos) { return m_inputs.at(pos); } QNEOutputPort *GraphicElement::output(int pos) { return m_outputs.at(pos); } void GraphicElement::setOutputs(const QVector &outputs) { m_outputs = outputs; } void GraphicElement::save(QDataStream &ds) const { COMMENT("Saving element. Type: " << objectName().toStdString(), 4); ds << pos(); ds << rotation(); /* */ ds << getLabel(); /* <\Version1.2> */ /* */ ds << m_minInputSz; ds << m_maxInputSz; ds << m_minOutputSz; ds << m_maxOutputSz; /* <\Version1.3> */ /* */ ds << m_trigger; /* <\Version1.9> */ ds << static_cast(m_inputs.size()); for (QNEPort *port : m_inputs) { ds << reinterpret_cast(port); ds << port->portName(); ds << port->portFlags(); } ds << static_cast(m_outputs.size()); for (QNEPort *port : m_outputs) { ds << reinterpret_cast(port); ds << port->portName(); ds << port->portFlags(); } /* <\Version2.7> */ ds << static_cast(m_pixmapSkinName.size()); for (const QString &skinName : m_pixmapSkinName) { ds << skinName; } COMMENT("Finished saving element.", 4); } void GraphicElement::load(QDataStream &ds, QMap &portMap, double version) { COMMENT("Loading element. Type: " << objectName().toStdString(), 4); loadPos(ds); loadAngle(ds); /* */ loadLabel(ds, version); /* <\Version1.2> */ /* */ loadMinMax(ds, version); /* <\Version1.3> */ /* */ loadTrigger(ds, version); /* <\Version1.9> */ loadInputPorts(ds, portMap); loadOutputPorts(ds, portMap); /* <\Version2.7> */ loadPixmapSkinNames(ds, version); COMMENT("Updating port positions.", 4); updatePorts(); COMMENT("Finished loading element.", 4); } void GraphicElement::loadPos(QDataStream &ds) { QPointF p; ds >> p; setPos(p); } void GraphicElement::loadAngle(QDataStream &ds) { qreal angle; ds >> angle; setRotation(angle); } void GraphicElement::loadLabel(QDataStream &ds, double version) { if (version >= 1.2) { QString label_text; ds >> label_text; setLabel(label_text); } } void GraphicElement::loadMinMax(QDataStream &ds, double version) { if (version >= 1.3) { quint64 min_isz, max_isz, min_osz, max_osz; ds >> min_isz; ds >> max_isz; ds >> min_osz; ds >> max_osz; // FIXME: Was it a bad decision to store Min and Max input/output sizes? /* Version 2.2 ?? fix ?? */ if (!((m_minInputSz == m_maxInputSz) && (m_minInputSz > max_isz))) { m_minInputSz = min_isz; m_maxInputSz = max_isz; } if (!((m_minOutputSz == m_maxOutputSz) && (m_minOutputSz > max_osz))) { m_minOutputSz = min_osz; m_maxOutputSz = max_osz; } } } void GraphicElement::loadTrigger(QDataStream &ds, double version) { if (version >= 1.9) { QKeySequence trigger; ds >> trigger; setTrigger(trigger); } } void GraphicElement::loadInputPorts(QDataStream &ds, QMap &portMap) { COMMENT("Loading input ports.", 4); quint64 inputSz; ds >> inputSz; if (inputSz > MAXIMUMVALIDINPUTSIZE) { throw std::runtime_error(ERRORMSG("Corrupted DataStream!")); } for (size_t port = 0; port < inputSz; ++port) { loadInputPort(ds, portMap, port); } removeSurplusInputs(inputSz, portMap); } void GraphicElement::loadInputPort(QDataStream &ds, QMap &portMap, size_t port) { QString name; int flags; quint64 ptr; ds >> ptr; ds >> name; ds >> flags; if ((port < static_cast(m_inputs.size()))) { if (elementType() == ElementType::IC) { m_inputs[port]->setName(name); } m_inputs[port]->setPortFlags(flags); m_inputs[port]->setPtr(ptr); } else { addPort(name, false, flags, ptr); } portMap[ptr] = m_inputs[port]; } void GraphicElement::removeSurplusInputs(quint64 inputSz, QMap &portMap) { while (inputSize() > static_cast(inputSz) && inputSz >= m_minInputSz) { QNEPort *deletedPort = m_inputs.back(); removePortFromMap(deletedPort, portMap); delete deletedPort; m_inputs.pop_back(); } } void GraphicElement::removeSurplusOutputs(quint64 outputSz, QMap &portMap) { while (outputSize() > static_cast(outputSz) && outputSz >= m_minOutputSz) { QNEPort *deletedPort = m_outputs.back(); removePortFromMap(deletedPort, portMap); delete deletedPort; m_outputs.pop_back(); } } void GraphicElement::removePortFromMap(QNEPort *deletedPort, QMap &portMap) { for (auto it = portMap.begin(); it != portMap.end();) { if (it.value() == deletedPort) { it = portMap.erase(it); } else { ++it; } } } void GraphicElement::loadOutputPorts(QDataStream &ds, QMap &portMap) { COMMENT("Loading output ports.", 4); quint64 outputSz; ds >> outputSz; if (outputSz > MAXIMUMVALIDINPUTSIZE) { throw std::runtime_error(ERRORMSG("Corrupted DataStream!")); } for (size_t port = 0; port < outputSz; ++port) { loadOutputPort(ds, portMap, port); } removeSurplusOutputs(outputSz, portMap); } void GraphicElement::loadOutputPort(QDataStream &ds, QMap &portMap, size_t port) { QString name; int flags; quint64 ptr; ds >> ptr; ds >> name; ds >> flags; if ((port < static_cast(m_outputs.size()))) { if (elementType() == ElementType::IC) { m_outputs[port]->setName(name); } m_outputs[port]->setPortFlags(flags); m_outputs[port]->setPtr(ptr); } else { addPort(name, true, flags, ptr); } portMap[ptr] = m_outputs[port]; } void GraphicElement::loadPixmapSkinNames(QDataStream &ds, double version) { if (version >= 2.7) { COMMENT("Loading pixmap skin names.", 4); quint64 outputSz; ds >> outputSz; if (outputSz > MAXIMUMVALIDINPUTSIZE) { throw std::runtime_error(ERRORMSG("Corrupted DataStream!")); } for (size_t port = 0; port < outputSz; ++port) { loadPixmapSkinName(ds, port); } refresh(); } } void GraphicElement::loadPixmapSkinName(QDataStream &ds, size_t skin) { QString name; ds >> name; if ((skin < static_cast(m_pixmapSkinName.size()))) { QFileInfo fileInfo(name); if (!fileInfo.isFile()) { std::cout << "Could not load skins: " << name.toStdString() << std::endl; } else m_pixmapSkinName[skin] = name; } else { std::cout << "Could not load some of the skins." << std::endl; } } void GraphicElement::updateSkinsPath(const QString &newSkinPath) { for (int i = 0; i < m_pixmapSkinName.size(); ++i) { QString name = m_pixmapSkinName[i]; if (name[0] != ':') { COMMENT("Detecting non-default skin name " << name.toStdString(), 0); QString newSkinName = newSkinPath + QFileInfo(name).fileName(); QFile fl(newSkinName); if (!fl.exists()) { COMMENT("Copying skin to local dir. File does not exist yet.", 0); QFile::copy(m_pixmapSkinName[i], newSkinName); } m_pixmapSkinName[i] = newSkinName; } } } QVector GraphicElement::inputs() const { return m_inputs; } void GraphicElement::setInputs(const QVector &inputs) { m_inputs = inputs; } QRectF GraphicElement::boundingRect() const { return m_pixmap->rect(); } void GraphicElement::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(widget) painter->setClipRect(option->exposedRect); if (isSelected()) { painter->setBrush(m_selectionBrush); painter->setPen(QPen(m_selectionPen, 0.5, Qt::SolidLine)); painter->drawRoundedRect(boundingRect(), 5, 5); } painter->drawPixmap(QPoint(0, 0), getPixmap()); } QNEPort *GraphicElement::addPort(const QString &name, bool isOutput, int flags, int ptr) { COMMENT("Adding new port.", 4); if (isOutput && (static_cast(m_outputs.size()) >= m_maxOutputSz)) { return nullptr; } if (!isOutput && (static_cast(m_inputs.size()) >= m_maxInputSz)) { return nullptr; } QNEPort *port = nullptr; if (isOutput) { m_outputs.push_back(new QNEOutputPort(this)); port = dynamic_cast(m_outputs.last()); port->setIndex(outputSize() - 1); } else { m_inputs.push_back(new QNEInputPort(this)); port = dynamic_cast(m_inputs.last()); port->setIndex(inputSize() - 1); } port->setName(name); port->setGraphicElement(this); port->setPortFlags(flags); port->setPtr(ptr); COMMENT("Updating new port.", 4); updatePorts(); port->show(); return port; } void GraphicElement::addInputPort(const QString &name) { addPort(name, false); } void GraphicElement::addOutputPort(const QString &name) { addPort(name, true); } void GraphicElement::setPortName(const QString &name) { setObjectName(name); setToolTip(name); } void GraphicElement::setSkin(bool defaultSkin, const QString &filename) { Q_UNUSED(defaultSkin); Q_UNUSED(filename); } void GraphicElement::updatePorts() { COMMENT("Updating port positions that belong to the IC.", 0); int inputPos = m_topPosition; int outputPos = m_bottomPosition; if (m_outputsOnTop) { inputPos = m_bottomPosition; outputPos = m_topPosition; } if (!m_outputs.isEmpty()) { int step = qMax(32 / m_outputs.size(), 6); int x = 32 - m_outputs.size() * step + step; foreach (QNEPort *port, m_outputs) { COMMENT("Setting output at " << x << ", " << outputPos, 0); port->setPos(x, outputPos); port->update(); x += step * 2; } } if (!m_inputs.isEmpty()) { int step = qMax(32 / m_inputs.size(), 6); int x = 32 - m_inputs.size() * step + step; foreach (QNEPort *port, m_inputs) { COMMENT("Setting input at " << x << ", " << inputPos, 0); port->setPos(x, inputPos); port->update(); x += step * 2; } } } void GraphicElement::refresh() { setPixmap(m_pixmapSkinName[0]); } QVariant GraphicElement::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) { COMMENT("Align to grid.", 4); if ((change == ItemPositionChange) && scene()) { QPointF newPos = value.toPointF(); auto *customScene = dynamic_cast(scene()); if (customScene) { int gridSize = customScene->gridSize(); qreal xV = qRound(newPos.x() / gridSize) * gridSize; qreal yV = qRound(newPos.y() / gridSize) * gridSize; return QPointF(xV, yV); } return newPos; } COMMENT("Moves wires.", 4); if ((change == ItemScenePositionHasChanged) || (change == ItemRotationHasChanged) || (change == ItemTransformHasChanged)) { foreach (QNEPort *port, m_outputs) { port->updateConnections(); } foreach (QNEPort *port, m_inputs) { port->updateConnections(); } } update(); return QGraphicsItem::itemChange(change, value); } bool GraphicElement::hasAudio() const { return m_hasAudio; } void GraphicElement::setHasAudio(bool hasaudio) { m_hasAudio = hasaudio; } QKeySequence GraphicElement::getTrigger() const { return m_trigger; } void GraphicElement::setTrigger(const QKeySequence &trigger) { m_trigger = trigger; updateLabel(); } QString GraphicElement::genericProperties() { return QString(); } void GraphicElement::updateLabel() { QString label = m_labelText; if ((!hasTrigger()) || (getTrigger().toString().isEmpty())) { m_label->setPlainText(label); } else { if (!label.isEmpty()) { label += " "; } m_label->setPlainText(label + QString("(%1)").arg(getTrigger().toString())); } } void GraphicElement::setLabel(const QString &label) { m_labelText = label; updateLabel(); } QString GraphicElement::getLabel() const { return m_labelText; } void GraphicElement::updateTheme() { if (ThemeManager::globalMngr) { const ThemeAttrs attrs = ThemeManager::globalMngr->getAttrs(); m_label->setDefaultTextColor(attrs.graphicElement_labelColor); m_selectionBrush = attrs.selectionBrush; m_selectionPen = attrs.selectionPen; for (QNEInputPort *input : qAsConst(m_inputs)) { input->updateTheme(); } for (QNEOutputPort *output : qAsConst(m_outputs)) { output->updateTheme(); } updateThemeLocal(); setPixmap(m_currentPixmapName); update(); } } void GraphicElement::updateThemeLocal() { } bool GraphicElement::isValid() { COMMENT("Checking if the element has the required signals to comput its value.", 4); bool valid = true; for (QNEInputPort *input : qAsConst(m_inputs)) { /* Required inputs must have exactly one connection. */ if (!input->isValid()) { valid = false; break; } } if (!valid) { for (QNEOutputPort *output : qAsConst(m_outputs)) { for (QNEConnection *conn : output->connections()) { conn->setStatus(QNEConnection::Status::Invalid); QNEInputPort *port = conn->otherPort(output); if (port) { port->setValue(-1); } } } } return valid; } bool GraphicElement::hasColors() const { return m_hasColors; } bool GraphicElement::hasTrigger() const { return m_hasTrigger; } void GraphicElement::setColor(const QString &color) { Q_UNUSED(color); } QString GraphicElement::getColor() const { return QString(); } void GraphicElement::setAudio(const QString &audio) { Q_UNUSED(audio); } QString GraphicElement::getAudio() const { return QString(); } void GraphicElement::setHasColors(bool hasColors) { m_hasColors = hasColors; } void GraphicElement::setCanChangeSkin(bool canChangeSkin) { m_canChangeSkin = canChangeSkin; } void GraphicElement::setHasTrigger(bool hasTrigger) { m_hasTrigger = hasTrigger; } bool GraphicElement::hasFrequency() const { return m_hasFrequency; } void GraphicElement::setHasFrequency(bool hasFrequency) { m_hasFrequency = hasFrequency; } bool GraphicElement::hasLabel() const { return m_hasLabel; } bool GraphicElement::canChangeSkin() const { return m_canChangeSkin; } void GraphicElement::setHasLabel(bool hasLabel) { m_hasLabel = hasLabel; m_label->setVisible(hasLabel); } bool GraphicElement::rotatable() const { return m_rotatable; } void GraphicElement::setRotatable(bool rotatable) { m_rotatable = rotatable; } int GraphicElement::minOutputSz() const { return m_minOutputSz; } int GraphicElement::inputSize() const { return m_inputs.size(); } void GraphicElement::setInputSize(int size) { if ((size >= minInputSz()) && (size <= maxInputSz())) { if (inputSize() < size) { while (inputSize() < size) { addInputPort(); } } else { qDeleteAll(m_inputs.begin() + size, m_inputs.end()); m_inputs.resize(size); updatePorts(); } } } int GraphicElement::outputSize() const { return m_outputs.size(); } void GraphicElement::setOutputSize(const int size) { if ((size >= minOutputSz()) && (size <= maxOutputSz())) { if (outputSize() < size) { for (int port = outputSize(); port < size; ++port) { addOutputPort(); } } else { qDeleteAll(m_outputs.begin() + size, m_outputs.end()); m_outputs.resize(size); } } } float GraphicElement::getFrequency() const { return 0.0; } void GraphicElement::setFrequency(float) { } void GraphicElement::setMinOutputSz(int minOutputSz) { m_minOutputSz = minOutputSz; } int GraphicElement::minInputSz() const { return m_minInputSz; } void GraphicElement::setMinInputSz(const int minInputSz) { m_minInputSz = minInputSz; } int GraphicElement::maxOutputSz() const { return m_maxOutputSz; } void GraphicElement::setMaxOutputSz(int maxOutputSz) { m_maxOutputSz = maxOutputSz; } int GraphicElement::maxInputSz() const { return m_maxInputSz; } void GraphicElement::setMaxInputSz(int maxInputSz) { m_maxInputSz = maxInputSz; } int GraphicElement::bottomPosition() const { return m_bottomPosition; } void GraphicElement::setBottomPosition(int bottomPosition) { m_bottomPosition = bottomPosition; updatePorts(); } int GraphicElement::topPosition() const { return m_topPosition; } void GraphicElement::setTopPosition(int topPosition) { m_topPosition = topPosition; updatePorts(); } bool GraphicElement::outputsOnTop() const { return m_outputsOnTop; } void GraphicElement::setOutputsOnTop(bool outputsOnTop) { m_outputsOnTop = outputsOnTop; updatePorts(); } bool GraphicElement::disabled() { return m_disabled; } wiRedPanda-3.0.1/app/graphicelement.h000066400000000000000000000174131406216046500174630ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef GRAPHICELEMENT_H #define GRAPHICELEMENT_H #include #include #include "elementtype.h" #include "itemwithid.h" enum class ElementGroup : uint_fast8_t { UNKNOWN, OTHER, IC, INPUT, GATE, MEMORY, OUTPUT, MUX, STATICINPUT }; #define MAXIMUMVALIDINPUTSIZE 256 class GraphicElement; class QNEPort; class QNEInputPort; class QNEOutputPort; typedef QVector ElementVector; /** * @brief Virtual class to implement graphical element appearance, input and output ports, and tooltips. * * The appearance includes editable features such as pose, colors, skins, shortcuts, and labels. * It also implements the functions to handle loading and saving the element into files. */ class GraphicElement : public QGraphicsObject, public ItemWithId { Q_OBJECT public: enum : uint32_t { Type = QGraphicsItem::UserType + 3 }; GraphicElement(ElementType type, ElementGroup group, int minInputSz, int maxInputSz, int minOutputSz, int maxOutputSz, QGraphicsItem *parent = nullptr); /* GraphicElement interface. */ ElementType elementType() const; ElementGroup elementGroup() const; /** * @brief Saves the graphic element through a binary data stream. */ virtual void save(QDataStream &ds) const; /** * @brief Loads the graphic element through a binary data stream. * @param portMap receives a reference to each input and output port. */ virtual void load(QDataStream &ds, QMap &portMap, double version); /** * @brief updatePorts: Updates the number and the connected elements to the ports whenever needed (e.g. loading the element, changing the number of inputs/outputs). */ virtual void updatePorts(); virtual void refresh(); /* QGraphicsItem interface */ int type() const override { return Type; } QRectF boundingRect() const override; void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override; /** * @brief addPort: adds an input or output port at the end of the port vector. */ QNEPort *addPort(const QString &name, bool isOutput, int flags = 0, int ptr = 0); /** * @brief addInputPort: adds an input port at the end of the input port vector. */ void addInputPort(const QString &name = QString()); /** * @brief addOutputPort: adds an output port at the end of the output port vector. */ void addOutputPort(const QString &name = QString()); virtual void setPortName(const QString &name); virtual void setSkin(bool defaultSkin, const QString &filename); int topPosition() const; int bottomPosition() const; int maxInputSz() const; int maxOutputSz() const; /** * @brief outputsOnTop: returns true if the output ports are on the top position of the GraphicElement. */ bool outputsOnTop() const; QVector inputs() const; void setInputs(const QVector &inputs); QVector outputs() const; const QNEInputPort *input(int pos = 0) const; const QNEOutputPort *output(int pos = 0) const; QNEInputPort *input(int pos = 0); QNEOutputPort *output(int pos = 0); void setOutputs(const QVector &outputs); int minInputSz() const; int minOutputSz() const; int inputSize() const; void setInputSize(int size); int outputSize() const; void setOutputSize(const int size); /** * @brief getFrequency: virtual function overloaded by clock element. Other elements have frequency of 0. */ virtual float getFrequency() const; virtual void setFrequency(float freq); void setPixmap(const QString &pixmapName); void setPixmap(const QString &pixmapName, QRect size); bool rotatable() const; bool hasLabel() const; bool canChangeSkin() const; bool hasFrequency() const; bool hasColors() const; bool hasTrigger() const; bool hasAudio() const; virtual void setColor(const QString &color); virtual QString getColor() const; virtual void setAudio(const QString &audio); virtual QString getAudio() const; bool isValid(); void setLabel(const QString &label); QString getLabel() const; /** * @brief updateTheme: Updates the GraphicElement theme according to the dark/light wiRed Panda theme. */ void updateTheme(); /** * @brief updateThemeLocal: unfinished function with no current use. */ virtual void updateThemeLocal(); void disable(); void enable(); bool disabled(); QPixmap getPixmap() const; QKeySequence getTrigger() const; void setTrigger(const QKeySequence &trigger); virtual QString genericProperties(); // Update label in graphical interface void updateLabel(); void updateSkinsPath(const QString &newSkinPath); private: /** * @brief Current pixmap displayed for this GraphicElement. */ QPixmap *m_pixmap; QString m_currentPixmapName; QColor m_selectionBrush; QColor m_selectionPen; QGraphicsTextItem *m_label; int m_topPosition; int m_bottomPosition; quint64 m_maxInputSz; quint64 m_maxOutputSz; quint64 m_minInputSz; quint64 m_minOutputSz; bool m_outputsOnTop; bool m_rotatable; bool m_hasLabel; bool m_canChangeSkin; bool m_hasFrequency; bool m_hasColors; bool m_hasTrigger; bool m_hasAudio; bool m_disabled; ElementType m_elementType; ElementGroup m_elementGroup; QString m_labelText; QKeySequence m_trigger; /** * functions to load GraphicElement atributes through a binary data stream */ void loadPos(QDataStream &ds); void loadAngle(QDataStream &ds); void loadLabel(QDataStream &ds, double version); void loadMinMax(QDataStream &ds, double version); void loadTrigger(QDataStream &ds, double version); void loadInputPorts(QDataStream &ds, QMap &portMap); void loadOutputPorts(QDataStream &ds, QMap &portMap); void loadInputPort(QDataStream &ds, QMap &portMap, size_t port); void loadOutputPort(QDataStream &ds, QMap &portMap, size_t port); void loadPixmapSkinNames(QDataStream &ds, double version); void loadPixmapSkinName(QDataStream &ds, size_t skin); void removePortFromMap(QNEPort *deletedPort, QMap &portMap); void removeSurplusInputs(quint64 inputSz, QMap &portMap); void removeSurplusOutputs(quint64 outputSz, QMap &portMap); protected: /** * @brief Path to all current skins. The default skin is in a research file. Custom skin names are system file paths defined by the user. */ QVector m_pixmapSkinName; void setRotatable(bool rotatable); void setHasLabel(bool hasLabel); void setHasFrequency(bool hasFrequency); void setHasColors(bool hasColors); void setCanChangeSkin(bool canChangeSkin); void setHasTrigger(bool hasTrigger); void setMinInputSz(const int minInputSz); void setMinOutputSz(int minOutputSz); void setHasAudio(bool hasAudio); void setOutputsOnTop(bool outputsOnTop); void setMaxOutputSz(int maxOutputSz); void setMaxInputSz(int maxInputSz); void setTopPosition(int topPosition); void setBottomPosition(int bottomPosition); QVariant itemChange(GraphicsItemChange change, const QVariant &value) override; bool m_usingDefaultSkin; /** * @brief m_inputs: input port vector */ QVector m_inputs; /** * @brief m_outputs: output port vector */ QVector m_outputs; }; #endif /* GRAPHICELEMENT_H */ wiRedPanda-3.0.1/app/graphicsview.cpp000066400000000000000000000040151406216046500175140ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "graphicsview.h" #include #include #include #include "graphicsviewzoom.h" GraphicsView::GraphicsView(QWidget *parent) : QGraphicsView(parent) { m_pan = false; m_space = false; m_panStartX = 0; m_panStartY = 0; setAcceptDrops(true); m_gvzoom = new GraphicsViewZoom(this); } void GraphicsView::mousePressEvent(QMouseEvent *e) { if (e->button() == Qt::MiddleButton) { m_pan = true; m_panStartX = e->x(); m_panStartY = e->y(); QApplication::setOverrideCursor(Qt::ClosedHandCursor); e->accept(); return; } QGraphicsView::mousePressEvent(e); } void GraphicsView::mouseReleaseEvent(QMouseEvent *e) { if (e->button() == Qt::MiddleButton) { m_pan = false; QApplication::restoreOverrideCursor(); e->accept(); return; } QGraphicsView::mouseReleaseEvent(e); } void GraphicsView::mouseMoveEvent(QMouseEvent *e) { if (m_pan || m_space) { horizontalScrollBar()->setValue(horizontalScrollBar()->value() - (e->x() - m_panStartX)); verticalScrollBar()->setValue(verticalScrollBar()->value() - (e->y() - m_panStartY)); m_panStartX = e->x(); m_panStartY = e->y(); e->accept(); return; } m_panStartX = e->x(); m_panStartY = e->y(); QGraphicsView::mouseMoveEvent(e); } void GraphicsView::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Space) { m_space = true; QApplication::setOverrideCursor(Qt::ClosedHandCursor); e->accept(); } QGraphicsView::keyPressEvent(e); } void GraphicsView::keyReleaseEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Space) { m_space = false; QApplication::restoreOverrideCursor(); e->accept(); } QGraphicsView::keyReleaseEvent(e); } GraphicsViewZoom *GraphicsView::gvzoom() const { return m_gvzoom; }wiRedPanda-3.0.1/app/graphicsview.h000066400000000000000000000014661406216046500171700ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef GRAPHICSVIEW_H #define GRAPHICSVIEW_H #include class GraphicsViewZoom; class GraphicsView : public QGraphicsView { Q_OBJECT public: explicit GraphicsView(QWidget *parent = nullptr); /* QWidget interface */ GraphicsViewZoom *gvzoom() const; protected: void mousePressEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void keyPressEvent(QKeyEvent *e) override; void keyReleaseEvent(QKeyEvent *e) override; private: bool m_pan; bool m_space; int m_panStartX; int m_panStartY; GraphicsViewZoom *m_gvzoom; }; #endif /* GRAPHICSVIEW_H */ wiRedPanda-3.0.1/app/graphicsviewzoom.cpp000066400000000000000000000065211406216046500204250ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "graphicsviewzoom.h" #include #include #include #include #include #include #define ZOOMFAC 0.1 GraphicsViewZoom::GraphicsViewZoom(QGraphicsView *view) : QObject(view) , m_view(view) { m_view->viewport()->installEventFilter(this); m_view->setMouseTracking(true); m_modifiers = Qt::ControlModifier; m_zoomFactorBase = 1.0015; } void GraphicsViewZoom::gentleZoom(double factor) { QTransform tr = m_view->transform().scale(factor, factor); double scfac = tr.m11(); if ((scfac >= minZoom) && (scfac <= maxZoom)) { m_view->setTransform(tr); m_view->centerOn(m_targetScenePos); QPointF delta_viewport_pos = m_targetViewportPos - QPointF(m_view->viewport()->width() / 2.0, m_view->viewport()->height() / 2.0); QPointF viewport_center = m_view->mapFromScene(m_targetScenePos) - delta_viewport_pos; m_view->centerOn(m_view->mapToScene(viewport_center.toPoint())); emit zoomed(); } } void GraphicsViewZoom::setModifiers(Qt::KeyboardModifiers modifiers) { m_modifiers = modifiers; } void GraphicsViewZoom::setZoomFactorBase(double value) { m_zoomFactorBase = value; } void GraphicsViewZoom::zoomIn() { m_view->setTransformationAnchor(QGraphicsView::AnchorUnderMouse); double newScale = std::round(scaleFactor() * 10) / 10.0 + ZOOMFAC; if (newScale <= GraphicsViewZoom::maxZoom) { setScaleFactor(newScale); } emit zoomed(); } void GraphicsViewZoom::zoomOut() { double newScale = std::round(scaleFactor() * 10) / 10.0 - ZOOMFAC; if (newScale >= GraphicsViewZoom::minZoom) { setScaleFactor(newScale); } emit zoomed(); } void GraphicsViewZoom::resetZoom() { setScaleFactor(1.0); emit zoomed(); } double GraphicsViewZoom::scaleFactor() { return m_view->transform().m11(); } void GraphicsViewZoom::setScaleFactor(double factor) { QTransform tr = m_view->transform(); tr *= QTransform::fromScale(1.0 / tr.m11(), 1.0 / tr.m11()); tr.scale(factor, factor); m_view->setTransform(tr); } bool GraphicsViewZoom::canZoomIn() { return (scaleFactor() + ZOOMFAC * 2) <= GraphicsViewZoom::maxZoom; } bool GraphicsViewZoom::canZoomOut() { return (scaleFactor() - ZOOMFAC * 2) >= GraphicsViewZoom::minZoom; } bool GraphicsViewZoom::eventFilter(QObject *object, QEvent *event) { if (event->type() == QEvent::MouseMove) { auto *mouse_event = static_cast(event); QPointF delta = m_targetViewportPos - mouse_event->pos(); if ((qAbs(delta.x()) > 5) || (qAbs(delta.y()) > 5)) { m_targetViewportPos = mouse_event->pos(); m_targetScenePos = m_view->mapToScene(mouse_event->pos()); } } else if (event->type() == QEvent::Wheel) { auto *wheel_event = static_cast(event); if (QApplication::keyboardModifiers() == m_modifiers) { // if( wheel_event->orientation( ) == Qt::Vertical ) { double angle = wheel_event->angleDelta().y(); double factor = qPow(m_zoomFactorBase, angle); gentleZoom(factor); return true; //} } } Q_UNUSED(object) return false; } wiRedPanda-3.0.1/app/graphicsviewzoom.h000066400000000000000000000044671406216046500201010ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef GRAPHICSVIEWZOOM_H #define GRAPHICSVIEWZOOM_H #include #include /*! * This class adds ability to zoom QGraphicsView using mouse wheel. The point under cursor * remains motionless while it's possible. * * Note that it becomes not possible when the scene's * size is not large enough comparing to the viewport size. QGraphicsView centers the picture * when it's smaller than the view. And QGraphicsView's scrolls boundaries don't allow to * put any picture point at any viewport position. * * When the user starts scrolling, this class remembers original scene position and * keeps it until scrolling is completed. It's better than getting original scene position at * each scrolling step because that approach leads to position errors due to before-mentioned * positioning restrictions. * * When zommed using scroll, this class emits zoomed() signal. * * Usage: * * new Graphics_view_zoom(view); * * The object will be deleted automatically when the view is deleted. * * You can set keyboard modifiers used for zooming using set_modified(). Zooming will be * performed only on exact match of modifiers combination. The default modifier is Ctrl. * * You can change zoom velocity by calling set_zoom_factor_base(). * Zoom coefficient is calculated as zoom_factor_base^angle_delta * (see QWheelEvent::angleDelta). * The default zoom factor base is 1.0015. */ class QGraphicsView; class GraphicsViewZoom : public QObject { Q_OBJECT public: static constexpr double maxZoom = 1.5; static constexpr double minZoom = 0.2; explicit GraphicsViewZoom(QGraphicsView *view); void gentleZoom(double factor); void setModifiers(Qt::KeyboardModifiers modifiers); void setZoomFactorBase(double value); void zoomIn(); void zoomOut(); bool canZoomIn(); bool canZoomOut(); void resetZoom(); private: double scaleFactor(); void setScaleFactor(double factor); QGraphicsView *m_view; Qt::KeyboardModifiers m_modifiers; double m_zoomFactorBase; QPointF m_targetScenePos, m_targetViewportPos; bool eventFilter(QObject *object, QEvent *event) override; signals: void zoomed(); }; #endif /* GRAPHICSVIEWZOOM_H */ wiRedPanda-3.0.1/app/ic.cpp000066400000000000000000000106361406216046500154220ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "ic.h" #include #include #include #include #include #include #include #include #include #include #include "common.h" #include "elementfactory.h" #include "globalproperties.h" #include "icmanager.h" #include "icnotfoundexception.h" #include "icprototype.h" #include "inputswitch.h" #include "nodes/qneconnection.h" #include "qneport.h" #include "serializationfunctions.h" IC::IC(QGraphicsItem *parent) : GraphicElement(ElementType::IC, ElementGroup::IC, 0, 0, 0, 0, parent) { m_pixmapSkinName.append(":/basic/box.png"); setHasLabel(true); setPixmap(m_pixmapSkinName[0], QRect(0, 0, 64, 64)); setOutputsOnTop(true); setPortName("IC"); } IC::~IC() { ICPrototype *prototype = ICManager::instance()->getPrototype(m_file); if (prototype) { prototype->removeICObserver(this); } } void IC::save(QDataStream &ds) const { GraphicElement::save(ds); ds << m_file; } void IC::load(QDataStream &ds, QMap &portMap, double version) { GraphicElement::load(ds, portMap, version); if (version >= 1.2) { ds >> m_file; } } void IC::loadInputs(ICPrototype *prototype) { setMaxInputSz(prototype->inputSize()); setMinInputSz(prototype->inputSize()); setInputSize(prototype->inputSize()); COMMENT("IC " << m_file.toStdString() << " -> Inputs. min: " << minInputSz() << ", max: " << maxInputSz() << ", current: " << inputSize() << ", m_inputs: " << m_inputs.size(), 0); for (int inputIdx = 0; inputIdx < prototype->inputSize(); ++inputIdx) { QNEPort *in = input(inputIdx); in->setName(prototype->inputLabel(inputIdx)); in->setRequired(prototype->isInputRequired(inputIdx)); in->setDefaultValue(prototype->defaultInputValue(inputIdx)); in->setValue(prototype->defaultInputValue(inputIdx)); } } void IC::loadOutputs(ICPrototype *prototype) { setMaxOutputSz(prototype->outputSize()); setMinOutputSz(prototype->outputSize()); setOutputSize(prototype->outputSize()); for (int outputIdx = 0; outputIdx < prototype->outputSize(); ++outputIdx) { QNEPort *in = output(outputIdx); in->setName(prototype->outputLabel(outputIdx)); } COMMENT("IC " << m_file.toStdString() << " -> Outputs. min: " << minOutputSz() << ", max: " << maxOutputSz() << ", current: " << outputSize() << ", m_outputs: " << m_outputs.size(), 0); } void IC::loadFile(const QString &fname) { ICPrototype *prototype = ICManager::instance()->getPrototype(fname); Q_ASSERT(prototype); m_file = prototype->fileName(); setToolTip(m_file); prototype->insertICObserver(this); if (getLabel().isEmpty()) { setLabel(prototype->baseName().toUpper()); } // Loading inputs loadInputs(prototype); // Loading outputs loadOutputs(prototype); updatePorts(); } QString IC::getFile() const { return m_file; } bool IC::setFile(const QString &newFileName) { COMMENT("Updating ic name.", 0); if (!ICManager::instance()->updatePrototypeFilePathName(m_file, newFileName)) { return false; } m_file = newFileName; return true; } ICPrototype *IC::getPrototype() { return ICManager::instance()->getPrototype(m_file); } void IC::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) { if (event->button() == Qt::LeftButton) { QMessageBox msgBox; /* msgBox.setParent( ); */ msgBox.setLocale(QLocale::Portuguese); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setText(tr("Do you want to load this file?
%1").arg(m_file)); msgBox.setWindowModality(Qt::ApplicationModal); msgBox.setDefaultButton(QMessageBox::Yes); if (msgBox.exec() == QMessageBox::Yes) { auto *wPanda = new QProcess(scene()); QStringList args; args << m_file; wPanda->start(QCoreApplication::applicationFilePath(), args); } } } void IC::setSkin(bool defaultSkin, const QString &filename) { if (defaultSkin) { m_pixmapSkinName[0] = ":/basic/box.png"; } else { m_pixmapSkinName[0] = filename; } setPixmap(m_pixmapSkinName[0]); } wiRedPanda-3.0.1/app/ic.h000066400000000000000000000020641406216046500150630ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef IC_H #define IC_H #include "graphicelement.h" #include "simulationcontroller.h" class Editor; class ICPrototype; class IC : public GraphicElement { Q_OBJECT friend class CodeGenerator; public: IC(QGraphicsItem *parent = nullptr); ~IC() override; void save(QDataStream &ds) const override; void load(QDataStream &ds, QMap &portMap, double version) override; void loadFile(const QString &fname); QString getFile() const; bool setFile(const QString &newFileName); ICPrototype *getPrototype(); QVector getElements() const; void setSkin(bool defaultSkin, const QString &filename) override; private: QString m_file; void loadInputs(ICPrototype *prototype); void loadOutputs(ICPrototype *prototype); /* QGraphicsItem interface */ protected: void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) override; }; #endif /* IC_H */ wiRedPanda-3.0.1/app/icmanager.cpp000066400000000000000000000125171406216046500167550ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "icmanager.h" #include #include #include #include #include "common.h" #include "filehelper.h" #include "ic.h" #include "icnotfoundexception.h" #include "icprototype.h" #include "mainwindow.h" ICManager *ICManager::globalICManager = nullptr; ICManager::ICManager(MainWindow *mainWindow, QObject *parent) : QObject(parent) , m_mainWindow(mainWindow) { if (globalICManager == nullptr) { globalICManager = this; } connect(&m_fileWatcher, &QFileSystemWatcher::fileChanged, this, &ICManager::reloadFile); if (m_mainWindow) { connect(this, &ICManager::addRecentIcFile, m_mainWindow, &MainWindow::addRecentIcFile); } } ICManager::~ICManager() { clear(); QSettings settings(QSettings::IniFormat, QSettings::UserScope, QApplication::organizationName(), QApplication::applicationName()); settings.setValue("recentICs", ""); // fprintf(stderr, "Removing IC manager"); if (globalICManager == this) { globalICManager = nullptr; } } bool ICManager::tryLoadFile(QString &fname, const QString& parentFile) { try { loadFile(fname, parentFile); } catch (ICNotFoundException &err) { COMMENT("ICNotFoundException thrown: " << err.what(), 0); int ret = QMessageBox::warning(m_mainWindow, tr("Error"), QString::fromStdString(err.what()), QMessageBox::Ok, QMessageBox::Cancel); if (ret == QMessageBox::Cancel) { return false; } fname = m_mainWindow->getOpenICFile(); if (fname.isEmpty()) { return false; } return tryLoadFile(fname, parentFile); } return true; } void ICManager::loadFile(QString &fname, const QString& parentFile) { QFileInfo finfo = FileHelper::findICFile(fname, parentFile); fname = finfo.filePath(); Q_ASSERT(finfo.exists() && finfo.isFile()); m_fileWatcher.addPath(finfo.absoluteFilePath()); if (m_ics.contains(finfo.baseName())) { COMMENT("IC already inserted: " << finfo.baseName().toStdString(), 0); } else { COMMENT("Inserting IC: " << finfo.baseName().toStdString(), 0); auto *prototype = new ICPrototype(finfo.absoluteFilePath()); prototype->reload(); m_ics.insert(finfo.baseName(), prototype); } } void ICManager::clear() { COMMENT("Clear ICManager", 0); QMap ics_aux = m_ics; m_ics.clear(); qDeleteAll(ics_aux); if (m_fileWatcher.files().size() > 0) { m_fileWatcher.removePaths(m_fileWatcher.files()); } COMMENT("Finished clearing ICManager.", 0); } bool ICManager::loadIC(IC *ic, QString fname, const QString &parentFile) { if (tryLoadFile(fname, parentFile)) { ic->loadFile(fname); } emit addRecentIcFile(fname); return true; } ICPrototype *ICManager::getPrototype(const QString& fname) { Q_ASSERT(!fname.isEmpty()); QFileInfo finfo(fname); if (!m_ics.contains(finfo.baseName())) { return nullptr; } return m_ics[finfo.baseName()]; } bool ICManager::updatePrototypeFilePathName(const QString& sourceName, const QString& targetName) { COMMENT("Updating IC name from " << sourceName.toStdString() << " to " << targetName.toStdString(), 0); Q_ASSERT(!sourceName.isEmpty()); Q_ASSERT(!targetName.isEmpty()); QFileInfo finfo(sourceName); if (!m_ics.contains(finfo.baseName())) { return false; } COMMENT("Updating prototype IC name.", 0); auto proto = m_ics[finfo.baseName()]; proto->fileName(targetName); COMMENT("Updating m_fileWatcher. Removing " << sourceName.toStdString(), 0); if (m_fileWatcher.removePath(sourceName)) { COMMENT("Adding " << targetName.toStdString() << " to m_fileWatcher.", 0); m_fileWatcher.addPath(targetName); } else { COMMENT("Warning. FileWatcher did not exist. Probably already changed by other IC instance update.", 0); } return true; } ICManager *ICManager::instance() { return globalICManager; } void ICManager::reloadFile(const QString& fileName) { COMMENT("Change in IC " << fileName.toStdString() << " detected.", 0); QString bname = QFileInfo(fileName).baseName(); m_fileWatcher.addPath(fileName); if (warnAboutFileChange(bname)) { if (m_ics.contains(bname)) { try { m_ics[bname]->reload(); } catch (std::runtime_error &e) { QMessageBox::warning(m_mainWindow, "Error", tr("Error reloading IC: ") + e.what(), QMessageBox::Ok, QMessageBox::NoButton); } } } emit updatedIC(); } // Maybe this function should never be called and the main project should reload the IC every time it changes. bool ICManager::warnAboutFileChange(const QString &fileName) { COMMENT("File " << fileName.toStdString() << " has changed!", 0); QMessageBox msgBox; if (m_mainWindow) { msgBox.setParent(m_mainWindow); } msgBox.setLocale(QLocale::Portuguese); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setText(tr("The file %1 changed, do you want to reload?").arg(fileName)); msgBox.setWindowModality(Qt::ApplicationModal); msgBox.setDefaultButton(QMessageBox::Yes); return msgBox.exec() == QMessageBox::Yes; } wiRedPanda-3.0.1/app/icmanager.h000066400000000000000000000023001406216046500164070ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef ICMANAGER_H #define ICMANAGER_H #include #include #include class MainWindow; class ICPrototype; class IC; class ICManager : public QObject { Q_OBJECT public: ICManager(MainWindow *mainWindow = nullptr, QObject *parent = nullptr); ~ICManager() override; void clear(); bool loadIC(IC *ic, QString fname, const QString &parentFile = ""); ICPrototype *getPrototype(const QString& fname); static ICManager *instance(); bool updatePrototypeFilePathName(const QString& sourceName, const QString& targetName); signals: void updatedIC(); void addRecentIcFile(const QString& fname); private slots: void reloadFile(const QString& bname); private: bool tryLoadFile(QString &fname, const QString& parentFile); void loadFile(QString &fname, const QString& parentFile); bool warnAboutFileChange(const QString &fileName); static ICManager *globalICManager; QMap m_ics; MainWindow *m_mainWindow; QFileSystemWatcher m_fileWatcher; }; #endif // ICMANAGER_H wiRedPanda-3.0.1/app/icmapping.cpp000066400000000000000000000022101406216046500167630ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "icmapping.h" ICMapping::ICMapping(const QString &file, const ElementVector &elms, const QNEPortVector &inputs, const QNEPortVector &outputs) : ElementMapping(elms, file) , m_icInputs(inputs) , m_icOutputs(outputs) { } ICMapping::~ICMapping() = default; void ICMapping::initialize() { ElementMapping::initialize(); for (QNEPort *port : qAsConst(m_icInputs)) { m_inputs.append(m_elementMap[port->graphicElement()]); } for (QNEPort *port : qAsConst(m_icOutputs)) { m_outputs.append(m_elementMap[port->graphicElement()]); } } void ICMapping::clearConnections() { for (LogicElement *in : qAsConst(m_inputs)) { in->clearPredecessors(); } for (LogicElement *out : qAsConst(m_outputs)) { out->clearSucessors(); } } LogicElement *ICMapping::getInput(int index) { Q_ASSERT(index < m_icInputs.size()); return m_inputs[index]; } LogicElement *ICMapping::getOutput(int index) { Q_ASSERT(index < m_icOutputs.size()); return m_outputs[index]; } wiRedPanda-3.0.1/app/icmapping.h000066400000000000000000000014171406216046500164400ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef ICMAPPING_H #define ICMAPPING_H #include "elementmapping.h" #include "graphicelement.h" #include "qneport.h" class LogicElement; class ICMapping : public ElementMapping { private: QNEPortVector m_icInputs; QNEPortVector m_icOutputs; QVector m_inputs; QVector m_outputs; public: ICMapping(const QString &file, const ElementVector &elms, const QNEPortVector &inputs, const QNEPortVector &outputs); ~ICMapping() override; void initialize() override; void clearConnections(); LogicElement *getInput(int index); LogicElement *getOutput(int index); }; #endif // ICMAPPING_H wiRedPanda-3.0.1/app/icnotfoundexception.cpp000066400000000000000000000005521406216046500211120ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "icnotfoundexception.h" #include ICNotFoundException::ICNotFoundException(const std::string &message, IC *ic) : std::runtime_error(message) , m_ic(ic) { } IC *ICNotFoundException::getIC() const { return m_ic; } wiRedPanda-3.0.1/app/icnotfoundexception.h000066400000000000000000000006551406216046500205630ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef ICNOTFOUNDEXCEPTION_H #define ICNOTFOUNDEXCEPTION_H #include class IC; class ICNotFoundException : public std::runtime_error { public: ICNotFoundException(const std::string &message, IC *ic); IC *getIC() const; private: IC *m_ic; }; #endif /* ICNOTFOUNDEXCEPTION_H */ wiRedPanda-3.0.1/app/icprototype.cpp000066400000000000000000000035341406216046500174070ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "icprototype.h" #include #include "ic.h" #include "icmapping.h" #include "qneport.h" ICPrototype::ICPrototype(const QString &fileName) : m_fileName(fileName) { } void ICPrototype::fileName(const QString &newFileName) { m_fileName = newFileName; } QString ICPrototype::fileName() const { return m_fileName; } QString ICPrototype::baseName() const { return QFileInfo(m_fileName).baseName(); } void ICPrototype::insertICObserver(IC *ic) { if (!m_icObservers.contains(ic)) { m_icObservers.append(ic); } } void ICPrototype::removeICObserver(IC *ic) { if (m_icObservers.contains(ic)) { m_icObservers.removeAll(ic); } } bool ICPrototype::updateLocalIC(const QString &fileName, const QString &dirName) { return m_ICImpl.updateLocalIC(fileName, dirName); } int ICPrototype::inputSize() const { return m_ICImpl.getInputSize(); } int ICPrototype::outputSize() const { return m_ICImpl.getOutputSize(); } QString ICPrototype::inputLabel(int index) const { return m_ICImpl.getInputLabel(index); } QString ICPrototype::outputLabel(int index) const { return m_ICImpl.getOutputLabel(index); } bool ICPrototype::defaultInputValue(int index) { return m_ICImpl.getInput(index)->value(); } bool ICPrototype::isInputRequired(int index) { return m_ICImpl.getInput(index)->isRequired(); } ICMapping *ICPrototype::generateMapping() const { return m_ICImpl.generateMapping(fileName()); } void ICPrototype::clear() { m_ICImpl.clear(); } void ICPrototype::reload() { // TODO: Verify file recursion // verifyRecursion( fname ); clear(); m_ICImpl.loadFile(m_fileName); for (IC *ic : qAsConst(m_icObservers)) { ic->loadFile(m_fileName); } } wiRedPanda-3.0.1/app/icprototype.h000066400000000000000000000025051406216046500170510ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef ICPROTOTYPE_H #define ICPROTOTYPE_H #include "icprototypeimpl.h" #include #include class ICMapping; class GraphicElement; class IC; class ICPrototype { public: ICPrototype(const QString &fileName); void reload(); void fileName(const QString &newFileName); QString fileName() const; QString baseName() const; void insertICObserver(IC *ic); void removeICObserver(IC *ic); /** * @brief updateLocalIC: Updates all subIC paths inside the file of this prototype. * While saving a local project, WiredPanda copies all ICs e subICs to a project local directory. * Then, this function is responsible for updating the correct references of IC files to their subICs. */ bool updateLocalIC(const QString &fileName, const QString &icDirName); int inputSize() const; int outputSize() const; QString inputLabel(int index) const; QString outputLabel(int index) const; bool defaultInputValue(int index); bool isInputRequired(int index); ICMapping *generateMapping() const; private: void clear(); QString m_fileName; ICPrototypeImpl m_ICImpl; QVector m_icObservers; }; #endif // ICPROTOTYPE_H wiRedPanda-3.0.1/app/icprototypeimpl.cpp000066400000000000000000000176221406216046500202740ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "icprototypeimpl.h" #include #include #include "common.h" #include "elementfactory.h" #include "ic.h" #include "icprototype.h" #include "icmapping.h" #include "qneconnection.h" #include "qneport.h" #include "serializationfunctions.h" ICPrototypeImpl::~ICPrototypeImpl() { qDeleteAll(m_elements); } bool comparePorts(QNEPort *port1, QNEPort *port2) { QPointF p1 = port1->graphicElement()->pos(); QPointF p2 = port2->graphicElement()->pos(); if (p1 != p2) { return p1.y() < p2.y() || (qFuzzyCompare(p1.y(), p2.y()) && p1.x() < p2.x()); } p1 = port1->pos(); p2 = port2->pos(); return p1.x() < p2.x() || (qFuzzyCompare(p1.x(), p2.x()) && p1.y() < p2.y()); } void ICPrototypeImpl::sortPorts(QVector &map) { std::stable_sort(map.begin(), map.end(), comparePorts); } bool ICPrototypeImpl::updateLocalIC(const QString &fileName, const QString &dirName) { COMMENT("Recursive call to sub ics.", 0); for (GraphicElement *elm : qAsConst(m_elements)) { if (elm->elementType() == ElementType::IC) { IC *ic = dynamic_cast(elm); QString originalSubICName = ic->getFile(); QString subICFileName = dirName + "/boxes/" + QFileInfo(originalSubICName).fileName(); auto prototype = ic->getPrototype(); if (!QFile::exists(subICFileName)) { COMMENT("Copying subic file to local dir. File does not exist yet.", 0); QFile::copy(originalSubICName, subICFileName); if (prototype->updateLocalIC(subICFileName, dirName)) { if (!ic->setFile(subICFileName)) { std::cerr << "Error updating subic name." << std::endl; return false; } } else { std::cerr << "Error saving subic." << std::endl; } } } } COMMENT("Updating references of subics in IC files!!!", 0); try { if (!SerializationFunctions::update(fileName, dirName)) { std::cerr << "Error saving local ic." << std::endl; return false; } COMMENT("Saved at prototype impl.", 0); } catch (std::runtime_error &e) { std::cerr << "Error saving project: " << e.what() << std::endl; return false; } return true; } void ICPrototypeImpl::loadFile(const QString &fileName) { COMMENT("Reading ic", 0); clear(); QFile file(fileName); if (file.open(QFile::ReadOnly)) { QDataStream ds(&file); QList items = SerializationFunctions::load(ds, fileName); for (QGraphicsItem *item : qAsConst(items)) { loadItem(item); } } setInputSize(m_inputs.size()); setOutputSize(m_outputs.size()); sortPorts(m_inputs); sortPorts(m_outputs); loadInputs(); loadOutputs(); COMMENT("Finished Reading ic", 0); } void ICPrototypeImpl::loadInputs() { for (int portIndex = 0; portIndex < getInputSize(); ++portIndex) { GraphicElement *elm = m_inputs.at(portIndex)->graphicElement(); QString lb = elm->getLabel(); if (lb.isEmpty()) { lb = elm->objectName(); } if (!m_inputs.at(portIndex)->portName().isEmpty()) { lb += " "; lb += m_inputs.at(portIndex)->portName(); } if (!elm->genericProperties().isEmpty()) { lb += " [" + elm->genericProperties() + "]"; } m_inputLabels[portIndex] = lb; } } void ICPrototypeImpl::loadOutputs() { for (int portIndex = 0; portIndex < getOutputSize(); ++portIndex) { GraphicElement *elm = m_outputs.at(portIndex)->graphicElement(); QString lb = elm->getLabel(); if (lb.isEmpty()) { lb = elm->objectName(); } if (!m_outputs.at(portIndex)->portName().isEmpty()) { lb += " "; lb += m_outputs.at(portIndex)->portName(); } if (!elm->genericProperties().isEmpty()) { lb += " [" + elm->genericProperties() + "]"; } m_outputLabels[portIndex] = lb; } } void ICPrototypeImpl::loadInputElement(GraphicElement *elm) { auto const outputs = elm->outputs(); for (QNEOutputPort *port : outputs) { GraphicElement *nodeElm = ElementFactory::buildElement( ElementType::NODE); // Problem here. Inputs and outputs are transformed into nodes. And I can not use created connections with new elements... nodeElm->setPos(elm->pos()); // Solution 1: Create new connections cloning all circuit. nodeElm->setLabel(elm->getLabel()); // Solution 2: Load and save ic circuit out of this code... QNEInputPort *nodeInput = nodeElm->input(); nodeInput->setPos(port->pos()); nodeInput->setName(port->getName()); nodeInput->setRequired(false); nodeInput->setDefaultValue(port->value()); nodeInput->setValue(port->value()); if (elm->elementType() == ElementType::CLOCK) { nodeInput->setRequired(true); } m_inputs.append(nodeInput); m_elements.append(nodeElm); QList conns = port->connections(); for (QNEConnection *conn : conns) { // Solution 3. Revert this process before saving and then make it again after saving... if (port == conn->start()) { conn->setStart(nodeElm->output()); } } } elm->disable(); // Maybe I will have to enable and disable it as well. } void ICPrototypeImpl::loadOutputElement(GraphicElement *elm) { auto const inputs = elm->inputs(); for (QNEInputPort *port : inputs) { GraphicElement *nodeElm = ElementFactory::buildElement(ElementType::NODE); nodeElm->setPos(elm->pos()); nodeElm->setLabel(elm->getLabel()); QNEOutputPort *nodeOutput = nodeElm->output(); nodeOutput->setPos(port->pos()); nodeOutput->setName(port->getName()); m_outputs.append(nodeOutput); m_elements.append(nodeElm); QList conns = port->connections(); for (QNEConnection *conn : conns) { if (port == conn->end()) { conn->setEnd(nodeElm->input()); } } } } void ICPrototypeImpl::loadItem(QGraphicsItem *item) { if (item->type() == GraphicElement::Type) { auto *elm = qgraphicsitem_cast(item); if (elm) { if (elm->elementGroup() == ElementGroup::INPUT) { loadInputElement(elm); } else if (elm->elementGroup() == ElementGroup::OUTPUT) { loadOutputElement(elm); } else { m_elements.append(elm); } } } } void ICPrototypeImpl::clear() { m_inputs.clear(); m_outputs.clear(); setInputSize(0); setOutputSize(0); qDeleteAll(m_elements); m_elements.clear(); } int ICPrototypeImpl::getInputSize() const { return m_inputs.size(); } int ICPrototypeImpl::getOutputSize() const { return m_outputs.size(); } void ICPrototypeImpl::setOutputSize(int outputSize) { m_outputLabels = QVector(outputSize); } void ICPrototypeImpl::setInputSize(int inputSize) { m_inputLabels = QVector(inputSize); } GraphicElement *ICPrototypeImpl::getElement(int index) { return m_elements[index]; } QString ICPrototypeImpl::getInputLabel(int index) const { return m_inputLabels[index]; } QString ICPrototypeImpl::getOutputLabel(int index) const { return m_outputLabels[index]; } QNEPort *ICPrototypeImpl::getInput(int index) { return m_inputs[index]; } QNEPort *ICPrototypeImpl::getOutput(int index) { return m_outputs[index]; } ICMapping *ICPrototypeImpl::generateMapping(const QString &fileName) const { return new ICMapping(fileName, m_elements, m_inputs, m_outputs); } wiRedPanda-3.0.1/app/icprototypeimpl.h000066400000000000000000000031431406216046500177320ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef ICPROTOTYPEIMPL_H #define ICPROTOTYPEIMPL_H #include class GraphicElement; class QGraphicsItem; class QNEPort; class ICMapping; class ICPrototypeImpl { public: ~ICPrototypeImpl(); void loadFile(const QString &fileName); void clear(); int getInputSize() const; int getOutputSize() const; void setOutputSize(int outSize); void setInputSize(int inSize); void loadInputElement(GraphicElement *elm); void loadOutputElement(GraphicElement *elm); /** * @brief updateLocalIC: Updates all subIC paths inside the file of this prototype. * While saving a local project, WiredPanda copies all ICs e subICs to a project local directory. * Then, this function is responsible for updating the correct references of IC files to their subICs. */ bool updateLocalIC(const QString &fileName, const QString &icDirName); GraphicElement *getElement(int index); QString getInputLabel(int index) const; QString getOutputLabel(int index) const; QNEPort *getInput(int index); QNEPort *getOutput(int index); ICMapping *generateMapping(const QString &fileName) const; private: void sortPorts(QVector &map); void loadItem(QGraphicsItem *item); void loadInputs(); void loadOutputs(); QVector m_elements; QVector m_inputLabels; QVector m_outputLabels; QVector m_inputs; QVector m_outputs; }; #endif // ICPROTOTYPEIMPL_H wiRedPanda-3.0.1/app/install.pri000066400000000000000000000055331406216046500165050ustar00rootroot00000000000000unix{ #VARIABLES isEmpty(PREFIX) { PREFIX = /usr/local } BINDIR = $$PREFIX/bin DATADIR = $$PREFIX/share DEFINES += DATADIR=\\\"$$DATADIR\\\" PKGDATADIR=\\\"$$PKGDATADIR\\\" #MAKE INSTALL INSTALLS += target shell desktop mime icon26 icon32 icon48 icon64 icon128 fileIcon26 fileIcon32 fileIcon48 fileIcon64 fileIcon128 postinst target.path = $$BINDIR shell.path = $$BINDIR shell.files = $${PWD}/resources/wpanda.sh desktop.path = $$DATADIR/applications desktop.files += resources/$${TARGET}.desktop mime.path = $$DATADIR/xml/misc mime.files += $${PWD}/resources/$${TARGET}-mime.xml icon128.path = $$DATADIR/icons/hicolor/128x128/apps icon128.files += resources/icons/128x128/$${TARGET}.png icon64.path = $$DATADIR/icons/hicolor/64x64/apps icon64.files += resources/icons/64x64/$${TARGET}.png icon48.path = $$DATADIR/icons/hicolor/48x48/apps icon48.files += resources/icons/48x48/$${TARGET}.png icon32.path = $$DATADIR/icons/hicolor/32x32/apps icon32.files += resources/icons/32x32/$${TARGET}.png icon26.path = $$DATADIR/icons/hicolor/26x26/apps icon26.files += resources/icons/26x26/$${TARGET}.png fileIcon128.path = $$DATADIR/icons/hicolor/128x128/apps fileIcon128.files += resources/icons/128x128/$${TARGET}-file.png fileIcon64.path = $$DATADIR/icons/hicolor/64x64/apps fileIcon64.files += resources/icons/64x64/$${TARGET}-file.png fileIcon48.path = $$DATADIR/icons/hicolor/48x48/apps fileIcon48.files += resources/icons/48x48/$${TARGET}-file.png fileIcon32.path = $$DATADIR/icons/hicolor/32x32/apps fileIcon32.files += resources/icons/32x32/$${TARGET}-file.png fileIcon26.path = $$DATADIR/icons/hicolor/26x26/apps fileIcon26.files += resources/icons/26x26/$${TARGET}-file.png postinst.path += $$DATADIR postinst.commands += desktop-file-install /usr/local/share/applications/wpanda.desktop ; postinst.commands += xdg-mime install --mode system /usr/local/share/xml/misc/wpanda-mime.xml ; #postinst.commands += xdg-mime default /usr/local/share/applications/wpanda.desktop application/x-wpanda ; postinst.commands += xdg-icon-resource install --context mimetypes --size 128 /usr/local/share/icons/hicolor/128x128/apps/wpanda-file.png application-x-wpanda ; postinst.commands += xdg-icon-resource install --context mimetypes --size 64 /usr/local/share/icons/hicolor/64x64/apps/wpanda-file.png application-x-wpanda ; postinst.commands += xdg-icon-resource install --context mimetypes --size 48 /usr/local/share/icons/hicolor/48x48/apps/wpanda-file.png application-x-wpanda ; postinst.commands += xdg-icon-resource install --context mimetypes --size 32 /usr/local/share/icons/hicolor/32x32/apps/wpanda-file.png application-x-wpanda ; postinst.commands += xdg-icon-resource install --context mimetypes --size 26 /usr/local/share/icons/hicolor/26x26/apps/wpanda-file.png application-x-wpanda } wiRedPanda-3.0.1/app/itemwithid.cpp000066400000000000000000000006231406216046500171710ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "itemwithid.h" #include "elementfactory.h" ItemWithId::ItemWithId() { ElementFactory::addItem(this); } int ItemWithId::id() const { return m_id; } void ItemWithId::setId(int id) { m_id = id; } ItemWithId::~ItemWithId() { ElementFactory::removeItem(this); }wiRedPanda-3.0.1/app/itemwithid.h000066400000000000000000000005101406216046500166310ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef ITEMWITHID_H #define ITEMWITHID_H class ItemWithId { int m_id; public: ItemWithId(); int id() const; void setId(int id); virtual ~ItemWithId(); }; #endif /* ITEMWITHID_H */wiRedPanda-3.0.1/app/label.cpp000066400000000000000000000036541406216046500161100ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "label.h" #include #include #include #include "elementfactory.h" Label::Label(QWidget *parent) : QLabel(parent) { } Label::~Label() = default; ElementType Label::elementType() { return m_elementType; } void Label::setElementType(ElementType elementType) { m_elementType = elementType; } void Label::mousePressEvent(QMouseEvent *event) { startDrag(event->pos()); } void Label::setPixmapData(const QPixmap &pixmapData) { m_pixmapData = pixmapData; setPixmap(pixmapData.scaled(64, 64)); } const QPixmap &Label::pixmapData() const { return m_pixmapData; } QString Label::name() const { return m_name; } void Label::setName(const QString &name) { m_name = name; } QString Label::auxData() const { return m_auxData; } void Label::setAuxData(const QString &auxData) { m_auxData = auxData; setProperty("Name", auxData); } void Label::startDrag(QPoint pos) { QPixmap pixMap = pixmapData(); if (pos.isNull()) { #if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0)) pos = pixmap(Qt::ReturnByValue).rect().center(); #else pos = pixmap()->rect().center(); #endif } QByteArray itemData; QDataStream dataStream(&itemData, QIODevice::WriteOnly); QString text = ElementFactory::typeToText(m_elementType); if (text.contains("_")) { text = text.split("_").last(); } ElementType type = ElementFactory::textToType(text); /* qDebug() << objectName(); */ dataStream << QPointF(pos) << static_cast(type) << m_auxData; auto *mimeData = new QMimeData; mimeData->setData("application/x-dnditemdata", itemData); auto *drag = new QDrag(parent()); drag->setMimeData(mimeData); drag->setPixmap(pixMap); drag->setHotSpot(pos); drag->exec(Qt::CopyAction, Qt::CopyAction); } wiRedPanda-3.0.1/app/label.h000066400000000000000000000015601406216046500155470ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LABEL_H #define LABEL_H #include #include "elementtype.h" class Label : public QLabel { Q_OBJECT public: explicit Label(QWidget *parent = nullptr); ~Label() override; ElementType elementType(); void setElementType(ElementType elementType); QString auxData() const; void setAuxData(const QString &auxData); void startDrag(QPoint pos = QPoint()); QString name() const; void setName(const QString &name); const QPixmap &pixmapData() const; void setPixmapData(const QPixmap &pixmapData); protected: void mousePressEvent(QMouseEvent *event) override; private: ElementType m_elementType; QPixmap m_pixmapData; QString m_name; QString m_auxData; }; #endif /* LABEL_H */wiRedPanda-3.0.1/app/listitemwidget.cpp000066400000000000000000000030561406216046500200630ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "listitemwidget.h" #include #include #include "elementfactory.h" #include "label.h" Label *ListItemWidget::getLabel() const { return m_label; } void ListItemWidget::mousePressEvent(QMouseEvent *) { m_label->startDrag(); } ListItemWidget::ListItemWidget(const QPixmap &pixmap, ElementType type, const QString &icFileName, QWidget *parent) : QFrame(parent) { auto *itemLayout = new QHBoxLayout(); QString name = ElementFactory::translatedName(type); if (type == ElementType::IC) { name = QFileInfo(icFileName).baseName().toUpper(); } itemLayout->setSpacing(6); itemLayout->setObjectName(QStringLiteral("itemLayout")); /* itemLayout->setSizeConstraint( QLayout::SetFixedSize ); */ setLayout(itemLayout); m_label = new Label(parent); m_label->setPixmapData(pixmap); m_label->setName(name); m_label->setAuxData(icFileName); m_label->setElementType(type); m_nameLabel = new QLabel(name, this); m_nameLabel->setText(name); itemLayout->addWidget(m_label); itemLayout->addStretch(); itemLayout->addWidget(m_nameLabel); itemLayout->addStretch(); itemLayout->setMargin(0); } void ListItemWidget::updateName() { ElementType type = m_label->elementType(); if (type != ElementType::IC) { QString name = ElementFactory::translatedName(type); m_nameLabel->setText(name); m_label->setName(name); } } wiRedPanda-3.0.1/app/listitemwidget.h000066400000000000000000000012011406216046500175160ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LISTITEMWIDGET_H #define LISTITEMWIDGET_H #include #include "label.h" class ListItemWidget : public QFrame { Q_OBJECT private: Label *m_label; QLabel *m_nameLabel; public: explicit ListItemWidget(const QPixmap &pixmap, ElementType elementType, const QString &icFileName, QWidget *parent = nullptr); Label *getLabel() const; void updateName(); protected: void mousePressEvent(QMouseEvent *event) override; signals: public slots: }; #endif /* LISTITEMWIDGET_H */ wiRedPanda-3.0.1/app/logicelement.cpp000066400000000000000000000051731406216046500174760ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "logicelement.h" bool LogicElement::isValid() const { return m_isValid; } void LogicElement::clearPredecessors() { std::fill(m_inputs.begin(), m_inputs.end(), std::make_pair(nullptr, 0)); } void LogicElement::clearSucessors() { for (auto &elm : qAsConst(m_successors)) { for (auto &input : elm->m_inputs) { if (input.first == this) { input.first = nullptr; input.second = 0; } } } m_successors.clear(); } LogicElement::LogicElement(size_t inputSize, size_t outputSize) : m_isValid(true) , m_beingVisited(false) , m_priority(-1) , m_inputs(inputSize, std::make_pair(nullptr, 0)) , m_inputvalues(inputSize, false) , m_outputs(outputSize, false) { } LogicElement::~LogicElement() = default; void LogicElement::updateLogic() { if (m_isValid) { for (size_t idx = 0; idx < m_inputs.size(); ++idx) { m_inputvalues[idx] = getInputValue(idx); } _updateLogic(m_inputvalues); } } void LogicElement::connectPredecessor(int index, LogicElement *elm, int port) { m_inputs.at(index) = std::make_pair(elm, port); elm->m_successors.insert(this); } void LogicElement::setOutputValue(size_t index, bool value) { m_outputs.at(index) = value; } void LogicElement::setOutputValue(bool value) { m_outputs.at(0) = value; } void LogicElement::validate() { m_isValid = true; for (size_t in = 0; in < m_inputs.size() && m_isValid; ++in) { if (m_inputs[in].first == nullptr) { m_isValid = false; } } if (!m_isValid) { for (LogicElement *elm : qAsConst(m_successors)) { elm->m_isValid = false; } } } bool LogicElement::operator<(const LogicElement &other) const { return m_priority < other.m_priority; } int LogicElement::calculatePriority() { if (m_beingVisited) { return 0; } if (m_priority != -1) { return m_priority; } m_beingVisited = true; int max = 0; for (LogicElement *s : qAsConst(m_successors)) { max = qMax(s->calculatePriority(), max); } const int p = max + 1; m_priority = p; m_beingVisited = false; return p; } bool LogicElement::getOutputValue(size_t index) const { return m_outputs.at(index); } bool LogicElement::getInputValue(size_t index) const { Q_ASSERT(m_isValid); LogicElement *pred = m_inputs[index].first; Q_ASSERT(pred); int port = m_inputs[index].second; return pred->getOutputValue(port); }wiRedPanda-3.0.1/app/logicelement.h000066400000000000000000000027551406216046500171460ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LOGICELEMENT_H #define LOGICELEMENT_H #include #include /** * @brief The LogicElement class was designed to represent logic * elements in the simulation layer of wiredpanda. */ class LogicElement { private: /** * @brief m_isValid is calculated at compilation time. */ bool m_isValid; bool m_beingVisited; int m_priority; std::vector> m_inputs; std::vector m_inputvalues; std::vector m_outputs; QSet m_successors; protected: // Main function to update the logic of an element. Computes the outputs, given the inputs virtual void _updateLogic(const std::vector &inputs) = 0; public: explicit LogicElement(size_t inputSize, size_t outputSize); virtual ~LogicElement(); void connectPredecessor(int index, LogicElement *elm, int port); void setOutputValue(size_t index, bool value); void setOutputValue(bool value); bool getOutputValue(size_t index = 0) const; bool getInputValue(size_t index = 0) const; void validate(); bool operator<(const LogicElement &other) const; int calculatePriority(); bool isValid() const; void clearPredecessors(); void clearSucessors(); // Secure call to _updateLogic() with current inputs. void updateLogic(); }; #endif /* LOGICELEMENT_H */ wiRedPanda-3.0.1/app/logicelement/000077500000000000000000000000001406216046500167645ustar00rootroot00000000000000wiRedPanda-3.0.1/app/logicelement/CMakeLists.txt000066400000000000000000000010471406216046500215260ustar00rootroot00000000000000 set ( WPANDA_LOGICELEMENT logicnode.cpp logicinput.cpp logicoutput.cpp logicand.cpp logicor.cpp logicnand.cpp logicnor.cpp logicxor.cpp logicxnor.cpp logicnot.cpp logicjkflipflop.cpp logicsrflipflop.cpp logictflipflop.cpp logicdflipflop.cpp logicdlatch.cpp logicmux.cpp logicdemux.cpp ) add_library(wpandalogicelement ${WPANDA_LOGICELEMENT} ) target_link_libraries(wpandalogicelement PUBLIC ${WPANDA_LIBS} ) target_compile_options(wpandalogicelement PRIVATE ${COMPILE_WARNS} ) wiRedPanda-3.0.1/app/logicelement/logicand.cpp000066400000000000000000000006211406216046500212470ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "logicand.h" LogicAnd::LogicAnd(size_t inputSize) : LogicElement(inputSize, 1) { } void LogicAnd::_updateLogic(const std::vector &inputs) { auto result = std::accumulate(inputs.begin(), inputs.end(), true, std::bit_and()); setOutputValue(result); }wiRedPanda-3.0.1/app/logicelement/logicand.h000066400000000000000000000006351406216046500207210ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LOGICAND_H #define LOGICAND_H #include "logicelement.h" class LogicAnd : public LogicElement { public: explicit LogicAnd(size_t inputSize); /* LogicElement interface */ protected: void _updateLogic(const std::vector &inputs) override; }; #endif // LOGICAND_HwiRedPanda-3.0.1/app/logicelement/logicdemux.cpp000066400000000000000000000007711406216046500216350ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "logicdemux.h" LogicDemux::LogicDemux() : LogicElement(2, 2) { } void LogicDemux::_updateLogic(const std::vector &inputs) { bool data = inputs[0]; bool choice = inputs[1]; bool out0 = false; bool out1 = false; if (!choice) { out0 = data; } else { out1 = data; } setOutputValue(0, out0); setOutputValue(1, out1); }wiRedPanda-3.0.1/app/logicelement/logicdemux.h000066400000000000000000000006271406216046500213020ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LOGICDEMUX_H #define LOGICDEMUX_H #include "logicelement.h" class LogicDemux : public LogicElement { public: explicit LogicDemux(); /* LogicElement interface */ protected: void _updateLogic(const std::vector &inputs) override; }; #endif // LOGICDEMUX_HwiRedPanda-3.0.1/app/logicelement/logicdflipflop.cpp000066400000000000000000000015661406216046500224750ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "logicdflipflop.h" LogicDFlipFlop::LogicDFlipFlop() : LogicElement(4, 2) , lastClk(false) , lastValue(true) { setOutputValue(0, false); setOutputValue(1, true); } void LogicDFlipFlop::_updateLogic(const std::vector &inputs) { bool q0 = getOutputValue(0); bool q1 = getOutputValue(1); bool D = inputs[0]; bool clk = inputs[1]; bool prst = inputs[2]; bool clr = inputs[3]; if (clk && !lastClk) { q0 = lastValue; q1 = !lastValue; } if ((!prst) || (!clr)) { q0 = !prst; q1 = !clr; } setOutputValue(0, q0); setOutputValue(1, q1); lastClk = clk; lastValue = D; /* Reference: https://en.wikipedia.org/wiki/Flip-flop_(electronics)#T_flip-flop */ }wiRedPanda-3.0.1/app/logicelement/logicdflipflop.h000066400000000000000000000007331406216046500221350ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LOGICDFLIPFLOP_H #define LOGICDFLIPFLOP_H #include "logicelement.h" class LogicDFlipFlop : public LogicElement { public: explicit LogicDFlipFlop(); /* LogicElement interface */ protected: void _updateLogic(const std::vector &inputs) override; private: bool lastClk; bool lastValue; }; #endif // LOGICDFLIPFLOP_HwiRedPanda-3.0.1/app/logicelement/logicdlatch.cpp000066400000000000000000000010551406216046500217460ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "logicdlatch.h" LogicDLatch::LogicDLatch() : LogicElement(2, 2) { setOutputValue(0, false); setOutputValue(1, true); } void LogicDLatch::_updateLogic(const std::vector &inputs) { bool q0 = getOutputValue(0); bool q1 = getOutputValue(1); bool D = inputs[0]; bool enable = inputs[1]; if (enable) { q0 = D; q1 = !D; } setOutputValue(0, q0); setOutputValue(1, q1); }wiRedPanda-3.0.1/app/logicelement/logicdlatch.h000066400000000000000000000006341406216046500214150ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LOGICDLATCH_H #define LOGICDLATCH_H #include "logicelement.h" class LogicDLatch : public LogicElement { public: explicit LogicDLatch(); /* LogicElement interface */ protected: void _updateLogic(const std::vector &inputs) override; }; #endif // LOGICDLATCH_HwiRedPanda-3.0.1/app/logicelement/logicelement.pri000066400000000000000000000016231406216046500221510ustar00rootroot00000000000000HEADERS += \ $$PWD/logicnode.h \ $$PWD/logicinput.h \ $$PWD/logicoutput.h \ $$PWD/logicand.h \ $$PWD/logicor.h \ $$PWD/logicnand.h \ $$PWD/logicnor.h \ $$PWD/logicxor.h \ $$PWD/logicxnor.h \ $$PWD/logicnot.h \ $$PWD/logicjkflipflop.h \ $$PWD/logicsrflipflop.h \ $$PWD/logictflipflop.h \ $$PWD/logicdflipflop.h \ $$PWD/logicdlatch.h \ $$PWD/logicmux.h \ $$PWD/logicdemux.h SOURCES += \ $$PWD/logicnode.cpp \ $$PWD/logicinput.cpp \ $$PWD/logicoutput.cpp \ $$PWD/logicand.cpp \ $$PWD/logicor.cpp \ $$PWD/logicnand.cpp \ $$PWD/logicnor.cpp \ $$PWD/logicxor.cpp \ $$PWD/logicxnor.cpp \ $$PWD/logicnot.cpp \ $$PWD/logicjkflipflop.cpp \ $$PWD/logicsrflipflop.cpp \ $$PWD/logictflipflop.cpp \ $$PWD/logicdflipflop.cpp \ $$PWD/logicdlatch.cpp \ $$PWD/logicmux.cpp \ $$PWD/logicdemux.cpp wiRedPanda-3.0.1/app/logicelement/logicinput.cpp000066400000000000000000000005261406216046500216500ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "logicinput.h" LogicInput::LogicInput(bool defaultValue) : LogicElement(0, 1) { setOutputValue(0, defaultValue); } void LogicInput::_updateLogic(const std::vector &) { // Does nothing on update }wiRedPanda-3.0.1/app/logicelement/logicinput.h000066400000000000000000000006521406216046500213150ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LOGICINPUT_H #define LOGICINPUT_H #include "logicelement.h" class LogicInput : public LogicElement { public: explicit LogicInput(bool defaultValue = false); /* LogicElement interface */ protected: void _updateLogic(const std::vector &) override; }; #endif // LOGICINPUT_HwiRedPanda-3.0.1/app/logicelement/logicjkflipflop.cpp000066400000000000000000000020011406216046500226370ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "logicjkflipflop.h" LogicJKFlipFlop::LogicJKFlipFlop() : LogicElement(5, 2) , lastClk(false) , lastJ(true) , lastK(true) { setOutputValue(0, false); setOutputValue(1, true); } void LogicJKFlipFlop::_updateLogic(const std::vector &inputs) { bool q0 = getOutputValue(0); bool q1 = getOutputValue(1); bool j = inputs[0]; bool clk = inputs[1]; bool k = inputs[2]; bool prst = inputs[3]; bool clr = inputs[4]; if (clk && !lastClk) { if (lastJ && lastK) { std::swap(q0, q1); } else if (lastJ) { q0 = true; q1 = false; } else if (lastK) { q0 = false; q1 = true; } } if ((!prst) || (!clr)) { q0 = !prst; q1 = !clr; } lastClk = clk; lastK = k; lastJ = j; setOutputValue(0, q0); setOutputValue(1, q1); }wiRedPanda-3.0.1/app/logicelement/logicjkflipflop.h000066400000000000000000000007541406216046500223210ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LOGICJKFLIPFLOP_H #define LOGICJKFLIPFLOP_H #include "logicelement.h" class LogicJKFlipFlop : public LogicElement { public: explicit LogicJKFlipFlop(); /* LogicElement interface */ protected: void _updateLogic(const std::vector &inputs) override; private: bool lastClk; bool lastJ; bool lastK; }; #endif // LOGICJKFLIPFLOP_HwiRedPanda-3.0.1/app/logicelement/logicmux.cpp000066400000000000000000000006711406216046500213230ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "logicmux.h" LogicMux::LogicMux() : LogicElement(3, 1) { } void LogicMux::_updateLogic(const std::vector &inputs) { bool data1 = inputs[0]; bool data2 = inputs[1]; bool choice = inputs[2]; if (!choice) { setOutputValue(data1); } else { setOutputValue(data2); } }wiRedPanda-3.0.1/app/logicelement/logicmux.h000066400000000000000000000006151406216046500207660ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LOGICMUX_H #define LOGICMUX_H #include "logicelement.h" class LogicMux : public LogicElement { public: explicit LogicMux(); /* LogicElement interface */ protected: void _updateLogic(const std::vector &inputs) override; }; #endif // LOGICMUX_HwiRedPanda-3.0.1/app/logicelement/logicnand.cpp000066400000000000000000000006261406216046500214320ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "logicnand.h" LogicNand::LogicNand(size_t inputSize) : LogicElement(inputSize, 1) { } void LogicNand::_updateLogic(const std::vector &inputs) { auto result = std::accumulate(inputs.begin(), inputs.end(), true, std::bit_and()); setOutputValue(!result); }wiRedPanda-3.0.1/app/logicelement/logicnand.h000066400000000000000000000006421406216046500210750ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LOGICNAND_H #define LOGICNAND_H #include "logicelement.h" class LogicNand : public LogicElement { public: explicit LogicNand(size_t inputSize); /* LogicElement interface */ protected: void _updateLogic(const std::vector &inputs) override; }; #endif // LOGICNAND_HwiRedPanda-3.0.1/app/logicelement/logicnode.cpp000066400000000000000000000004431406216046500214340ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "logicnode.h" LogicNode::LogicNode() : LogicElement(1, 1) { } void LogicNode::_updateLogic(const std::vector &inputs) { setOutputValue(inputs[0]); }wiRedPanda-3.0.1/app/logicelement/logicnode.h000066400000000000000000000006141406216046500211010ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LOGICNODE_H #define LOGICNODE_H #include "logicelement.h" class LogicNode : public LogicElement { public: explicit LogicNode(); /* LogicElement interface */ protected: void _updateLogic(const std::vector &) override; }; #endif // LOGICNODE_HwiRedPanda-3.0.1/app/logicelement/logicnor.cpp000066400000000000000000000006221406216046500213040ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "logicnor.h" LogicNor::LogicNor(size_t inputSize) : LogicElement(inputSize, 1) { } void LogicNor::_updateLogic(const std::vector &inputs) { auto result = std::accumulate(inputs.begin(), inputs.end(), false, std::bit_or()); setOutputValue(!result); }wiRedPanda-3.0.1/app/logicelement/logicnor.h000066400000000000000000000006341406216046500207540ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LOGICNOR_H #define LOGICNOR_H #include "logicelement.h" class LogicNor : public LogicElement { public: explicit LogicNor(size_t inputSize); /* LogicElement interface */ protected: void _updateLogic(const std::vector &inputs) override; }; #endif // LOGICNOR_HwiRedPanda-3.0.1/app/logicelement/logicnot.cpp000066400000000000000000000004401406216046500213040ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "logicnot.h" LogicNot::LogicNot() : LogicElement(1, 1) { } void LogicNot::_updateLogic(const std::vector &inputs) { setOutputValue(!inputs[0]); }wiRedPanda-3.0.1/app/logicelement/logicnot.h000066400000000000000000000006151406216046500207550ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LOGICNOT_H #define LOGICNOT_H #include "logicelement.h" class LogicNot : public LogicElement { public: explicit LogicNot(); /* LogicElement interface */ protected: void _updateLogic(const std::vector &inputs) override; }; #endif // LOGICNOT_HwiRedPanda-3.0.1/app/logicelement/logicor.cpp000066400000000000000000000006151406216046500211300ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "logicor.h" LogicOr::LogicOr(size_t inputSize) : LogicElement(inputSize, 1) { } void LogicOr::_updateLogic(const std::vector &inputs) { auto result = std::accumulate(inputs.begin(), inputs.end(), false, std::bit_or()); setOutputValue(result); }wiRedPanda-3.0.1/app/logicelement/logicor.h000066400000000000000000000006301406216046500205720ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LOGICOR_H #define LOGICOR_H #include "logicelement.h" class LogicOr : public LogicElement { public: explicit LogicOr(size_t inputSize); /* LogicElement interface */ protected: void _updateLogic(const std::vector &inputs) override; }; #endif // LOGICOR_HwiRedPanda-3.0.1/app/logicelement/logicoutput.cpp000066400000000000000000000006151406216046500220500ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "logicoutput.h" LogicOutput::LogicOutput(size_t inputSz) : LogicElement(inputSz, inputSz) { } void LogicOutput::_updateLogic(const std::vector &inputs) { for (size_t idx = 0; idx < inputs.size(); ++idx) { setOutputValue(idx, inputs[idx]); } }wiRedPanda-3.0.1/app/logicelement/logicoutput.h000066400000000000000000000006521406216046500215160ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LOGICOUTPUT_H #define LOGICOUTPUT_H #include "logicelement.h" class LogicOutput : public LogicElement { public: explicit LogicOutput(size_t inputSz); /* LogicElement interface */ protected: void _updateLogic(const std::vector &inputs) override; }; #endif // LOGICOUTPUT_HwiRedPanda-3.0.1/app/logicelement/logicsrflipflop.cpp000066400000000000000000000017101406216046500226650ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "logicsrflipflop.h" LogicSRFlipFlop::LogicSRFlipFlop() : LogicElement(5, 2) , lastClk(false) { setOutputValue(0, false); setOutputValue(1, true); } void LogicSRFlipFlop::_updateLogic(const std::vector &inputs) { bool q0 = getOutputValue(0); bool q1 = getOutputValue(1); bool s = inputs[0]; bool clk = inputs[1]; bool r = inputs[2]; bool prst = inputs[3]; bool clr = inputs[4]; if (clk && !lastClk) { if (s && r) { q0 = true; q1 = true; } else if (s != r) { q0 = s; q1 = r; } } if ((!prst) || (!clr)) { q0 = !prst; q1 = !clr; } lastClk = clk; setOutputValue(0, q0); setOutputValue(1, q1); /* Reference: https://pt.wikipedia.org/wiki/Flip-flop#Flip-flop_SR_Sincrono */ }wiRedPanda-3.0.1/app/logicelement/logicsrflipflop.h000066400000000000000000000007141406216046500223350ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LOGICSRFLIPFLOP_H #define LOGICSRFLIPFLOP_H #include "logicelement.h" class LogicSRFlipFlop : public LogicElement { public: explicit LogicSRFlipFlop(); /* LogicElement interface */ protected: void _updateLogic(const std::vector &inputs) override; private: bool lastClk; }; #endif // LOGICSRFLIPFLOP_HwiRedPanda-3.0.1/app/logicelement/logictflipflop.cpp000066400000000000000000000016241406216046500225100ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "logictflipflop.h" LogicTFlipFlop::LogicTFlipFlop() : LogicElement(4, 2) , lastClk(false) , lastValue(true) { setOutputValue(0, false); setOutputValue(1, true); } void LogicTFlipFlop::_updateLogic(const std::vector &inputs) { bool q0 = getOutputValue(0); bool q1 = getOutputValue(1); bool T = inputs[0]; bool clk = inputs[1]; bool prst = inputs[2]; bool clr = inputs[3]; if (clk && !lastClk) { if (lastValue) { q0 = !q0; q1 = !q0; } } if ((!prst) || (!clr)) { q0 = !prst; q1 = !clr; } setOutputValue(0, q0); setOutputValue(1, q1); lastClk = clk; lastValue = T; /* Reference: https://en.wikipedia.org/wiki/Flip-flop_(electronics)#T_flip-flop */ }wiRedPanda-3.0.1/app/logicelement/logictflipflop.h000066400000000000000000000007331406216046500221550ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LOGICTFLIPFLOP_H #define LOGICTFLIPFLOP_H #include "logicelement.h" class LogicTFlipFlop : public LogicElement { public: explicit LogicTFlipFlop(); /* LogicElement interface */ protected: void _updateLogic(const std::vector &inputs) override; private: bool lastClk; bool lastValue; }; #endif // LOGICTFLIPFLOP_HwiRedPanda-3.0.1/app/logicelement/logicxnor.cpp000066400000000000000000000006271406216046500215010ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "logicxnor.h" LogicXnor::LogicXnor(size_t inputSize) : LogicElement(inputSize, 1) { } void LogicXnor::_updateLogic(const std::vector &inputs) { auto result = std::accumulate(inputs.begin(), inputs.end(), false, std::bit_xor()); setOutputValue(!result); }wiRedPanda-3.0.1/app/logicelement/logicxnor.h000066400000000000000000000006421406216046500211430ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LOGICXNOR_H #define LOGICXNOR_H #include "logicelement.h" class LogicXnor : public LogicElement { public: explicit LogicXnor(size_t inputSize); /* LogicElement interface */ protected: void _updateLogic(const std::vector &inputs) override; }; #endif // LOGICXNOR_HwiRedPanda-3.0.1/app/logicelement/logicxor.cpp000066400000000000000000000006221406216046500213160ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "logicxor.h" LogicXor::LogicXor(size_t inputSize) : LogicElement(inputSize, 1) { } void LogicXor::_updateLogic(const std::vector &inputs) { auto result = std::accumulate(inputs.begin(), inputs.end(), false, std::bit_xor()); setOutputValue(result); }wiRedPanda-3.0.1/app/logicelement/logicxor.h000066400000000000000000000006351406216046500207670ustar00rootroot00000000000000/* * Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors * SPDX-License-Identifier: GPL-3.0-or-later */ #ifndef LOGICXOR_H #define LOGICXOR_H #include "logicelement.h" class LogicXor : public LogicElement { public: explicit LogicXor(size_t inputSize); /* LogicElement interface */ protected: void _updateLogic(const std::vector &inputs) override; }; #endif // LOGICXOR_HwiRedPanda-3.0.1/app/main.cpp000066400000000000000000000045521406216046500157530ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "mainwindow.h" #include #include int main(int argc, char *argv[]) { QApplication a(argc, argv); a.setOrganizationName("GIBIS-UNIFESP"); a.setApplicationName("WiredPanda"); a.setApplicationVersion(APP_VERSION); QCommandLineParser parser; parser.setApplicationDescription(a.applicationName()); parser.addHelpOption(); parser.addVersionOption(); parser.addPositionalArgument("file", QCoreApplication::translate("main", "Circuit file to open.")); QCommandLineOption arduinoFileOption(QStringList() << "a" << "arduino-file", QCoreApplication::translate("main", "Export circuit to "), QCoreApplication::translate("main", "arduino file")); parser.addOption(arduinoFileOption); QCommandLineOption waveformFileOption(QStringList() << "w" << "waveform", QCoreApplication::translate("main", "Export circuit to text file"), QCoreApplication::translate("main", "waveform text file")); parser.addOption(waveformFileOption); parser.process(a); QStringList args = parser.positionalArguments(); MainWindow w(nullptr, (args.size() > 0 ? QString(args[0]) : QString())); QString arduFile = parser.value(arduinoFileOption); if (!arduFile.isEmpty()) { if (args.size() > 0) { w.loadPandaFile(args[0]); return !w.exportToArduino(arduFile); } return 0; } QString wfFile = parser.value(waveformFileOption); if (!wfFile.isEmpty()) { if (args.size() > 0) { w.loadPandaFile(args[0]); return !w.exportToWaveFormFile(wfFile); } return 0; } w.show(); if (args.size() > 0) { w.loadPandaFile(args[0]); } return a.exec(); } /* * TODO: Tests for all elements * TODO: Create arduino version of all elements * TODO: Select some elements, and input wires become input buttons, output wires become leds... Connections are then * transferred to the IC's ports. */ wiRedPanda-3.0.1/app/mainwindow.cpp000066400000000000000000001061721406216046500172040ustar00rootroot00000000000000// Copyright 2015 - 2021, GIBIS-Unifesp and the wiRedPanda contributors // SPDX-License-Identifier: GPL-3.0-or-later #include "mainwindow.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "arduino/codegenerator.h" #include "bewaveddolphin.h" #include "common.h" #include "editor.h" #include "elementfactory.h" #include "elementmapping.h" #include "globalproperties.h" #include "graphicsview.h" #include "graphicsviewzoom.h" #include "label.h" #include "listitemwidget.h" #include "thememanager.h" #include "simulationcontroller.h" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent, const QString &filename) : QMainWindow(parent) , ui(new Ui::MainWindow) , editor(new Editor(this)) , undoView(nullptr) , firstResult(nullptr) , loadedAutosave(false) , autosaveFilename("") , dolphinFilename("none") , bd(nullptr) , translator(nullptr) { COMMENT("WIRED PANDA Version = " << APP_VERSION << " OR " << GlobalProperties::version, 0); ui->setupUi(this); ThemeManager::globalMngr = new ThemeManager(this); buildFullScreenDialog(); ui->graphicsView->setScene(editor->getScene()); /* Translation */ QSettings settings(QSettings::IniFormat, QSettings::UserScope, QApplication::organizationName(), QApplication::applicationName()); if (settings.value("language").isValid()) { loadTranslation(settings.value("language").toString()); } if (settings.contains("autosaveFile")) { // If autosaveFile was found within the config. file, then WiredPanda probably didn't save its last project correctly, perhaps due to a crash. autosaveFilename = settings.value("autosaveFile").toString(); if ((QFile(autosaveFilename).exists()) && (autosaveFilename != filename)) { int ret = recoverAutoSaveFile(autosaveFilename); if (ret == QMessageBox::Yes) { auto *wPanda = new QProcess(nullptr); QStringList args; args << autosaveFilename; wPanda->start(QCoreApplication::applicationFilePath(), args); COMMENT("Reloaded autosave: " << args[0].toStdString(), 0); } } else if ((QFile(autosaveFilename).exists()) && (autosaveFilename == filename)) { COMMENT("Loading autosave!", 0); loadedAutosave = true; } } settings.beginGroup("MainWindow"); restoreGeometry(settings.value("geometry").toByteArray()); restoreState(settings.value("windowState").toByteArray()); settings.beginGroup("splitter"); ui->splitter->restoreGeometry(settings.value("geometry").toByteArray()); ui->splitter->restoreState(settings.value("state").toByteArray()); ui->actionExport_to_Arduino->setEnabled(false); settings.endGroup(); settings.endGroup(); QList zoom_in_shortcuts; zoom_in_shortcuts << QKeySequence("Ctrl++") << QKeySequence("Ctrl+="); ui->actionZoom_in->setShortcuts(zoom_in_shortcuts); /* THEME */ auto *themeGroup = new QActionGroup(this); auto const actions = ui->menuTheme->actions(); for (QAction *action : actions) { themeGroup->addAction(action); } themeGroup->setExclusive(true); connect(ThemeManager::globalMngr, &ThemeManager::themeChanged, this, &MainWindow::updateTheme); connect(ThemeManager::globalMngr, &ThemeManager::themeChanged, editor, &Editor::updateTheme); ThemeManager::globalMngr->initialize(); /* ui->graphicsView->setBackgroundBrush(QBrush(QColor(Qt::gray))); */ if (settings.value("fastMode").isValid()) { setFastMode(settings.value("fastMode").toBool()); } else { setFastMode(false); } editor->setElementEditor(ui->widgetElementEditor); ui->searchScrollArea->hide(); setCurrentFile(QFileInfo()); /* * #ifdef DEBUG * createUndoView( ); * #endif */ connect(ui->actionCopy, &QAction::triggered, editor, &Editor::copyAction); connect(ui->actionCut, &QAction::triggered, editor, &Editor::cutAction); connect(ui->actionPaste, &QAction::triggered, editor, &Editor::pasteAction); connect(ui->actionDelete, &QAction::triggered, editor, &Editor::deleteAction); undoAction = editor->getUndoStack()->createUndoAction(this, tr("&Undo")); undoAction->setIcon(QIcon(QPixmap(":/toolbar/undo.png"))); undoAction->setShortcuts(QKeySequence::Undo); redoAction = editor->getUndoStack()->createRedoAction(this, tr("&Redo")); redoAction->setIcon(QIcon(QPixmap(":/toolbar/redo.png"))); redoAction->setShortcuts(QKeySequence::Redo); ui->menuEdit->insertAction(ui->menuEdit->actions().at(0), undoAction); ui->menuEdit->insertAction(undoAction, redoAction); connect(ui->graphicsView->gvzoom(), &GraphicsViewZoom::zoomed, this, &MainWindow::zoomChanged); connect(editor, &Editor::scroll, this, &MainWindow::scrollView); connect(editor, &Editor::circuitHasChanged, this, &MainWindow::autoSave); rfController = new RecentFilesController("recentFileList", this, true); ricController = new RecentFilesController("recentICs", this, false); connect(this, &MainWindow::addRecentFile, rfController, &RecentFilesController::addRecentFile); connect(this, &MainWindow::addRecentIcFile, ricController, &RecentFilesController::addRecentFile); auto *shortcut = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_F), this); connect(shortcut, &QShortcut::activated, ui->lineEdit, QOverload<>::of(&QWidget::setFocus)); ui->graphicsView->setCacheMode(QGraphicsView::CacheBackground); firstResult = nullptr; updateRecentICs(); createRecentFileActions(); updateRecentFileActions(); connect(rfController, &RecentFilesController::recentFilesUpdated, this, &MainWindow::updateRecentFileActions); connect(ricController, &RecentFilesController::recentFilesUpdated, this, &MainWindow::updateRecentICs); /* QApplication::setStyle( QStyleFactory::create( "Fusion" ) ); */ ui->actionPlay->setChecked(true); populateLeftMenu(); } void MainWindow::setFastMode(bool fastModeEnabled) { ui->graphicsView->setRenderHint(QPainter::Antialiasing, !fastModeEnabled); ui->graphicsView->setRenderHint(QPainter::SmoothPixmapTransform, !fastModeEnabled); fullscreenView->setRenderHint(QPainter::Antialiasing, !fastModeEnabled); fullscreenView->setRenderHint(QPainter::SmoothPixmapTransform, !fastModeEnabled); ui->actionFast_Mode->setChecked(fastModeEnabled); } void MainWindow::createUndoView() { undoView = new QUndoView(editor->getUndoStack()); undoView->setWindowTitle(tr("Command List")); undoView->show(); undoView->setAttribute(Qt::WA_QuitOnClose, false); } MainWindow::~MainWindow() { if (undoView) { delete undoView; } delete ui; } void MainWindow::on_actionExit_triggered() { close(); } bool MainWindow::save(QString fname) { COMMENT("fname: " << fname.toStdString() << ", autosave: " << autosaveFilename.toStdString(), 0); if ((fname.isEmpty()) || (loadedAutosave)) { fname = currentFile.absoluteFilePath(); if ((currentFile.fileName().isEmpty()) || (loadedAutosave)) { fname = QFileDialog::getSaveFileName(this, tr("Save File"), defaultDirectory.absolutePath(), tr("Panda files (*.panda)")); } } if (fname.isEmpty()) { return false; } if (!fname.endsWith(".panda")) { fname.append(".panda"); } QSaveFile fl(fname); if (fl.open(QFile::WriteOnly)) { QDataStream ds(&fl); try { editor->save(ds, dolphinFilename); } catch (std::runtime_error &e) { std::cerr << tr("Error saving project: ").toStdString() << e.what() << std::endl; return false; } } if (fl.commit()) { loadedAutosave = false; setCurrentFile(QFileInfo(fname)); ui->statusBar->showMessage(tr("Saved file successfully."), 2000); editor->getUndoStack()->setClean(); if (autosaveFile.isOpen()) { autosaveFile.remove(); QSettings settings(QSettings::IniFormat, QSettings::UserScope, QApplication::organizationName(), QApplication::applicationName()); settings.remove("autosaveFile"); } return true; } std::cerr << QString(tr("Could not save file: ") + fl.errorString() + ".").toStdString() << std::endl; return false; } void MainWindow::show() { QMainWindow::show(); editor->clear(); } void MainWindow::clear() { editor->clear(); dolphinFilename = "none"; setCurrentFile(QFileInfo()); } int MainWindow::recoverAutoSaveFile(const QString& autosaveFilename) { QMessageBox msgBox; msgBox.setParent(this); msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No); msgBox.setText(QString(tr("We have found an autosave file. Do you want to load it?\n Autosave: ") + autosaveFilename)); msgBox.setWindowModality(Qt::WindowModal); msgBox.setDefaultButton(QMessageBox::Save); return msgBox.exec(); } int MainWindow::confirmSave() { QMessageBox msgBox; msgBox.setParent(this); /* msgBox.setLocale( QLocale::Portuguese ); */ msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); msgBox.setText(tr("Do you want to save your changes?")); msgBox.setWindowModality(Qt::WindowModal); msgBox.setDefaultButton(QMessageBox::Save); return msgBox.exec(); } void MainWindow::on_actionNew_triggered() { if (closeFile()) { clear(); } } void MainWindow::on_actionWires_triggered(bool checked) { editor->showWires(checked); } void MainWindow::on_actionRotate_right_triggered() { editor->rotate(true); } void MainWindow::on_actionRotate_left_triggered() { editor->rotate(false); } bool MainWindow::loadPandaFile(const QString &fname) { QFile fl(fname); if (!fl.exists()) { QMessageBox::warning(this, tr("Error!"), tr("File \"%1\" does not exists!").arg(fname), QMessageBox::Ok, QMessageBox::NoButton); std::cerr << tr("Error: This file does not exists: ").toStdString() << fname.toStdString() << std::endl; return false; } COMMENT("File exists.", 0); if (fl.open(QFile::ReadOnly)) { COMMENT("File opened.", 0); QDataStream ds(&fl); setCurrentFile(QFileInfo(fname)); COMMENT("Current file set.", 0); try { COMMENT("Loading in editor.", 0); editor->load(ds); COMMENT("Loaded. Emitting changed signal.", 0); emit editor->circuitHasChanged(); COMMENT("Finished updating changed by signal.", 0); } catch (std::runtime_error &e) { std::cerr << tr("Error loading project: ").toStdString() << e.what() << std::endl; QMessageBox::warning(this, tr("Error!"), tr("Could not open file.\nError: %1").arg(e.what()), QMessageBox::Ok, QMessageBox::NoButton); clear(); return false; } } else { std::cerr << tr("Could not open file in ReadOnly mode : ").toStdString() << fname.toStdString() << "." << std::endl; return false; } COMMENT("Closing file.", 0); fl.close(); // COMMENT( "Adding to controller.", 0 ); // emit addRecentFile( fname ); ui->statusBar->showMessage(tr("File loaded successfully."), 2000); /* on_actionWaveform_triggered( ); */ return true; } void MainWindow::scrollView(int dx, int dy) { ui->graphicsView->scroll(dx, dy); } void MainWindow::on_actionOpen_triggered() { QString fname = QFileDialog::getOpenFileName(this, tr("Open File"), defaultDirectory.absolutePath(), tr("Panda files (*.panda)")); if (fname.isEmpty()) { return; } loadPandaFile(fname); } void MainWindow::on_actionSave_triggered() { save(); } void MainWindow::on_actionAbout_triggered() { QMessageBox::about(this, "wiRED Panda", tr("

wiRED Panda is a software developed by the students of the Federal University of São Paulo." " This project was created in order to help students to learn about logic circuits.

" "

Software version: %1

" "

Creators:

" "
    " "
  • Davi Morales
  • " "
  • Lucas Lellis
  • " "
  • Rodrigo Torres
  • " "
  • Prof. Fábio Cappabianco, Ph.D.
  • " "
" "

wiRed Panda is currently maintained by Prof. Fábio Cappabianco, Ph.D. and Vinícius R. Miguel.

" "

Please file a report at our GitHub page if bugs are found or if you wish for a new functionality to be implemented.

" "

Visit our website!

") .arg(QApplication::applicationVersion())); } void MainWindow::on_actionAbout_Qt_triggered() { QMessageBox::aboutQt(this); } bool MainWindow::closeFile() { bool ok = true; if (!editor->getUndoStack()->isClean()) { int ret = confirmSave(); if (ret == QMessageBox::Save) { ok = save(); } else if (ret == QMessageBox::Cancel) { ok = false; } else { // Close without saving. Deleting autosave if it was opened. if (loadedAutosave) { autosaveFile.remove(); QSettings settings(QSettings::IniFormat, QSettings::UserScope, QApplication::organizationName(), QApplication::applicationName()); settings.remove("autosaveFile"); } } } return ok; } void MainWindow::closeEvent(QCloseEvent *e) { Q_UNUSED(e); QSettings settings(QSettings::IniFormat, QSettings::UserScope, QApplication::organizationName(), QApplication::applicationName()); settings.beginGroup("MainWindow"); settings.setValue("geometry", saveGeometry()); settings.setValue("windowState", saveState()); settings.beginGroup("splitter"); settings.setValue("geometry", ui->splitter->saveGeometry()); settings.setValue("state", ui->splitter->saveState()); settings.endGroup(); settings.endGroup(); if (closeFile()) { close(); } else { e->ignore(); } } void MainWindow::on_actionSave_As_triggered() { QString fname = currentFile.absoluteFilePath(); QString path = defaultDirectory.absolutePath(); if (!currentFile.fileName().isEmpty()) { path = currentFile.absoluteFilePath(); } fname = QFileDialog::getSaveFileName(this, tr("Save File as ..."), path, tr("Panda files (*.panda)")); if (fname.isEmpty()) { return; } if (!fname.endsWith(".panda")) { fname.append(".panda"); } setCurrentFile(QFileInfo(fname)); save(); } QFileInfo MainWindow::getCurrentFile() const { return currentFile; } void MainWindow::setCurrentFile(const QFileInfo &file) { COMMENT("Default the autosave path to the temporary directory of the system.", 0); if (file.exists()) { QDir autosavePath(QDir::temp()); COMMENT("Autosave path set to the current file's directory, if there is one.", 0); autosavePath = file.dir(); COMMENT("Autosavepath: " << autosavePath.absolutePath().toStdString(), 0); autosaveFile.setFileTemplate(autosavePath.absoluteFilePath(file.baseName() + "XXXXXX.panda")); COMMENT("Setting current file to: " << file.absoluteFilePath().toStdString(), 0); } else { COMMENT("Default file does not exist: " << file.absoluteFilePath().toStdString(), 0); QDir autosavePath(QDir::temp()); COMMENT("Autosavepath: " << autosavePath.absolutePath().toStdString(), 0); autosaveFile.setFileTemplate(autosavePath.absoluteFilePath("XXXXXX.panda")); COMMENT("Setting current file to random file in tmp.", 0); } currentFile = file; if (file.fileName().isEmpty()) { setWindowTitle("wiRED PANDA v" + QString(APP_VERSION)); } else { setWindowTitle(QString("wiRED PANDA v%1 [%2]").arg(APP_VERSION, file.fileName())); } if (!loadedAutosave) { COMMENT("Adding file to controller.", 0); emit addRecentFile(file.absoluteFilePath()); } GlobalProperties::currentFile = currentFile.absoluteFilePath(); COMMENT("Setting global current file.", 0); if (currentFile.exists()) { defaultDirectory = currentFile.dir(); } else { defaultDirectory = QDir::home(); } } void MainWindow::on_actionSelect_all_triggered() { editor->selectAll(); } void MainWindow::updateRecentICs() { ui->scrollAreaWidgetContents_Box->layout()->removeItem(ui->verticalSpacer_BOX); for (ListItemWidget *item : qAsConst(boxItemWidgets)) { item->deleteLater(); } /* qDeleteAll( boxItemWidgets ); */ boxItemWidgets.clear(); //! Show recent files const QStringList files = ricController->getRecentFiles(); for (const QString &file : files) { QPixmap pixmap(QString::fromUtf8(":/basic/box.png")); auto *item = new ListItemWidget(pixmap, ElementType::IC, file, this); boxItemWidgets.append(item); ui->scrollAreaWidgetContents_Box->layout()->addWidget(item); } ui->scrollAreaWidgetContents_Box->layout()->addItem(ui->verticalSpacer_BOX); } QString MainWindow::getOpenICFile() { return QFileDialog::getOpenFileName(this, tr("Load File as IC"), defaultDirectory.absolutePath(), tr("Panda files (*.panda)")); } void MainWindow::on_actionOpen_IC_triggered() { /* LOAD FILE AS IC */ QString fname = getOpenICFile(); if (fname.isEmpty()) { return; } QFile fl(fname); if (!fl.exists()) { std::cerr << tr("Error: This file does not exists: ").toStdString() << fname.toStdString() << std::endl; return; } if (fl.open(QFile::ReadOnly)) { emit addRecentIcFile(fname); } else { std::cerr << tr("Could not open file in ReadOnly mode : ").toStdString() << fname.toStdString() << "." << std::endl; return; } fl.close(); ui->statusBar->showMessage(tr("Loaded IC successfully."), 2000); } void MainWindow::on_lineEdit_textChanged(const QString &text) { ui->searchLayout->removeItem(ui->VSpacer); for (ListItemWidget *item : qAsConst(searchItemWidgets)) { item->deleteLater(); } searchItemWidgets.clear(); firstResult = nullptr; if (text.isEmpty()) { ui->searchScrollArea->hide(); ui->tabWidget->show(); } else { ui->searchScrollArea->show(); ui->tabWidget->hide(); QList