Repository: liferooter/textpieces Branch: main Commit: 6a6c41ecb566 Files: 119 Total size: 346.4 KB Directory structure: gitextract_lymngp0k/ ├── .github/ │ └── workflows/ │ ├── ci.yml │ └── test.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE ├── LICENSES/ │ ├── CC0-1.0.txt │ └── GPL-3.0-or-later.txt ├── README.md ├── build-aux/ │ ├── flatpak/ │ │ └── com.github.liferooter.textpieces.yaml │ └── meson/ │ └── postinstall.py ├── ci/ │ ├── generate-tools-po.sh │ └── update-po.sh ├── data/ │ ├── com.github.liferooter.textpieces.appdata.xml.in │ ├── com.github.liferooter.textpieces.desktop.in │ ├── com.github.liferooter.textpieces.gschema.xml │ ├── icons/ │ │ ├── com.github.liferooter.textpieces-symbolic.svg.license │ │ ├── com.github.liferooter.textpieces.Source.svg.license │ │ └── com.github.liferooter.textpieces.svg.license │ ├── meson.build │ ├── tools.json │ └── tools.json.license ├── icon.png.license ├── meson.build ├── po/ │ ├── LINGUAS │ ├── POTFILES │ ├── es.po │ ├── meson.build │ ├── nl.po │ ├── pt_BR.po │ ├── textpieces.pot │ ├── textpieces.pot.license │ ├── tools.pot │ └── tools.pot.license ├── resources/ │ ├── icons/ │ │ └── scalable/ │ │ └── status/ │ │ ├── applications-utilities-symbolic.svg.license │ │ ├── fingerprint-symbolic.svg.license │ │ ├── funnel-symbolic.svg.license │ │ ├── lock-symbolic.svg.license │ │ ├── preferences-symbolic.svg.license │ │ ├── rss-symbolic.svg.license │ │ └── unlock-symbolic.svg.license │ ├── meson.build │ ├── style-dark.css │ ├── style.css │ ├── textpieces.gresource.xml │ └── ui/ │ ├── CustomToolPage.blp │ ├── Editor.blp │ ├── NewToolPage.blp │ ├── Preferences.blp │ ├── Search.blp │ ├── SearchBar.blp │ ├── SearchEntry.blp │ ├── ShortcutsWindow.blp │ ├── ToolSettings.blp │ └── Window.blp ├── screenshots/ │ ├── screenshot.png.license │ ├── slide1.png.license │ ├── slide2.png.license │ ├── slide3.png.license │ ├── slide4.png.license │ └── slide5.png.license ├── scripts/ │ ├── base64-decode │ ├── base64-encode │ ├── count-lines │ ├── count-symbols │ ├── count-words │ ├── escape-html │ ├── escape-string │ ├── escape-url │ ├── filter-regex │ ├── filter-substring │ ├── format-json │ ├── format-xml │ ├── json-to-yaml │ ├── md5 │ ├── minify-json │ ├── remove-regex │ ├── remove-substring │ ├── remove-trailing │ ├── replace-regex │ ├── replace-substring │ ├── reverse-filter-regex │ ├── reverse-filter-substring │ ├── reverse-lines │ ├── reverse-string │ ├── rss-from-opml │ ├── sha1 │ ├── sha256 │ ├── sha384 │ ├── sha512 │ ├── sort-lines │ ├── sort-lines-reverse │ ├── template │ ├── template.license │ ├── trim │ ├── unescape-html │ ├── unescape-string │ ├── unescape-url │ └── yaml-to-json ├── src/ │ ├── Application.vala │ ├── meson.build │ ├── tools/ │ │ ├── Tool.vala │ │ └── ToolsController.vala │ ├── utils/ │ │ ├── Recoloring.vala │ │ └── Utils.vala │ ├── vapi/ │ │ └── config.vapi │ └── widgets/ │ ├── Editor.vala │ ├── Search.vala │ ├── SearchBar.vala │ ├── SearchEntry.vala │ ├── Window.vala │ └── preferences/ │ ├── Preferences.vala │ ├── ToolSettings.vala │ └── pages/ │ ├── CustomToolPage.vala │ └── NewToolPage.vala ├── subprojects/ │ ├── blueprint-compiler.wrap │ ├── gtksourceview.wrap │ ├── json-glib.wrap │ └── libadwaita.wrap └── textpieces.doap ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/ci.yml ================================================ # SPDX-FileCopyrightText: 2022 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later on: push: branches: [main] name: CI jobs: update-po: name: Update translations runs-on: ubuntu-latest container: image: bilelmoussaoui/flatpak-github-actions:gnome-42 options: --privileged steps: - name: Checkout source code uses: actions/checkout@v3 with: fetch-depth: 0 - name: Configure Git run: | git --version git config --global --add safe.directory $(pwd) git config user.name 'github-actions' git config user.email 'github-actions@github.com' - name: Add Flathub repository run: flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo - name: Install dependencies run: > xvfb-run -a -s "-screen 0 1024x768x24" flatpak-builder --keep-build-dirs --user --install-deps-from=flathub --disable-rofiles-fuse flatpak_app --stop-at=textpieces build-aux/flatpak/com.github.liferooter.textpieces.yaml - name: Update translations run: > xvfb-run -a -s "-screen 0 1024x768x24" flatpak-builder --run flatpak_app build-aux/flatpak/com.github.liferooter.textpieces.yaml ci/update-po.sh - name: Push changes to GitHub run: git push ================================================ FILE: .github/workflows/test.yml ================================================ # SPDX-FileCopyrightText: 2022 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later on: push: branches: [main] pull_request: name: Test jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: REUSE Compliance Check uses: fsfe/reuse-action@v1.1.1 flatpak: name: Flatpak runs-on: ubuntu-20.04 container: image: bilelmoussaoui/flatpak-github-actions:gnome-nightly options: --privileged steps: - uses: actions/checkout@v2 - uses: bilelmoussaoui/flatpak-github-actions/flatpak-builder@v4 with: bundle: textpieces.flatpak manifest-path: build-aux/flatpak/com.github.liferooter.textpieces.yaml run-tests: true cache-key: flatpak-builder-${{ github.sha }} ================================================ FILE: .gitignore ================================================ # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: CC0-1.0 **/*~ **/#*# .flatpak-builder _build .vscode /subprojects/*/ /.flatpak /.fenv ================================================ FILE: CONTRIBUTING.md ================================================ # Contributing tips This file describes some non-obvious points of this project which needs to be known by contributors. ## Technology stack Text Pieces is written in [Vala](https://wiki.gnome.org/Projects/Vala) using [GTK 4](https://gtk.org) and [libadwaita](https://gitlab.gnome.org/GNOME/libadwaita/) as GUI libraries. Text Pieces is designed to comply [GNOME HIG](https://developer.gnome.org/hig) guidelines. It also uses [gtksourceview](https://gitlab.gnome.org/GNOME/gtksourceview) for text editor widget and [json-glib](https://gitlab.gnome.org/GNOME/json-glib/) for JSON serialization and deserialization. Text Pieces' built-in tools uses [Python](https://www.python.org/) and [Bash](https://www.gnu.org/software/bash/) as interpreters, [pyyaml](https://pyyaml.org/) for JSON to YAML and YAML to JSON conversion and [gettext](https://www.gnu.org/software/gettext/) for translations. It also uses [shebang](https://wikipedia.org/en/Shebang_(Unix)) for script executing, so it can work only on UNIX-like OSes. ## Build system Text Pieces uses [Meson](https://mesonbuild.com) as build system and [Flatpak](https://flatpak.org) as packaging system. Flatpak manifest is stored at `build-aux/flatpak/com.github.liferooter.textpieces.yaml`. ## Conventions Text Pieces uses some conventions: - [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for commit messages - [Elementary Codestyle](https://docs.elementary.io/develop/writing-apps/code-style) for Vala code - [PEP 8](https://peps.python.org/pep-0008/) for Python-based tools - [REUSE](https://reuse.software/) licensing conventions - [GNU GPL 3.0 or later](https://www.gnu.org/licenses/gpl-3.0.en.html) license for code, [CC0](https://creativecommons.org/share-your-work/public-domain/cc0) licencse for metadata and anything else - [Semantic Versioning](https://semver.org/) - [GNOME HIG](https://developer.gnome.org/hig) for user interface ## Post-commit checks There are some things you should do after making changes in project code: ### Update translation templates If you've changed any translatable strings in application, you should update translations: ```bash ./po/update-po.sh ``` If you've changed translatable strings in `data/tools.json`, please add your changes manually to `po/tools.pot`. ### Licensing checks As Text Pieces is REUSE-compilant project, you have to copyright headers in any file you create or edit. The simpliest way to do it is: ```bash pip install --user reuse reuse addheader --copyright 'YOUR NAME ' --year CURRENT_YEAR LICENSE_NAME FILE_NAME ``` ## Copyright policy Text Pieces is open-source project and will stay open-source forever. To make this rule non-breakable, you should add your copyright to any file you change or create. This makes license changing impossible without permission from everyone who ever contributes to the project. ## Creating translation Text Pieces uses `gettext` for localization. Translations can be suggested by PR. The only important point is `po/tools.pot`. It's manually-written translate template for tools metadata (`gettext` can't extract translatable strings from JSON files). Translators have to insert its translation right after `po/textpieces.pot` translation. Also translator should place their copyright note in translation file. ================================================ FILE: LICENSE ================================================ 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. Text Pieces -- Swiss knife of text transformations Copyright (C) 2021, Gleb Smirnov 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: Text Pieces, Copyright (C) 2021, Gleb Smirnov 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 . ================================================ FILE: LICENSES/CC0-1.0.txt ================================================ Creative Commons Legal Code CC0 1.0 Universal CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED HEREUNDER. Statement of Purpose The laws of most jurisdictions throughout the world automatically confer exclusive Copyright and Related Rights (defined below) upon the creator and subsequent owner(s) (each and all, an "owner") of an original work of authorship and/or a database (each, a "Work"). Certain owners wish to permanently relinquish those rights to a Work for the purpose of contributing to a commons of creative, cultural and scientific works ("Commons") that the public can reliably and without fear of later claims of infringement build upon, modify, incorporate in other works, reuse and redistribute as freely as possible in any form whatsoever and for any purposes, including without limitation commercial purposes. These owners may contribute to the Commons to promote the ideal of a free culture and the further production of creative, cultural and scientific works, or to gain reputation or greater distribution for their Work in part through the use and efforts of others. For these and/or other purposes and motivations, and without any expectation of additional consideration or compensation, the person associating CC0 with a Work (the "Affirmer"), to the extent that he or she is an owner of Copyright and Related Rights in the Work, voluntarily elects to apply CC0 to the Work and publicly distribute the Work under its terms, with knowledge of his or her Copyright and Related Rights in the Work and the meaning and intended legal effect of CC0 on those rights. 1. Copyright and Related Rights. A Work made available under CC0 may be protected by copyright and related or neighboring rights ("Copyright and Related Rights"). Copyright and Related Rights include, but are not limited to, the following: i. the right to reproduce, adapt, distribute, perform, display, communicate, and translate a Work; ii. moral rights retained by the original author(s) and/or performer(s); iii. publicity and privacy rights pertaining to a person's image or likeness depicted in a Work; iv. rights protecting against unfair competition in regards to a Work, subject to the limitations in paragraph 4(a), below; v. rights protecting the extraction, dissemination, use and reuse of data in a Work; vi. database rights (such as those arising under Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, and under any national implementation thereof, including any amended or successor version of such directive); and vii. other similar, equivalent or corresponding rights throughout the world based on applicable law or treaty, and any national implementations thereof. 2. Waiver. To the greatest extent permitted by, but not in contravention of, applicable law, Affirmer hereby overtly, fully, permanently, irrevocably and unconditionally waives, abandons, and surrenders all of Affirmer's Copyright and Related Rights and associated claims and causes of action, whether now known or unknown (including existing as well as future claims and causes of action), in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each member of the public at large and to the detriment of Affirmer's heirs and successors, fully intending that such Waiver shall not be subject to revocation, rescission, cancellation, termination, or any other legal or equitable action to disrupt the quiet enjoyment of the Work by the public as contemplated by Affirmer's express Statement of Purpose. 3. Public License Fallback. Should any part of the Waiver for any reason be judged legally invalid or ineffective under applicable law, then the Waiver shall be preserved to the maximum extent permitted taking into account Affirmer's express Statement of Purpose. In addition, to the extent the Waiver is so judged Affirmer hereby grants to each affected person a royalty-free, non transferable, non sublicensable, non exclusive, irrevocable and unconditional license to exercise Affirmer's Copyright and Related Rights in the Work (i) in all territories worldwide, (ii) for the maximum duration provided by applicable law or treaty (including future time extensions), (iii) in any current or future medium and for any number of copies, and (iv) for any purpose whatsoever, including without limitation commercial, advertising or promotional purposes (the "License"). The License shall be deemed effective as of the date CC0 was applied by Affirmer to the Work. Should any part of the License for any reason be judged legally invalid or ineffective under applicable law, such partial invalidity or ineffectiveness shall not invalidate the remainder of the License, and in such case Affirmer hereby affirms that he or she will not (i) exercise any of his or her remaining Copyright and Related Rights in the Work or (ii) assert any associated claims and causes of action with respect to the Work, in either case contrary to Affirmer's express Statement of Purpose. 4. Limitations and Disclaimers. a. No trademark or patent rights held by Affirmer are waived, abandoned, surrendered, licensed or otherwise affected by this document. b. Affirmer offers the Work as-is and makes no representations or warranties of any kind concerning the Work, express, implied, statutory or otherwise, including without limitation warranties of title, merchantability, fitness for a particular purpose, non infringement, or the absence of latent or other defects, accuracy, or the present or absence of errors, whether or not discoverable, all to the greatest extent permissible under applicable law. c. Affirmer disclaims responsibility for clearing rights of other persons that may apply to the Work or any use thereof, including without limitation any person's Copyright and Related Rights in the Work. Further, Affirmer disclaims responsibility for obtaining any necessary consents, permissions or other rights required for any use of the Work. d. Affirmer understands and acknowledges that Creative Commons is not a party to this document and has no duty or obligation with respect to this CC0 or use of the Work. ================================================ FILE: LICENSES/GPL-3.0-or-later.txt ================================================ GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright © 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 . ================================================ FILE: README.md ================================================

Text Pieces
Text Pieces

Swiss knife of text processing


Download on Flathub

Screenshot

# State of the project The project was rewritten from scratch and moved to [a new repository](https://gitlab.com/liferooter/textpieces). This repository will never receive updates. # What is it? Text Pieces is a small tool for quick text transformations such as checksums, encoding, decoding and so on. Written in Vala for GNOME desktop in the hope to be useful. # Features - Base64 encoding and decoding - SHA-1, SHA-2 and MD5 checksums - Prettify and minify JSON - Convert JSON to YAML and vice versa - Count lines, symbols and words - Escape and unescape string, URL and HTML - Remove leading and trailing whitespaces - Sort and reverse sort lines - Reverse lines and whole text - Extendable with third-party scripts and custom tools # Installation ## From Flathub > **Recommended** Click here to install app from Flathub. ## Build from source ### Via GNOME Builder Text Pieces can be built with GNOME Builder >= 3.38. Clone this repo and click run button. ### Via Flatpak Text Pieces has a Flatpak manifest to enable it to be built with Flatpak. ### Via Meson Text Pieces can be built directly via Meson: ```bash git clone https://github.com/liferooter/textpieces cd textpieces meson _build cd _build meson compile ``` Next, it can be installed by `meson install`. # Dependencies If you use GNOME Builder or Flatpak, dependencies will be installed automatically. If you use pure Meson, dependencies will be: - vala >= 0.52 - gtk >= 4.2 - gtksourceview >= 5.2 - gio >= 2.50 - gee >= 0.8 - json-glib >= 1.6 - libadwaita >= 1.0 - python >= 3.8 - pyyaml >= 5.4 ================================================ FILE: build-aux/flatpak/com.github.liferooter.textpieces.yaml ================================================ # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later app-id: com.github.liferooter.textpieces command: textpieces runtime: org.gnome.Platform runtime-version: '43' sdk: org.gnome.Sdk sdk-extensions: - org.freedesktop.Sdk.Extension.vala finish-args: - --share=ipc - --socket=fallback-x11 - --socket=wayland - --device=dri - --filesystem=~/.local/share/textpieces:create - --talk-name=org.freedesktop.Flatpak cleanup: - /include - /lib/pkgconfig - /man - /share/doc - /share/pkgconfig - /share/vala - '*blueprint*' - '*.la' - '*.a' modules: - name: textpieces builddir: true buildsystem: meson sources: - type: dir path: ../.. modules: - name: blueprint buildsystem: meson sources: - type: git url: https://gitlab.gnome.org/jwestman/blueprint-compiler branch: main - name: python3-pyyaml buildsystem: simple build-commands: - pip3 install --verbose --exists-action=i --no-index --find-links="file://${PWD}" --prefix=${FLATPAK_DEST} "pyyaml" --no-build-isolation sources: - type: file url: https://files.pythonhosted.org/packages/36/2b/61d51a2c4f25ef062ae3f74576b01638bebad5e045f747ff12643df63844/PyYAML-6.0.tar.gz sha256: 68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2 - name: libportal buildsystem: meson config-opts: - -Dbackend-gtk3=disabled - -Dbackend-gtk4=enabled - -Dbackend-qt5=disabled - -Ddocs=false - -Dtests=false sources: - type: git url: https://github.com/flatpak/libportal tag: main ================================================ FILE: build-aux/meson/postinstall.py ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from os import environ, path from subprocess import call prefix = environ.get('MESON_INSTALL_PREFIX', '/usr/local') datadir = path.join(prefix, 'share') destdir = environ.get('DESTDIR', '') # Package managers set this so we don't need to run if not destdir: print('Updating icon cache...') call(['gtk-update-icon-cache', '-qtf', path.join(datadir, 'icons', 'hicolor')]) print('Updating desktop database...') call(['update-desktop-database', '-q', path.join(datadir, 'applications')]) print('Compiling GSettings schemas...') call(['glib-compile-schemas', path.join(datadir, 'glib-2.0', 'schemas')]) ================================================ FILE: ci/generate-tools-po.sh ================================================ #!/usr/bin/env bash # SPDX-FileCopyrightText: 2022 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later cd "$(git rev-parse --show-toplevel)" || exit 1 jq '.tools[] | .name, .description, .args[]?' data/tools.json | sort | uniq | sed 's/"\(.*\)"/\n#: data\/tools.json\nmsgctxt "tools"\nmsgid "\1"\nmsgstr ""/g' > po/tools.pot ================================================ FILE: ci/update-po.sh ================================================ #!/usr/bin/env bash # SPDX-FileCopyrightText: 2022 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later cd "$(git rev-parse --show-toplevel)" || exit 1 echo "Changes in files related to translations:" changes="$(git diff @~ @ -- ci/update-po.sh po/POTFILES $(grep '^\w' po/POTFILES) || exit 1)" echo "$changes" if [[ -z "$changes" ]]; then echo "No changes" exit fi ci/generate-tools-po.sh && meson _build && ninja -C _build textpieces-pot && cat po/tools.pot >>po/textpieces.pot && for file in $(ls po/*.po); do msgmerge --backup=off -U $file po/textpieces.pot -D . done || exit 1 git add po && git commit -m 'chore(po): update translations' || exit 1 ================================================ FILE: data/com.github.liferooter.textpieces.appdata.xml.in ================================================ com.github.liferooter.textpieces.desktop Text Pieces Transform text without using random websites CC0-1.0 GPL-3.0-or-later

Powerful scratchpad with ability to perform a lot of text transformations, such as:

  • Calculate hashes
  • Encode text
  • Decode text
  • Remove trailing spaces and lines
  • Count lines, symbols and words
  • Format JSON and XML
  • Escape and unescape strings
  • Convert JSON to YAML and vice versa
  • Filter lines
  • Replace substrings and regular expressions
  • And so on.

The application is extendable with your own script-based actions.

@app-id@.desktop textpieces Gleb Smirnov https://github.com/liferooter/textpieces https://github.com/liferooter/textpieces/issues pointing touch keyboard 360 offline-only https://raw.githubusercontent.com/liferooter/textpieces/6b766fbb9956175db454c3266ab3d8c2bfa57b07/screenshots/slide1.png https://raw.githubusercontent.com/liferooter/textpieces/6b766fbb9956175db454c3266ab3d8c2bfa57b07/screenshots/slide2.png https://raw.githubusercontent.com/liferooter/textpieces/6b766fbb9956175db454c3266ab3d8c2bfa57b07/screenshots/slide3.png https://raw.githubusercontent.com/liferooter/textpieces/6b766fbb9956175db454c3266ab3d8c2bfa57b07/screenshots/slide4.png https://raw.githubusercontent.com/liferooter/textpieces/6b766fbb9956175db454c3266ab3d8c2bfa57b07/screenshots/slide5.png

Bug fixes:

  • Script files can be correctly opened using an editor of your choice
  • Newly created script names are generated from tool name

New features:

  • Text Pieces now can open files

Bug fixes:

  • Some translation issues are fixed
  • Some tools are fixed and optimized
  • Shortcuts window is updated and fixed

Bug fixes:

  • Tool arguments now work as expected
  • Translate some untranslated strings

New features and improvements:

  • Add tool for extracting RSS URLs from OPML files
  • Use more verbose error reporting for Base64 decode tool
  • Focus editor on startup
  • Save selected tool between sessions
  • Minor UI improvements

New features and improvements:

  • Add hotkey to open search in replace mode

New features and improvements:

  • Add some keywords to the application for system search

New features and improvement:

  • Add style schemes
  • Add search and replace feature to editor
  • Fix some bugs
Bug fix

New features and improvements:

  • Refresh UI
  • Fix a lot of bugs
  • Add powerful settings
  • Add arguments support for custom tools
  • Add translation support
  • Improve search
Fix theme switcher Add theme switcher for system which don't support color schemes

New features and improvements:

  • Minor UI changes
  • Add XML formatter
Some bugfixes and improvements Bring back tools with arguments

New features and improvements:

  • Redesign the application
  • Add custom tools support
  • Improve search
  • Add ability to text load from file and save to file

New features and improvements:

  • Exit by Ctrl+Q
  • Monospace argument entry
  • Update new runtime

New features and improvements:

  • Sort tools
  • Filter tools
  • Minify C-like code

New features and improvements:

  • Add more tools
  • Make tools popover more powerful
  • Redesign icon
  • General improvements
Add tools for replacement and removing Application release
================================================ FILE: data/com.github.liferooter.textpieces.desktop.in ================================================ # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later [Desktop Entry] Name=Text Pieces Icon=com.github.liferooter.textpieces Exec=textpieces %U MimeType=text/plain; Terminal=false Type=Application Categories=Utility;TextTools;GTK;GNOME; StartupNotify=true Actions=new-window; # Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! Keywords=GNOME;GTK;Text;Base64;JSON;YAML;Count;SHA1;SHA256;SHA384;SHA512;MD5;Checksum;Escape;Encode;HTML;URL;Prettify;Filter;Reverse;Trim; [Desktop Action new-window] Name=New Window Exec=textpieces ================================================ FILE: data/com.github.liferooter.textpieces.gschema.xml ================================================ 'Adwaita-dark' Style scheme Which style scheme to use for application interface and editor. 'Monospace' Font name Name of font used in editor true Wrap lines Whether to wrap long lines false Tabs to spaces Whether to insert spaces instead of tab 4 Spaces in tab Tab width in spaces 800 Window width Saved width of application window 500 Window height Saved height of the window false Is maximized Whether the application window is maximized '' Selected tool Used to restore tool choice on restart. Its value must be a name of tool's script. ================================================ FILE: data/icons/com.github.liferooter.textpieces-symbolic.svg.license ================================================ SPDX-FileCopyrightText: 2021 Gleb Smirnov SPDX-License-Identifier: CC0-1.0 ================================================ FILE: data/icons/com.github.liferooter.textpieces.Source.svg.license ================================================ SPDX-FileCopyrightText: 2021 Gleb Smirnov SPDX-License-Identifier: CC0-1.0 ================================================ FILE: data/icons/com.github.liferooter.textpieces.svg.license ================================================ SPDX-FileCopyrightText: 2021 Gleb Smirnov SPDX-License-Identifier: CC0-1.0 ================================================ FILE: data/meson.build ================================================ # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later desktop_file = i18n.merge_file( input: 'com.github.liferooter.textpieces.desktop.in', output: 'com.github.liferooter.textpieces.desktop', type: 'desktop', po_dir: '../po', install: true, install_dir: join_paths(get_option('datadir'), 'applications') ) desktop_utils = find_program('desktop-file-validate', required: false) if desktop_utils.found() test('Validate desktop file', desktop_utils, args: [desktop_file] ) endif appstream_file = i18n.merge_file( input: 'com.github.liferooter.textpieces.appdata.xml.in', output: 'com.github.liferooter.textpieces.appdata.xml', po_dir: '../po', install: true, install_dir: join_paths(get_option('datadir'), 'appdata') ) appstreamcli = find_program('appstreamcli', required: false) if appstreamcli.found() test('Validate appstream file', appstreamcli, args: ['validate', appstream_file] ) endif compile_schemas = find_program('glib-compile-schemas', required: false) if compile_schemas.found() test('Validate schema file', compile_schemas, args: ['--strict', '--dry-run', meson.current_source_dir()] ) endif install_data('com.github.liferooter.textpieces.gschema.xml', install_dir: join_paths(get_option('datadir'), 'glib-2.0/schemas') ) # Icons install_data( join_paths('icons', 'com.github.liferooter.textpieces.svg'), install_dir: join_paths (get_option('datadir'), 'icons', 'hicolor', 'scalable', 'apps') ) install_data( join_paths('icons', 'com.github.liferooter.textpieces-symbolic.svg'), install_dir: join_paths (get_option('datadir'), 'icons', 'hicolor', 'symbolic', 'apps') ) # Tools' JSON install_data ( 'tools.json', install_dir: pkgdata_dir ) ================================================ FILE: data/tools.json ================================================ { "is_system": true, "tools": [ { "name": "SHA1 Checksum", "description": "Compute SHA1 hash sum", "icon": "fingerprint-symbolic", "script": "sha1" }, { "name": "SHA256 Checksum", "description": "Compute SHA256 hash sum", "icon": "fingerprint-symbolic", "script": "sha256" }, { "name": "SHA384 Checksum", "description": "Compute SHA384 hash sum", "icon": "fingerprint-symbolic", "script": "sha384" }, { "name": "SHA512 Checksum", "description": "Compute SHA512 hash sum", "icon": "fingerprint-symbolic", "script": "sha512" }, { "name": "MD5 Checksum", "description": "Compute MD5 hash sum", "icon": "fingerprint-symbolic", "script": "md5" }, { "name": "Base64 Encode", "description": "Encode text to Base64", "icon": "lock-symbolic", "script": "base64-encode" }, { "name": "Base64 Decode", "description": "Decode text from Base64", "icon": "unlock-symbolic", "script": "base64-decode" }, { "name": "Remove Trailing", "description": "Remove trailing whitespaces and newlines", "icon": "edit-cut-symbolic", "script": "remove-trailing" }, { "name": "Trim Lines", "description": "Remove leading and trailing whitespaces and newlines", "icon": "edit-cut-symbolic", "script": "trim" }, { "name": "OPML to RSS links", "description": "Extract RSS links from OPML outline", "icon": "rss-symbolic", "script": "rss-from-opml" }, { "name": "Count Symbols", "description": "Count how many symbols are in text", "icon": "view-list-ordered-symbolic", "script": "count-symbols" }, { "name": "Count Lines", "description": "Count how many lines are in text", "icon": "view-list-ordered-symbolic", "script": "count-lines" }, { "name": "Count Words", "description": "Count how many words are in text", "icon": "view-list-ordered-symbolic", "script": "count-words" }, { "name": "Prettify JSON", "description": "Parse JSON and prettify it", "icon": "format-justify-left-symbolic", "script": "format-json" }, { "name": "Minify JSON", "description": "Parse JSON and minify it", "icon": "format-justify-fill-symbolic", "script": "minify-json" }, { "name": "Escape String", "description": "Replace special characters with escape sequences", "icon": "security-high-symbolic", "script": "escape-string" }, { "name": "Escape HTML", "description": "Replace special characters with HTML-safe sequences", "icon": "security-high-symbolic", "script": "escape-html" }, { "name": "Escape URL", "description": "Replace special characters with URL-safe sequences", "icon": "security-high-symbolic", "script": "escape-url" }, { "name": "Unescape String", "description": "Replace escape sequences with characters", "icon": "security-low-symbolic", "script": "unescape-string" }, { "name": "Unescape HTML", "description": "Replace HTML sequences with characters", "icon": "security-low-symbolic", "script": "unescape-html" }, { "name": "Unescape URL", "description": "Replace URL sequences with characters", "icon": "security-low-symbolic", "script": "unescape-url" }, { "name": "Convert JSON to YAML", "description": "Parse JSON and convert it to YAML document", "icon": "network-transmit-symbolic", "script": "json-to-yaml" }, { "name": "Convert YAML to JSON", "description": "Parse YAML document and convert it to JSON", "icon": "network-transmit-symbolic", "script": "yaml-to-json" }, { "name": "Reverse Lines", "description": "Reoder lines in reverse order", "icon": "object-flip-vertical-symbolic", "script": "reverse-lines" }, { "name": "Reverse Text", "description": "Reorder characters in reverse order", "icon": "object-flip-horizontal-symbolic", "script": "reverse-string" }, { "name": "Sort Lines", "description": "Sort lines in text in alphabetical order", "icon": "view-sort-ascending-symbolic", "script": "sort-lines" }, { "name": "Reverse Sort Lines", "description": "Sort lines in text in reverse alphabetical order", "icon": "view-sort-descending-symbolic", "script": "sort-lines-reverse" }, { "name": "Filter Lines by Regex", "description": "Remove all lines which don't match given regular expression", "icon": "funnel-symbolic", "script": "filter-regex", "args": [ "Regex" ] }, { "name": "Filter Lines by Substring", "description": "Remove all lines which don't contain given substring", "icon": "funnel-symbolic", "script": "filter-substring", "args": [ "Substring" ] }, { "name": "Reverse Filter Lines by Regex", "description": "Remove all lines which match given regular expression", "icon": "funnel-symbolic", "script": "reverse-filter-regex", "args": [ "Regex" ] }, { "name": "Reverse Filter Lines by Substring", "description": "Remove all lines which contain given substring", "icon": "funnel-symbolic", "script": "reverse-filter-substring", "args": [ "Substring" ] }, { "name": "Remove Regex", "description": "Remove all matches of regular expression", "icon": "edit-cut-symbolic", "script": "remove-regex", "args": [ "Regex" ] }, { "name": "Remove Substring", "description": "Remove all occurrences of given substring in text", "icon": "edit-cut-symbolic", "script": "remove-substring", "args": [ "Substring" ] }, { "name": "Replace Substring", "description": "Replace all occurrences of given substring in text with other string", "icon": "edit-find-replace-symbolic", "script": "replace-substring", "args": [ "Find", "Replace With" ] }, { "name": "Replace Regex", "description": "Replace all given regex matches in text with given string", "icon": "edit-find-replace-symbolic", "script": "replace-regex", "args": [ "Find (regex)", "Replace With" ] }, { "name": "Prettify XML", "description": "Parse XML and prettify it", "icon": "format-justify-left-symbolic", "script": "format-xml" } ] } ================================================ FILE: data/tools.json.license ================================================ SPDX-FileCopyrightText: 2021 Gleb Smirnov SPDX-FileCopyrightText: 2022 Adrián Bíro SPDX-License-Identifier: GPL-3.0-or-later ================================================ FILE: icon.png.license ================================================ SPDX-FileCopyrightText: 2021 Gleb Smirnov SPDX-License-Identifier: CC0-1.0 ================================================ FILE: meson.build ================================================ # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later project('textpieces', 'vala', version: '3.4.1', meson_version: '>= 0.59.0', default_options: [ 'warning_level=2', ], ) # Get package data directory (usually /usr/share/textpieces) pkgdata_dir = get_option('prefix') / get_option('datadir') / meson.project_name() textpieces_sources = [] cc = meson.get_compiler('c') valac = meson.get_compiler('vala') vapi_dir = join_paths (meson.project_source_root(), 'src', 'vapi') add_project_arguments( [ '--target-glib=2.50', '--enable-experimental', '--vapidir', vapi_dir, ], language: 'vala' ) # Dependencies { libadwaita_dep = dependency('libadwaita-1', version: '>= 1.0.0-alpha.2', required: false) if not libadwaita_dep.found() libadwaita = subproject( 'libadwaita', default_options: [ 'examples=false', 'package_subdir=@0@'.format(meson.project_name()), 'tests=false' ] ) libadwaita_dep = declare_dependency( dependencies: [ libadwaita.get_variable('libadwaita_dep'), libadwaita.get_variable('libadwaita_vapi') ] ) endif gtksourceview_dep = dependency('gtksourceview-5', version: '>= 5.3.0', required: false) if not gtksourceview_dep.found() gtksourceview = subproject( 'gtksourceview', default_options: [ 'package_subdir=@0@'.format(meson.project_name()), 'tests=false' ] ) gtksourceview_dep = declare_dependency( dependencies: [ gtksourceview.get_variable('gtksource_dep'), gtksourceview.get_variable('gtksource_vapi') ] ) endif json_glib_dep = dependency('json-glib-1.0', version: '>= 1.6.0', required: false) if not json_glib_dep.found() json_glib = subproject( 'json-glib', default_options: [ 'package_subdir=@0@'.format(meson.project_name()), 'tests=false', 'gtk_doc=disabled', 'introspection=enabled' ] ) json_glib_dep = declare_dependency( dependencies: [ json_glib.get_variable('json_glib_dep'), json_glib.get_variable('json_glib_gir') ] ) endif libm_dep = cc.find_library('m') textpieces_deps = [ dependency('gio-2.0', version: '>= 2.50'), dependency('gio-unix-2.0', version: '>= 2.50'), dependency('gtk4', version: '>= 4.2'), dependency('gee-0.8', version: '>= 0.8'), dependency('libportal', version: '>= 0.6'), dependency('libportal-gtk4', version: '>= 0.6'), json_glib_dep, gtksourceview_dep, libadwaita_dep, libm_dep, valac.find_library('config', dirs: vapi_dir), ] # } i18n = import('i18n') gnome = import('gnome') subdir('data') subdir('resources') subdir('src') subdir('po') install_subdir('scripts', install_dir: pkgdata_dir) gnome.post_install( glib_compile_schemas: true, gtk_update_icon_cache: true, update_desktop_database: true ) ================================================ FILE: po/LINGUAS ================================================ # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: CC0-1.0 es nl pt_BR ================================================ FILE: po/POTFILES ================================================ # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: CC0-1.0 data/com.github.liferooter.textpieces.desktop.in data/com.github.liferooter.textpieces.appdata.xml.in data/com.github.liferooter.textpieces.gschema.xml src/widgets/Editor.vala src/widgets/Window.vala src/widgets/SearchEntry.vala src/widgets/preferences/Preferences.vala src/widgets/preferences/ToolSettings.vala src/widgets/preferences/pages/NewToolPage.vala resources/ui/CustomToolPage.blp resources/ui/Search.blp resources/ui/Editor.blp resources/ui/NewToolPage.blp resources/ui/Preferences.blp resources/ui/SearchBar.blp resources/ui/SearchEntry.blp resources/ui/ShortcutsWindow.blp resources/ui/ToolSettings.blp resources/ui/Window.blp ================================================ FILE: po/es.po ================================================ # SPDX-FileCopyrightText: 2022 Óscar Fernández Díaz # # SPDX-License-Identifier: CC0-1.0 msgid "" msgstr "" "Project-Id-Version: textpieces\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-12-21 08:25+0000\n" "PO-Revision-Date: 2022-01-24 14:00+0100\n" "Last-Translator: Automatically generated\n" "Language-Team: none\n" "Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.0.1\n" #: data/com.github.liferooter.textpieces.desktop.in:7 #: data/com.github.liferooter.textpieces.appdata.xml.in:9 #: src/widgets/Window.vala:241 resources/ui/Window.blp:9 msgid "Text Pieces" msgstr "Text Pieces" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/com.github.liferooter.textpieces.desktop.in:18 msgid "" "GNOME;GTK;Text;Base64;JSON;YAML;Count;SHA1;SHA256;SHA384;SHA512;MD5;Checksum;" "Escape;Encode;HTML;URL;Prettify;Filter;Reverse;Trim;" msgstr "" #: data/com.github.liferooter.textpieces.desktop.in:21 msgid "New Window" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:10 #: src/widgets/Window.vala:242 msgid "Transform text without using random websites" msgstr "Transformar el texto sin usar sitios web aleatorios" #: data/com.github.liferooter.textpieces.appdata.xml.in:14 msgid "Do a lot of text transformations, such as:" msgstr "Hacer un montón de transformaciones de texto, tales como:" #: data/com.github.liferooter.textpieces.appdata.xml.in:16 msgid "Calculate hashes" msgstr "Calcular los hashes" #: data/com.github.liferooter.textpieces.appdata.xml.in:17 msgid "Encode text" msgstr "Codificar texto" #: data/com.github.liferooter.textpieces.appdata.xml.in:18 msgid "Decode text" msgstr "Decodificar texto" #: data/com.github.liferooter.textpieces.appdata.xml.in:19 msgid "Remove trailing spaces and lines" msgstr "Eliminar los espacios y líneas finales" #: data/com.github.liferooter.textpieces.appdata.xml.in:20 msgid "Count lines, symbols and words" msgstr "Contar líneas, símbolos y palabras" #: data/com.github.liferooter.textpieces.appdata.xml.in:21 msgid "Format JSON and XML" msgstr "Formato JSON y XML" #: data/com.github.liferooter.textpieces.appdata.xml.in:22 msgid "Escape and unescape strings" msgstr "Cadenas de escape y desescape" #: data/com.github.liferooter.textpieces.appdata.xml.in:23 msgid "Convert JSON to YAML and vice versa" msgstr "Convertir JSON a YAML y viceversa" #: data/com.github.liferooter.textpieces.appdata.xml.in:24 msgid "Filter lines" msgstr "Filtrar líneas" #: data/com.github.liferooter.textpieces.appdata.xml.in:25 msgid "Replace substrings and regular expressions" msgstr "Reemplazar subcadenas y expresiones regulares" #: data/com.github.liferooter.textpieces.appdata.xml.in:27 msgid "...and so on." msgstr "...y más." #: data/com.github.liferooter.textpieces.appdata.xml.in:32 #: src/widgets/Window.vala:252 msgid "Gleb Smirnov" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:64 #: data/com.github.liferooter.textpieces.appdata.xml.in:77 #: data/com.github.liferooter.textpieces.appdata.xml.in:87 msgid "Bug fixes:" msgstr "Corrección de errores" #: data/com.github.liferooter.textpieces.appdata.xml.in:66 msgid "Script files can be correctly opened using an editor of your choice" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:67 msgid "Newly created script names are generated from tool name" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:73 #, fuzzy msgid "New features:" msgstr "Algunas correcciones de errores y mejoras" #: data/com.github.liferooter.textpieces.appdata.xml.in:75 msgid "Text Pieces now can open files" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:79 msgid "Some translation issues are fixed" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:80 msgid "Some tools are fixed and optimized" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:81 msgid "Shortcuts window is updated and fixed" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:89 msgid "Tool arguments now work as expected" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:90 msgid "Translate some untranslated strings" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:96 #: data/com.github.liferooter.textpieces.appdata.xml.in:108 #: data/com.github.liferooter.textpieces.appdata.xml.in:116 #: data/com.github.liferooter.textpieces.appdata.xml.in:137 #: data/com.github.liferooter.textpieces.appdata.xml.in:156 #: data/com.github.liferooter.textpieces.appdata.xml.in:171 #: data/com.github.liferooter.textpieces.appdata.xml.in:182 #: data/com.github.liferooter.textpieces.appdata.xml.in:192 #: data/com.github.liferooter.textpieces.appdata.xml.in:202 msgid "New features and improvements:" msgstr "Algunas correcciones de errores y mejoras" #: data/com.github.liferooter.textpieces.appdata.xml.in:98 msgid "Add tool for extracting RSS URLs from OPML files" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:99 msgid "Use more verbose error reporting for Base64 decode tool" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:100 msgid "Focus editor on startup" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:101 msgid "Save selected tool between sessions" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:102 msgid "Minor UI improvements" msgstr "Mejoras generales" #: data/com.github.liferooter.textpieces.appdata.xml.in:110 msgid "Add hotkey to open search in replace mode" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:118 msgid "Add some keywords to the application for system search" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:124 msgid "New features and improvement:" msgstr "Algunas correcciones de errores y mejoras" #: data/com.github.liferooter.textpieces.appdata.xml.in:126 msgid "Add style schemes" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:127 msgid "Add search and replace feature to editor" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:128 msgid "Fix some bugs" msgstr "Corrección de un montón de errores" #: data/com.github.liferooter.textpieces.appdata.xml.in:133 msgid "Bug fix" msgstr "Corrección de errores" #: data/com.github.liferooter.textpieces.appdata.xml.in:139 msgid "Refresh UI" msgstr "Actualización de la interfaz de usuario" #: data/com.github.liferooter.textpieces.appdata.xml.in:140 msgid "Fix a lot of bugs" msgstr "Corrección de un montón de errores" #: data/com.github.liferooter.textpieces.appdata.xml.in:141 msgid "Add powerful settings" msgstr "Añade ajustes potentes" #: data/com.github.liferooter.textpieces.appdata.xml.in:142 msgid "Add arguments support for custom tools" msgstr "Añade soporte de argumentos para herramientas personalizadas" #: data/com.github.liferooter.textpieces.appdata.xml.in:143 msgid "Add translation support" msgstr "Añade soporte de traducción" #: data/com.github.liferooter.textpieces.appdata.xml.in:144 #: data/com.github.liferooter.textpieces.appdata.xml.in:175 msgid "Improve search" msgstr "Mejora de la búsqueda" #: data/com.github.liferooter.textpieces.appdata.xml.in:158 msgid "Minor UI changes" msgstr "Cambios menores en la interfaz de usuario" #: data/com.github.liferooter.textpieces.appdata.xml.in:159 msgid "Add XML formatter" msgstr "Añade un formateador XML" #: data/com.github.liferooter.textpieces.appdata.xml.in:164 msgid "Some bugfixes and improvements" msgstr "Algunas correcciones de errores y mejoras" #: data/com.github.liferooter.textpieces.appdata.xml.in:167 msgid "Bring back tools with arguments" msgstr "Vuelven las herramientas con argumentos" #: data/com.github.liferooter.textpieces.appdata.xml.in:173 msgid "Redesign the application" msgstr "Rediseño de la aplicación" #: data/com.github.liferooter.textpieces.appdata.xml.in:174 msgid "Add custom tools support" msgstr "Añade soporte para herramientas personalizadas" #: data/com.github.liferooter.textpieces.appdata.xml.in:176 msgid "Add ability to text load from file and save to file" msgstr "" "Añade la posibilidad de cargar texto desde un archivo y guardarlo en un " "archivo" #: data/com.github.liferooter.textpieces.appdata.xml.in:184 msgid "Exit by Ctrl+Q" msgstr "Salir con Ctrl+Q" #: data/com.github.liferooter.textpieces.appdata.xml.in:185 msgid "Monospace argument entry" msgstr "Entrada de argumentos en monoespaciado" #: data/com.github.liferooter.textpieces.appdata.xml.in:186 msgid "Update new runtime" msgstr "Actualizar el nuevo tiempo de ejecución" #: data/com.github.liferooter.textpieces.appdata.xml.in:194 msgid "Sort tools" msgstr "Herramientas de clasificación" #: data/com.github.liferooter.textpieces.appdata.xml.in:195 msgid "Filter tools" msgstr "Herramientas de filtrado" #: data/com.github.liferooter.textpieces.appdata.xml.in:196 msgid "Minify C-like code" msgstr "Reducir el código de tipo C" #: data/com.github.liferooter.textpieces.appdata.xml.in:204 msgid "Add more tools" msgstr "Añade más herramientas" #: data/com.github.liferooter.textpieces.appdata.xml.in:205 msgid "Make tools popover more powerful" msgstr "Potencia el menú emergente de herramientas" #: data/com.github.liferooter.textpieces.appdata.xml.in:206 msgid "Redesign icon" msgstr "Rediseño del icono" #: data/com.github.liferooter.textpieces.appdata.xml.in:207 msgid "General improvements" msgstr "Mejoras generales" #: data/com.github.liferooter.textpieces.appdata.xml.in:212 msgid "Add tools for replacement and removing" msgstr "Añade herramientas para la sustitución y el desmontaje" #: data/com.github.liferooter.textpieces.appdata.xml.in:215 msgid "Application release" msgstr "Publicación de la aplicación" #: data/com.github.liferooter.textpieces.gschema.xml:13 msgid "Style scheme" msgstr "Esquema de colores" #: data/com.github.liferooter.textpieces.gschema.xml:14 msgid "Which style scheme to use for application interface and editor." msgstr "Qué combinación de colores usar para la interfaz de la aplicación." #: data/com.github.liferooter.textpieces.gschema.xml:18 msgid "Font name" msgstr "Nombre de la fuente" #: data/com.github.liferooter.textpieces.gschema.xml:19 msgid "Name of font used in editor" msgstr "Nombre de la fuente usada en el editor" #: data/com.github.liferooter.textpieces.gschema.xml:23 msgid "Wrap lines" msgstr "Ajuste de líneas" #: data/com.github.liferooter.textpieces.gschema.xml:24 msgid "Whether to wrap long lines" msgstr "Si las líneas largas continúan en la siguiente línea" #: data/com.github.liferooter.textpieces.gschema.xml:28 msgid "Tabs to spaces" msgstr "Tabulaciones a espacios" #: data/com.github.liferooter.textpieces.gschema.xml:29 msgid "Whether to insert spaces instead of tab" msgstr "Si se insertan espacios en lugar del tabulador" #: data/com.github.liferooter.textpieces.gschema.xml:33 msgid "Spaces in tab" msgstr "Espacios en tabulador" #: data/com.github.liferooter.textpieces.gschema.xml:34 msgid "Tab width in spaces" msgstr "Ancho de tabulado en espacios" #: data/com.github.liferooter.textpieces.gschema.xml:38 msgid "Window width" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:39 msgid "Saved width of application window" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:43 msgid "Window height" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:44 msgid "Saved height of the window" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:48 msgid "Is maximized" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:49 msgid "Whether the application window is maximized" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:53 msgid "Selected tool" msgstr "Seleccionar herramienta" #: data/com.github.liferooter.textpieces.gschema.xml:54 msgid "" "Used to restore tool choice on restart. Its value must be a name of tool's " "script." msgstr "" #: src/widgets/Editor.vala:145 msgid "Text is copied" msgstr "Texto copiado al portapapeles" #. * #. * Text used instead of tool name #. * when where is no tool selected #. #: src/widgets/Window.vala:21 resources/ui/ShortcutsWindow.blp:50 msgid "Select tool" msgstr "Seleccionar herramienta" #: src/widgets/Window.vala:161 #, c-format msgid "Can't load from file: %s" msgstr "No se puede cargar desde el archivo: %s" #: src/widgets/Window.vala:249 msgid "Tobias Bernard https://tobiasbernard.com" msgstr "" #: src/widgets/Window.vala:250 msgid "Gleb Smirnov " msgstr "" #: src/widgets/Window.vala:253 msgid "translator-credits" msgstr "Óscar Fernández Díaz " #: src/widgets/Window.vala:280 resources/ui/Window.blp:78 msgid "Save to File" msgstr "Guardar en archivo" #: src/widgets/Window.vala:305 #, c-format msgid "Can't save to file: %s" msgstr "No se puede guardar en el archivo: %s" #: src/widgets/Window.vala:321 resources/ui/Window.blp:85 msgid "Load from File" msgstr "Cargar desde archivo" #: src/widgets/SearchEntry.vala:93 #, c-format msgid "%u of %d" msgstr "" #. Create dialog #: src/widgets/preferences/Preferences.vala:128 msgid "Select font" msgstr "Seleccionar tipografía" #: src/widgets/preferences/ToolSettings.vala:187 #, c-format msgid "Argument %d" msgstr "Argumento %d" #: src/widgets/preferences/pages/NewToolPage.vala:113 #, c-format msgid "Error occured: %s" msgstr "Se ha producido un error: %s" #: resources/ui/CustomToolPage.blp:27 msgid "Edit Tool" msgstr "Editor" #: resources/ui/CustomToolPage.blp:38 msgid "D_elete" msgstr "_Eliminar" #: resources/ui/Search.blp:41 msgid "No Tools Found" msgstr "No se han encontrado herramientas" #: resources/ui/Search.blp:53 msgid "_Add Custom Tool" msgstr "Añadir herramienta personalizada" #: resources/ui/NewToolPage.blp:27 msgid "New Tool" msgstr "Herramientas" #: resources/ui/NewToolPage.blp:42 msgid "_Create" msgstr "_Crear" #: resources/ui/Preferences.blp:13 msgid "_General" msgstr "_General" #: resources/ui/Preferences.blp:19 msgid "Editor" msgstr "Editor" #: resources/ui/Preferences.blp:22 msgid "Editor _Font" msgstr "_Tipografía del editor" #: resources/ui/Preferences.blp:40 msgid "_Wrap Lines" msgstr "Ajustar _líneas" #: resources/ui/Preferences.blp:51 msgid "Tabs to _Spaces" msgstr "Tabulaciones a _espacios" #: resources/ui/Preferences.blp:62 msgid "_Tab Width in Spaces" msgstr "Ancho del _tabulado en espacios" #: resources/ui/Preferences.blp:82 msgid "Appearance" msgstr "" #: resources/ui/Preferences.blp:94 msgid "T_ools" msgstr "_Herramientas" #: resources/ui/Preferences.blp:100 msgid "Custom tools" msgstr "Herramientas personalizadas" #: resources/ui/Preferences.blp:112 msgid "_Add custom tool" msgstr "Añadir herramienta personalizada" #: resources/ui/Preferences.blp:128 msgid "No Custom Tools" msgstr "Herramientas personalizadas" #: resources/ui/Preferences.blp:129 msgid "You can create one" msgstr "" #: resources/ui/SearchBar.blp:40 resources/ui/ShortcutsWindow.blp:102 msgid "Move to previous match" msgstr "" #: resources/ui/SearchBar.blp:46 resources/ui/ShortcutsWindow.blp:107 msgid "Move to next match" msgstr "" #: resources/ui/SearchBar.blp:58 msgid "Find and replace" msgstr "" #: resources/ui/SearchBar.blp:68 msgid "Toggle search options" msgstr "" #: resources/ui/SearchBar.blp:84 msgid "Close search" msgstr "Mejora de la búsqueda" #: resources/ui/SearchBar.blp:95 resources/ui/ShortcutsWindow.blp:97 msgid "Replace" msgstr "Reemplazar con" #: resources/ui/SearchBar.blp:106 msgid "_Replace" msgstr "Reemplazar con" #: resources/ui/SearchBar.blp:119 msgid "Replace _All" msgstr "Reemplazar con" #: resources/ui/SearchBar.blp:138 msgid "Re_gular expressions" msgstr "Reemplazar subcadenas y expresiones regulares" #: resources/ui/SearchBar.blp:144 msgid "_Case sensitive" msgstr "" #: resources/ui/SearchBar.blp:150 msgid "Match whole _word only" msgstr "" #: resources/ui/SearchEntry.blp:15 msgid "Find" msgstr "Buscar" #: resources/ui/ShortcutsWindow.blp:13 msgid "General" msgstr "General" #: resources/ui/ShortcutsWindow.blp:16 msgid "New window" msgstr "" #: resources/ui/ShortcutsWindow.blp:21 msgid "Keyboard shortcuts" msgstr "Atajos del teclado" #: resources/ui/ShortcutsWindow.blp:26 msgid "Preferences" msgstr "Preferencias" #: resources/ui/ShortcutsWindow.blp:31 msgid "Close window" msgstr "" #: resources/ui/ShortcutsWindow.blp:36 msgid "Quit" msgstr "Salir" #: resources/ui/ShortcutsWindow.blp:42 msgid "Tools" msgstr "Herramientas" #: resources/ui/ShortcutsWindow.blp:45 msgid "Apply" msgstr "Aplicar" #: resources/ui/ShortcutsWindow.blp:55 msgid "Just to the tool options" msgstr "Saltar a las opciones de la herramienta" #: resources/ui/ShortcutsWindow.blp:61 msgid "Undo and Redo" msgstr "Deshacer y rehacer" #: resources/ui/ShortcutsWindow.blp:64 msgid "Undo" msgstr "Deshacer" #: resources/ui/ShortcutsWindow.blp:69 msgid "Redo" msgstr "Rehacer" #: resources/ui/ShortcutsWindow.blp:75 msgid "Files" msgstr "Archivos" #: resources/ui/ShortcutsWindow.blp:78 msgid "Load from file" msgstr "Cargar desde archivo" #: resources/ui/ShortcutsWindow.blp:83 msgid "Save to file" msgstr "Guardar en archivo" #: resources/ui/ShortcutsWindow.blp:89 msgid "Search and Replace" msgstr "" #: resources/ui/ShortcutsWindow.blp:92 msgid "Search" msgstr "" #: resources/ui/ToolSettings.blp:31 msgid "_Name" msgstr "_Nombre" #: resources/ui/ToolSettings.blp:42 msgid "_Description" msgstr "_Descripción" #: resources/ui/ToolSettings.blp:52 msgid "_Arguments" msgstr "_Argumentos" #: resources/ui/ToolSettings.blp:80 msgid "Edit _Script" msgstr "" #: resources/ui/Window.blp:32 msgid "_Apply" msgstr "_Aplicar" #: resources/ui/Window.blp:35 msgid "Apply Selected Tool" msgstr "Aplicar la herramienta seleccionada" #: resources/ui/Window.blp:45 msgid "Copy" msgstr "Copiar" #: resources/ui/Window.blp:71 msgid "Main Menu" msgstr "Menú principal" #: resources/ui/Window.blp:123 msgid "_New Window" msgstr "" #: resources/ui/Window.blp:131 msgid "_Find/Replace…" msgstr "" #: resources/ui/Window.blp:139 msgid "_Preferences" msgstr "_Preferencias" #: resources/ui/Window.blp:145 msgid "_Keyboard Shortcuts" msgstr "Atajos del _teclado" #: resources/ui/Window.blp:151 msgid "_About Text Pieces" msgstr "_Acerca de Text Pieces" #: data/tools.json msgctxt "tools" msgid "Base64 Decode" msgstr "Decodificación en Base64" #: data/tools.json msgctxt "tools" msgid "Base64 Encode" msgstr "Codificación en Base64" #: data/tools.json msgctxt "tools" msgid "Compute MD5 hash sum" msgstr "Calcular la suma del hash MD5" #: data/tools.json msgctxt "tools" msgid "Compute SHA1 hash sum" msgstr "Calcular la suma del hash SHA1" #: data/tools.json msgctxt "tools" msgid "Compute SHA256 hash sum" msgstr "Calcular la suma del hash SHA256" #: data/tools.json msgctxt "tools" msgid "Compute SHA384 hash sum" msgstr "Calcular la suma del hash SHA384" #: data/tools.json msgctxt "tools" msgid "Compute SHA512 hash sum" msgstr "Calcular la suma del hash SHA512" #: data/tools.json msgctxt "tools" msgid "Convert JSON to YAML" msgstr "Convertir JSON en YAML" #: data/tools.json msgctxt "tools" msgid "Convert YAML to JSON" msgstr "Convertir YAML en JSON" #: data/tools.json msgctxt "tools" msgid "Count how many lines are in text" msgstr "Contar cuántas líneas hay en el texto" #: data/tools.json msgctxt "tools" msgid "Count how many symbols are in text" msgstr "Contar cuántos símbolos hay en el texto" #: data/tools.json msgctxt "tools" msgid "Count how many words are in text" msgstr "Contar cuántas palabras hay en el texto" #: data/tools.json msgctxt "tools" msgid "Count Lines" msgstr "Contar líneas" #: data/tools.json msgctxt "tools" msgid "Count Symbols" msgstr "Contar símbolos" #: data/tools.json msgctxt "tools" msgid "Count Words" msgstr "Contar palabras" #: data/tools.json msgctxt "tools" msgid "Decode text from Base64" msgstr "Decodificación de texto en Base64" #: data/tools.json msgctxt "tools" msgid "Encode text to Base64" msgstr "Codificar texto en Base64" #: data/tools.json msgctxt "tools" msgid "Escape HTML" msgstr "Escape HTML" #: data/tools.json msgctxt "tools" msgid "Escape String" msgstr "Cadena de escape" #: data/tools.json msgctxt "tools" msgid "Escape URL" msgstr "Escape URL" #: data/tools.json msgctxt "tools" msgid "Extract RSS links from OPML outline" msgstr "" #: data/tools.json msgctxt "tools" msgid "Filter Lines by Regex" msgstr "Filtrar líneas por regex" #: data/tools.json msgctxt "tools" msgid "Filter Lines by Substring" msgstr "Filtrar líneas por subcadena" #: data/tools.json msgctxt "tools" msgid "Find" msgstr "Buscar" #: data/tools.json msgctxt "tools" msgid "Find (regex)" msgstr "Buscar (regex)" #: data/tools.json msgctxt "tools" msgid "MD5 Checksum" msgstr "Suma de verificación MD5" #: data/tools.json msgctxt "tools" msgid "Minify JSON" msgstr "Minimizar JSON" #: data/tools.json msgctxt "tools" msgid "OPML to RSS links" msgstr "" #: data/tools.json msgctxt "tools" msgid "Parse JSON and convert it to YAML document" msgstr "Analizar JSON y convertirlo en un documento YAML" #: data/tools.json msgctxt "tools" msgid "Parse JSON and minify it" msgstr "Analizar JSON y minimizarlo" #: data/tools.json msgctxt "tools" msgid "Parse JSON and prettify it" msgstr "Analizar JSON y mejorarlo" #: data/tools.json msgctxt "tools" msgid "Parse XML and prettify it" msgstr "Analizar XML y mejorarlo" #: data/tools.json msgctxt "tools" msgid "Parse YAML document and convert it to JSON" msgstr "Analizar el documento YAML y convertirlo en JSON" #: data/tools.json msgctxt "tools" msgid "Prettify JSON" msgstr "Mejorar JSON" #: data/tools.json msgctxt "tools" msgid "Prettify XML" msgstr "Mejorar XML" #: data/tools.json msgctxt "tools" msgid "Regex" msgstr "Regex" #: data/tools.json msgctxt "tools" msgid "Remove all lines which contain given substring" msgstr "Quitar todas las líneas que contengan la subcadena dada" #: data/tools.json msgctxt "tools" msgid "Remove all lines which don't contain given substring" msgstr "Quitar todas las líneas que no contengan la subcadena dada" #: data/tools.json msgctxt "tools" msgid "Remove all lines which don't match given regular expression" msgstr "Quitar todas las líneas que no coincidan con la expresión regular dada" #: data/tools.json msgctxt "tools" msgid "Remove all lines which match given regular expression" msgstr "Quitar todas las líneas que coincidan con la expresión regular dada" #: data/tools.json msgctxt "tools" msgid "Remove all matches of regular expression" msgstr "Quitar todas las coincidencias de la expresión regular" #: data/tools.json msgctxt "tools" msgid "Remove all occurrences of given substring in text" msgstr "Quitar todas las ocurrencias de la subcadena dada en el texto" #: data/tools.json msgctxt "tools" msgid "Remove leading and trailing whitespaces and newlines" msgstr "Quitar los espacios en blanco iniciales y finales y las líneas nuevas" #: data/tools.json msgctxt "tools" msgid "Remove Regex" msgstr "Quitar regex" #: data/tools.json msgctxt "tools" msgid "Remove Substring" msgstr "Quitar la subcadena" #: data/tools.json msgctxt "tools" msgid "Remove Trailing" msgstr "Quitar los restos" #: data/tools.json msgctxt "tools" msgid "Remove trailing whitespaces and newlines" msgstr "Quitar los espacios en blanco al final y los saltos de línea" #: data/tools.json msgctxt "tools" msgid "Reoder lines in reverse order" msgstr "Reordenar las líneas en orden inverso" #: data/tools.json msgctxt "tools" msgid "Reorder characters in reverse order" msgstr "Reordenar los caracteres en orden inverso" #: data/tools.json msgctxt "tools" msgid "Replace all given regex matches in text with given string" msgstr "" "Reemplazar todas las coincidencias regex dadas en el texto con la cadena dada" #: data/tools.json msgctxt "tools" msgid "Replace all occurrences of given substring in text with other string" msgstr "" "Reemplazar todas las ocurrencias de la subcadena dada en el texto con otra " "cadena" #: data/tools.json msgctxt "tools" msgid "Replace escape sequences with characters" msgstr "Reemplazar las secuencias de escape por caracteres" #: data/tools.json msgctxt "tools" msgid "Replace HTML sequences with characters" msgstr "Reemplazar las secuencias HTML por caracteres" #: data/tools.json msgctxt "tools" msgid "Replace Regex" msgstr "Reemplazar regex" #: data/tools.json msgctxt "tools" msgid "Replace special characters with escape sequences" msgstr "Reemplazar los caracteres especiales por secuencias de escape" #: data/tools.json msgctxt "tools" msgid "Replace special characters with HTML-safe sequences" msgstr "Reemplazar los caracteres especiales por secuencias seguras para HTML" #: data/tools.json msgctxt "tools" msgid "Replace special characters with URL-safe sequences" msgstr "" "Reemplazar los caracteres especiales por secuencias seguras para la URL" #: data/tools.json msgctxt "tools" msgid "Replace Substring" msgstr "Reemplazar subcadena" #: data/tools.json msgctxt "tools" msgid "Replace URL sequences with characters" msgstr "Reemplazar las secuencias de URL con caracteres" #: data/tools.json msgctxt "tools" msgid "Replace With" msgstr "Reemplazar con" #: data/tools.json msgctxt "tools" msgid "Reverse Filter Lines by Regex" msgstr "Invertir las líneas de filtrado por regex" #: data/tools.json msgctxt "tools" msgid "Reverse Filter Lines by Substring" msgstr "Filtrar líneas por subcadena de forma inversa" #: data/tools.json msgctxt "tools" msgid "Reverse Lines" msgstr "Invertir líneas" #: data/tools.json msgctxt "tools" msgid "Reverse Sort Lines" msgstr "Ordenar las líneas de forma inversa" #: data/tools.json msgctxt "tools" msgid "Reverse Text" msgstr "Invertir texto" #: data/tools.json msgctxt "tools" msgid "SHA1 Checksum" msgstr "Suma de verificación SHA1" #: data/tools.json msgctxt "tools" msgid "SHA256 Checksum" msgstr "Suma de verificación SHA256" #: data/tools.json msgctxt "tools" msgid "SHA384 Checksum" msgstr "Suma de verificación SHA384" #: data/tools.json msgctxt "tools" msgid "SHA512 Checksum" msgstr "Suma de verificación SHA512" #: data/tools.json msgctxt "tools" msgid "Sort Lines" msgstr "Ordenar líneas" #: data/tools.json msgctxt "tools" msgid "Sort lines in text in alphabetical order" msgstr "Ordenar las líneas del texto por orden alfabético" #: data/tools.json msgctxt "tools" msgid "Sort lines in text in reverse alphabetical order" msgstr "Ordenar las líneas del texto en orden alfabético inverso" #: data/tools.json msgctxt "tools" msgid "Substring" msgstr "Subcadena" #: data/tools.json msgctxt "tools" msgid "Trim Lines" msgstr "Recortar líneas" #: data/tools.json msgctxt "tools" msgid "Unescape HTML" msgstr "Desescape HTML" #: data/tools.json msgctxt "tools" msgid "Unescape String" msgstr "Cadena de desescape" #: data/tools.json msgctxt "tools" msgid "Unescape URL" msgstr "Desescape URL" #~ msgid "Color Scheme" #~ msgstr "Esquema de colores" #~ msgid "_Light" #~ msgstr "_Claro" #~ msgid "_Dark" #~ msgstr "_Oscuro" #~ msgid "S_ystem Default" #~ msgstr "_Predeterminado del sistema" #~ msgid "Copy and Paste" #~ msgstr "Copiar y pegar" #~ msgid "Paste" #~ msgstr "Pegar" #~ msgid "Copy all" #~ msgstr "Pegar todo" ================================================ FILE: po/meson.build ================================================ # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later i18n.gettext(meson.project_name(), preset: 'glib') ================================================ FILE: po/nl.po ================================================ # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: CC0-1.0 msgid "" msgstr "" "Project-Id-Version: \n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-12-21 08:25+0000\n" "PO-Revision-Date: 2022-03-24 14:33+0100\n" "Last-Translator: Heimen Stoffels \n" "Language-Team: \n" "Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n != 1);\n" "X-Generator: Poedit 3.0.1\n" #: data/com.github.liferooter.textpieces.desktop.in:7 #: data/com.github.liferooter.textpieces.appdata.xml.in:9 #: src/widgets/Window.vala:241 resources/ui/Window.blp:9 msgid "Text Pieces" msgstr "Tekstdelen" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/com.github.liferooter.textpieces.desktop.in:18 msgid "" "GNOME;GTK;Text;Base64;JSON;YAML;Count;SHA1;SHA256;SHA384;SHA512;MD5;Checksum;" "Escape;Encode;HTML;URL;Prettify;Filter;Reverse;Trim;" msgstr "" #: data/com.github.liferooter.textpieces.desktop.in:21 msgid "New Window" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:10 #: src/widgets/Window.vala:242 msgid "Transform text without using random websites" msgstr "Zet tekst om zonder het gebruik van willekeurige websites" #: data/com.github.liferooter.textpieces.appdata.xml.in:14 msgid "Do a lot of text transformations, such as:" msgstr "Voer diverse tekstbewerkingen uit, waaronder:" #: data/com.github.liferooter.textpieces.appdata.xml.in:16 msgid "Calculate hashes" msgstr "Sommen berekenen" #: data/com.github.liferooter.textpieces.appdata.xml.in:17 msgid "Encode text" msgstr "Tekst versleutelen" #: data/com.github.liferooter.textpieces.appdata.xml.in:18 msgid "Decode text" msgstr "Tekst ontsleutelen" #: data/com.github.liferooter.textpieces.appdata.xml.in:19 msgid "Remove trailing spaces and lines" msgstr "Witruimtes verwijderen" #: data/com.github.liferooter.textpieces.appdata.xml.in:20 msgid "Count lines, symbols and words" msgstr "Regels, symbolen en woorden tellen" #: data/com.github.liferooter.textpieces.appdata.xml.in:21 msgid "Format JSON and XML" msgstr "JSON en XML opmaken" #: data/com.github.liferooter.textpieces.appdata.xml.in:22 msgid "Escape and unescape strings" msgstr "Tekenreeksen markeren als aanpassen/niet-aanpassen" #: data/com.github.liferooter.textpieces.appdata.xml.in:23 msgid "Convert JSON to YAML and vice versa" msgstr "JSON naar YAML converteren en vice-versa" #: data/com.github.liferooter.textpieces.appdata.xml.in:24 msgid "Filter lines" msgstr "Regels filteren" #: data/com.github.liferooter.textpieces.appdata.xml.in:25 msgid "Replace substrings and regular expressions" msgstr "Bijzinnen en reguliere uitdrukkingen vervangen" #: data/com.github.liferooter.textpieces.appdata.xml.in:27 msgid "...and so on." msgstr "…en nog veel meer." #: data/com.github.liferooter.textpieces.appdata.xml.in:32 #: src/widgets/Window.vala:252 msgid "Gleb Smirnov" msgstr "Gleb Smirnov" #: data/com.github.liferooter.textpieces.appdata.xml.in:64 #: data/com.github.liferooter.textpieces.appdata.xml.in:77 #: data/com.github.liferooter.textpieces.appdata.xml.in:87 msgid "Bug fixes:" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:66 msgid "Script files can be correctly opened using an editor of your choice" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:67 msgid "Newly created script names are generated from tool name" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:73 #, fuzzy msgid "New features:" msgstr "Enkele bugs opgelost en andere kleine verbeteringen aangebracht" #: data/com.github.liferooter.textpieces.appdata.xml.in:75 msgid "Text Pieces now can open files" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:79 msgid "Some translation issues are fixed" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:80 msgid "Some tools are fixed and optimized" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:81 msgid "Shortcuts window is updated and fixed" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:89 msgid "Tool arguments now work as expected" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:90 msgid "Translate some untranslated strings" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:96 #: data/com.github.liferooter.textpieces.appdata.xml.in:108 #: data/com.github.liferooter.textpieces.appdata.xml.in:116 #: data/com.github.liferooter.textpieces.appdata.xml.in:137 #: data/com.github.liferooter.textpieces.appdata.xml.in:156 #: data/com.github.liferooter.textpieces.appdata.xml.in:171 #: data/com.github.liferooter.textpieces.appdata.xml.in:182 #: data/com.github.liferooter.textpieces.appdata.xml.in:192 #: data/com.github.liferooter.textpieces.appdata.xml.in:202 msgid "New features and improvements:" msgstr "Enkele bugs opgelost en andere kleine verbeteringen aangebracht" #: data/com.github.liferooter.textpieces.appdata.xml.in:98 msgid "Add tool for extracting RSS URLs from OPML files" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:99 msgid "Use more verbose error reporting for Base64 decode tool" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:100 msgid "Focus editor on startup" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:101 msgid "Save selected tool between sessions" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:102 msgid "Minor UI improvements" msgstr "Algemene verbeteringen" #: data/com.github.liferooter.textpieces.appdata.xml.in:110 msgid "Add hotkey to open search in replace mode" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:118 msgid "Add some keywords to the application for system search" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:124 msgid "New features and improvement:" msgstr "Enkele bugs opgelost en andere kleine verbeteringen aangebracht" #: data/com.github.liferooter.textpieces.appdata.xml.in:126 msgid "Add style schemes" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:127 msgid "Add search and replace feature to editor" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:128 msgid "Fix some bugs" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:133 msgid "Bug fix" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:139 msgid "Refresh UI" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:140 msgid "Fix a lot of bugs" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:141 msgid "Add powerful settings" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:142 msgid "Add arguments support for custom tools" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:143 msgid "Add translation support" msgstr "Nieuw: ondersteuning voor eigen gereedschap" #: data/com.github.liferooter.textpieces.appdata.xml.in:144 #: data/com.github.liferooter.textpieces.appdata.xml.in:175 msgid "Improve search" msgstr "Verbeterd: zoekfunctie" #: data/com.github.liferooter.textpieces.appdata.xml.in:158 msgid "Minor UI changes" msgstr "Kleine aanpassingen aan de vormgeving" #: data/com.github.liferooter.textpieces.appdata.xml.in:159 msgid "Add XML formatter" msgstr "Nieuw: xml-opmaakgereedschap" #: data/com.github.liferooter.textpieces.appdata.xml.in:164 msgid "Some bugfixes and improvements" msgstr "Enkele bugs opgelost en andere kleine verbeteringen aangebracht" #: data/com.github.liferooter.textpieces.appdata.xml.in:167 msgid "Bring back tools with arguments" msgstr "Gereedschap kan worden voorzien van opties" #: data/com.github.liferooter.textpieces.appdata.xml.in:173 msgid "Redesign the application" msgstr "De toepassing is opnieuw ontworpen" #: data/com.github.liferooter.textpieces.appdata.xml.in:174 msgid "Add custom tools support" msgstr "Nieuw: ondersteuning voor eigen gereedschap" #: data/com.github.liferooter.textpieces.appdata.xml.in:176 msgid "Add ability to text load from file and save to file" msgstr "" "Nieuw: mogelijkheid om tekst te laden uit een bestand en te bewaren als " "bestand" #: data/com.github.liferooter.textpieces.appdata.xml.in:184 msgid "Exit by Ctrl+Q" msgstr "Sluit af met Ctrl + Q" #: data/com.github.liferooter.textpieces.appdata.xml.in:185 msgid "Monospace argument entry" msgstr "Vastebreedtetekst kan worden voorzien van opties" #: data/com.github.liferooter.textpieces.appdata.xml.in:186 msgid "Update new runtime" msgstr "Aandrijving bijgewerkt" #: data/com.github.liferooter.textpieces.appdata.xml.in:194 msgid "Sort tools" msgstr "Sorteer gereedschap" #: data/com.github.liferooter.textpieces.appdata.xml.in:195 msgid "Filter tools" msgstr "Filter gereedschap" #: data/com.github.liferooter.textpieces.appdata.xml.in:196 msgid "Minify C-like code" msgstr "Verklein C-achtige code" #: data/com.github.liferooter.textpieces.appdata.xml.in:204 msgid "Add more tools" msgstr "Nieuw: meer gereedschap" #: data/com.github.liferooter.textpieces.appdata.xml.in:205 msgid "Make tools popover more powerful" msgstr "Gereedschapsmenu uitgebreid" #: data/com.github.liferooter.textpieces.appdata.xml.in:206 msgid "Redesign icon" msgstr "Het pictogram is opnieuw ontworpen" #: data/com.github.liferooter.textpieces.appdata.xml.in:207 msgid "General improvements" msgstr "Algemene verbeteringen" #: data/com.github.liferooter.textpieces.appdata.xml.in:212 msgid "Add tools for replacement and removing" msgstr "Nieuw: gereedschap voor vervangen en wissen" #: data/com.github.liferooter.textpieces.appdata.xml.in:215 msgid "Application release" msgstr "Eerste uitgave" #: data/com.github.liferooter.textpieces.gschema.xml:13 msgid "Style scheme" msgstr "Thema" #: data/com.github.liferooter.textpieces.gschema.xml:14 msgid "Which style scheme to use for application interface and editor." msgstr "Kies het thema dat de toepassing dient te gebruiken." #: data/com.github.liferooter.textpieces.gschema.xml:18 msgid "Font name" msgstr "Lettertype" #: data/com.github.liferooter.textpieces.gschema.xml:19 msgid "Name of font used in editor" msgstr "Kies het in de bewerker te gebruiken lettertype." #: data/com.github.liferooter.textpieces.gschema.xml:23 msgid "Wrap lines" msgstr "Regels afbreken" #: data/com.github.liferooter.textpieces.gschema.xml:24 msgid "Whether to wrap long lines" msgstr "Schakel in om lange regels af te breken." #: data/com.github.liferooter.textpieces.gschema.xml:28 msgid "Tabs to spaces" msgstr "Tabs vervangen door spaties" #: data/com.github.liferooter.textpieces.gschema.xml:29 msgid "Whether to insert spaces instead of tab" msgstr "Schakel in om spaties in te voegen in plaats van tabs." #: data/com.github.liferooter.textpieces.gschema.xml:33 msgid "Spaces in tab" msgstr "Aantal spaties" #: data/com.github.liferooter.textpieces.gschema.xml:34 msgid "Tab width in spaces" msgstr "De tabbreedte in spaties." #: data/com.github.liferooter.textpieces.gschema.xml:38 msgid "Window width" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:39 msgid "Saved width of application window" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:43 msgid "Window height" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:44 msgid "Saved height of the window" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:48 msgid "Is maximized" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:49 msgid "Whether the application window is maximized" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:53 msgid "Selected tool" msgstr "Kies een gereedschap" #: data/com.github.liferooter.textpieces.gschema.xml:54 msgid "" "Used to restore tool choice on restart. Its value must be a name of tool's " "script." msgstr "" #: src/widgets/Editor.vala:145 msgid "Text is copied" msgstr "De tekst is gekopieerd naar het klembord" #. * #. * Text used instead of tool name #. * when where is no tool selected #. #: src/widgets/Window.vala:21 resources/ui/ShortcutsWindow.blp:50 msgid "Select tool" msgstr "Kies een gereedschap" #: src/widgets/Window.vala:161 #, c-format msgid "Can't load from file: %s" msgstr "Het bestand kan niet worden geladen: %s" #: src/widgets/Window.vala:249 msgid "Tobias Bernard https://tobiasbernard.com" msgstr "Tobias Bernard https://tobiasbernard.com" #: src/widgets/Window.vala:250 msgid "Gleb Smirnov " msgstr "Gleb Smirnov " #: src/widgets/Window.vala:253 msgid "translator-credits" msgstr "Heimen Stoffels " #: src/widgets/Window.vala:280 resources/ui/Window.blp:78 msgid "Save to File" msgstr "Opslaan als bestand" #: src/widgets/Window.vala:305 #, c-format msgid "Can't save to file: %s" msgstr "Het bestand kan niet worden opgeslagen: %s" #: src/widgets/Window.vala:321 resources/ui/Window.blp:85 msgid "Load from File" msgstr "Laden uit bestand" #: src/widgets/SearchEntry.vala:93 #, c-format msgid "%u of %d" msgstr "" #. Create dialog #: src/widgets/preferences/Preferences.vala:128 msgid "Select font" msgstr "Kies een lettertype" #: src/widgets/preferences/ToolSettings.vala:187 #, c-format msgid "Argument %d" msgstr "Optie %d" #: src/widgets/preferences/pages/NewToolPage.vala:113 #, c-format msgid "Error occured: %s" msgstr "Er is een fout opgetreden: %s" #: resources/ui/CustomToolPage.blp:27 msgid "Edit Tool" msgstr "Bewerker" #: resources/ui/CustomToolPage.blp:38 msgid "D_elete" msgstr "Wiss_en" #: resources/ui/Search.blp:41 msgid "No Tools Found" msgstr "Er is geen gereedschap aangetroffen" #: resources/ui/Search.blp:53 msgid "_Add Custom Tool" msgstr "Eigen gereedschap toevoegen" #: resources/ui/NewToolPage.blp:27 msgid "New Tool" msgstr "Gereedschap" #: resources/ui/NewToolPage.blp:42 msgid "_Create" msgstr "_Aanmaken" #: resources/ui/Preferences.blp:13 msgid "_General" msgstr "Al_gemeen" #: resources/ui/Preferences.blp:19 msgid "Editor" msgstr "Bewerker" #: resources/ui/Preferences.blp:22 msgid "Editor _Font" msgstr "Letter_type van bewerker" #: resources/ui/Preferences.blp:40 msgid "_Wrap Lines" msgstr "Regels af_breken" #: resources/ui/Preferences.blp:51 msgid "Tabs to _Spaces" msgstr "Tabs vervangen door _spaties" #: resources/ui/Preferences.blp:62 msgid "_Tab Width in Spaces" msgstr "_Tabbreedte in spaties" #: resources/ui/Preferences.blp:82 msgid "Appearance" msgstr "" #: resources/ui/Preferences.blp:94 msgid "T_ools" msgstr "Gereedsch_ap" #: resources/ui/Preferences.blp:100 msgid "Custom tools" msgstr "Eigen gereedschap" #: resources/ui/Preferences.blp:112 msgid "_Add custom tool" msgstr "Eigen gereedschap toevoegen" #: resources/ui/Preferences.blp:128 msgid "No Custom Tools" msgstr "Eigen gereedschap" #: resources/ui/Preferences.blp:129 msgid "You can create one" msgstr "" #: resources/ui/SearchBar.blp:40 resources/ui/ShortcutsWindow.blp:102 msgid "Move to previous match" msgstr "" #: resources/ui/SearchBar.blp:46 resources/ui/ShortcutsWindow.blp:107 msgid "Move to next match" msgstr "" #: resources/ui/SearchBar.blp:58 msgid "Find and replace" msgstr "" #: resources/ui/SearchBar.blp:68 msgid "Toggle search options" msgstr "" #: resources/ui/SearchBar.blp:84 msgid "Close search" msgstr "Verbeterd: zoekfunctie" #: resources/ui/SearchBar.blp:95 resources/ui/ShortcutsWindow.blp:97 msgid "Replace" msgstr "Vervangen door" #: resources/ui/SearchBar.blp:106 msgid "_Replace" msgstr "Vervangen door" #: resources/ui/SearchBar.blp:119 msgid "Replace _All" msgstr "Vervangen door" #: resources/ui/SearchBar.blp:138 msgid "Re_gular expressions" msgstr "Bijzinnen en reguliere uitdrukkingen vervangen" #: resources/ui/SearchBar.blp:144 msgid "_Case sensitive" msgstr "" #: resources/ui/SearchBar.blp:150 msgid "Match whole _word only" msgstr "" #: resources/ui/SearchEntry.blp:15 msgid "Find" msgstr "Zoeken" #: resources/ui/ShortcutsWindow.blp:13 msgid "General" msgstr "Algemeen" #: resources/ui/ShortcutsWindow.blp:16 msgid "New window" msgstr "" #: resources/ui/ShortcutsWindow.blp:21 msgid "Keyboard shortcuts" msgstr "Sneltoetsen" #: resources/ui/ShortcutsWindow.blp:26 msgid "Preferences" msgstr "Voorkeuren" #: resources/ui/ShortcutsWindow.blp:31 msgid "Close window" msgstr "" #: resources/ui/ShortcutsWindow.blp:36 msgid "Quit" msgstr "Afsluiten" #: resources/ui/ShortcutsWindow.blp:42 msgid "Tools" msgstr "Gereedschap" #: resources/ui/ShortcutsWindow.blp:45 msgid "Apply" msgstr "Toepassen" #: resources/ui/ShortcutsWindow.blp:55 msgid "Just to the tool options" msgstr "Open het gereedschapsoptiemenu" #: resources/ui/ShortcutsWindow.blp:61 msgid "Undo and Redo" msgstr "Ongedaan maken/Opnieuw" #: resources/ui/ShortcutsWindow.blp:64 msgid "Undo" msgstr "Ongedaan maken" #: resources/ui/ShortcutsWindow.blp:69 msgid "Redo" msgstr "Opnieuw" #: resources/ui/ShortcutsWindow.blp:75 msgid "Files" msgstr "Bestanden" #: resources/ui/ShortcutsWindow.blp:78 msgid "Load from file" msgstr "Laden uit bestand" #: resources/ui/ShortcutsWindow.blp:83 msgid "Save to file" msgstr "Opslaan als bestand" #: resources/ui/ShortcutsWindow.blp:89 msgid "Search and Replace" msgstr "" #: resources/ui/ShortcutsWindow.blp:92 msgid "Search" msgstr "" #: resources/ui/ToolSettings.blp:31 msgid "_Name" msgstr "_Naam" #: resources/ui/ToolSettings.blp:42 msgid "_Description" msgstr "_Omschrijving" #: resources/ui/ToolSettings.blp:52 msgid "_Arguments" msgstr "Opdr_achtregelopties" #: resources/ui/ToolSettings.blp:80 msgid "Edit _Script" msgstr "" #: resources/ui/Window.blp:32 msgid "_Apply" msgstr "Toep_assen" #: resources/ui/Window.blp:35 msgid "Apply Selected Tool" msgstr "Gebruik het gekozen gereedschap" #: resources/ui/Window.blp:45 msgid "Copy" msgstr "Kopiëren" #: resources/ui/Window.blp:71 msgid "Main Menu" msgstr "Hoofdmenu" #: resources/ui/Window.blp:123 msgid "_New Window" msgstr "" #: resources/ui/Window.blp:131 msgid "_Find/Replace…" msgstr "" #: resources/ui/Window.blp:139 msgid "_Preferences" msgstr "_Voorkeuren" #: resources/ui/Window.blp:145 msgid "_Keyboard Shortcuts" msgstr "_Sneltoetsen" #: resources/ui/Window.blp:151 msgid "_About Text Pieces" msgstr "_Over Tekstdelen" #: data/tools.json msgctxt "tools" msgid "Base64 Decode" msgstr "Base64-ontsleuteling" #: data/tools.json msgctxt "tools" msgid "Base64 Encode" msgstr "Base64-versleuteling" #: data/tools.json msgctxt "tools" msgid "Compute MD5 hash sum" msgstr "Bereken de md5-controlesom" #: data/tools.json msgctxt "tools" msgid "Compute SHA1 hash sum" msgstr "Bereken de sha1-controlesom" #: data/tools.json msgctxt "tools" msgid "Compute SHA256 hash sum" msgstr "Bereken de sha256-controlesom" #: data/tools.json msgctxt "tools" msgid "Compute SHA384 hash sum" msgstr "Bereken de sha384-controlesom" #: data/tools.json msgctxt "tools" msgid "Compute SHA512 hash sum" msgstr "Bereken de sha512-controlesom" #: data/tools.json msgctxt "tools" msgid "Convert JSON to YAML" msgstr "JSON naar YAML converteren" #: data/tools.json msgctxt "tools" msgid "Convert YAML to JSON" msgstr "YAML naar JSON converteren" #: data/tools.json msgctxt "tools" msgid "Count how many lines are in text" msgstr "Tel het aantal regels in de tekst" #: data/tools.json msgctxt "tools" msgid "Count how many symbols are in text" msgstr "Tel het aantal symbolen in de tekst" #: data/tools.json msgctxt "tools" msgid "Count how many words are in text" msgstr "Tel het aantal woorden in de tekst" #: data/tools.json msgctxt "tools" msgid "Count Lines" msgstr "Regels tellen" #: data/tools.json msgctxt "tools" msgid "Count Symbols" msgstr "Symbolen tellen" #: data/tools.json msgctxt "tools" msgid "Count Words" msgstr "Woorden tellen" #: data/tools.json msgctxt "tools" msgid "Decode text from Base64" msgstr "Ontsleutel met Base64 versleutelde tekst" #: data/tools.json msgctxt "tools" msgid "Encode text to Base64" msgstr "Versleutel tekst met Base64" #: data/tools.json msgctxt "tools" msgid "Escape HTML" msgstr "Html aanpassen" #: data/tools.json msgctxt "tools" msgid "Escape String" msgstr "Tekenreeks aanpassen" #: data/tools.json msgctxt "tools" msgid "Escape URL" msgstr "Url aanpassen" #: data/tools.json msgctxt "tools" msgid "Extract RSS links from OPML outline" msgstr "" #: data/tools.json msgctxt "tools" msgid "Filter Lines by Regex" msgstr "Regels filteren op reguliere uitdrukkingen" #: data/tools.json msgctxt "tools" msgid "Filter Lines by Substring" msgstr "Regels filteren op onderliggende tekenreeks" #: data/tools.json msgctxt "tools" msgid "Find" msgstr "Zoeken" #: data/tools.json msgctxt "tools" msgid "Find (regex)" msgstr "Zoeken op reguliere uitdrukking" #: data/tools.json msgctxt "tools" msgid "MD5 Checksum" msgstr "Md5-controlesom" #: data/tools.json msgctxt "tools" msgid "Minify JSON" msgstr "JSON verkleinen" #: data/tools.json msgctxt "tools" msgid "OPML to RSS links" msgstr "" #: data/tools.json msgctxt "tools" msgid "Parse JSON and convert it to YAML document" msgstr "Verwerk json en converteer het naar yaml" #: data/tools.json msgctxt "tools" msgid "Parse JSON and minify it" msgstr "Verwerk json en verklein het" #: data/tools.json msgctxt "tools" msgid "Parse JSON and prettify it" msgstr "Verwerk json en voorzie het van een mooie opmaak" #: data/tools.json msgctxt "tools" msgid "Parse XML and prettify it" msgstr "Verwerk xml en voorzie het van een mooie opmaak" #: data/tools.json msgctxt "tools" msgid "Parse YAML document and convert it to JSON" msgstr "Verwerk yaml en converteer het naar json" #: data/tools.json msgctxt "tools" msgid "Prettify JSON" msgstr "JSON opmaken" #: data/tools.json msgctxt "tools" msgid "Prettify XML" msgstr "XML opmaken" #: data/tools.json msgctxt "tools" msgid "Regex" msgstr "Reguliere uitdrukking" #: data/tools.json msgctxt "tools" msgid "Remove all lines which contain given substring" msgstr "Verwijder alle regels die de opgegeven tekenreeks bevatten" #: data/tools.json msgctxt "tools" msgid "Remove all lines which don't contain given substring" msgstr "Verwijder alle regels die niet de opgegeven tekenreeks bevatten" #: data/tools.json msgctxt "tools" msgid "Remove all lines which don't match given regular expression" msgstr "" "Verwijder alle regels die niet overeenkomen met de opgegeven reguliere " "uitdrukking" #: data/tools.json msgctxt "tools" msgid "Remove all lines which match given regular expression" msgstr "" "Verwijder alle regels die overeenkomen met de opgegeven reguliere uitdrukking" #: data/tools.json msgctxt "tools" msgid "Remove all matches of regular expression" msgstr "Verwijder alle overeenkomende reguliere uitdrukkingen" #: data/tools.json msgctxt "tools" msgid "Remove all occurrences of given substring in text" msgstr "Verwijder alle regels die de opgegeven tekenreeks bevatten" #: data/tools.json msgctxt "tools" msgid "Remove leading and trailing whitespaces and newlines" msgstr "Verwijder witruimtes voor- en achteraan tekst en nieuwe regels" #: data/tools.json msgctxt "tools" msgid "Remove Regex" msgstr "Reguliere uitdrukking verwijderen" #: data/tools.json msgctxt "tools" msgid "Remove Substring" msgstr "Onderliggende tekenreeks verwijderen" #: data/tools.json msgctxt "tools" msgid "Remove Trailing" msgstr "Witruimtes verwijderen" #: data/tools.json msgctxt "tools" msgid "Remove trailing whitespaces and newlines" msgstr "Verwijder witruimtes en nieuwe regels" #: data/tools.json msgctxt "tools" msgid "Reoder lines in reverse order" msgstr "Plaats alle regels in omgekeerde volgorde" #: data/tools.json msgctxt "tools" msgid "Reorder characters in reverse order" msgstr "Plaats alle tekens in omgekeerde volgorde" #: data/tools.json msgctxt "tools" msgid "Replace all given regex matches in text with given string" msgstr "Vervang alle overeenkomende reguliere uitdrukkingen door een andere" #: data/tools.json msgctxt "tools" msgid "Replace all occurrences of given substring in text with other string" msgstr "Vervang alle overeenkomende tekenreeksen door een andere" #: data/tools.json msgctxt "tools" msgid "Replace escape sequences with characters" msgstr "Vervang ‘escape’tekens door reguliere tekens" #: data/tools.json msgctxt "tools" msgid "Replace HTML sequences with characters" msgstr "Vervang html-tekens door reguliere tekens" #: data/tools.json msgctxt "tools" msgid "Replace Regex" msgstr "Reguliere uitdrukking vervangen" #: data/tools.json msgctxt "tools" msgid "Replace special characters with escape sequences" msgstr "Vervang speciale tekens door ‘escape’tekens" #: data/tools.json msgctxt "tools" msgid "Replace special characters with HTML-safe sequences" msgstr "Vervang speciale tekens door html-tekens" #: data/tools.json msgctxt "tools" msgid "Replace special characters with URL-safe sequences" msgstr "Vervang speciale tekens door url-tekens" #: data/tools.json msgctxt "tools" msgid "Replace Substring" msgstr "Onderliggende tekenreeks vervangen" #: data/tools.json msgctxt "tools" msgid "Replace URL sequences with characters" msgstr "Vervang url-tekens door reguliere tekens" #: data/tools.json msgctxt "tools" msgid "Replace With" msgstr "Vervangen door" #: data/tools.json msgctxt "tools" msgid "Reverse Filter Lines by Regex" msgstr "Regels omgekeerd filteren op reguliere uitdrukkingen" #: data/tools.json msgctxt "tools" msgid "Reverse Filter Lines by Substring" msgstr "Regels omgekeerd filteren op onderliggende tekenreeks" #: data/tools.json msgctxt "tools" msgid "Reverse Lines" msgstr "Regels omkeren" #: data/tools.json msgctxt "tools" msgid "Reverse Sort Lines" msgstr "Regels omgekeerd sorteren" #: data/tools.json msgctxt "tools" msgid "Reverse Text" msgstr "Tekst omkeren" #: data/tools.json msgctxt "tools" msgid "SHA1 Checksum" msgstr "Sha1-controlesom" #: data/tools.json msgctxt "tools" msgid "SHA256 Checksum" msgstr "Sha256-controlesom" #: data/tools.json msgctxt "tools" msgid "SHA384 Checksum" msgstr "Sha384-controlesom" #: data/tools.json msgctxt "tools" msgid "SHA512 Checksum" msgstr "Sha512-controlesom" #: data/tools.json msgctxt "tools" msgid "Sort Lines" msgstr "Regels sorteren" #: data/tools.json msgctxt "tools" msgid "Sort lines in text in alphabetical order" msgstr "Sorteer alle regels op alfabetische volgorde" #: data/tools.json msgctxt "tools" msgid "Sort lines in text in reverse alphabetical order" msgstr "Sorteer alle regels op omgekeerde alfabetische volgorde" #: data/tools.json msgctxt "tools" msgid "Substring" msgstr "Onderliggende tekenreeks" #: data/tools.json msgctxt "tools" msgid "Trim Lines" msgstr "Regels afbreken" #: data/tools.json msgctxt "tools" msgid "Unescape HTML" msgstr "Html niet aanpassen" #: data/tools.json msgctxt "tools" msgid "Unescape String" msgstr "Tekenreeksen niet aanpassen" #: data/tools.json msgctxt "tools" msgid "Unescape URL" msgstr "Url niet aanpassen" #~ msgid "Color Scheme" #~ msgstr "Thema" #~ msgid "_Light" #~ msgstr "_Licht" #~ msgid "_Dark" #~ msgstr "_Donker" #~ msgid "S_ystem Default" #~ msgstr "S_ysteemthema" #~ msgid "Copy and Paste" #~ msgstr "Kopiëren en plakken" #~ msgid "Paste" #~ msgstr "Plakken" #~ msgid "Copy all" #~ msgstr "Alles kopiëren" ================================================ FILE: po/pt_BR.po ================================================ # SPDX-FileCopyrightText: 2022 Éden Alencar # # SPDX-License-Identifier: CC0-1.0 msgid "" msgstr "" "Project-Id-Version: textpieces\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-12-21 08:25+0000\n" "PO-Revision-Date: 2022-02-03 14:21-0300\n" "Last-Translator: Éden Alencar \n" "Language-Team: Brazilian Portuguese \n" "Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=2; plural=(n > 1)\n" "X-Generator: Gtranslator 41.0\n" #: data/com.github.liferooter.textpieces.desktop.in:7 #: data/com.github.liferooter.textpieces.appdata.xml.in:9 #: src/widgets/Window.vala:241 resources/ui/Window.blp:9 msgid "Text Pieces" msgstr "Text Pieces" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/com.github.liferooter.textpieces.desktop.in:18 msgid "" "GNOME;GTK;Text;Base64;JSON;YAML;Count;SHA1;SHA256;SHA384;SHA512;MD5;Checksum;" "Escape;Encode;HTML;URL;Prettify;Filter;Reverse;Trim;" msgstr "" #: data/com.github.liferooter.textpieces.desktop.in:21 msgid "New Window" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:10 #: src/widgets/Window.vala:242 msgid "Transform text without using random websites" msgstr "Transforme texto sem usar sites aleatórios" #: data/com.github.liferooter.textpieces.appdata.xml.in:14 msgid "Do a lot of text transformations, such as:" msgstr "Faça muitas transformações de texto, como:" #: data/com.github.liferooter.textpieces.appdata.xml.in:16 msgid "Calculate hashes" msgstr "Calcular hashes" #: data/com.github.liferooter.textpieces.appdata.xml.in:17 msgid "Encode text" msgstr "Codificar texto" #: data/com.github.liferooter.textpieces.appdata.xml.in:18 msgid "Decode text" msgstr "Decodificar texto" #: data/com.github.liferooter.textpieces.appdata.xml.in:19 msgid "Remove trailing spaces and lines" msgstr "Remova espaços e linhas à direita" #: data/com.github.liferooter.textpieces.appdata.xml.in:20 msgid "Count lines, symbols and words" msgstr "Contar linhas, símbolos e palavras" #: data/com.github.liferooter.textpieces.appdata.xml.in:21 msgid "Format JSON and XML" msgstr "Formate JSON e XML" #: data/com.github.liferooter.textpieces.appdata.xml.in:22 msgid "Escape and unescape strings" msgstr "Escape e não escape de strings" #: data/com.github.liferooter.textpieces.appdata.xml.in:23 msgid "Convert JSON to YAML and vice versa" msgstr "Converter JSON para YAML e vice-versa" #: data/com.github.liferooter.textpieces.appdata.xml.in:24 msgid "Filter lines" msgstr "Filtrar linhas" #: data/com.github.liferooter.textpieces.appdata.xml.in:25 msgid "Replace substrings and regular expressions" msgstr "Substituir substrings e expressões regulares" #: data/com.github.liferooter.textpieces.appdata.xml.in:27 msgid "...and so on." msgstr "...e assim por diante." #: data/com.github.liferooter.textpieces.appdata.xml.in:32 #: src/widgets/Window.vala:252 msgid "Gleb Smirnov" msgstr "Gleb Smirnov" #: data/com.github.liferooter.textpieces.appdata.xml.in:64 #: data/com.github.liferooter.textpieces.appdata.xml.in:77 #: data/com.github.liferooter.textpieces.appdata.xml.in:87 msgid "Bug fixes:" msgstr "Correção de bug" #: data/com.github.liferooter.textpieces.appdata.xml.in:66 msgid "Script files can be correctly opened using an editor of your choice" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:67 msgid "Newly created script names are generated from tool name" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:73 #, fuzzy msgid "New features:" msgstr "Algumas correções de bugs e melhorias" #: data/com.github.liferooter.textpieces.appdata.xml.in:75 msgid "Text Pieces now can open files" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:79 msgid "Some translation issues are fixed" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:80 msgid "Some tools are fixed and optimized" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:81 msgid "Shortcuts window is updated and fixed" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:89 msgid "Tool arguments now work as expected" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:90 msgid "Translate some untranslated strings" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:96 #: data/com.github.liferooter.textpieces.appdata.xml.in:108 #: data/com.github.liferooter.textpieces.appdata.xml.in:116 #: data/com.github.liferooter.textpieces.appdata.xml.in:137 #: data/com.github.liferooter.textpieces.appdata.xml.in:156 #: data/com.github.liferooter.textpieces.appdata.xml.in:171 #: data/com.github.liferooter.textpieces.appdata.xml.in:182 #: data/com.github.liferooter.textpieces.appdata.xml.in:192 #: data/com.github.liferooter.textpieces.appdata.xml.in:202 msgid "New features and improvements:" msgstr "Algumas correções de bugs e melhorias" #: data/com.github.liferooter.textpieces.appdata.xml.in:98 msgid "Add tool for extracting RSS URLs from OPML files" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:99 msgid "Use more verbose error reporting for Base64 decode tool" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:100 msgid "Focus editor on startup" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:101 msgid "Save selected tool between sessions" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:102 msgid "Minor UI improvements" msgstr "Melhorias gerais" #: data/com.github.liferooter.textpieces.appdata.xml.in:110 msgid "Add hotkey to open search in replace mode" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:118 msgid "Add some keywords to the application for system search" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:124 msgid "New features and improvement:" msgstr "Algumas correções de bugs e melhorias" #: data/com.github.liferooter.textpieces.appdata.xml.in:126 msgid "Add style schemes" msgstr "Esquema de estilo" #: data/com.github.liferooter.textpieces.appdata.xml.in:127 msgid "Add search and replace feature to editor" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:128 msgid "Fix some bugs" msgstr "Corrija muitos bugs" #: data/com.github.liferooter.textpieces.appdata.xml.in:133 msgid "Bug fix" msgstr "Correção de bug" #: data/com.github.liferooter.textpieces.appdata.xml.in:139 msgid "Refresh UI" msgstr "Atualizar interface do usuário" #: data/com.github.liferooter.textpieces.appdata.xml.in:140 msgid "Fix a lot of bugs" msgstr "Corrija muitos bugs" #: data/com.github.liferooter.textpieces.appdata.xml.in:141 msgid "Add powerful settings" msgstr "Adicione configurações poderosas" #: data/com.github.liferooter.textpieces.appdata.xml.in:142 msgid "Add arguments support for custom tools" msgstr "Adicionar suporte a argumentos para ferramentas personalizadas" #: data/com.github.liferooter.textpieces.appdata.xml.in:143 msgid "Add translation support" msgstr "Adicionar suporte de tradução" #: data/com.github.liferooter.textpieces.appdata.xml.in:144 #: data/com.github.liferooter.textpieces.appdata.xml.in:175 msgid "Improve search" msgstr "Melhorar a pesquisa" #: data/com.github.liferooter.textpieces.appdata.xml.in:158 msgid "Minor UI changes" msgstr "Pequenas alterações na interface do usuário" #: data/com.github.liferooter.textpieces.appdata.xml.in:159 msgid "Add XML formatter" msgstr "Adicionar formatador XML" #: data/com.github.liferooter.textpieces.appdata.xml.in:164 msgid "Some bugfixes and improvements" msgstr "Algumas correções de bugs e melhorias" #: data/com.github.liferooter.textpieces.appdata.xml.in:167 msgid "Bring back tools with arguments" msgstr "Traga de volta ferramentas com argumentos" #: data/com.github.liferooter.textpieces.appdata.xml.in:173 msgid "Redesign the application" msgstr "Redesenhe o aplicativo" #: data/com.github.liferooter.textpieces.appdata.xml.in:174 msgid "Add custom tools support" msgstr "Adicionar suporte a ferramentas personalizadas" #: data/com.github.liferooter.textpieces.appdata.xml.in:176 msgid "Add ability to text load from file and save to file" msgstr "Adicione a capacidade de carregar texto do arquivo e salvar no arquivo" #: data/com.github.liferooter.textpieces.appdata.xml.in:184 msgid "Exit by Ctrl+Q" msgstr "Sair com Ctrl+Q" #: data/com.github.liferooter.textpieces.appdata.xml.in:185 msgid "Monospace argument entry" msgstr "Entrada de argumento monoespaçado" #: data/com.github.liferooter.textpieces.appdata.xml.in:186 msgid "Update new runtime" msgstr "Atualizar novo tempo de execução" #: data/com.github.liferooter.textpieces.appdata.xml.in:194 msgid "Sort tools" msgstr "Ferramentas de classificação" #: data/com.github.liferooter.textpieces.appdata.xml.in:195 msgid "Filter tools" msgstr "Ferramentas de filtro" #: data/com.github.liferooter.textpieces.appdata.xml.in:196 msgid "Minify C-like code" msgstr "Minificar código semelhante a C" #: data/com.github.liferooter.textpieces.appdata.xml.in:204 msgid "Add more tools" msgstr "Adicionar mais ferramentas" #: data/com.github.liferooter.textpieces.appdata.xml.in:205 msgid "Make tools popover more powerful" msgstr "Torne as ferramentas popover mais poderosas" #: data/com.github.liferooter.textpieces.appdata.xml.in:206 msgid "Redesign icon" msgstr "Ícone de redesenho" #: data/com.github.liferooter.textpieces.appdata.xml.in:207 msgid "General improvements" msgstr "Melhorias gerais" #: data/com.github.liferooter.textpieces.appdata.xml.in:212 msgid "Add tools for replacement and removing" msgstr "Adicione ferramentas para substituição e remoção" #: data/com.github.liferooter.textpieces.appdata.xml.in:215 msgid "Application release" msgstr "Versão do aplicativo" #: data/com.github.liferooter.textpieces.gschema.xml:13 msgid "Style scheme" msgstr "Esquema de estilo" #: data/com.github.liferooter.textpieces.gschema.xml:14 msgid "Which style scheme to use for application interface and editor." msgstr "Qual esquema de estilo usar para a interface e o editor do aplicativo." #: data/com.github.liferooter.textpieces.gschema.xml:18 msgid "Font name" msgstr "Nome da fonte" #: data/com.github.liferooter.textpieces.gschema.xml:19 msgid "Name of font used in editor" msgstr "Nome da fonte usada no editor" #: data/com.github.liferooter.textpieces.gschema.xml:23 msgid "Wrap lines" msgstr "Quebra de linhas" #: data/com.github.liferooter.textpieces.gschema.xml:24 msgid "Whether to wrap long lines" msgstr "Se deve envolver linhas longas" #: data/com.github.liferooter.textpieces.gschema.xml:28 msgid "Tabs to spaces" msgstr "Guias para _Espaços" #: data/com.github.liferooter.textpieces.gschema.xml:29 msgid "Whether to insert spaces instead of tab" msgstr "Se deseja inserir espaços em vez de tabulação" #: data/com.github.liferooter.textpieces.gschema.xml:33 msgid "Spaces in tab" msgstr "Espaços na guia" #: data/com.github.liferooter.textpieces.gschema.xml:34 msgid "Tab width in spaces" msgstr "Largura da guia em espaços" #: data/com.github.liferooter.textpieces.gschema.xml:38 msgid "Window width" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:39 msgid "Saved width of application window" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:43 msgid "Window height" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:44 msgid "Saved height of the window" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:48 msgid "Is maximized" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:49 msgid "Whether the application window is maximized" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:53 msgid "Selected tool" msgstr "Selecionar ferramenta" #: data/com.github.liferooter.textpieces.gschema.xml:54 msgid "" "Used to restore tool choice on restart. Its value must be a name of tool's " "script." msgstr "" #: src/widgets/Editor.vala:145 msgid "Text is copied" msgstr "O texto é copiado para a área de transferência" #. * #. * Text used instead of tool name #. * when where is no tool selected #. #: src/widgets/Window.vala:21 resources/ui/ShortcutsWindow.blp:50 msgid "Select tool" msgstr "Selecionar ferramenta" #: src/widgets/Window.vala:161 #, c-format msgid "Can't load from file: %s" msgstr "Não é possível carregar do arquivo: %s" #: src/widgets/Window.vala:249 msgid "Tobias Bernard https://tobiasbernard.com" msgstr "" #: src/widgets/Window.vala:250 msgid "Gleb Smirnov " msgstr "Gleb Smirnov " #: src/widgets/Window.vala:253 msgid "translator-credits" msgstr "créditos do tradutor" #: src/widgets/Window.vala:280 resources/ui/Window.blp:78 msgid "Save to File" msgstr "Salvar em arquivo" #: src/widgets/Window.vala:305 #, c-format msgid "Can't save to file: %s" msgstr "Não é possível salvar no arquivo: %s" #: src/widgets/Window.vala:321 resources/ui/Window.blp:85 msgid "Load from File" msgstr "Carregar do arquivo" #: src/widgets/SearchEntry.vala:93 #, c-format msgid "%u of %d" msgstr "" #. Create dialog #: src/widgets/preferences/Preferences.vala:128 msgid "Select font" msgstr "Selecionar fonte" #: src/widgets/preferences/ToolSettings.vala:187 #, c-format msgid "Argument %d" msgstr "Argumento %d" #: src/widgets/preferences/pages/NewToolPage.vala:113 #, c-format msgid "Error occured: %s" msgstr "Ocorreu um erro: %s" #: resources/ui/CustomToolPage.blp:27 msgid "Edit Tool" msgstr "Editor" #: resources/ui/CustomToolPage.blp:38 msgid "D_elete" msgstr "E_xcluir" #: resources/ui/Search.blp:41 msgid "No Tools Found" msgstr "Nenhuma ferramenta encontrada" #: resources/ui/Search.blp:53 msgid "_Add Custom Tool" msgstr "Adicionar ferramenta personalizada" #: resources/ui/NewToolPage.blp:27 msgid "New Tool" msgstr "Ferramentas" #: resources/ui/NewToolPage.blp:42 msgid "_Create" msgstr "_Criar" #: resources/ui/Preferences.blp:13 msgid "_General" msgstr "_Geral" #: resources/ui/Preferences.blp:19 msgid "Editor" msgstr "Editor" #: resources/ui/Preferences.blp:22 msgid "Editor _Font" msgstr "Editor _Fonte" #: resources/ui/Preferences.blp:40 msgid "_Wrap Lines" msgstr "_Linhas de quebra" #: resources/ui/Preferences.blp:51 msgid "Tabs to _Spaces" msgstr "Guias para _Espaços" #: resources/ui/Preferences.blp:62 msgid "_Tab Width in Spaces" msgstr "_Largura da Tabulação nos Espaços" #: resources/ui/Preferences.blp:82 msgid "Appearance" msgstr "Aparência" #: resources/ui/Preferences.blp:94 msgid "T_ools" msgstr "F_erramentas" #: resources/ui/Preferences.blp:100 msgid "Custom tools" msgstr "Ferramentas personalizadas" #: resources/ui/Preferences.blp:112 msgid "_Add custom tool" msgstr "Adicionar ferramenta personalizada" #: resources/ui/Preferences.blp:128 msgid "No Custom Tools" msgstr "Ferramentas personalizadas" #: resources/ui/Preferences.blp:129 msgid "You can create one" msgstr "" #: resources/ui/SearchBar.blp:40 resources/ui/ShortcutsWindow.blp:102 msgid "Move to previous match" msgstr "" #: resources/ui/SearchBar.blp:46 resources/ui/ShortcutsWindow.blp:107 msgid "Move to next match" msgstr "" #: resources/ui/SearchBar.blp:58 msgid "Find and replace" msgstr "" #: resources/ui/SearchBar.blp:68 msgid "Toggle search options" msgstr "" #: resources/ui/SearchBar.blp:84 msgid "Close search" msgstr "Melhorar a pesquisa" #: resources/ui/SearchBar.blp:95 resources/ui/ShortcutsWindow.blp:97 msgid "Replace" msgstr "" #: resources/ui/SearchBar.blp:106 msgid "_Replace" msgstr "" #: resources/ui/SearchBar.blp:119 msgid "Replace _All" msgstr "" #: resources/ui/SearchBar.blp:138 msgid "Re_gular expressions" msgstr "Substituir substrings e expressões regulares" #: resources/ui/SearchBar.blp:144 msgid "_Case sensitive" msgstr "" #: resources/ui/SearchBar.blp:150 msgid "Match whole _word only" msgstr "" #: resources/ui/SearchEntry.blp:15 msgid "Find" msgstr "" #: resources/ui/ShortcutsWindow.blp:13 msgid "General" msgstr "Geral" #: resources/ui/ShortcutsWindow.blp:16 msgid "New window" msgstr "" #: resources/ui/ShortcutsWindow.blp:21 msgid "Keyboard shortcuts" msgstr "Atalhos do teclado" #: resources/ui/ShortcutsWindow.blp:26 msgid "Preferences" msgstr "Preferências" #: resources/ui/ShortcutsWindow.blp:31 msgid "Close window" msgstr "" #: resources/ui/ShortcutsWindow.blp:36 msgid "Quit" msgstr "Sair" #: resources/ui/ShortcutsWindow.blp:42 msgid "Tools" msgstr "Ferramentas" #: resources/ui/ShortcutsWindow.blp:45 msgid "Apply" msgstr "Aplicar" #: resources/ui/ShortcutsWindow.blp:55 msgid "Just to the tool options" msgstr "Ir para as opções da ferramenta" #: resources/ui/ShortcutsWindow.blp:61 msgid "Undo and Redo" msgstr "Desfazer e refazer" #: resources/ui/ShortcutsWindow.blp:64 msgid "Undo" msgstr "Desfazer" #: resources/ui/ShortcutsWindow.blp:69 msgid "Redo" msgstr "Refazer" #: resources/ui/ShortcutsWindow.blp:75 msgid "Files" msgstr "Arquivos" #: resources/ui/ShortcutsWindow.blp:78 msgid "Load from file" msgstr "Carregar do arquivo" #: resources/ui/ShortcutsWindow.blp:83 msgid "Save to file" msgstr "Salvar em arquivo" #: resources/ui/ShortcutsWindow.blp:89 msgid "Search and Replace" msgstr "" #: resources/ui/ShortcutsWindow.blp:92 msgid "Search" msgstr "" #: resources/ui/ToolSettings.blp:31 msgid "_Name" msgstr "_Nome" #: resources/ui/ToolSettings.blp:42 msgid "_Description" msgstr "_Descrição" #: resources/ui/ToolSettings.blp:52 msgid "_Arguments" msgstr "_Argumentos" #: resources/ui/ToolSettings.blp:80 msgid "Edit _Script" msgstr "" #: resources/ui/Window.blp:32 msgid "_Apply" msgstr "_Aplicar" #: resources/ui/Window.blp:35 msgid "Apply Selected Tool" msgstr "Aplicar ferramenta selecionada" #: resources/ui/Window.blp:45 msgid "Copy" msgstr "Copiar" #: resources/ui/Window.blp:71 msgid "Main Menu" msgstr "Menu principal" #: resources/ui/Window.blp:123 msgid "_New Window" msgstr "" #: resources/ui/Window.blp:131 msgid "_Find/Replace…" msgstr "" #: resources/ui/Window.blp:139 msgid "_Preferences" msgstr "_Preferências" #: resources/ui/Window.blp:145 msgid "_Keyboard Shortcuts" msgstr "_Atalhos do teclado" #: resources/ui/Window.blp:151 msgid "_About Text Pieces" msgstr "_Sobre Text Pieces" #: data/tools.json msgctxt "tools" msgid "Base64 Decode" msgstr "" #: data/tools.json msgctxt "tools" msgid "Base64 Encode" msgstr "" #: data/tools.json msgctxt "tools" msgid "Compute MD5 hash sum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Compute SHA1 hash sum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Compute SHA256 hash sum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Compute SHA384 hash sum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Compute SHA512 hash sum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Convert JSON to YAML" msgstr "Converter JSON para YAML e vice-versa" #: data/tools.json msgctxt "tools" msgid "Convert YAML to JSON" msgstr "Converter JSON para YAML e vice-versa" #: data/tools.json msgctxt "tools" msgid "Count how many lines are in text" msgstr "" #: data/tools.json msgctxt "tools" msgid "Count how many symbols are in text" msgstr "" #: data/tools.json msgctxt "tools" msgid "Count how many words are in text" msgstr "" #: data/tools.json msgctxt "tools" msgid "Count Lines" msgstr "_Linhas de quebra" #: data/tools.json msgctxt "tools" msgid "Count Symbols" msgstr "" #: data/tools.json msgctxt "tools" msgid "Count Words" msgstr "" #: data/tools.json msgctxt "tools" msgid "Decode text from Base64" msgstr "Decodificar texto" #: data/tools.json msgctxt "tools" msgid "Encode text to Base64" msgstr "Codificar texto" #: data/tools.json msgctxt "tools" msgid "Escape HTML" msgstr "" #: data/tools.json msgctxt "tools" msgid "Escape String" msgstr "Escape e não escape de strings" #: data/tools.json msgctxt "tools" msgid "Escape URL" msgstr "Escape e não escape de strings" #: data/tools.json msgctxt "tools" msgid "Extract RSS links from OPML outline" msgstr "" #: data/tools.json msgctxt "tools" msgid "Filter Lines by Regex" msgstr "Filtrar linhas" #: data/tools.json msgctxt "tools" msgid "Filter Lines by Substring" msgstr "Filtrar linhas" #: data/tools.json msgctxt "tools" msgid "Find" msgstr "" #: data/tools.json msgctxt "tools" msgid "Find (regex)" msgstr "" #: data/tools.json msgctxt "tools" msgid "MD5 Checksum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Minify JSON" msgstr "" #: data/tools.json msgctxt "tools" msgid "OPML to RSS links" msgstr "" #: data/tools.json msgctxt "tools" msgid "Parse JSON and convert it to YAML document" msgstr "" #: data/tools.json msgctxt "tools" msgid "Parse JSON and minify it" msgstr "" #: data/tools.json msgctxt "tools" msgid "Parse JSON and prettify it" msgstr "" #: data/tools.json msgctxt "tools" msgid "Parse XML and prettify it" msgstr "" #: data/tools.json msgctxt "tools" msgid "Parse YAML document and convert it to JSON" msgstr "" #: data/tools.json msgctxt "tools" msgid "Prettify JSON" msgstr "" #: data/tools.json msgctxt "tools" msgid "Prettify XML" msgstr "" #: data/tools.json msgctxt "tools" msgid "Regex" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove all lines which contain given substring" msgstr "Substituir substrings e expressões regulares" #: data/tools.json msgctxt "tools" msgid "Remove all lines which don't contain given substring" msgstr "Substituir substrings e expressões regulares" #: data/tools.json msgctxt "tools" msgid "Remove all lines which don't match given regular expression" msgstr "Substituir substrings e expressões regulares" #: data/tools.json msgctxt "tools" msgid "Remove all lines which match given regular expression" msgstr "Substituir substrings e expressões regulares" #: data/tools.json msgctxt "tools" msgid "Remove all matches of regular expression" msgstr "Substituir substrings e expressões regulares" #: data/tools.json msgctxt "tools" msgid "Remove all occurrences of given substring in text" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove leading and trailing whitespaces and newlines" msgstr "Remova espaços e linhas à direita" #: data/tools.json msgctxt "tools" msgid "Remove Regex" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove Substring" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove Trailing" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove trailing whitespaces and newlines" msgstr "Remova espaços e linhas à direita" #: data/tools.json msgctxt "tools" msgid "Reoder lines in reverse order" msgstr "" #: data/tools.json msgctxt "tools" msgid "Reorder characters in reverse order" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace all given regex matches in text with given string" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace all occurrences of given substring in text with other string" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace escape sequences with characters" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace HTML sequences with characters" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace Regex" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace special characters with escape sequences" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace special characters with HTML-safe sequences" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace special characters with URL-safe sequences" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace Substring" msgstr "Escape e não escape de strings" #: data/tools.json msgctxt "tools" msgid "Replace URL sequences with characters" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace With" msgstr "" #: data/tools.json msgctxt "tools" msgid "Reverse Filter Lines by Regex" msgstr "Filtrar linhas" #: data/tools.json msgctxt "tools" msgid "Reverse Filter Lines by Substring" msgstr "Filtrar linhas" #: data/tools.json msgctxt "tools" msgid "Reverse Lines" msgstr "_Linhas de quebra" #: data/tools.json msgctxt "tools" msgid "Reverse Sort Lines" msgstr "_Linhas de quebra" #: data/tools.json msgctxt "tools" msgid "Reverse Text" msgstr "" #: data/tools.json msgctxt "tools" msgid "SHA1 Checksum" msgstr "" #: data/tools.json msgctxt "tools" msgid "SHA256 Checksum" msgstr "" #: data/tools.json msgctxt "tools" msgid "SHA384 Checksum" msgstr "" #: data/tools.json msgctxt "tools" msgid "SHA512 Checksum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Sort Lines" msgstr "_Linhas de quebra" #: data/tools.json msgctxt "tools" msgid "Sort lines in text in alphabetical order" msgstr "" #: data/tools.json msgctxt "tools" msgid "Sort lines in text in reverse alphabetical order" msgstr "" #: data/tools.json msgctxt "tools" msgid "Substring" msgstr "" #: data/tools.json msgctxt "tools" msgid "Trim Lines" msgstr "_Linhas de quebra" #: data/tools.json msgctxt "tools" msgid "Unescape HTML" msgstr "Escape e não escape de strings" #: data/tools.json msgctxt "tools" msgid "Unescape String" msgstr "Escape e não escape de strings" #: data/tools.json msgctxt "tools" msgid "Unescape URL" msgstr "Escape e não escape de strings" #~ msgid "Copy and Paste" #~ msgstr "Copiar e colar" #~ msgid "Paste" #~ msgstr "Colar" #~ msgid "Copy all" #~ msgstr "Copiar tudo" ================================================ FILE: po/textpieces.pot ================================================ # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the textpieces package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: textpieces\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2022-12-21 08:25+0000\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: data/com.github.liferooter.textpieces.desktop.in:7 #: data/com.github.liferooter.textpieces.appdata.xml.in:9 #: src/widgets/Window.vala:241 resources/ui/Window.blp:9 msgid "Text Pieces" msgstr "" #. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon! #: data/com.github.liferooter.textpieces.desktop.in:18 msgid "" "GNOME;GTK;Text;Base64;JSON;YAML;Count;SHA1;SHA256;SHA384;SHA512;MD5;Checksum;" "Escape;Encode;HTML;URL;Prettify;Filter;Reverse;Trim;" msgstr "" #: data/com.github.liferooter.textpieces.desktop.in:21 msgid "New Window" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:10 #: src/widgets/Window.vala:242 msgid "Transform text without using random websites" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:14 msgid "Do a lot of text transformations, such as:" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:16 msgid "Calculate hashes" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:17 msgid "Encode text" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:18 msgid "Decode text" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:19 msgid "Remove trailing spaces and lines" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:20 msgid "Count lines, symbols and words" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:21 msgid "Format JSON and XML" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:22 msgid "Escape and unescape strings" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:23 msgid "Convert JSON to YAML and vice versa" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:24 msgid "Filter lines" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:25 msgid "Replace substrings and regular expressions" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:27 msgid "...and so on." msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:32 #: src/widgets/Window.vala:252 msgid "Gleb Smirnov" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:64 #: data/com.github.liferooter.textpieces.appdata.xml.in:77 #: data/com.github.liferooter.textpieces.appdata.xml.in:87 msgid "Bug fixes:" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:66 msgid "Script files can be correctly opened using an editor of your choice" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:67 msgid "Newly created script names are generated from tool name" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:73 msgid "New features:" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:75 msgid "Text Pieces now can open files" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:79 msgid "Some translation issues are fixed" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:80 msgid "Some tools are fixed and optimized" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:81 msgid "Shortcuts window is updated and fixed" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:89 msgid "Tool arguments now work as expected" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:90 msgid "Translate some untranslated strings" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:96 #: data/com.github.liferooter.textpieces.appdata.xml.in:108 #: data/com.github.liferooter.textpieces.appdata.xml.in:116 #: data/com.github.liferooter.textpieces.appdata.xml.in:137 #: data/com.github.liferooter.textpieces.appdata.xml.in:156 #: data/com.github.liferooter.textpieces.appdata.xml.in:171 #: data/com.github.liferooter.textpieces.appdata.xml.in:182 #: data/com.github.liferooter.textpieces.appdata.xml.in:192 #: data/com.github.liferooter.textpieces.appdata.xml.in:202 msgid "New features and improvements:" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:98 msgid "Add tool for extracting RSS URLs from OPML files" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:99 msgid "Use more verbose error reporting for Base64 decode tool" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:100 msgid "Focus editor on startup" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:101 msgid "Save selected tool between sessions" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:102 msgid "Minor UI improvements" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:110 msgid "Add hotkey to open search in replace mode" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:118 msgid "Add some keywords to the application for system search" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:124 msgid "New features and improvement:" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:126 msgid "Add style schemes" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:127 msgid "Add search and replace feature to editor" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:128 msgid "Fix some bugs" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:133 msgid "Bug fix" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:139 msgid "Refresh UI" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:140 msgid "Fix a lot of bugs" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:141 msgid "Add powerful settings" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:142 msgid "Add arguments support for custom tools" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:143 msgid "Add translation support" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:144 #: data/com.github.liferooter.textpieces.appdata.xml.in:175 msgid "Improve search" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:158 msgid "Minor UI changes" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:159 msgid "Add XML formatter" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:164 msgid "Some bugfixes and improvements" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:167 msgid "Bring back tools with arguments" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:173 msgid "Redesign the application" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:174 msgid "Add custom tools support" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:176 msgid "Add ability to text load from file and save to file" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:184 msgid "Exit by Ctrl+Q" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:185 msgid "Monospace argument entry" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:186 msgid "Update new runtime" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:194 msgid "Sort tools" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:195 msgid "Filter tools" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:196 msgid "Minify C-like code" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:204 msgid "Add more tools" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:205 msgid "Make tools popover more powerful" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:206 msgid "Redesign icon" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:207 msgid "General improvements" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:212 msgid "Add tools for replacement and removing" msgstr "" #: data/com.github.liferooter.textpieces.appdata.xml.in:215 msgid "Application release" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:13 msgid "Style scheme" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:14 msgid "Which style scheme to use for application interface and editor." msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:18 msgid "Font name" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:19 msgid "Name of font used in editor" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:23 msgid "Wrap lines" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:24 msgid "Whether to wrap long lines" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:28 msgid "Tabs to spaces" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:29 msgid "Whether to insert spaces instead of tab" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:33 msgid "Spaces in tab" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:34 msgid "Tab width in spaces" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:38 msgid "Window width" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:39 msgid "Saved width of application window" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:43 msgid "Window height" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:44 msgid "Saved height of the window" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:48 msgid "Is maximized" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:49 msgid "Whether the application window is maximized" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:53 msgid "Selected tool" msgstr "" #: data/com.github.liferooter.textpieces.gschema.xml:54 msgid "" "Used to restore tool choice on restart. Its value must be a name of tool's " "script." msgstr "" #: src/widgets/Editor.vala:145 msgid "Text is copied" msgstr "" #. * #. * Text used instead of tool name #. * when where is no tool selected #. #: src/widgets/Window.vala:21 resources/ui/ShortcutsWindow.blp:50 msgid "Select tool" msgstr "" #: src/widgets/Window.vala:161 #, c-format msgid "Can't load from file: %s" msgstr "" #: src/widgets/Window.vala:249 msgid "Tobias Bernard https://tobiasbernard.com" msgstr "" #: src/widgets/Window.vala:250 msgid "Gleb Smirnov " msgstr "" #: src/widgets/Window.vala:253 msgid "translator-credits" msgstr "" #: src/widgets/Window.vala:280 resources/ui/Window.blp:78 msgid "Save to File" msgstr "" #: src/widgets/Window.vala:305 #, c-format msgid "Can't save to file: %s" msgstr "" #: src/widgets/Window.vala:321 resources/ui/Window.blp:85 msgid "Load from File" msgstr "" #: src/widgets/SearchEntry.vala:93 #, c-format msgid "%u of %d" msgstr "" #. Create dialog #: src/widgets/preferences/Preferences.vala:128 msgid "Select font" msgstr "" #: src/widgets/preferences/ToolSettings.vala:187 #, c-format msgid "Argument %d" msgstr "" #: src/widgets/preferences/pages/NewToolPage.vala:113 #, c-format msgid "Error occured: %s" msgstr "" #: resources/ui/CustomToolPage.blp:27 msgid "Edit Tool" msgstr "" #: resources/ui/CustomToolPage.blp:38 msgid "D_elete" msgstr "" #: resources/ui/Search.blp:41 msgid "No Tools Found" msgstr "" #: resources/ui/Search.blp:53 msgid "_Add Custom Tool" msgstr "" #: resources/ui/NewToolPage.blp:27 msgid "New Tool" msgstr "" #: resources/ui/NewToolPage.blp:42 msgid "_Create" msgstr "" #: resources/ui/Preferences.blp:13 msgid "_General" msgstr "" #: resources/ui/Preferences.blp:19 msgid "Editor" msgstr "" #: resources/ui/Preferences.blp:22 msgid "Editor _Font" msgstr "" #: resources/ui/Preferences.blp:40 msgid "_Wrap Lines" msgstr "" #: resources/ui/Preferences.blp:51 msgid "Tabs to _Spaces" msgstr "" #: resources/ui/Preferences.blp:62 msgid "_Tab Width in Spaces" msgstr "" #: resources/ui/Preferences.blp:82 msgid "Appearance" msgstr "" #: resources/ui/Preferences.blp:94 msgid "T_ools" msgstr "" #: resources/ui/Preferences.blp:100 msgid "Custom tools" msgstr "" #: resources/ui/Preferences.blp:112 msgid "_Add custom tool" msgstr "" #: resources/ui/Preferences.blp:128 msgid "No Custom Tools" msgstr "" #: resources/ui/Preferences.blp:129 msgid "You can create one" msgstr "" #: resources/ui/SearchBar.blp:40 resources/ui/ShortcutsWindow.blp:102 msgid "Move to previous match" msgstr "" #: resources/ui/SearchBar.blp:46 resources/ui/ShortcutsWindow.blp:107 msgid "Move to next match" msgstr "" #: resources/ui/SearchBar.blp:58 msgid "Find and replace" msgstr "" #: resources/ui/SearchBar.blp:68 msgid "Toggle search options" msgstr "" #: resources/ui/SearchBar.blp:84 msgid "Close search" msgstr "" #: resources/ui/SearchBar.blp:95 resources/ui/ShortcutsWindow.blp:97 msgid "Replace" msgstr "" #: resources/ui/SearchBar.blp:106 msgid "_Replace" msgstr "" #: resources/ui/SearchBar.blp:119 msgid "Replace _All" msgstr "" #: resources/ui/SearchBar.blp:138 msgid "Re_gular expressions" msgstr "" #: resources/ui/SearchBar.blp:144 msgid "_Case sensitive" msgstr "" #: resources/ui/SearchBar.blp:150 msgid "Match whole _word only" msgstr "" #: resources/ui/SearchEntry.blp:15 msgid "Find" msgstr "" #: resources/ui/ShortcutsWindow.blp:13 msgid "General" msgstr "" #: resources/ui/ShortcutsWindow.blp:16 msgid "New window" msgstr "" #: resources/ui/ShortcutsWindow.blp:21 msgid "Keyboard shortcuts" msgstr "" #: resources/ui/ShortcutsWindow.blp:26 msgid "Preferences" msgstr "" #: resources/ui/ShortcutsWindow.blp:31 msgid "Close window" msgstr "" #: resources/ui/ShortcutsWindow.blp:36 msgid "Quit" msgstr "" #: resources/ui/ShortcutsWindow.blp:42 msgid "Tools" msgstr "" #: resources/ui/ShortcutsWindow.blp:45 msgid "Apply" msgstr "" #: resources/ui/ShortcutsWindow.blp:55 msgid "Just to the tool options" msgstr "" #: resources/ui/ShortcutsWindow.blp:61 msgid "Undo and Redo" msgstr "" #: resources/ui/ShortcutsWindow.blp:64 msgid "Undo" msgstr "" #: resources/ui/ShortcutsWindow.blp:69 msgid "Redo" msgstr "" #: resources/ui/ShortcutsWindow.blp:75 msgid "Files" msgstr "" #: resources/ui/ShortcutsWindow.blp:78 msgid "Load from file" msgstr "" #: resources/ui/ShortcutsWindow.blp:83 msgid "Save to file" msgstr "" #: resources/ui/ShortcutsWindow.blp:89 msgid "Search and Replace" msgstr "" #: resources/ui/ShortcutsWindow.blp:92 msgid "Search" msgstr "" #: resources/ui/ToolSettings.blp:31 msgid "_Name" msgstr "" #: resources/ui/ToolSettings.blp:42 msgid "_Description" msgstr "" #: resources/ui/ToolSettings.blp:52 msgid "_Arguments" msgstr "" #: resources/ui/ToolSettings.blp:80 msgid "Edit _Script" msgstr "" #: resources/ui/Window.blp:32 msgid "_Apply" msgstr "" #: resources/ui/Window.blp:35 msgid "Apply Selected Tool" msgstr "" #: resources/ui/Window.blp:45 msgid "Copy" msgstr "" #: resources/ui/Window.blp:71 msgid "Main Menu" msgstr "" #: resources/ui/Window.blp:123 msgid "_New Window" msgstr "" #: resources/ui/Window.blp:131 msgid "_Find/Replace…" msgstr "" #: resources/ui/Window.blp:139 msgid "_Preferences" msgstr "" #: resources/ui/Window.blp:145 msgid "_Keyboard Shortcuts" msgstr "" #: resources/ui/Window.blp:151 msgid "_About Text Pieces" msgstr "" #: data/tools.json msgctxt "tools" msgid "Base64 Decode" msgstr "" #: data/tools.json msgctxt "tools" msgid "Base64 Encode" msgstr "" #: data/tools.json msgctxt "tools" msgid "Compute MD5 hash sum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Compute SHA1 hash sum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Compute SHA256 hash sum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Compute SHA384 hash sum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Compute SHA512 hash sum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Convert JSON to YAML" msgstr "" #: data/tools.json msgctxt "tools" msgid "Convert YAML to JSON" msgstr "" #: data/tools.json msgctxt "tools" msgid "Count how many lines are in text" msgstr "" #: data/tools.json msgctxt "tools" msgid "Count how many symbols are in text" msgstr "" #: data/tools.json msgctxt "tools" msgid "Count how many words are in text" msgstr "" #: data/tools.json msgctxt "tools" msgid "Count Lines" msgstr "" #: data/tools.json msgctxt "tools" msgid "Count Symbols" msgstr "" #: data/tools.json msgctxt "tools" msgid "Count Words" msgstr "" #: data/tools.json msgctxt "tools" msgid "Decode text from Base64" msgstr "" #: data/tools.json msgctxt "tools" msgid "Encode text to Base64" msgstr "" #: data/tools.json msgctxt "tools" msgid "Escape HTML" msgstr "" #: data/tools.json msgctxt "tools" msgid "Escape String" msgstr "" #: data/tools.json msgctxt "tools" msgid "Escape URL" msgstr "" #: data/tools.json msgctxt "tools" msgid "Extract RSS links from OPML outline" msgstr "" #: data/tools.json msgctxt "tools" msgid "Filter Lines by Regex" msgstr "" #: data/tools.json msgctxt "tools" msgid "Filter Lines by Substring" msgstr "" #: data/tools.json msgctxt "tools" msgid "Find" msgstr "" #: data/tools.json msgctxt "tools" msgid "Find (regex)" msgstr "" #: data/tools.json msgctxt "tools" msgid "MD5 Checksum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Minify JSON" msgstr "" #: data/tools.json msgctxt "tools" msgid "OPML to RSS links" msgstr "" #: data/tools.json msgctxt "tools" msgid "Parse JSON and convert it to YAML document" msgstr "" #: data/tools.json msgctxt "tools" msgid "Parse JSON and minify it" msgstr "" #: data/tools.json msgctxt "tools" msgid "Parse JSON and prettify it" msgstr "" #: data/tools.json msgctxt "tools" msgid "Parse XML and prettify it" msgstr "" #: data/tools.json msgctxt "tools" msgid "Parse YAML document and convert it to JSON" msgstr "" #: data/tools.json msgctxt "tools" msgid "Prettify JSON" msgstr "" #: data/tools.json msgctxt "tools" msgid "Prettify XML" msgstr "" #: data/tools.json msgctxt "tools" msgid "Regex" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove all lines which contain given substring" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove all lines which don't contain given substring" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove all lines which don't match given regular expression" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove all lines which match given regular expression" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove all matches of regular expression" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove all occurrences of given substring in text" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove leading and trailing whitespaces and newlines" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove Regex" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove Substring" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove Trailing" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove trailing whitespaces and newlines" msgstr "" #: data/tools.json msgctxt "tools" msgid "Reoder lines in reverse order" msgstr "" #: data/tools.json msgctxt "tools" msgid "Reorder characters in reverse order" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace all given regex matches in text with given string" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace all occurrences of given substring in text with other string" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace escape sequences with characters" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace HTML sequences with characters" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace Regex" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace special characters with escape sequences" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace special characters with HTML-safe sequences" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace special characters with URL-safe sequences" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace Substring" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace URL sequences with characters" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace With" msgstr "" #: data/tools.json msgctxt "tools" msgid "Reverse Filter Lines by Regex" msgstr "" #: data/tools.json msgctxt "tools" msgid "Reverse Filter Lines by Substring" msgstr "" #: data/tools.json msgctxt "tools" msgid "Reverse Lines" msgstr "" #: data/tools.json msgctxt "tools" msgid "Reverse Sort Lines" msgstr "" #: data/tools.json msgctxt "tools" msgid "Reverse Text" msgstr "" #: data/tools.json msgctxt "tools" msgid "SHA1 Checksum" msgstr "" #: data/tools.json msgctxt "tools" msgid "SHA256 Checksum" msgstr "" #: data/tools.json msgctxt "tools" msgid "SHA384 Checksum" msgstr "" #: data/tools.json msgctxt "tools" msgid "SHA512 Checksum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Sort Lines" msgstr "" #: data/tools.json msgctxt "tools" msgid "Sort lines in text in alphabetical order" msgstr "" #: data/tools.json msgctxt "tools" msgid "Sort lines in text in reverse alphabetical order" msgstr "" #: data/tools.json msgctxt "tools" msgid "Substring" msgstr "" #: data/tools.json msgctxt "tools" msgid "Trim Lines" msgstr "" #: data/tools.json msgctxt "tools" msgid "Unescape HTML" msgstr "" #: data/tools.json msgctxt "tools" msgid "Unescape String" msgstr "" #: data/tools.json msgctxt "tools" msgid "Unescape URL" msgstr "" ================================================ FILE: po/textpieces.pot.license ================================================ SPDX-FileCopyrightText: 2022 Gleb Smirnov SPDX-License-Identifier: CC0-1.0 ================================================ FILE: po/tools.pot ================================================ #: data/tools.json msgctxt "tools" msgid "Base64 Decode" msgstr "" #: data/tools.json msgctxt "tools" msgid "Base64 Encode" msgstr "" #: data/tools.json msgctxt "tools" msgid "Compute MD5 hash sum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Compute SHA1 hash sum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Compute SHA256 hash sum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Compute SHA384 hash sum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Compute SHA512 hash sum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Convert JSON to YAML" msgstr "" #: data/tools.json msgctxt "tools" msgid "Convert YAML to JSON" msgstr "" #: data/tools.json msgctxt "tools" msgid "Count how many lines are in text" msgstr "" #: data/tools.json msgctxt "tools" msgid "Count how many symbols are in text" msgstr "" #: data/tools.json msgctxt "tools" msgid "Count how many words are in text" msgstr "" #: data/tools.json msgctxt "tools" msgid "Count Lines" msgstr "" #: data/tools.json msgctxt "tools" msgid "Count Symbols" msgstr "" #: data/tools.json msgctxt "tools" msgid "Count Words" msgstr "" #: data/tools.json msgctxt "tools" msgid "Decode text from Base64" msgstr "" #: data/tools.json msgctxt "tools" msgid "Encode text to Base64" msgstr "" #: data/tools.json msgctxt "tools" msgid "Escape HTML" msgstr "" #: data/tools.json msgctxt "tools" msgid "Escape String" msgstr "" #: data/tools.json msgctxt "tools" msgid "Escape URL" msgstr "" #: data/tools.json msgctxt "tools" msgid "Extract RSS links from OPML outline" msgstr "" #: data/tools.json msgctxt "tools" msgid "Filter Lines by Regex" msgstr "" #: data/tools.json msgctxt "tools" msgid "Filter Lines by Substring" msgstr "" #: data/tools.json msgctxt "tools" msgid "Find" msgstr "" #: data/tools.json msgctxt "tools" msgid "Find (regex)" msgstr "" #: data/tools.json msgctxt "tools" msgid "MD5 Checksum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Minify JSON" msgstr "" #: data/tools.json msgctxt "tools" msgid "OPML to RSS links" msgstr "" #: data/tools.json msgctxt "tools" msgid "Parse JSON and convert it to YAML document" msgstr "" #: data/tools.json msgctxt "tools" msgid "Parse JSON and minify it" msgstr "" #: data/tools.json msgctxt "tools" msgid "Parse JSON and prettify it" msgstr "" #: data/tools.json msgctxt "tools" msgid "Parse XML and prettify it" msgstr "" #: data/tools.json msgctxt "tools" msgid "Parse YAML document and convert it to JSON" msgstr "" #: data/tools.json msgctxt "tools" msgid "Prettify JSON" msgstr "" #: data/tools.json msgctxt "tools" msgid "Prettify XML" msgstr "" #: data/tools.json msgctxt "tools" msgid "Regex" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove all lines which contain given substring" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove all lines which don't contain given substring" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove all lines which don't match given regular expression" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove all lines which match given regular expression" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove all matches of regular expression" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove all occurrences of given substring in text" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove leading and trailing whitespaces and newlines" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove Regex" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove Substring" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove Trailing" msgstr "" #: data/tools.json msgctxt "tools" msgid "Remove trailing whitespaces and newlines" msgstr "" #: data/tools.json msgctxt "tools" msgid "Reoder lines in reverse order" msgstr "" #: data/tools.json msgctxt "tools" msgid "Reorder characters in reverse order" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace all given regex matches in text with given string" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace all occurrences of given substring in text with other string" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace escape sequences with characters" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace HTML sequences with characters" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace Regex" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace special characters with escape sequences" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace special characters with HTML-safe sequences" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace special characters with URL-safe sequences" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace Substring" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace URL sequences with characters" msgstr "" #: data/tools.json msgctxt "tools" msgid "Replace With" msgstr "" #: data/tools.json msgctxt "tools" msgid "Reverse Filter Lines by Regex" msgstr "" #: data/tools.json msgctxt "tools" msgid "Reverse Filter Lines by Substring" msgstr "" #: data/tools.json msgctxt "tools" msgid "Reverse Lines" msgstr "" #: data/tools.json msgctxt "tools" msgid "Reverse Sort Lines" msgstr "" #: data/tools.json msgctxt "tools" msgid "Reverse Text" msgstr "" #: data/tools.json msgctxt "tools" msgid "SHA1 Checksum" msgstr "" #: data/tools.json msgctxt "tools" msgid "SHA256 Checksum" msgstr "" #: data/tools.json msgctxt "tools" msgid "SHA384 Checksum" msgstr "" #: data/tools.json msgctxt "tools" msgid "SHA512 Checksum" msgstr "" #: data/tools.json msgctxt "tools" msgid "Sort Lines" msgstr "" #: data/tools.json msgctxt "tools" msgid "Sort lines in text in alphabetical order" msgstr "" #: data/tools.json msgctxt "tools" msgid "Sort lines in text in reverse alphabetical order" msgstr "" #: data/tools.json msgctxt "tools" msgid "Substring" msgstr "" #: data/tools.json msgctxt "tools" msgid "Trim Lines" msgstr "" #: data/tools.json msgctxt "tools" msgid "Unescape HTML" msgstr "" #: data/tools.json msgctxt "tools" msgid "Unescape String" msgstr "" #: data/tools.json msgctxt "tools" msgid "Unescape URL" msgstr "" ================================================ FILE: po/tools.pot.license ================================================ SPDX-FileCopyrightText: 2022 Gleb Smirnov SPDX-License-Identifier: CC0-1.0 ================================================ FILE: resources/icons/scalable/status/applications-utilities-symbolic.svg.license ================================================ SPDX-FileCopyrightText: 2022 Gleb Smirnov SPDX-License-Identifier: CC0-1.0 ================================================ FILE: resources/icons/scalable/status/fingerprint-symbolic.svg.license ================================================ SPDX-FileCopyrightText: 2021 Gleb Smirnov SPDX-License-Identifier: CC0-1.0 SPDX-License-Identifier: GPL-3.0-or-later ================================================ FILE: resources/icons/scalable/status/funnel-symbolic.svg.license ================================================ SPDX-FileCopyrightText: 2021 Gleb Smirnov SPDX-License-Identifier: CC0-1.0 SPDX-License-Identifier: GPL-3.0-or-later ================================================ FILE: resources/icons/scalable/status/lock-symbolic.svg.license ================================================ SPDX-FileCopyrightText: 2021 Gleb Smirnov SPDX-License-Identifier: CC0-1.0 SPDX-License-Identifier: GPL-3.0-or-later ================================================ FILE: resources/icons/scalable/status/preferences-symbolic.svg.license ================================================ SPDX-FileCopyrightText: 2021 Gleb Smirnov SPDX-License-Identifier: CC0-1.0 SPDX-License-Identifier: GPL-3.0-or-later ================================================ FILE: resources/icons/scalable/status/rss-symbolic.svg.license ================================================ SPDX-FileCopyrightText: 2022 Gleb Smirnov SPDX-License-Identifier: CC0-1.0 ================================================ FILE: resources/icons/scalable/status/unlock-symbolic.svg.license ================================================ SPDX-FileCopyrightText: 2021 Gleb Smirnov SPDX-License-Identifier: CC0-1.0 SPDX-License-Identifier: GPL-3.0-or-later ================================================ FILE: resources/meson.build ================================================ # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later blueprints = custom_target('blueprints', input: files( 'ui/CustomToolPage.blp', 'ui/Editor.blp', 'ui/NewToolPage.blp', 'ui/Preferences.blp', 'ui/SearchBar.blp', 'ui/SearchEntry.blp', 'ui/ShortcutsWindow.blp', 'ui/ToolSettings.blp', 'ui/Window.blp', 'ui/Search.blp', ), output: '.', command: [find_program('blueprint-compiler'), 'batch-compile', '@OUTPUT@', '@CURRENT_SOURCE_DIR@', '@INPUT@'], ) blueprint_vala = 'blueprint.vala' blueprint_hack = custom_target('blueprint-hack', input: blueprints, output: blueprint_vala, command: [find_program('touch'), '@OUTPUT@'] ) textpieces_sources += gnome.compile_resources('textpieces-resources', 'textpieces.gresource.xml', source_dir: 'resources', c_name: 'textpieces', dependencies: blueprints ) ================================================ FILE: resources/style-dark.css ================================================ /* style.css * * Copyright 2021 Gleb Smirnov * * 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 . * * SPDX-License-Identifier: GPL-3.0-or-later */ ================================================ FILE: resources/style.css ================================================ /* style.css * * Copyright 2021 Gleb Smirnov * * 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 . * * SPDX-License-Identifier: GPL-3.0-or-later */ /* Argument view */ .editor-overlay { padding: 6px; background-color: lighter(@window_bg_color); border-radius: 9px; box-shadow: 0 0 0 1px alpha(black, 0.03), 0 1px 3px 1px alpha(black, 0.07), 0 2px 6px 2px alpha(black, 0.03); } ================================================ FILE: resources/textpieces.gresource.xml ================================================ ui/CustomToolPage.ui ui/NewToolPage.ui ui/Preferences.ui ui/SearchBar.ui ui/SearchEntry.ui ui/ToolSettings.ui ui/Window.ui ui/Editor.ui ui/Search.ui ui/ShortcutsWindow.ui style.css style-dark.css icons/scalable/status/fingerprint-symbolic.svg icons/scalable/status/funnel-symbolic.svg icons/scalable/status/lock-symbolic.svg icons/scalable/status/preferences-symbolic.svg icons/scalable/status/unlock-symbolic.svg icons/scalable/status/applications-utilities-symbolic.svg icons/scalable/status/rss-symbolic.svg ================================================ FILE: resources/ui/CustomToolPage.blp ================================================ // SPDX-FileCopyrightText: 2022 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later using Gtk 4.0; using Adw 1; template TextPiecesCustomToolPage : Box { orientation: vertical; Adw.HeaderBar { show-start-title-buttons: false; show-end-title-buttons: false; [start] Button { clicked => go_back(); icon-name: "go-previous-symbolic"; } [title] Label { styles [ "heading", ] label: _("Edit Tool"); } [end] Button { styles [ "destructive-action", ] clicked => delete_tool(); clicked => go_back(); label: _("D_elete"); use-underline: true; } } .TextPiecesToolSettings tool_settings { can-edit-script: "true"; } } ================================================ FILE: resources/ui/Editor.blp ================================================ // SPDX-FileCopyrightText: 2022 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later using Gtk 4.0; using GtkSource 5; using Adw 1; template TextPiecesEditor : Adw.Bin { child: Adw.ToastOverlay message_overlay { Overlay { ScrolledWindow { GtkSource.View editor { monospace: true; auto-indent: true; show-line-numbers: true; smart-backspace: true; smart-home-end: before; top-margin: 6; } } [overlay] Box { valign: start; halign: end; margin-top: 4; orientation: vertical; spacing: 6; .TextPiecesSearchBar search_bar { editor: "editor"; } Box arguments_box { styles [ "linked", "editor-overlay", ] halign: end; hexpand: false; width-request: 300; margin-top: 5; margin-bottom: 2.5; margin-start: 5; margin-end: 10; spacing: 3; orientation: vertical; } } } } ; } ================================================ FILE: resources/ui/NewToolPage.blp ================================================ // SPDX-FileCopyrightText: 2022 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later using Gtk 4.0; using Adw 1; template TextPiecesNewToolPage : Box { orientation: vertical; Adw.HeaderBar { show-start-title-buttons: false; show-end-title-buttons: false; [start] Button { clicked => go_back(); icon-name: "go-previous-symbolic"; } [title] Label { styles [ "heading", ] label: _("New Tool"); } [end] Revealer { transition-type: crossfade; reveal-child: bind tool_settings.is_valid; child: Button { styles [ "suggested-action", ] clicked => create(); clicked => go_back(); label: _("_Create"); use-underline: true; } ; } } .TextPiecesToolSettings tool_settings { } } ================================================ FILE: resources/ui/Preferences.blp ================================================ // SPDX-FileCopyrightText: 2022 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later using Gtk 4.0; using Adw 1; template TextPiecesPreferences : Adw.PreferencesWindow { can-navigate-back: true; modal: true; Adw.PreferencesPage { title: _("_General"); use-underline: true; name: "general-settings"; icon-name: "preferences-symbolic"; Adw.PreferencesGroup { title: _("Editor"); Adw.ActionRow { title: _("Editor _Font"); use-underline: true; activatable: true; action-name: "prefs.select-font"; Box { spacing: 12; Label font_label { } Image { icon-name: "go-next-symbolic"; } } } Adw.ActionRow { title: _("_Wrap Lines"); use-underline: true; activatable-widget: wrap_lines_switch; Switch wrap_lines_switch { valign: center; action-name: "settings.wrap-lines"; } } Adw.ActionRow { title: _("Tabs to _Spaces"); use-underline: true; activatable-widget: tabs_to_spaces; Switch tabs_to_spaces { valign: center; action-name: "settings.tabs-to-spaces"; } } Adw.ActionRow { title: _("_Tab Width in Spaces"); use-underline: true; activatable-widget: spaces_in_tab; SpinButton spaces_in_tab { valign: center; climb-rate: 1; adjustment: Adjustment { lower: 1; upper: 21; step-increment: 1; } ; } } } Adw.PreferencesGroup { title: _("Appearance"); Adw.Clamp { maximum-size: 450; .GtkSourceStyleSchemeChooserWidget style_scheme_chooser { } } } } Adw.PreferencesPage { title: _("T_ools"); use-underline: true; name: "custom-tools"; icon-name: "applications-utilities-symbolic"; Adw.PreferencesGroup { title: _("Custom tools"); [header-suffix] Button { styles [ "flat", ] clicked => add_new_tool(); Adw.ButtonContent { icon-name: "list-add-symbolic"; label: _("_Add custom tool"); use-underline: true; } } ListBox custom_tools_listbox { styles [ "boxed-list", ] row-activated => edit_tool(); selection-mode: none; } Adw.StatusPage { icon-name: "applications-utilities-symbolic"; title: _("No Custom Tools"); description: _("You can create one"); visible: bind custom_tools_listbox.visible inverted; vexpand: true; valign: center; } } } } ================================================ FILE: resources/ui/Search.blp ================================================ // SPDX-FileCopyrightText: 2022 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later using Gtk 4.0; using Adw 1; template TextPiecesSearch : Adw.Bin { Stack search_stack { transition-type: crossfade; StackPage { name: "search"; child: ScrolledWindow { child: Viewport results_viewport { scroll-to-focus: true; Adw.Clamp { maximum-size: 600; ListBox results_listbox { styles [ "boxed-list", ] row-activated => on_row_activated(); margin-top: 24; margin-bottom: 24; valign: start; selection-mode: browse; } } }; }; } StackPage { name: "placeholder"; child: Adw.StatusPage { icon-name: "applications-utilities-symbolic"; title: _("No Tools Found"); Button { styles [ "flat", ] halign: center; action-name: "win.tools-settings"; Adw.ButtonContent { icon-name: "list-add-symbolic"; label: _("_Add Custom Tool"); use-underline: true; } } }; } } } ================================================ FILE: resources/ui/SearchBar.blp ================================================ // SPDX-FileCopyrightText: 2022 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later using Gtk 4.0; using Adw 1; template TextPiecesSearchBar : Adw.Bin { Revealer search_revealer { transition-type: slide_down; Grid { styles [ "editor-overlay", ] margin-top: 5; margin-bottom: 2.5; margin-start: 5; margin-end: 10; row-spacing: 6; column-spacing: 6; .TextPiecesSearchEntry search_entry { layout { row: 0; column: 0; } } Box { styles [ "linked", ] homogeneous: true; Button { icon-name: "go-up-symbolic"; tooltip-text: _("Move to previous match"); action-name: "search.prev-match"; } Button { icon-name: "go-down-symbolic"; tooltip-text: _("Move to next match"); action-name: "search.next-match"; } layout { row: 0; column: 1; } } ToggleButton search_replace { icon-name: "edit-find-replace-symbolic"; tooltip-text: _("Find and replace"); layout { row: 0; column: 2; } } ToggleButton search_options { icon-name: "preferences-symbolic"; tooltip-text: _("Toggle search options"); layout { row: 0; column: 3; } } Button { styles [ "flat", "circular", ] icon-name: "window-close-symbolic"; action-name: "search.hide"; tooltip-text: _("Close search"); layout { row: 0; column: 4; } } Entry replace_entry { primary-icon-name: "edit-find-replace-symbolic"; primary-icon-activatable: false; placeholder-text: _("Replace"); visible: bind search_replace.active; changed => remove_error_style(); layout { row: 1; column: 0; } } Button { label: _("_Replace"); use-underline: true; visible: bind search_replace.active; sensitive: bind search_entry.occurrence_position; action-name: "search.replace"; layout { row: 1; column: 1; } } Button { label: _("Replace _All"); use-underline: true; visible: bind search_replace.active; sensitive: bind search_entry.occurrences_count; action-name: "search.replace-all"; layout { row: 1; column: 2; column-span: 3; } } Box { spacing: 6; visible: bind search_options.active; CheckButton { use-underline: true; label: _("Re_gular expressions"); active: bind TextPiecesSearchBar.use-regex bidirectional; } CheckButton { use-underline: true; label: _("_Case sensitive"); active: bind TextPiecesSearchBar.case-sensitive bidirectional; } CheckButton { use-underline: true; label: _("Match whole _word only"); active: bind TextPiecesSearchBar.whole-words bidirectional; } layout { row: 2; column: 0; column-span: 5; } } } ShortcutController { scope: managed; Shortcut { trigger: "Escape"; action: "action(search.hide)"; } } } ShortcutController { scope: managed; Shortcut { trigger: "f"; action: "action(search.show)"; } Shortcut { trigger: "h"; action: "action(search.show-replace)"; } } } ================================================ FILE: resources/ui/SearchEntry.blp ================================================ // SPDX-FileCopyrightText: 2022 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later using Gtk 4.0; template TextPiecesSearchEntry : Widget { width-request: 300; Image { icon-name: "edit-find-symbolic"; } Text text { placeholder-text: _("Find"); hexpand: true; vexpand: true; width-chars: 12; max-width-chars: 12; ShortcutController { scope: local; Shortcut { action: "action(search.prev-match)"; trigger: "Return"; } Shortcut { action: "action(search.next-match)"; trigger: "Return"; } } } Label { xalign: 1; opacity: 0.3; label: bind TextPiecesSearchEntry.occurrences_info; } } ================================================ FILE: resources/ui/ShortcutsWindow.blp ================================================ // SPDX-FileCopyrightText: 2022 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later using Gtk 4.0; ShortcutsWindow help_overlay { ShortcutsSection { section-name: "shortcuts"; max-height: 12; ShortcutsGroup { title: _("General"); ShortcutsShortcut { title: _("New window"); action-name: "app.new-window"; } ShortcutsShortcut { title: _("Keyboard shortcuts"); action-name: "win.show-help-overlay"; } ShortcutsShortcut { title: _("Preferences"); action-name: "win.open-preferences"; } ShortcutsShortcut { title: _("Close window"); action-name: "window.close"; } ShortcutsShortcut { title: _("Quit"); action-name: "app.quit"; } } ShortcutsGroup { title: _("Tools"); ShortcutsShortcut { title: _("Apply"); action-name: "win.apply"; } ShortcutsShortcut { title: _("Select tool"); action-name: "win.toggle-search"; } ShortcutsShortcut { title: _("Just to the tool options"); action-name: "win.jump-to-args"; } } ShortcutsGroup { title: _("Undo and Redo"); ShortcutsShortcut { title: _("Undo"); accelerator: "z"; } ShortcutsShortcut { title: _("Redo"); accelerator: "z"; } } ShortcutsGroup { title: _("Files"); ShortcutsShortcut { title: _("Load from file"); action-name: "win.load-file"; } ShortcutsShortcut { title: _("Save to file"); action-name: "win.save-as"; } } ShortcutsGroup { title: _("Search and Replace"); ShortcutsShortcut { title: _("Search"); accelerator: "f"; } ShortcutsShortcut { title: _("Replace"); accelerator: "h"; } ShortcutsShortcut { title: _("Move to previous match"); accelerator: "Return"; } ShortcutsShortcut { title: _("Move to next match"); accelerator: "Return"; } } } } ================================================ FILE: resources/ui/ToolSettings.blp ================================================ // SPDX-FileCopyrightText: 2022 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later using Gtk 4.0; using Adw 1; template TextPiecesToolSettings : Adw.Bin { ScrolledWindow { Viewport { vexpand: true; scroll-to-focus: true; Adw.Clamp { maximum-size: 400; Box { orientation: vertical; margin-top: 20; margin-bottom: 20; spacing: 20; ListBox { styles [ "boxed-list", ] selection-mode: none; Adw.ActionRow { title: _("_Name"); use-underline: true; activatable-widget: name_entry; Entry name_entry { activate => gtk_widget_grab_focus(description_entry); valign: center; } } Adw.ActionRow { title: _("_Description"); use-underline: true; activatable-widget: description_entry; Entry description_entry { valign: center; } } Adw.ActionRow { title: _("_Arguments"); use-underline: true; activatable-widget: arguments_number; SpinButton arguments_number { valign: center; adjustment: Adjustment { lower: 0; upper: 100; step-increment: 1; value: 0; } ; } } } ListBox edit_script_list { styles [ "boxed-list", ] selection-mode: none; Adw.ActionRow { activated => edit_script(); title: _("Edit _Script"); use-underline: true; activatable: true; Image { valign: center; icon-name: "document-edit-symbolic"; } } } ListBox argument_list { styles [ "boxed-list", ] visible: false; selection-mode: none; } } } } } } ================================================ FILE: resources/ui/Window.blp ================================================ // SPDX-FileCopyrightText: 2022 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later using Gtk 4.0; using Adw 1; template TextPiecesWindow : Adw.ApplicationWindow { title: _("Text Pieces"); focus-widget: editor; icon-name: "com.github.liferooter.textpieces"; Box { orientation: vertical; Adw.HeaderBar headerbar { [start] Revealer { transition-type: crossfade; reveal-child: bind search_bar.search-mode-enabled inverted; child: Box { styles [ "linked", ] Button { styles [ "suggested-action", ] label: _("_Apply"); use-underline: true; action-name: "win.apply"; tooltip-text: _("Apply Selected Tool"); } Button { styles [ "suggested-action", ] icon-name: "edit-copy-symbolic"; action-name: "win.copy"; tooltip-text: _("Copy"); } } ; } [title] ToggleButton tool_button { styles [ "flat", "heading", ] toggled => on_search_toggled(); child: Adw.ButtonContent tool_button_content { } ; } [end] MenuButton { icon-name: "open-menu-symbolic"; menu-model: app-menu; tooltip-text: _("Main Menu"); primary: true; } [end] Button { icon-name: "document-save-symbolic"; tooltip-text: _("Save to File"); action-name: "win.save-as"; } [end] Button { icon-name: "document-open-symbolic"; tooltip-text: _("Load from File"); action-name: "win.load-file"; } } SearchBar search_bar { search-mode-enabled: bind tool_button.active bidirectional; child: SearchEntry search_entry {}; } Stack content_stack { vexpand: true; transition-type: crossfade; StackPage { name: "editor"; child: .TextPiecesEditor editor { vexpand: "true"; } ; } StackPage { name: "search"; child: .TextPiecesSearch search { search-entry: search_entry; tool-selected => on_tool_selected(); }; } } } } menu app-menu { section { item { label: _("_New Window"); action: "app.new-window"; use-underline: "true"; } } section { item { label: _("_Find/Replace…"); action: "win.find"; use-underline: "true"; } } section { item { label: _("_Preferences"); action: "win.open-preferences"; use-underline: "true"; } item { label: _("_Keyboard Shortcuts"); action: "win.show-help-overlay"; use-underline: "true"; } item { label: _("_About Text Pieces"); action: "win.about"; use-underline: "true"; } } } ================================================ FILE: screenshots/screenshot.png.license ================================================ SPDX-FileCopyrightText: 2021 Gleb Smirnov SPDX-License-Identifier: CC0-1.0 ================================================ FILE: screenshots/slide1.png.license ================================================ SPDX-FileCopyrightText: 2021 Gleb Smirnov SPDX-License-Identifier: CC0-1.0 ================================================ FILE: screenshots/slide2.png.license ================================================ SPDX-FileCopyrightText: 2021 Gleb Smirnov SPDX-License-Identifier: CC0-1.0 ================================================ FILE: screenshots/slide3.png.license ================================================ SPDX-FileCopyrightText: 2021 Gleb Smirnov SPDX-License-Identifier: CC0-1.0 ================================================ FILE: screenshots/slide4.png.license ================================================ SPDX-FileCopyrightText: 2021 Gleb Smirnov SPDX-License-Identifier: CC0-1.0 ================================================ FILE: screenshots/slide5.png.license ================================================ SPDX-FileCopyrightText: 2021 Gleb Smirnov SPDX-License-Identifier: CC0-1.0 ================================================ FILE: scripts/base64-decode ================================================ #!/usr/bin/env bash # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later base64 -d ================================================ FILE: scripts/base64-encode ================================================ #!/usr/bin/env bash # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later base64 ================================================ FILE: scripts/count-lines ================================================ #!/usr/bin/env bash # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later wc -l ================================================ FILE: scripts/count-symbols ================================================ #!/usr/bin/env bash # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later wc -m ================================================ FILE: scripts/count-words ================================================ #!/usr/bin/env bash # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later wc -w ================================================ FILE: scripts/escape-html ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from html import escape from sys import stdin, stdout stdout.write(escape(stdin.read())) ================================================ FILE: scripts/escape-string ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from sys import stdin, stdout stdout.write(repr(stdin.read())[1:-1].replace('"', r'\"')) ================================================ FILE: scripts/escape-url ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from sys import stdin, stdout from urllib.parse import quote_plus stdout.write(quote_plus(stdin.read())) ================================================ FILE: scripts/filter-regex ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later import re import warnings from sys import argv, stderr, stdin, stdout warnings.simplefilter(action='ignore', category=FutureWarning) pattern = argv[1] try: regex = re.compile(pattern) except re.error as err: stderr.write(f'Invalid regex: {err}') exit(1) for line in stdin: if regex.search(line): stdout.write(line) ================================================ FILE: scripts/filter-substring ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from sys import argv, stdin, stdout for line in stdin: if argv[1] in line: stdout.write(line) ================================================ FILE: scripts/format-json ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later import json from sys import stderr, stdin, stdout try: _dict = json.load(stdin) except json.JSONDecodeError as err: stderr.write(f'Invalid JSON: {err}') exit(1) json.dump( _dict, stdout, indent=' ', ensure_ascii=False ) ================================================ FILE: scripts/format-xml ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later import xml.etree.ElementTree as xml from sys import stderr, stdin, stdout etree = xml.ElementTree() try: etree.parse(stdin) except xml.ParseError as err: stderr.write(f"Can't parse XML: {err}") exit(1) xml.indent(etree) etree.write( stdout, xml_declaration=True, encoding='unicode' ) ================================================ FILE: scripts/json-to-yaml ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later import json from sys import stderr, stdin, stdout import yaml try: _dict = json.load(stdin) except json.JSONDecodeError as err: stderr.write(f'Invalid JSON: {err}') exit(1) yaml.dump( _dict, stdout, sort_keys=False, allow_unicode=True ) ================================================ FILE: scripts/md5 ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from hashlib import md5 from sys import stdin, stdout stdout.write(md5(stdin.read().encode()).hexdigest()) ================================================ FILE: scripts/minify-json ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later import json from sys import stderr, stdin, stdout try: _dict = json.load(stdin) except json.JSONDecodeError as err: stderr.write(f'Invalid JSON: {err}') exit(1) json.dump( _dict, stdout, separators=(',', ':'), ensure_ascii=False ) ================================================ FILE: scripts/remove-regex ================================================ #!/usr/bin/python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later import re import warnings from sys import argv, stderr, stdin, stdout warnings.simplefilter(action='ignore', category=FutureWarning) try: stdout.write(re.sub(argv[1], '', stdin.read(), flags=re.MULTILINE)) except re.error as err: stderr.write(f'Invalid regex: {err}') ================================================ FILE: scripts/remove-substring ================================================ #!/usr/bin/python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from sys import argv, stdin, stdout stdout.write(stdin.read().replace(argv[1], '')) ================================================ FILE: scripts/remove-trailing ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from sys import stdin, stdout stdout.write('\n'.join([ line.rstrip() for line in stdin.read().rstrip().split('\n') ])) ================================================ FILE: scripts/replace-regex ================================================ #!/usr/bin/python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later import re import warnings from sys import argv, stderr, stdin, stdout warnings.simplefilter(action='ignore', category=FutureWarning) try: stdout.write(re.sub(argv[1], argv[2], stdin.read(), flags=re.MULTILINE)) except re.error as err: stderr.write(f'Invalid regex: {err}') exit(1) ================================================ FILE: scripts/replace-substring ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from sys import argv, stdin, stdout stdout.write(stdin.read().replace(argv[1], argv[2])) ================================================ FILE: scripts/reverse-filter-regex ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later import re import warnings from sys import argv, stderr, stdin, stdout warnings.simplefilter(action='ignore', category=FutureWarning) pattern = argv[1] try: regex = re.compile(pattern, flags=re.MULTILINE) except re.error as err: stderr.write(f'Invalid regex: {err}') exit(1) for line in stdin: if not regex.search(line): stdout.write(line) ================================================ FILE: scripts/reverse-filter-substring ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from sys import stdin, stdout, argv for line in stdin: if argv[1] not in line: stdout.write(line) ================================================ FILE: scripts/reverse-lines ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from sys import stdin, stdout stdout.write('\n'.join(stdin.read().split('\n')[::-1])) ================================================ FILE: scripts/reverse-string ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from sys import stdin, stdout stdout.write(stdin.read()[::-1]) ================================================ FILE: scripts/rss-from-opml ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2022 Adrián Bíro # # SPDX-License-Identifier: GPL-3.0-or-later from sys import stderr, stdin, stdout from xml.etree import ElementTree try: tree = ElementTree.parse(stdin) except ElementTree.ParseError as err: stderr.write(f"Can't parse OPML-XML: {err}") exit(1) titles_urls = {} for i in tree.findall('.//outline'): url = i.attrib.get('xmlUrl') title = i.attrib.get('title') titles_urls.update({title: url}) for v in titles_urls.values(): # Some RSS readers tend to export invalid duplicities, this takes care of that. if v is not None: stdout.write(f'{v.strip()}\n') ================================================ FILE: scripts/sha1 ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from hashlib import sha1 from sys import stdin, stdout stdout.write(sha1(stdin.read().encode()).hexdigest()) ================================================ FILE: scripts/sha256 ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from hashlib import sha256 from sys import stdin, stdout stdout.write(sha256(stdin.read().encode()).hexdigest()) ================================================ FILE: scripts/sha384 ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from hashlib import sha384 from sys import stdin, stdout stdout.write(sha384(stdin.read().encode()).hexdigest()) ================================================ FILE: scripts/sha512 ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from hashlib import sha512 from sys import stdin, stdout stdout.write(sha512(stdin.read().encode()).hexdigest()) ================================================ FILE: scripts/sort-lines ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from sys import stdin, stdout stdout.write('\n'.join(sorted(stdin.read().split('\n')))) ================================================ FILE: scripts/sort-lines-reverse ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from sys import stdin, stdout stdout.write('\n'.join(sorted(stdin.read().split('\n'), reverse=True))) ================================================ FILE: scripts/template ================================================ #!/usr/bin/env python3 # Hey, this is the script of your new tool. # It's easy to create tool. # - Get selected text from stdin # - Write result to stdout # - Write errors to stderr # - Exit with non-zero exit code to prevent applying on error # - Get arguments from command line # Here is example: from sys import stdin, stdout, stderr, argv # Read from stdin... input_string = stdin.read() # ...process string... output_string = 'You said "' + input_string + '"' # IMPORTANT: trailing newline isn't ignored, so you shouldn't # print it if you don't really want it to be in result string. # ...handle errors... if len(output_string) > 30: stderr.write('The message is too long') exit(1) # ...and write result to stdout stdout.write(output_string) # You can use any language, not only Python. # Just replace comand in first line with # other the interpreter. (https://en.wikipedia.org/wiki/Shebang_(Unix)) # Compiled languages is not supported, but # if you *really* want, you can put any # binary to this script location ================================================ FILE: scripts/template.license ================================================ SPDX-FileCopyrightText: 2021 Gleb Smirnov SPDX-License-Identifier: GPL-3.0-or-later ================================================ FILE: scripts/trim ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from sys import stdin, stdout stdout.write('\n'.join([ line.strip() for line in stdin.read().strip().split('\n') ])) ================================================ FILE: scripts/unescape-html ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from html import unescape from sys import stdin, stdout stdout.write(unescape(stdin.read())) ================================================ FILE: scripts/unescape-string ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later import warnings from sys import stderr, stdin, stdout with warnings.catch_warnings(record=True) as w: stdout.write(stdin.read().encode('utf-8').decode('unicode_escape')) if w: for warning in map(lambda warning: warning.message, w): if isinstance(warning, DeprecationWarning): stderr.write(f"Can't unescape string: {str(warning)}") exit(1) ================================================ FILE: scripts/unescape-url ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later from sys import stdin, stdout from urllib.parse import unquote_plus stdout.write(unquote_plus(stdin.read())) ================================================ FILE: scripts/yaml-to-json ================================================ #!/usr/bin/env python3 # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later import json from sys import stderr, stdin, stdout import yaml try: _dict = yaml.load(stdin, Loader=yaml.SafeLoader) except Exception as err: stderr.write(f'Invalid YAML: {err}') exit(1) json.dump( _dict, stdout, indent=' ', ensure_ascii=False ) ================================================ FILE: src/Application.vala ================================================ // Copyright 2021 Gleb Smirnov // SPDX-FileCopyrightText: 2021 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later namespace TextPieces { /** * Action accelerator */ struct ActionAccel { string action; string accel; } /** * Text Pieces application */ class Application : Adw.Application { /** * Application instance */ public static TextPieces.Application instance; /** * Text Pieces settings */ public static GLib.Settings settings = new GLib.Settings ("com.github.liferooter.textpieces"); /** * Tools controller */ public static ToolsController tools = new ToolsController (); /** * Application actions * * Every entry has this form: * {{{ * { "application-name", action_callback } * }}} */ private static ActionEntry[] ACTIONS = { { "quit", quit }, { "new-window", new_window } }; /** * Shortcuts for actions * * Every entry has this form: * {{{ * { "prefix.action", "shortcut" } * }}} */ private static ActionAccel[] ACTION_ACCELS = { /* Window actions */ { "win.apply" , "Return" }, { "win.copy" , "c" }, { "win.open-preferences" , "comma" }, { "win.load-file" , "o" }, { "win.save-as" , "s" }, { "win.toggle-search" , "s" }, { "win.jump-to-args" , "e" }, { "window.close" , "w" }, /* Application actions */ { "app.quit" , "q" }, { "app.new-window" , "n" } }; /** * CSS provider used to set style scheme */ private Gtk.CssProvider style_scheme_css_provider = new Gtk.CssProvider (); /** * Style scheme */ public GtkSource.StyleScheme style_scheme { set { _style_scheme = value; /* Apply style scheme to the application */ var is_dark = Recoloring.is_scheme_dark (value); Adw.StyleManager.get_default () .color_scheme = is_dark ? Adw.ColorScheme.FORCE_DARK : Adw.ColorScheme.FORCE_LIGHT; style_scheme_css_provider.load_from_data ( Recoloring.generate_css (value).data ); settings.set_string ("style-scheme", value.id); } get { return _style_scheme; } } private GtkSource.StyleScheme _style_scheme = GtkSource.StyleSchemeManager .get_default () .get_scheme (settings.get_string ("style-scheme")); public Application () { ApplicationFlags flags = FLAGS_NONE | HANDLES_OPEN; Object ( flags: flags, application_id: "com.github.liferooter.textpieces" ); } /** * Startup method * * This method is called once at application * startup. Do here everything that have to be * done only once. Think of it as another `main`. */ protected override void startup () { base.startup (); /* Place an instance to static field to bring it into global scope */ instance = this; /* Initialize localization */ Intl.setlocale (); Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.GNOMELOCALEDIR); Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, "UTF-8"); Intl.textdomain (Config.GETTEXT_PACKAGE); /* Setup style scheme */ Gtk.StyleContext.add_provider_for_display ( Gdk.Display.get_default (), style_scheme_css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER ); /* Initialize libs */ GtkSource.init (); /* Initialize recoloring mechanism */ style_scheme = style_scheme; /* Setup actions */ add_action_entries (ACTIONS, this); /* Setup accels */ foreach (var action_accel in ACTION_ACCELS) { set_accels_for_action ( action_accel.action, { action_accel.accel } ); } /* Load tools */ tools.load.begin (); } /** * Activate method * * This method is called every time * when application is activated. * Depending on system it may be done * once or many times. Think of it as * an application entry point. */ protected override void activate () { /* Create window window */ new_window (); } /** * Open file method * * This method is called when user * opens a file with the application. */ protected override void open (File[] files, string hint) { foreach (var file in files) { var win = new TextPieces.Window (this); win.load_from (file); win.present(); } } /** * Shutdown method * * This method is called when app * is being terminated. */ protected override void shutdown () { /* Save window geometry if can */ var window = get_active_window (); if (window is TextPieces.Window) { ((TextPieces.Window) window).save_window_size (); } else { (window?.get_transient_for () as TextPieces.Window)?.save_window_size (); } base.shutdown (); } /** * New window action */ public void new_window () { var win = new TextPieces.Window (this); win.present (); } /** * Program entry point * * Do nothing here except creating * application instance. For everything * else use `Application::startup` and * `Application::activate`. */ public static int main (string[] args) { ensure_types (); var app = new TextPieces.Application (); return app.run (args); } /** * Ensure needed types are registered */ private static void ensure_types () { typeof (TextPieces.SearchBar).ensure (); } } } ================================================ FILE: src/meson.build ================================================ # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: GPL-3.0-or-later textpieces_sources += [ 'Application.vala', 'tools/Tool.vala', 'tools/ToolsController.vala', 'widgets/Window.vala', 'widgets/Editor.vala', 'widgets/Search.vala', 'widgets/SearchBar.vala', 'widgets/SearchEntry.vala', 'widgets/preferences/Preferences.vala', 'widgets/preferences/ToolSettings.vala', 'widgets/preferences/pages/CustomToolPage.vala', 'widgets/preferences/pages/NewToolPage.vala', 'utils/Utils.vala', 'utils/Recoloring.vala', ] executable('textpieces', [blueprint_hack, textpieces_sources], c_args: [ '-DVERSION="' + meson.project_version() + '"', '-DPKGDATADIR="' + pkgdata_dir + '"', '-DSCRIPTDIR="' + pkgdata_dir / 'scripts' + '"', '-DGETTEXT_PACKAGE="' + meson.project_name() + '"', '-DGNOMELOCALEDIR="' + get_option('prefix') / get_option('localedir') + '"', ], vala_args: [ '--gresourcesdir=' + blueprints.full_path(), ], dependencies: textpieces_deps, install: true, ) ================================================ FILE: src/tools/Tool.vala ================================================ // Copyright 2021 Gleb Smirnov // SPDX-FileCopyrightText: 2021 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later using Gee; namespace TextPieces { /** * Script result */ public struct ScriptResult { string? output; string? error; } /** * Tool object class */ public class Tool : Object { /** * Whether the application * is running in Flatpak */ public static Lazy IN_FLATPAK = new Lazy ( () => File.new_for_path ("/.flatpak-info").query_exists (null) ); /** * Path to the directory containing * custom tool scripts */ public static Lazy CUSTOM_TOOLS_DIR = new Lazy ( /* Custom tools are stored in ~/.local/share/textpieces/scripts. They are there even in Flatpak because Flatpak-ed apps can't write to files from other apps' directories even through portals */ () => Path.build_filename ( Environment.get_home_dir (), ".local", "share", "textpieces", "scripts" ) ); /** * Name of the tool */ public string name { get; set; } public string translated_name { get { return dpgettext2(null, "tools", name); } } /** * Description of the tool */ public string description { get; set; } public string translated_description { get { return dpgettext2(null, "tools", description); } } /** * Tool script's command * line aguments */ public string[] arguments; public string[] translated_arguments { owned get { string[] res = {}; foreach (var arg in arguments) { res += dpgettext2(null, "tools", arg); } return res; } } /** * The icon of the tool */ public string icon = "applications-utilities-symbolic"; /** * Tool script filename */ public string script; /** * Whether the tool is pre-installed */ public bool is_system; static construct { var tools_dir = File.new_for_path (CUSTOM_TOOLS_DIR.get ()); /* Create tools directory */ Utils.ensure_directory_exists.begin (tools_dir); } /** * Apply tool on text * * @param input input text * @param args command line args tool script * * @return result of script executing */ public ScriptResult apply (string input, string[] args) { /* Get directory containing script */ var scriptdir = is_system ? Config.SCRIPTDIR : CUSTOM_TOOLS_DIR.get (); /* Build script command line */ string[] cmdline = {}; /* Run script on host via `flatpak-spawn --host` if running in Flatpak */ if (!is_system && IN_FLATPAK.get ()) { cmdline += "flatpak-spawn"; cmdline += "--host"; } cmdline += Path.build_filename (scriptdir, script); /* Append args to command line */ foreach (var arg in args) cmdline += arg; try { /* Run script */ var process = new Subprocess.newv ( cmdline, STDIN_PIPE | STDOUT_PIPE | STDERR_PIPE ); string stdout; string stderr; /* Send input to script, catch stdout and stderr */ process.communicate_utf8 (input, null, out stdout, out stderr); bool success = process.get_successful (); return { /* Return output if script successed */ success ? stdout : null, /* Return error if stderr is not empty */ (stderr ?? "") != "" ? Markup.escape_text (stderr, -1) : null }; } catch (Error e) { critical ("Internal error: %s", e.message); return { e.message, null }; } } /** * Edit tool script with external editor */ public void open (Gtk.Window? window) requires (!this.is_system) { new Xdp.Portal ().open_uri.begin ( Xdp.parent_new_gtk(window), File.new_build_filename ( Tool.CUSTOM_TOOLS_DIR.get (), this.script ).get_uri (), ASK | WRITABLE, null ); } /** * Generate filename for tool script */ public static string generate_filename (string name) { /* Generate salt */ var salt = Checksum.compute_for_string ( SHA256, Random.next_int () .to_string () ).slice (0, 8); /* Generate filename in form: "filename-salt", where salt is eight random characters */ return "%s-%s".printf ( name.down () .replace (" ", "_") .replace ("?", "x"), salt ); } } /** * Build tool row */ Gtk.Widget build_list_row (Object item) { var tool = (Tool) item; return new Adw.ActionRow () { title = tool.translated_name, subtitle = tool.translated_description, icon_name = tool.icon, activatable = true }; } } ================================================ FILE: src/tools/ToolsController.vala ================================================ // Copyright 2021 Gleb Smirnov // SPDX-FileCopyrightText: 2021 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later using Gee; namespace TextPieces { /** * Tools controller */ class ToolsController : Object { /** * Configuration directory */ static Lazy CONFIG_DIR = new Lazy ( () => Path.build_filename ( Environment.get_user_config_dir (), "textpieces" ) ); /** * File containing * pre-installed tools metadata */ static Lazy SYSTEM_TOOLS_FILE = new Lazy ( () => File.new_build_filename ( Config.PKGDATADIR, "tools.json" ) ); /** * File containing * custom tools metadata */ static Lazy CUSTOM_TOOLS_FILE = new Lazy ( () => File.new_build_filename ( CONFIG_DIR.get (), "tools.json" ) ); /** * List model of pre-installed tools */ public ListStore system_tools = new ListStore (typeof (Tool)); /** * List model of custom tools */ public ListStore custom_tools = new ListStore (typeof (Tool)); /** * List model of all tools */ public ListStore all_tools { get; private set; default = new ListStore (typeof (Tool)); } /** * Queue of deleted tools * pending script removal */ private GLib.Queue removed_tools = new GLib.Queue (); public async void load () { /* Create custom tools index if not exists */ if (!CUSTOM_TOOLS_FILE.get ().query_exists ()) { try { yield save_custom_tools (); } catch (Error e) { critical ("Can't create custom tools index: %s", e.message); } } yield update_tools (); } /** * Update tools */ public async void update_tools () { /* Remove and load pre-installed tools */ system_tools.remove_all (); var new_system_tools = load_tools_from_file (SYSTEM_TOOLS_FILE.get ()) ?? new ListStore (typeof (Tool)); for (var i = 0; i < new_system_tools.get_n_items (); i++) system_tools.append (new_system_tools.get_item (i)); /* Remove and load custom tools */ custom_tools.remove_all (); var new_custom_tools = load_tools_from_file (CUSTOM_TOOLS_FILE.get ()) ?? new ListStore (typeof (Tool)); for (var i = 0; i < new_custom_tools.get_n_items (); i++) custom_tools.append (new_custom_tools.get_item (i)); /* Update model containing all tools */ update_all_tools (); } /** * Update list model of all tools */ void update_all_tools () { /* Remove all tools */ all_tools.remove_all (); /* Append pre-installed tools */ for (var i = 0; i < system_tools.get_n_items (); i++) all_tools.append (system_tools.get_item (i)); /* Append custom tools */ for (var i = 0; i < custom_tools.get_n_items (); i++) all_tools.append (custom_tools.get_item (i)); } /** * Load tools from JSON file with tools metadata * * @param file metadata file * * @return list model containing loaded tools */ private static ListStore load_tools_from_file (File file) { var tools = new ListStore (typeof (Tool)); /* Return empty list model if file doesn't exist */ if (!file.query_exists ()) { critical ("Can't load tools from non-existing file \"%s\"", file.get_path ()); return tools; } /* Load JSON to parser */ var parser = new Json.Parser (); try { parser.load_from_file (file.get_path ()); } catch (Error e) { critical ("Can't load tools from \"%s\": %s", file.get_path (), e.message); return tools; } /* Get root object */ var root = parser.get_root (); var obj = root?.get_object (); /* Return empty list if there are no root object */ if (obj == null) { critical ("Can't load tools: file doesn't contain valid JSON object"); return tools; } /* Get whether file contains pre-installed tools metadata */ var is_system = obj.get_boolean_member_with_default ( "is_system", false ); /* Get tools array */ var json_tools = obj.get_array_member ("tools"); /* Return empty list model if there are no tools list */ if (json_tools == null) { critical ("Can't load tools: file doesn't contain list of tools"); return tools; } foreach (var json_tool in json_tools.get_elements ()) { /* Get tool object */ var tool = json_tool.get_object (); if (tool == null) { critical ("Error write loading tools: tool is not JSON object"); continue; } if (!tool.has_member ("script")) { critical ("Error write loading tools: tool has no script"); continue; } string[] arguments = {}; if (tool.has_member ("args")) tool.get_array_member ("args").foreach_element ((a, i, node) => { if (node.get_node_type () == VALUE && node.get_value_type () == typeof (string)) { arguments += node.get_string (); } else { critical ("Arguments of tools must be string"); } }); var new_tool = new Tool () { name = tool.has_member ("name") ? tool.get_string_member ("name") : "", description = tool.has_member ("description") ? tool.get_string_member ("description") : "", icon = tool.has_member ("icon") ? tool.get_string_member ("icon") : "applications-utilities-symbolic", script = tool.get_string_member ("script"), is_system = is_system, arguments = arguments }; tools.append (new_tool); } return tools; } /** * Save custom tools' metadata */ async void save_custom_tools () throws Error { /* Ensure configuration directory exists */ yield Utils.ensure_directory_exists (File.new_for_path (CONFIG_DIR.get ())); /* Create JSON builder */ var builder = new Json.Builder (); // { builder.begin_object (); // "tools": builder.set_member_name ("tools"); // [ builder.begin_array (); /* Dump tools */ for (var i = 0; i < custom_tools.get_n_items (); i++) { Tool tool = (Tool) custom_tools.get_item (i); // { builder .begin_object () // "name": `tool.name`, .set_member_name ("name") .add_string_value (tool.name) // "description": `tool.description`, .set_member_name ("description") .add_string_value (tool.description) // "script": `tool.script`, .set_member_name ("script") .add_string_value (tool.script) // "args": [ .set_member_name ("args") .begin_array (); foreach (var arg in tool.arguments) { // `arg`, builder .add_string_value (arg ?? ""); } // ] builder .end_array (); // }, builder .end_object (); } // ] builder.end_array (); // } builder.end_object (); /* Convert JSON builder to string... */ var generator = new Json.Generator (); var node = builder.get_root (); generator.set_root (node); /* ...and save to file */ try { generator.to_file (CUSTOM_TOOLS_FILE.get ().get_path ()); } catch (Error e) { error ("Can't save custom tools: %s", e.message); } } /** * Commit changed in custom tools */ public async void commit () throws Error { /* Save metadata on disk */ yield save_custom_tools (); /* Update model of all tools */ update_all_tools (); /* Delete scripts of removed tools */ while (!removed_tools.is_empty ()) { var tool = removed_tools.pop_head (); /* Delete tool script */ FileUtils.remove ( Path.build_filename ( Tool.CUSTOM_TOOLS_DIR.get (), tool.script ) ); } } /** * Add and save tool * * @param tool custom tool to add */ public signal void add_tool (Tool tool) { /* Append tool to model */ custom_tools.append (tool); } public signal void delete_tool (Tool tool) { /* Remove tool from tools */ uint pos; if (custom_tools.find (tool, out pos)) custom_tools.remove (pos); removed_tools.push_tail (tool); } } } ================================================ FILE: src/utils/Recoloring.vala ================================================ // SPDX-FileCopyrightText: 2021 Christian Hergert // SPDX-FileCopyrightText: 2022 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later using GtkSource; using Gdk; /** * CSS that should be loaded for any style scheme * except Adwaita or Adwaita-dark. */ const string SHARED_CSS = """ @define-color card_fg_color @window_fg_color; @define-color headerbar_fg_color @window_fg_color; @define-color headerbar_border_color @window_fg_color; @define-color popover_fg_color @window_fg_color; @define-color dark_fill_bg_color @headerbar_bg_color; @define-color view_bg_color @card_bg_color; @define-color view_fg_color @window_fg_color; """; /** * CSS that should be loaded for any light style * schemes except Adwaita. */ const string LIGHT_CSS_SUFFIX = """ @define-color popover_bg_color mix(@window_bg_color, white, .1); @define-color card_bg_color alpha(white, .6); """; /** * CSS that should be loaded for any dark style * schemes except Adwaita-dark. */ const string DARK_CSS_SUFFIX = """ @define-color popover_bg_color mix(@window_bg_color, white, 0.07); @define-color card_bg_color @popover_bg_color; @define-color view_bg_color darker(@window_bg_color); """; /** * Implementation of application-wide style schemes */ namespace Recoloring { /** * Color kind * * Can be background or foreground. */ enum ColorKind { FOREGROUND, BACKGROUND, } /** * Try to parse color string * * Returns `null` if string is invalid. * * @param color color string * * @return color got from string, or `null` if string is invalid */ RGBA? parse_color (string color) { var rgba = RGBA (); if (rgba.parse (color)) return rgba; else return null; } /** * Get color from style scheme * * @param scheme style scheme * @param style_name style name that color is belong to * @param kind color kind * * @return color */ RGBA? get_color (StyleScheme scheme, string style_name, ColorKind kind) { var style = scheme.get_style (style_name); if (style == null) return null; var fg = style.foreground; var fg_set = style.foreground_set; var bg = style.background; var bg_set = style.background_set; var color = RGBA (); if (kind == FOREGROUND && fg != null && fg_set) color.parse (fg); else if (kind == BACKGROUND && bg != null && bg_set) color.parse (bg); else return null; if (color.alpha >= 0.1) return color; else return null; } /** * Get color from style scheme metadata * * @param scheme style scheme * @param key color key * * @return color, or `null` if not defined */ RGBA? get_metadata_color (StyleScheme scheme, string key) { var str = scheme.get_metadata (key); if (str == null) return null; else return parse_color (str); } /** * Define color in CSS string builder * * @param str CSS string builder * @param name color name * @param color color to define */ void define_color (StringBuilder str, string name, RGBA? color) { if (color == null) return; var opaque = color.copy (); opaque.alpha = 1.0f; var color_str = opaque.to_string (); str.append_printf ("@define-color %s %s;\n", name, color_str); } /** * Define mixed color in CSS string builder * * @param str CSS string builder * @param name color name * @param a first color * @param b second color * @param level mix level */ void define_color_mixed (StringBuilder str, string name, RGBA a, RGBA b, double level) { var a_str = a.to_string (); var b_str = b.to_string (); var level_string = level.to_string (); str.append_printf ( "@define-color %s mix(%s, %s, %s);\n", name, a_str, b_str, level_string ); } /** * Get whether style scheme is dark * * @param scheme style scheme * * @return whether the style scheme is dark */ public bool is_scheme_dark (StyleScheme scheme) { string id = scheme.get_id (); string variant = scheme.get_metadata ("variant"); if (variant == "light") return false; else if (variant == "dark") return true; else if (id.has_suffix ("-dark")) return true; var text_bg = get_color (scheme, "text", BACKGROUND); if (text_bg != null) { /* http://alienryderflex.com/hsp.html */ double r = text_bg.red * 255.0; double g = text_bg.green * 255.0; double b = text_bg.blue * 255.0; double hsp = Math.sqrt (0.299 * (r * r) + 0.587 * (g * g) + 0.114 * (b * b)); return hsp <= 127.5; } return false; } /** * Generate CSS for style scheme * * This function is used to apply `gtksourceview`'s * style schemes to whole application. * * @param style_scheme style scheme * * @return CSS string to apply on application */ public string generate_css (StyleScheme style_scheme) { RGBA black = {0, 0, 0, 1}; RGBA white = {1, 1, 1, 1}; /* Don't restyle Adwaita as we already have it */ var id = style_scheme.get_name (); if (id.has_prefix ("Adwaita")) return ""; var name = style_scheme.get_name (); var is_dark = is_scheme_dark (style_scheme); var alt = is_dark ? white : black; var str = new StringBuilder (SHARED_CSS); str.append_printf ("/* %s */\n", name); /* TODO: Improve error checking and fallbacks */ var text_bg = get_color (style_scheme, "text", BACKGROUND) ?? (is_dark ? black : white); var text_fg = get_color (style_scheme, "text", FOREGROUND) ?? (is_dark ? white : black); var right_margin = get_color (style_scheme, "right-margin", BACKGROUND) ?? text_bg; right_margin.alpha = 1; if (is_dark) { define_color_mixed (str, "window_bg_color", text_bg, alt, 0.025); } else { define_color_mixed (str, "window_bg_color", text_bg, white, 0.1); } define_color_mixed (str, "headerbar_bg_color", text_bg, alt, 0.05); define_color_mixed (str, "window_fg_color", text_fg, alt, 0.1); define_color (str, "headerbar_fg_color", text_fg); define_color_mixed (str, "view_bg_color", text_bg, white, is_dark ? 0.1 : 0.3); define_color (str, "view_fg_color", text_fg); define_color ( str, "accent_bg_color", get_metadata_color (style_scheme, "accent_bg_color") ?? get_color (style_scheme, "selection", BACKGROUND) ); define_color ( str, "accent_fg_color", get_metadata_color (style_scheme, "accent_fg_color") ?? get_color (style_scheme, "selection", FOREGROUND) ); var accent_color = get_metadata_color (style_scheme, "accent_color"); if (accent_color != null) { define_color (str, "accent_color", accent_color); } else { accent_color = get_metadata_color (style_scheme, "accent_bg_color") ?? get_color (style_scheme, "selection", BACKGROUND); if (accent_color != null) { accent_color.alpha = 1; define_color_mixed (str, "accent_color", accent_color, alt, 0.1); } } if (is_dark) str.append(DARK_CSS_SUFFIX); else str.append(LIGHT_CSS_SUFFIX); return str.str; } } ================================================ FILE: src/utils/Utils.vala ================================================ // Copyright 2021 Gleb Smirnov // SPDX-FileCopyrightText: 2021 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later namespace TextPieces.Utils { /** * Create directory if not exists * * @param dir directory to ensure */ public async void ensure_directory_exists (File dir) throws Error { /* Get parent directory */ var parent_dir = dir.get_parent (); /* Create parent directory if not exists */ if (!parent_dir.query_exists ()) yield ensure_directory_exists (parent_dir); /* Create target directory if not exists */ if (!dir.query_exists ()) yield dir.make_directory_async (); } } ================================================ FILE: src/vapi/config.vapi ================================================ // SPDX-FileCopyrightText: 2021 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later [CCode (cprefix = "", lower_case_cprefix = "")] namespace Config { public const string VERSION; public const string PKGDATADIR; public const string SCRIPTDIR; public const string GETTEXT_PACKAGE; public const string GNOMELOCALEDIR; } ================================================ FILE: src/widgets/Editor.vala ================================================ // SPDX-FileCopyrightText: 2022 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later namespace TextPieces { /** * Message toast timeout, in seconds */ const uint TOAST_TIMEOUT = 2; /** * Text Pieces' editor widget */ [GtkTemplate (ui = "/com/github/liferooter/textpieces/ui/Editor.ui")] class Editor : Adw.Bin { [GtkChild] unowned GtkSource.View editor; [GtkChild] unowned Gtk.Box arguments_box; [GtkChild] unowned Adw.ToastOverlay message_overlay; [GtkChild] public unowned TextPieces.SearchBar search_bar; /** * Whether to wrap lines */ public bool wrap_lines { set { editor.wrap_mode = value ? Gtk.WrapMode.WORD_CHAR : Gtk.WrapMode.NONE; } get { return editor.wrap_mode == WORD_CHAR; } } /** * CSS provider used to set editor font */ Gtk.CssProvider editor_font_css_provider = new Gtk.CssProvider (); /** * Editor font */ public string? editor_font { set { if (value != null) { editor_font_css_provider.load_from_data (""" .monospace { font-family: %s; } """.printf (value).data); } else { editor_font_css_provider.load_from_data ({}); } } } /** * Editor's application * * Used to bind editor style scheme * to global style scheme. */ public TextPieces.Application application { get { return Application.instance; } } construct { /* Bind some values to settings */ with (Application.settings) { bind ("tabs-to-spaces", editor, "insert-spaces-instead-of-tabs", GET); bind ("spaces-in-tab", editor, "tab-width", GET); bind ("font-name", this, "editor-font", GET); bind ("wrap-lines", this, "wrap-lines", GET); } /* Add style provider with editor font */ get_style_context ().add_provider ( editor_font_css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER ); /* Bind editor style scheme to application style scheme */ application.bind_property ( "style-scheme", editor.buffer, "style-scheme", SYNC_CREATE ); /* Enable drag-n-drop to open file content */ var drop_target = new Gtk.DropTarget (typeof (File), COPY); drop_target.on_drop.connect ((file) => { var path = ((File) file).get_path (); string contents; try { FileUtils.get_contents (path, out contents); editor.buffer.text = contents; return true; } catch (Error err) { critical ("failed to load file %s: %s", path, err.message); return false; } }); editor.add_controller (drop_target); } /** * Move focus to the editor */ public override bool grab_focus () { return editor.grab_focus (); } /** * Get selection or whole text if there are no selection */ public string get_selection () { var buffer = editor.buffer; Gtk.TextIter start, end; if (buffer.has_selection) { buffer.get_selection_bounds (out start, out end); } else { buffer.get_bounds (out start, out end); } return buffer.get_text (start, end, false); } /** * Copy text from editor */ public void copy_text () { Gdk.Display .get_default () .get_clipboard () .set_text (editor.buffer.text); show_message (_("Text is copied")); } /** * Replace selection or whole text if there are no selection */ public void replace_selection (string text) { var buffer = editor.buffer; Gtk.TextIter start, end; var has_selection = buffer.has_selection; if (has_selection) { buffer.get_selection_bounds (out start, out end); } else { buffer.get_bounds (out start, out end); } var start_offset = start.get_offset(); buffer.begin_user_action (); /* Replace selection by given text */ buffer.delete (ref start, ref end); buffer.insert (ref start, text, -1); buffer.end_user_action (); if (has_selection) { buffer.get_iter_at_offset ( out start, start_offset ); buffer.get_iter_at_offset ( out end, start_offset + text.length ); buffer.select_range (start, end); } else { /* TODO: Invent better behavior of cursor after whole-text transformations */ buffer.place_cursor (start); } } /** * Set argument list */ public void set_arguments (string[] arguments) { var children = arguments_box.observe_children (); for (int i = (int) children.get_n_items () - 1; i >= 0; i--) { var entry = (Gtk.Widget) children.get_item (i); arguments_box.remove (entry); } foreach (var argument in arguments) { var entry = new Gtk.Entry () { placeholder_text = argument, css_classes = {"monospace"} }; entry.activate.connect (() => { move_focus (TAB_FORWARD); }); arguments_box.append (entry); } arguments_box.visible = arguments.length != 0; } /** * Get argument values */ public string[] get_arguments () { var args = new string[0]; var children = arguments_box.observe_children (); for (var i = 0; i < children.get_n_items (); i++) { args += ((Gtk.Entry) children.get_item (i)).text; } return args; } /** * Move focus to the first argument entry */ public void jump_to_args () { if (arguments_box.visible) ((Gtk.Widget) arguments_box.observe_children ().get_item (0)) .grab_focus (); } /** * Save text to file */ public void save_as (File file) throws FileError { /* Try to save text to file */ FileUtils.set_contents ( file.get_path (), editor.buffer.text ); } /** * Load text from file */ public void load_from (File file) throws FileError { /* Try to load text from file */ string text; FileUtils.get_contents ( file.get_path (), out text ); editor.buffer.text = text; } /** * Show message in a toast */ public void show_message (string message) { message_overlay.add_toast (new Adw.Toast (message) { priority = HIGH, timeout = TOAST_TIMEOUT }); } /** * Update editor's top margin */ public bool update_editor_margin () { /* Calculate new margin */ Graphene.Rect bounds; assert (arguments_box.compute_bounds (this, out bounds)); var old_margin = editor.top_margin; var new_margin = arguments_box.visible ? (int) Math.ceil (bounds.get_height ()) : 6; /* Set margin */ editor.top_margin = new_margin; /* Restore scroll position */ editor.vadjustment.value = double.max(editor.vadjustment.value - old_margin + new_margin, 0); return Source.REMOVE; } } } ================================================ FILE: src/widgets/Search.vala ================================================ // SPDX-FileCopyrightText: 2022 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later using Gtk; using Gdk; using Adw; [GtkTemplate(ui = "/com/github/liferooter/textpieces/ui/Search.ui")] class TextPieces.Search : Bin { [GtkChild] unowned Viewport results_viewport; [GtkChild] unowned ListBox results_listbox; [GtkChild] unowned Stack search_stack; /** * Search entry to use */ public Gtk.SearchEntry search_entry { get; construct; default = null; } /** * Tool list model */ GLib.ListModel results_model; /** * Sorter for search results */ Sorter sorter; /** * Filter for search results */ Filter filter; /** * Search entry key event controller */ EventControllerKey search_event_controller; construct { /* Create model for search results */ sorter = new CustomSorter ((a, b) => tool_sort_func ((Tool) a, (Tool) b)); filter = new CustomFilter (tool_filter_func); results_model = new SortListModel ( new FilterListModel ( Application.tools.all_tools, filter ), sorter ); /* Bind model to list box */ results_listbox.bind_model ( results_model, build_list_row ); /* Update search results on query changes */ search_entry.changed.connect (update_search_results); /* Activate current row on activate */ search_entry.activate.connect (activate_selected_result); /* Pass arrow keys events from search entry to list box */ search_event_controller = new EventControllerKey (); search_event_controller.key_pressed.connect (on_search_entry_key); search_entry.add_controller (search_event_controller); } /** * Emitted when a tool is selected * * @param tool selected tool */ public signal void tool_selected (Tool tool); /** * Reset search * * Clear the search entry, scroll to the top, reset selection */ public void reset () { /* Clear the search entry */ search_entry.text = ""; /* Scroll to the top */ results_viewport.vadjustment.value = 0; /* Select the first row */ var row = results_listbox.get_row_at_index (0); results_listbox.select_row (row); } /** * Choose selected result */ void activate_selected_result () { var row = results_listbox.get_selected_row () ?? results_listbox.get_row_at_index (0); if (row != null) results_listbox.row_activated (row); } /** * Process key pressed in search entry * * Move focus to the first search result * if `Gdk.Key.Down` is pressed * * @param keyval pressed key value * @param keycode pressed key code * @param modifiers pressed modifiers * * @returnif whether the key press was handled */ bool on_search_entry_key (uint keyval, uint keycode, ModifierType modifiers) { switch (keyval) { case (Key.Down): case (Key.Up): /* Move focus to listbox */ results_listbox.get_selected_row()?.grab_focus (); /* Forward event to listbox */ search_event_controller.forward (results_listbox); /* Grab focus back when done */ search_entry.grab_focus (); return true; default: return false; } } /** * Emit `tool-selected` signal when tool is selected */ [GtkCallback] void on_row_activated (ListBoxRow row) { var index = row.get_index (); var tool = (Tool) results_model.get_item (index); tool_selected (tool); } /** * Update list of search results */ void update_search_results () { /* Invalidate search sorter and filter */ sorter.changed (DIFFERENT); filter.changed (DIFFERENT); /* Select the first row if no row is selected */ if (results_listbox.get_selected_row () == null) { results_listbox.select_row ( results_listbox.get_row_at_index (0) ); } /* Show placeholder if there are no tools found */ search_stack.set_visible_child_name ( results_model.get_n_items () == 0 ? "placeholder" : "search" ); } /** * Compare tools by search irrelevance * * Less relevant tool should be * larger than more relevant * to be below more relevant tool * in search results. If tools are * equal, compare tools' names. * * @param a one tool * @param b another tool * * @return zero if tools are equal, positive if `a` is larger, negative otherwise */ public int tool_sort_func (Tool a, Tool b) { var query = search_entry.text; var res = calculate_irrelevance (a, query) - calculate_irrelevance (b, query); if (res == 0) return strcmp (a.translated_name, b.translated_name); return res; } /** * Functions used to filter tools by search relevance * * Don't show tool if its irrelevance is infinite * * @param tool the tool * @param query search query * * @return whether to show tool in search results */ public bool tool_filter_func (Object tool) { return calculate_irrelevance ((Tool) tool, search_entry.text) != int.MAX; } /** * Calculate tool irrelevance * * This method is used to filter * and sort search results * * @param tool the tool * @param query search query * * @return tool's search irrelevance */ static int calculate_irrelevance (Tool tool, string query) { /* Get case-independent form of search query */ var casefolded_query = query.casefold (); return int.min ( /* Calculate non-translated tool's irrelevance */ calculate_irrelevance_for_fields ({ tool.name .casefold (), tool.description.casefold () }, casefolded_query), /* Calculate translated tool's irrelevance */ calculate_irrelevance_for_fields ({ tool.translated_name .casefold (), tool.translated_description.casefold () }, casefolded_query) ); } /** * Calculate fields' irrelevance * * This method gets list of fields * and search query and returns fields' * search irrelevance. The alghorythm is as follows: * * 1. If query is empty string, algorythm finishes, * irrelevance is zero. It's used to fallback * to alphabetical sort. * 2. At the start of the alghorythm irrelevance * is equals to zero * 3. Query is divided into words, called terms * 4. Algorythm iterates over terms * 5. I search for term in fields, from the first * field to the last field * 6. If term is not found, algorythm finishes, * irrelevance is infinite * 7. If term is found in the field, increase * irrelevance by the difference from the index of * the match start and then cut out part of field * from the start of field to the end of match * 8. At the end of the algorythm, increase * irrelevance by sum of lengths of first fields * which don't contain any terms * * @param fields list of the fields * @param query search query * * @return tool's search irrelevance */ static int calculate_irrelevance_for_fields (string[] fields, string query) { /* If query is empty, return zero */ if (query == "") return 0; /* Split query to terms */ var terms = query.split (" "); /* Create array of field begginings. It's used to easily cut fields as said in the algorythm */ var field_beginning = new int[fields.length]; /* Initial irrelevance is zero */ var irrelevance = 0; /* Iterate over terms */ foreach (var term in terms) { /* Find first field containing the term */ var matching_field = 0; var match = 0; while (matching_field < fields.length && (match = fields[matching_field].index_of (term, field_beginning[matching_field])) == -1) { matching_field++; } /* If there are no such field, return infinity */ if (match == -1) return int.MAX; /* Increase irrelevance by match beginning index */ irrelevance += match - field_beginning[matching_field]; /* Cut the field */ field_beginning[matching_field] = match + term.length; } /* Increase irrelevance by sum of lengths of first non-matched fields */ for (var i = 0; i < field_beginning.length && field_beginning[i] == 0; i++) irrelevance += fields[i].length; /* Return the result */ return irrelevance; } } ================================================ FILE: src/widgets/SearchBar.vala ================================================ // SPDX-FileCopyrightText: 2022 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later [GtkTemplate(ui = "/com/github/liferooter/textpieces/ui/SearchBar.ui")] class TextPieces.SearchBar : Adw.Bin { [GtkChild] unowned SearchEntry search_entry; [GtkChild] unowned Gtk.Entry replace_entry; [GtkChild] unowned Gtk.Revealer search_revealer; [GtkChild] unowned Gtk.ToggleButton search_replace; /** * Source view that this search bar is associated with */ public GtkSource.View editor { get; set construct; } /** * Whether to parse query as regular expression */ public bool use_regex { get; set; default = false; } /** * Whether the search is case sensitive. */ public bool case_sensitive { get; set; default = false; } /** * Whether the search must match whole words only */ public bool whole_words { get; set; default = false; } /** * Search settings */ private GtkSource.SearchSettings search_settings = new GtkSource.SearchSettings () { wrap_around = true }; /** * Search context */ private GtkSource.SearchContext search_context; /** * Search cancelable */ private Cancellable search_cancelable = new Cancellable (); /** * Get action entries */ private ActionEntry[] action_entries () { return { { "hide" , hide_search }, { "show" , show_search }, { "show-replace", show_replace }, { "next-match" , () => { next_match.begin (); } }, { "prev-match" , () => { prev_match.begin (); } }, { "replace" , replace }, { "replace-all" , replace_all }, }; } construct { search_context = new GtkSource.SearchContext ( (GtkSource.Buffer) editor.buffer, search_settings ); /* Bind properties */ search_entry.bind_property ( "query", search_settings, "search-text" ); search_revealer.bind_property ( "child-revealed", search_context, "highlight" ); this.bind_property ( "use-regex", search_settings, "regex-enabled" ); this.bind_property ( "case-sensitive", search_settings, "case-sensitive" ); this.bind_property ( "whole-words", search_settings, "at-word-boundaries" ); /* Setup search entry */ search_entry.set_context (search_context); /* Setup action entries */ var action_group = new SimpleActionGroup (); action_group.add_action_entries (action_entries (), this); insert_action_group ("search", action_group); } /** * Show search */ public void show_search () { search_entry.query = ""; search_revealer.reveal_child = true; search_cancelable.reset (); search_entry.grab_focus (); } /** * Show search in replace mode */ public void show_replace () { show_search (); search_replace.active = true; } /** * Hide search */ private void hide_search () { search_revealer.reveal_child = false; search_replace.active = false; search_cancelable.cancel (); } /** * Move to next match */ public async void next_match () { var buffer = editor.buffer; Gtk.TextIter begin, end; buffer.get_selection_bounds (out begin, out end); begin.order (ref end); var cursor = end.copy (); try { bool has_wrapped; var found = yield search_context.forward_async ( cursor, search_cancelable, out begin, out end, out has_wrapped ); if (!found) { return; } buffer.select_range (begin, end); } catch (Error e) { critical ("failed to move to next search match: %s", e.message); } editor.scroll_mark_onscreen (buffer.get_insert ()); } /** * Move to previous match */ public async void prev_match () { var buffer = editor.buffer; Gtk.TextIter begin, end; buffer.get_selection_bounds (out begin, out end); begin.order (ref end); var cursor = begin.copy (); try { bool has_wrapped; var found = yield search_context.backward_async ( cursor, search_cancelable, out begin, out end, out has_wrapped ); if (!found) { return; } buffer.select_range (begin, end); } catch (Error e) { critical ("failed to move to next search match: %s", e.message); } editor.scroll_mark_onscreen (buffer.get_insert ()); } /** * Replace current occurrence */ public void replace () requires (search_entry.occurrence_position > 0) { var replacement = replace_entry.text; Gtk.TextIter start, end; editor.buffer.get_selection_bounds (out start, out end); try { search_context.replace ( start, end, replacement, -1 ); } catch (Error e) { replace_entry.add_css_class ("error"); replace_entry.error_bell (); } if (search_context.occurrences_count != 0) next_match.begin (); } /** * Replace all occurrences */ public void replace_all () { var replacement = replace_entry.text; try { search_context.replace_all ( replacement, -1 ); } catch (Error e) { replace_entry.add_css_class ("error"); replace_entry.error_bell (); } } /** * Remove error style */ [GtkCallback] void remove_error_style (Gtk.Widget widget) { widget.remove_css_class ("error"); } } ================================================ FILE: src/widgets/SearchEntry.vala ================================================ // SPDX-FileCopyrightText: 2022 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later [GtkTemplate(ui = "/com/github/liferooter/textpieces/ui/SearchEntry.ui")] class TextPieces.SearchEntry : Gtk.Widget { [GtkChild] unowned Gtk.Text text; /** * Current search query */ public string query { get; set; } /** * Current information string about occurrences count and position */ public string occurrences_info { get; set; } /** * occurrences count */ public int occurrences_count { get; set; } /** * Position of current occurrence */ public uint occurrence_position { get; set; } class construct { set_layout_manager_type (typeof (Gtk.BoxLayout)); set_css_name ("entry"); set_accessible_role (TEXT_BOX); } construct { text.bind_property ( "text", this, "query" ); notify["occurrences-count"].connect (update_info); notify["occurrence-position"].connect (update_info); update_info (); } public void set_context (GtkSource.SearchContext context) { /* Bind occurrences count */ context.bind_property ( "occurrences-count", this, "occurrences-count" ); /* Bind occurrence position */ var buffer = context.buffer; buffer.changed.connect (() => { update_occurrence_position (context); }); buffer.notify["cursor-position"].connect (() => { update_occurrence_position (context); }); text.changed.connect (() => { update_occurrence_position (context); }); context.notify.connect(() => { update_occurrence_position (context); }); } private void update_occurrence_position (GtkSource.SearchContext context) { Gtk.TextIter? start, end; context.buffer.get_selection_bounds (out start, out end); if (start != null && end != null) { var position = context.get_occurrence_position (start, end); occurrence_position = position >= 0 ? position : 0; } } public override bool grab_focus () { return text.grab_focus (); } public void update_info () { var info = ""; if (occurrences_count != 0) { info = _("%u of %d").printf ( occurrence_position, occurrences_count ); } this.occurrences_info = info; } } ================================================ FILE: src/widgets/Window.vala ================================================ // SPDX-FileCopyrightText: 2021 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later namespace TextPieces { /** * Main window class */ [GtkTemplate (ui = "/com/github/liferooter/textpieces/ui/Window.ui")] class Window : Adw.ApplicationWindow { [GtkChild] unowned Adw.ButtonContent tool_button_content; [GtkChild] unowned TextPieces.Editor editor; [GtkChild] unowned Gtk.Stack content_stack; [GtkChild] unowned Gtk.ToggleButton tool_button; [GtkChild] unowned Search search; /** * Text used instead of tool name * when where is no tool selected */ const string NO_TOOL_LABEL = _("Select tool"); /** * Name of icon used * instead of tool icon * where is no tool selected */ const string NO_TOOL_ICON = "applications-utilities-symbolic"; /** * Selected tool */ private Tool? _selected_tool; public Tool? selected_tool { get { return _selected_tool; } set { /* Disconnect callback frow old tool */ if (_selected_tool != null) _selected_tool.notify .disconnect (tool_changed); /* Save new tool */ _selected_tool = value; /* Connect callback to tool changes */ if (_selected_tool != null) _selected_tool.notify .connect (tool_changed); /* Save selected tool in GSettings */ Application.settings.set_string ( "selected-tool", value?.script ?? "" ); /* Trigger tool change callback */ tool_changed (); } } /** * Window actions */ private const ActionEntry[] ACTION_ENTRIES = { { "apply" , action_apply }, { "open-preferences" , action_open_preferences }, { "about" , action_about }, { "copy" , action_copy }, { "tools-settings" , action_tools_settings }, { "toggle-search" , action_toggle_search }, { "save-as" , action_save_as }, { "load-file" , action_load_file }, { "jump-to-args" , action_jump_to_args }, { "find" , action_find }, }; public Window (Application application) { Object ( application: application ); } construct { /* Restore window geometry from settings */ with (Application.settings) { default_width = get_int ("window-width"); default_height = get_int ("window-height"); maximized = get_boolean ("is-maximized"); } /* Load actions */ add_action_entries (ACTION_ENTRIES, this); /* Unselect tool if it's deleted */ Application.tools.delete_tool.connect ((tool) => { if (tool == selected_tool) selected_tool = null; }); /* Initialize selected tool property and run its callback */ var selected_tool_script = Application.settings.get_string ("selected-tool"); var tool_found = false; if (selected_tool_script != "") { for (uint i = 0; i < Application.tools.all_tools.get_n_items (); i++) { var tool = (Tool) Application.tools.all_tools.get_item (i); if (tool.script == selected_tool_script) { selected_tool = tool; tool_found = true; break; } } if (!tool_found) { message ("Previously selected tool is not found, so will be reset"); selected_tool = null; } } else { selected_tool = null; } } /** * Close request callback * * Saves window geometry. */ protected override bool close_request () { save_window_size (); return false; } /** * Save window size */ public void save_window_size () { with (Application.settings) { set_int ("window-width", default_width); set_int ("window-height", default_height); set_boolean ("is-maximized", maximized); } } /** * Load content from file */ public void load_from (File file) { try { editor.load_from (file); } catch (FileError e) { /* Show error dialog if error occurs */ var dialog = new Gtk.MessageDialog ( this, MODAL | DESTROY_WITH_PARENT, WARNING, CLOSE, _("Can't load from file: %s"), e.message ); dialog.response.connect (dialog.destroy); dialog.show (); } } /** * Apply tool */ void action_apply () { /* Don't apply non-existing tool */ if (selected_tool == null) return; /* Get arguments from argument entries */ var args = editor.get_arguments (); /* Apply tool on text */ var result = selected_tool.apply ( editor.get_selection (), args ); string result_text; /* Send error notification if stderr is not null */ if (result.error != null) { editor.show_message (result.error); } /* Set new text if stdout is not null */ if (result.output != null) { result_text = result.output; } else { return; } editor.replace_selection (result_text); } /** * Open preferences */ void action_open_preferences () { /* Create preferences window */ var prefs = new Preferences (this) { transient_for = this, /* Pass application to the window to get application's shortcuts */ application = application }; prefs.present (); } /** * Open custom tools settings */ void action_tools_settings () { var prefs = new Preferences (this) { transient_for = this, application = application, visible_page_name = "custom-tools" }; prefs.present (); } /** * Show about dialog */ void action_about () { var about_window = new Adw.AboutWindow () { transient_for = this, application = application, application_icon = "com.github.liferooter.textpieces", application_name = _("Text Pieces"), comments = _("Transform text without using random websites"), version = Config.VERSION, license_type = Gtk.License.GPL_3_0, website = "https://github.com/liferooter/textpieces", issue_url = "https://github.com/liferooter/textpieces/issues/new", artists = {_("Tobias Bernard https://tobiasbernard.com")}, developers = {_("Gleb Smirnov ")}, developer_name = _("Gleb Smirnov"), translator_credits = _("translator-credits") }; about_window.present (); } /** * Toggle search */ void action_toggle_search () { tool_button.active = !tool_button.active; } /** * Copy all text from the * editor to clipboard */ void action_copy () { editor.copy_text (); } /** * Save editor content to file */ void action_save_as () { /* Create file chooser dialog */ var file_chooser = new Gtk.FileChooserNative ( _("Save to File"), this, SAVE, null, null ) { transient_for = this, modal = true }; file_chooser.response.connect (() => { /* Get selected file */ var file = file_chooser.get_file (); if (file == null) return; try { editor.save_as (file); } catch (FileError e) { /* Show error dialog if error occurs */ var dialog = new Gtk.MessageDialog ( this, MODAL | DESTROY_WITH_PARENT, WARNING, CLOSE, _("Can't save to file: %s"), e.message ); dialog.response.connect (dialog.destroy); dialog.show (); } }); file_chooser.show (); } /** * Load editor content from file */ void action_load_file () { /* Create file chooser dialog */ var file_chooser = new Gtk.FileChooserNative ( _("Load from File"), this, OPEN, null, null ) { transient_for = this, modal = true }; file_chooser.response.connect (() => { /* Get selected file */ var file = file_chooser.get_file (); if (file == null) return; load_from (file); }); file_chooser.show (); } /** * Move focus to the first arguments entry */ void action_jump_to_args () { editor.jump_to_args (); } /** * Show search overlay */ void action_find () { editor.search_bar.show_search (); } /** * Tool change callback * * Set tool button's * label and icon */ void tool_changed () { /* Update tool button */ with (tool_button_content) { label = selected_tool?.translated_name ?? NO_TOOL_LABEL; icon_name = selected_tool?.icon ?? NO_TOOL_ICON; } /* Disable applying if there are no tool */ ((SimpleAction) lookup_action ("apply")) .set_enabled (selected_tool != null); editor.set_arguments (selected_tool?.translated_arguments ?? new string[0]); /* Update editor's top margin */ Idle.add (() => { editor.update_editor_margin (); return Source.REMOVE; }); } /** * Select tool and close the search */ [GtkCallback] void on_tool_selected (Tool tool) { selected_tool = tool; tool_button.active = false; } /** * Change application state * according to new search state */ [GtkCallback] void on_search_toggled () { if (tool_button.active) { /* Show search */ content_stack.visible_child_name = "search"; } else { /* Show editor */ content_stack.visible_child_name = "editor"; search.reset (); } } } } ================================================ FILE: src/widgets/preferences/Preferences.vala ================================================ // Copyright 2021 Gleb Smirnov // SPDX-FileCopyrightText: 2021 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later namespace TextPieces { /** * Preferences window */ [GtkTemplate (ui = "/com/github/liferooter/textpieces/ui/Preferences.ui")] class Preferences : Adw.PreferencesWindow { [GtkChild] unowned Gtk.ListBox custom_tools_listbox; [GtkChild] unowned Gtk.Label font_label; [GtkChild] unowned Gtk.SpinButton spaces_in_tab; [GtkChild] unowned GtkSource.StyleSchemeChooserWidget style_scheme_chooser; /** * Preferences window actions */ const ActionEntry[] ACTION_ENTRIES = { { "select-font", action_select_font } }; /** * List of settings keys * binded to the actions */ const string[] SETTINGS_ACTIONS = { "wrap-lines", "tabs-to-spaces" }; public Preferences (TextPieces.Window win) { Application.instance.bind_property ( "style-scheme", style_scheme_chooser, "style-scheme", SYNC_CREATE | BIDIRECTIONAL ); } construct { /* Create actions from entries */ var action_group = new SimpleActionGroup (); action_group.add_action_entries (ACTION_ENTRIES, this); insert_action_group ("prefs", action_group); /* Create settings actions */ var settings_group = new SimpleActionGroup (); foreach (var setting in SETTINGS_ACTIONS) { var action = Application.settings .create_action (setting); settings_group.add_action (action); } insert_action_group ("settings", settings_group); /* Bind settings to corresponding widgets */ with (Application.settings) { bind ( "font-name", font_label, "label", DEFAULT ); bind ( "spaces-in-tab", spaces_in_tab, "value", DEFAULT ); } /* Bind list of custom tools to `custom_tools_listbox` */ var tools = Application.tools.custom_tools; custom_tools_listbox.bind_model ( tools, (obj) => { var item = (Tool) obj; var row = new Adw.ActionRow () { title = item.translated_name, subtitle = item.translated_description, activatable = true }; row.add_suffix (new Gtk.Image () { icon_name = "go-next-symbolic" }); return row; } ); /* Don't show empty list */ update_tools_visibility (); tools.items_changed.connect (update_tools_visibility); } ~Preferences () { /* Unbind list visibility */ Application .tools .custom_tools .items_changed .disconnect (update_tools_visibility); } /** * Update tools listbox visibility * * Hide if there are no custom tools, * show if there are any custom tools */ void update_tools_visibility () { custom_tools_listbox.visible = Application.tools.custom_tools.get_n_items () > 0; } /** * Select font action * * Show dialog to select * font for editor and * argument entries */ void action_select_font () { /* Create dialog */ var dialog = new Gtk.FontChooserDialog (_("Select font"), this) { modal = true, transient_for = this, font = Application.settings.get_string ("font-name"), level = FAMILY }; /* Set font and close dialog on response */ dialog.response.connect ((res) => { if (res == Gtk.ResponseType.OK) { Application.settings.set_string ("font-name", dialog.font_desc.get_family ()); } dialog.close (); }); /* Show dialog */ dialog.present (); } /** * Open tool creating page */ [GtkCallback] void add_new_tool () { present_subpage (new NewToolPage (this)); } /** * Open tool settings page */ [GtkCallback] void edit_tool (Gtk.ListBoxRow row) { var tool = (Tool) Application .tools .custom_tools .get_item (row.get_index ()); present_subpage (new CustomToolPage (this, tool)); } } } ================================================ FILE: src/widgets/preferences/ToolSettings.vala ================================================ // Copyright 2021 Gleb Smirnov // SPDX-FileCopyrightText: 2021 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later namespace TextPieces { [GtkTemplate (ui = "/com/github/liferooter/textpieces/ui/ToolSettings.ui")] class ToolSettings : Adw.Bin { [GtkChild] unowned Gtk.Entry name_entry; [GtkChild] unowned Gtk.Entry description_entry; [GtkChild] unowned Gtk.SpinButton arguments_number; [GtkChild] unowned Gtk.ListBox argument_list; [GtkChild] unowned Gtk.ListBox edit_script_list; public Gtk.Window? window = null; /** * Whether all entered data is valid */ public bool is_valid { get; set; } /** * Whether to allow edit script */ public bool can_edit_script { get; construct; default = false; } /** * Tool which this settings is of */ private Tool? tool = null; /** * Model of tool arguments */ private ListStore arguments = new ListStore (typeof (Argument)); /** * Tool argument class */ private class Argument : Object { /** * Index of the argument */ public int index { get; construct set; } /** * Tool which the argument belongs to */ public Tool tool { get; construct; } /** * Name of the argument */ public string name { get; construct set; } /** * Translated name of the argument */ public string translated_name { get { return _(name); } } /** * Create argument object * * @param index index of the argument * @param tool tool which the argument belongs to */ public Argument (int index, Tool tool) { Object ( index: index, tool: tool ); } construct { /* Get argument name from tool */ name = tool.arguments[index] ?? ""; /* Update tool argument name on argument's name changes */ notify["name"].connect_after (() => { tool.arguments[index] = name; }); } } construct { edit_script_list.visible = can_edit_script; name_entry.bind_property ( "text", this, "is-valid", SYNC_CREATE, (_, from, ref to) => { to.set_boolean (from.get_string () != ""); return true; } ); arguments_number.value_changed.connect (() => { /* Add arguments */ while (arguments_number.value > arguments.get_n_items ()) { var arg = new Argument ( (int) arguments.get_n_items (), tool ); arguments.append (arg); } /* Remove arguments */ while (arguments_number.value < arguments.get_n_items ()) { arguments.remove (arguments.get_n_items () - 1); } }); } /** * Set tool for tool settings. * * Can be called only when * tool is not set. * * @param tool tool to set */ public void set_tool (Tool tool) requires (this.tool == null) { /* Save tool in private field */ this.tool = tool; /* Bind tool name */ tool.bind_property ( "name", name_entry, "text", BIDIRECTIONAL | SYNC_CREATE, null, (_, from, ref to) => { var name = from.get_string (); if (name != "") { to.set_string (name); name_entry.remove_css_class ("error"); return true; } else { name_entry.add_css_class ("error"); return false; } } ); /* Bind tool description */ tool.bind_property ( "description", description_entry, "text", BIDIRECTIONAL | SYNC_CREATE ); /* Load tool arguments to model */ arguments_number.value = tool.arguments.length; arguments_number.value_changed (); /* Show arguments list if there are any arguments */ argument_list.visible = arguments.get_n_items () != 0; /* Setup model rendering */ argument_list.bind_model ( arguments, (obj) => { var arg = obj as Argument; /* Create entry for argument name, align it to the center to don't stretch to the full height of row */ var entry = new Gtk.Entry () { valign = CENTER }; /* Create row itself set title "Argument N", make entry focusable by click on row */ var row = new Adw.ActionRow () { title = _("Argument %d").printf (arg.index + 1), activatable_widget = entry }; /* Add entry to the row */ row.add_suffix (entry); /* Bind entry text to argument name */ arg.bind_property ( "name", entry, "text", BIDIRECTIONAL | SYNC_CREATE ); return row; } ); /* Update tool arguments on model chages */ arguments.items_changed.connect ((pos, removed, added) => { /* Get new arguments */ var new_args = new string[added]; for (var i = 0; i < added; i++) { var arg = arguments.get_item (pos + i) as Argument; new_args[i] = arg.translated_name; } /* Update tool arguments */ var args = tool.arguments[:pos]; foreach (var new_arg in new_args) args += new_arg; foreach (var old_arg in tool.arguments[pos + removed:]) args += old_arg; tool.arguments = args; /* Update arguments' indexes */ for (var i = pos + added; i < arguments.get_n_items (); i++) { var arg = arguments.get_item (i) as Argument; arg.index = (int) i; } /* Show list if there are any tools */ argument_list.visible = arguments.get_n_items () != 0; }); } [GtkCallback] async void edit_script () { tool.open (window); } } } ================================================ FILE: src/widgets/preferences/pages/CustomToolPage.vala ================================================ // Copyright 2021 Gleb Smirnov // SPDX-FileCopyrightText: 2021 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later namespace TextPieces { /** * Custom tool settings page */ [GtkTemplate (ui = "/com/github/liferooter/textpieces/ui/CustomToolPage.ui")] class CustomToolPage : Gtk.Box { [GtkChild] unowned ToolSettings tool_settings; /** * Tool which this settings is of */ public Tool tool { get; construct; } /** * Parent preferences window */ public Preferences prefs { get; construct; } public CustomToolPage (Preferences prefs, Tool tool) { Object ( tool: tool, prefs: prefs ); } /** * Unmap signal override used to * save changes when page is closed * * It's called when page is no * longer visible */ public override void unmap () { base.unmap (); var custom_tools = Application.tools.custom_tools; /* Find tool index */ uint pos; custom_tools.find (tool, out pos); /* Trigger tools update to apply changes */ custom_tools.items_changed (pos, 1, 1); /* Save changes */ Application.tools.commit.begin ((obj, res) => { try { Application.tools.commit.end (res); } catch (Error e) { critical ("Can't commit tools: %s", e.message); prefs.add_toast (new Adw.Toast ( ("Error occured: %s").printf (e.message) )); } }); } construct { tool_settings.set_tool (tool); tool_settings.window = prefs; } /** * Go back and close the page */ [GtkCallback] void go_back () { prefs.close_subpage (); } /** * Delete tool */ [GtkCallback] void delete_tool () { Application.tools.delete_tool (tool); } } } ================================================ FILE: src/widgets/preferences/pages/NewToolPage.vala ================================================ // Copyright 2021 Gleb Smirnov // SPDX-FileCopyrightText: 2021 Gleb Smirnov // // SPDX-License-Identifier: GPL-3.0-or-later namespace TextPieces { /** * Custom tool create page */ [GtkTemplate (ui = "/com/github/liferooter/textpieces/ui/NewToolPage.ui")] class NewToolPage : Gtk.Box { [GtkChild] unowned ToolSettings tool_settings; /** * Parent preferences window */ public Preferences prefs { get; construct; } /** * Pre-created tool * * This tool isn't presented * in tools model and added to * the model on create */ private Tool new_tool; public NewToolPage (Preferences prefs) { Object ( prefs: prefs ); } construct { /* Initialize tool */ new_tool = new Tool () { name = "", description = "", arguments = {}, script = "", is_system = false }; /* Setup tool settings widget */ tool_settings.set_tool (new_tool); tool_settings.window = prefs; } [GtkCallback] void go_back () { prefs.close_subpage (); } /** * Create tool * * This method is called when * create button is clicked. * It saves tool and exits. */ [GtkCallback] async void create () { /* Create tool directory if not exists */ var dir = File.new_for_path (Tool.CUSTOM_TOOLS_DIR.get ()); if (!dir.query_exists ()) { try { yield Utils.ensure_directory_exists (dir); } catch (Error e) { error ("Can't create directory for tool scripts: %s", e.message); } } /* Generate script filename with salt */ var filename = Tool.generate_filename (new_tool.name); /* Get script file */ var script_file = File.new_build_filename ( Tool.CUSTOM_TOOLS_DIR.get (), filename ); /* Get template file */ var template_file = File.new_build_filename ( Config.SCRIPTDIR, "template" ); /* Copy template to file */ try { yield template_file.copy_async ( script_file, OVERWRITE ); } catch (Error err) { error ("Can't copy script template to file: %s".printf (err.message)); } /* Change file permissions */ FileUtils.chmod (script_file.get_path (), 0750); // rwxr-x--- /* Set script filename to the tool */ new_tool.script = filename; /* Add new tool to tools */ Application.tools.add_tool ( new_tool ); try { yield Application.tools.commit (); } catch (Error e) { critical ("Can't commit tools: %s", e.message); prefs.add_toast (new Adw.Toast ( _("Error occured: %s").printf (e.message) )); } /* Open tool script in your favorite editor */ new_tool.open (prefs); } } } ================================================ FILE: subprojects/blueprint-compiler.wrap ================================================ # SPDX-FileCopyrightText: 2022 Gleb Smirnov # # SPDX-License-Identifier: CC0-1.0 [wrap-git] directory = blueprint-compiler url = https://gitlab.gnome.org/jwestman/blueprint-compiler.git revision = main depth = 1 [provide] program_names = blueprint-compiler ================================================ FILE: subprojects/gtksourceview.wrap ================================================ # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: CC0-1.0 [wrap-file] directory = gtksourceview-5.3.2 source_url = https://download.gnome.org/sources/gtksourceview/5.3/gtksourceview-5.3.2.tar.xz source_filename = gtksourceview-5.3.2.tar.xz source_hash = af7736e2ee3cdbc1013090e8caf35fb89d65cf41c9c399cac5d8992d955ded30 [provide] dependency_names = gtksourceview-5 ================================================ FILE: subprojects/json-glib.wrap ================================================ # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: CC0-1.0 [wrap-file] directory = json-glib-1.6.6 source_url = https://download.gnome.org/sources/json-glib/1.6/json-glib-1.6.6.tar.xz source_filename = json-glib-1.6.6.tar.xz source_hash = 96ec98be7a91f6dde33636720e3da2ff6ecbb90e76ccaa49497f31a6855a490e ================================================ FILE: subprojects/libadwaita.wrap ================================================ # SPDX-FileCopyrightText: 2021 Gleb Smirnov # # SPDX-License-Identifier: CC0-1.0 [wrap-file] directory = libadwaita-1.0.1 source_url = https://download.gnome.org/sources/libadwaita/1.0/libadwaita-1.0.1.tar.xz source_filename = libadwaita-1.0.1.tar.xz source_hash = bb49cf5a09d2e8bc144946c2c3272aecd611667fd027f3808b95d7101ed473d6 [provide] dependency_names = libadwaita-1 ================================================ FILE: textpieces.doap ================================================ Text Pieces Transform text without using random websites Calculate hashes, encode text, decode text, remove trailing spaces and lines, count lines, symbols and words, format JSON, escape and unescape strings, convert JSON to YAML and vice versa, filter lines, replace substrings and regular expressions and so on. You can use predefined tools to transform text and even create your own. Vala Gleb Smirnov liferooter liferooter liferooter