[
  {
    "path": ".github/workflows/ci.yml",
    "content": "# SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\non:\n  push:\n    branches: [main]\n\nname: CI\n\njobs:\n\n  update-po:\n    name: Update translations\n    runs-on: ubuntu-latest\n    container:\n      image: bilelmoussaoui/flatpak-github-actions:gnome-42\n      options: --privileged\n    steps:\n\n    - name: Checkout source code\n      uses: actions/checkout@v3\n      with:\n        fetch-depth: 0\n\n    - name: Configure Git\n      run: |\n        git --version\n        git config --global --add safe.directory $(pwd)\n        git config user.name 'github-actions'\n        git config user.email 'github-actions@github.com'\n\n    - name: Add Flathub repository\n      run: flatpak remote-add --user --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo\n\n    - name: Install dependencies\n      run: >\n        xvfb-run -a -s \"-screen 0 1024x768x24\"\n        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\n\n\n    - name: Update translations\n      run: >\n        xvfb-run -a -s \"-screen 0 1024x768x24\"\n        flatpak-builder --run flatpak_app build-aux/flatpak/com.github.liferooter.textpieces.yaml\n        ci/update-po.sh\n\n    - name: Push changes to GitHub\n      run: git push\n"
  },
  {
    "path": ".github/workflows/test.yml",
    "content": "# SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\non:\n  push:\n    branches: [main]\n  pull_request:\n\nname: Test\n\njobs:\n\n  test:\n    runs-on: ubuntu-latest\n    steps:\n    - uses: actions/checkout@v2\n    - name: REUSE Compliance Check\n      uses: fsfe/reuse-action@v1.1.1\n\n  flatpak:\n    name: Flatpak\n    runs-on: ubuntu-20.04\n    container:\n      image: bilelmoussaoui/flatpak-github-actions:gnome-nightly\n      options: --privileged\n    steps:\n    - uses: actions/checkout@v2\n    - uses: bilelmoussaoui/flatpak-github-actions/flatpak-builder@v4\n      with:\n        bundle: textpieces.flatpak\n        manifest-path: build-aux/flatpak/com.github.liferooter.textpieces.yaml\n        run-tests: true\n        cache-key: flatpak-builder-${{ github.sha }}\n"
  },
  {
    "path": ".gitignore",
    "content": "# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: CC0-1.0\n\n**/*~\n**/#*#\n.flatpak-builder\n_build\n.vscode\n/subprojects/*/\n/.flatpak\n/.fenv\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "<!--\nSPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0\n-->\n\n# Contributing tips\n\nThis file describes some non-obvious points of this project which needs to be known by contributors.\n\n## Technology stack\n\nText 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.\n\nText 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.\n\nIt also uses [shebang](https://wikipedia.org/en/Shebang_(Unix)) for script executing, so it can work only on UNIX-like OSes.\n\n## Build system\n\nText 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`.\n\n## Conventions\n\nText Pieces uses some conventions:\n- [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for commit messages\n- [Elementary Codestyle](https://docs.elementary.io/develop/writing-apps/code-style) for Vala code\n- [PEP 8](https://peps.python.org/pep-0008/) for Python-based tools\n- [REUSE](https://reuse.software/) licensing conventions\n- [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\n- [Semantic Versioning](https://semver.org/)\n- [GNOME HIG](https://developer.gnome.org/hig) for user interface\n\n## Post-commit checks\n\nThere are some things you should do after making changes in project code:\n\n### Update translation templates\n\nIf you've changed any translatable strings in application, you should update translations:\n\n```bash\n./po/update-po.sh\n```\n\nIf you've changed translatable strings in `data/tools.json`, please add your changes manually to `po/tools.pot`.\n\n### Licensing checks\n\nAs 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:\n\n```bash\npip install --user reuse\nreuse addheader --copyright 'YOUR NAME <YOUR@EMAIL.ADDRESS>' --year CURRENT_YEAR LICENSE_NAME FILE_NAME\n```\n\n## Copyright policy\n\nText 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.\n\n## Creating translation\n\nText 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.\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>\n Everyone is permitted to copy and distribute verbatim copies\n of this license document, but changing it is not allowed.\n\n                            Preamble\n\n  The GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  You can apply it to\nyour programs, too.\n\n  When we speak of free software, we are referring to freedom, not\nprice.  Our General Public Licenses are designed to make sure that you\nhave the freedom to distribute copies of free software (and charge for\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions are imposed on you (whether by court order, agreement or\notherwise) that contradict the conditions of this License, they do not\nexcuse you from the conditions of this License.  If you cannot convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU General Public License from time to time.  Such new versions will\nbe similar in spirit to the present version, but may differ in detail to\naddress new problems or concerns.\n\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\n\n                     END OF TERMS AND CONDITIONS\n\n            How to Apply These Terms to Your New Programs\n\n  If you develop a new program, and you want it to be of the greatest\npossible use to the public, the best way to achieve this is to make it\nfree software which everyone can redistribute and change under these terms.\n\n  To do so, attach the following notices to the program.  It is safest\nto attach them to the start of each source file to most effectively\nstate the exclusion of warranty; and each file should have at least\nthe \"copyright\" line and a pointer to where the full notice is found.\n\n    Text Pieces -- Swiss knife of text transformations\n    Copyright (C) 2021, Gleb Smirnov\n\n    This program is free software: you can redistribute it and/or modify\n    it under the terms of the GNU General Public License as published by\n    the Free Software Foundation, either version 3 of the License, or\n    (at your option) any later version.\n\n    This program is distributed in the hope that it will be useful,\n    but WITHOUT ANY WARRANTY; without even the implied warranty of\n    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n    GNU General Public License for more details.\n\n    You should have received a copy of the GNU General Public License\n    along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    Text Pieces,  Copyright (C) 2021,  Gleb Smirnov\n    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n    This is free software, and you are welcome to redistribute it\n    under certain conditions; type `show c' for details.\n\nThe hypothetical commands `show w' and `show c' should show the appropriate\nparts of the General Public License.  Of course, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<http://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<http://www.gnu.org/philosophy/why-not-lgpl.html>.\n\n"
  },
  {
    "path": "LICENSES/CC0-1.0.txt",
    "content": "Creative Commons Legal Code\n\nCC0 1.0 Universal\n\n    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE\n    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN\n    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS\n    INFORMATION ON AN \"AS-IS\" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES\n    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS\n    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM\n    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED\n    HEREUNDER.\n\nStatement of Purpose\n\nThe laws of most jurisdictions throughout the world automatically confer\nexclusive Copyright and Related Rights (defined below) upon the creator\nand subsequent owner(s) (each and all, an \"owner\") of an original work of\nauthorship and/or a database (each, a \"Work\").\n\nCertain owners wish to permanently relinquish those rights to a Work for\nthe purpose of contributing to a commons of creative, cultural and\nscientific works (\"Commons\") that the public can reliably and without fear\nof later claims of infringement build upon, modify, incorporate in other\nworks, reuse and redistribute as freely as possible in any form whatsoever\nand for any purposes, including without limitation commercial purposes.\nThese owners may contribute to the Commons to promote the ideal of a free\nculture and the further production of creative, cultural and scientific\nworks, or to gain reputation or greater distribution for their Work in\npart through the use and efforts of others.\n\nFor these and/or other purposes and motivations, and without any\nexpectation of additional consideration or compensation, the person\nassociating CC0 with a Work (the \"Affirmer\"), to the extent that he or she\nis an owner of Copyright and Related Rights in the Work, voluntarily\nelects to apply CC0 to the Work and publicly distribute the Work under its\nterms, with knowledge of his or her Copyright and Related Rights in the\nWork and the meaning and intended legal effect of CC0 on those rights.\n\n1. Copyright and Related Rights. A Work made available under CC0 may be\nprotected by copyright and related or neighboring rights (\"Copyright and\nRelated Rights\"). Copyright and Related Rights include, but are not\nlimited to, the following:\n\n  i. the right to reproduce, adapt, distribute, perform, display,\n     communicate, and translate a Work;\n ii. moral rights retained by the original author(s) and/or performer(s);\niii. publicity and privacy rights pertaining to a person's image or\n     likeness depicted in a Work;\n iv. rights protecting against unfair competition in regards to a Work,\n     subject to the limitations in paragraph 4(a), below;\n  v. rights protecting the extraction, dissemination, use and reuse of data\n     in a Work;\n vi. database rights (such as those arising under Directive 96/9/EC of the\n     European Parliament and of the Council of 11 March 1996 on the legal\n     protection of databases, and under any national implementation\n     thereof, including any amended or successor version of such\n     directive); and\nvii. other similar, equivalent or corresponding rights throughout the\n     world based on applicable law or treaty, and any national\n     implementations thereof.\n\n2. Waiver. To the greatest extent permitted by, but not in contravention\nof, applicable law, Affirmer hereby overtly, fully, permanently,\nirrevocably and unconditionally waives, abandons, and surrenders all of\nAffirmer's Copyright and Related Rights and associated claims and causes\nof action, whether now known or unknown (including existing as well as\nfuture claims and causes of action), in the Work (i) in all territories\nworldwide, (ii) for the maximum duration provided by applicable law or\ntreaty (including future time extensions), (iii) in any current or future\nmedium and for any number of copies, and (iv) for any purpose whatsoever,\nincluding without limitation commercial, advertising or promotional\npurposes (the \"Waiver\"). Affirmer makes the Waiver for the benefit of each\nmember of the public at large and to the detriment of Affirmer's heirs and\nsuccessors, fully intending that such Waiver shall not be subject to\nrevocation, rescission, cancellation, termination, or any other legal or\nequitable action to disrupt the quiet enjoyment of the Work by the public\nas contemplated by Affirmer's express Statement of Purpose.\n\n3. Public License Fallback. Should any part of the Waiver for any reason\nbe judged legally invalid or ineffective under applicable law, then the\nWaiver shall be preserved to the maximum extent permitted taking into\naccount Affirmer's express Statement of Purpose. In addition, to the\nextent the Waiver is so judged Affirmer hereby grants to each affected\nperson a royalty-free, non transferable, non sublicensable, non exclusive,\nirrevocable and unconditional license to exercise Affirmer's Copyright and\nRelated Rights in the Work (i) in all territories worldwide, (ii) for the\nmaximum duration provided by applicable law or treaty (including future\ntime extensions), (iii) in any current or future medium and for any number\nof copies, and (iv) for any purpose whatsoever, including without\nlimitation commercial, advertising or promotional purposes (the\n\"License\"). The License shall be deemed effective as of the date CC0 was\napplied by Affirmer to the Work. Should any part of the License for any\nreason be judged legally invalid or ineffective under applicable law, such\npartial invalidity or ineffectiveness shall not invalidate the remainder\nof the License, and in such case Affirmer hereby affirms that he or she\nwill not (i) exercise any of his or her remaining Copyright and Related\nRights in the Work or (ii) assert any associated claims and causes of\naction with respect to the Work, in either case contrary to Affirmer's\nexpress Statement of Purpose.\n\n4. Limitations and Disclaimers.\n\n a. No trademark or patent rights held by Affirmer are waived, abandoned,\n    surrendered, licensed or otherwise affected by this document.\n b. Affirmer offers the Work as-is and makes no representations or\n    warranties of any kind concerning the Work, express, implied,\n    statutory or otherwise, including without limitation warranties of\n    title, merchantability, fitness for a particular purpose, non\n    infringement, or the absence of latent or other defects, accuracy, or\n    the present or absence of errors, whether or not discoverable, all to\n    the greatest extent permissible under applicable law.\n c. Affirmer disclaims responsibility for clearing rights of other persons\n    that may apply to the Work or any use thereof, including without\n    limitation any person's Copyright and Related Rights in the Work.\n    Further, Affirmer disclaims responsibility for obtaining any necessary\n    consents, permissions or other rights required for any use of the\n    Work.\n d. Affirmer understands and acknowledges that Creative Commons is not a\n    party to this document and has no duty or obligation with respect to\n    this CC0 or use of the Work.\n"
  },
  {
    "path": "LICENSES/GPL-3.0-or-later.txt",
    "content": "GNU GENERAL PUBLIC LICENSE\nVersion 3, 29 June 2007\n\nCopyright © 2007 Free Software Foundation, Inc. <http://fsf.org/>\n\nEveryone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed.\n\nPreamble\n\nThe GNU General Public License is a free, copyleft license for software and other kinds of works.\n\nThe 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.\n\nWhen 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.\n\nTo 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.\n\nFor 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.\n\nDevelopers 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.\n\nFor 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.\n\nSome 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.\n\nFinally, 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.\n\nThe precise terms and conditions for copying, distribution and modification follow.\n\nTERMS AND CONDITIONS\n\n0. Definitions.\n\n“This License” refers to version 3 of the GNU General Public License.\n\n“Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks.\n\n“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.\n\nTo “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.\n\nA “covered work” means either the unmodified Program or a work based on the Program.\n\nTo “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.\n\nTo “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.\n\nAn 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.\n\n1. Source Code.\nThe “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.\n\nA “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.\n\nThe “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.\n\nThe “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.\n\nThe Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source.\n\nThe Corresponding Source for a work in source code form is that same work.\n\n2. Basic Permissions.\nAll 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.\n\nYou 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.\n\nConveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary.\n\n3. Protecting Users' Legal Rights From Anti-Circumvention Law.\nNo 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.\n\nWhen 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.\n\n4. Conveying Verbatim Copies.\nYou 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.\n\nYou may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee.\n\n5. Conveying Modified Source Versions.\nYou 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:\n\n     a) The work must carry prominent notices stating that you modified it, and giving a relevant date.\n\n     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”.\n\n     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.\n\n     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.\n\nA 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.\n\n6. Conveying Non-Source Forms.\nYou 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:\n\n     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.\n\n     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.\n\n     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.\n\n     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.\n\n     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.\n\nA 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.\n\nA “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.\n\n“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.\n\nIf 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).\n\nThe 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.\n\nCorresponding 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.\n\n7. Additional Terms.\n“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.\n\nWhen 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.\n\nNotwithstanding 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:\n\n     a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or\n\n     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\n\n     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\n\n     d) Limiting the use for publicity purposes of names of licensors or authors of the material; or\n\n     e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or\n\n     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.\n\nAll 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.\n\nIf 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.\n\nAdditional 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.\n\n8. Termination.\nYou 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).\n\nHowever, 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.\n\nMoreover, 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.\n\nTermination 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.\n\n9. Acceptance Not Required for Having Copies.\nYou 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.\n\n10. Automatic Licensing of Downstream Recipients.\nEach 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.\n\nAn “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.\n\nYou 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.\n\n11. Patents.\nA “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”.\n\nA 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.\n\nEach 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.\n\nIn 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.\n\nIf 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.\n\nIf, 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.\n\nA 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.\n\nNothing 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.\n\n12. No Surrender of Others' Freedom.\nIf 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.\n\n13. Use with the GNU Affero General Public License.\nNotwithstanding 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.\n\n14. Revised Versions of this License.\nThe 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.\n\nEach 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.\n\nIf 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.\n\nLater 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.\n\n15. Disclaimer of Warranty.\nTHERE 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.\n\n16. Limitation of Liability.\nIN 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.\n\n17. Interpretation of Sections 15 and 16.\nIf 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.\n\nEND OF TERMS AND CONDITIONS\n\nHow to Apply These Terms to Your New Programs\n\nIf 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.\n\nTo 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.\n\n     <one line to give the program's name and a brief idea of what it does.>\n     Copyright (C) <year>  <name of author>\n\n     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.\n\n     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.\n\n     You should have received a copy of the GNU General Public License along with this program.  If not, see <http://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\nIf the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode:\n\n     <program>  Copyright (C) <year>  <name of author>\n     This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.\n     This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details.\n\nThe 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”.\n\nYou 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 <http://www.gnu.org/licenses/>.\n\nThe 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 <http://www.gnu.org/philosophy/why-not-lgpl.html>.\n"
  },
  {
    "path": "README.md",
    "content": "<!--\nSPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0\n-->\n\n<h1 align=\"center\">\n<img\n    src=\"data/icons/com.github.liferooter.textpieces.svg\" alt=\"Text Pieces\"\n    width=\"128\"\n    height=\"128\"/><br/>\nText Pieces\n</h1>\n\n<p align=\"center\"><strong>\nSwiss knife of text processing\n</strong></p>\n\n<br/>\n\n<p align=\"center\">\n<a href=\"https://stopthemingmy.app\">\n    <img width=\"200\" src=\"https://stopthemingmy.app/badge.svg\"/>\n</a>\n</p>\n\n<p align=\"center\">\n<a href=\"https://flathub.org/apps/details/com.github.liferooter.textpieces\">\n    <img width=\"200\" src=\"https://flathub.org/assets/badges/flathub-badge-en.png\" alt=\"Download on Flathub\">\n</a>\n</p>\n\n<p align=\"center\">\n    <img alt=\"Screenshot\" src=\"screenshots/screenshot.png\"/>\n</p>\n\n# State of the project\n\nThe project was rewritten from scratch and moved to [a new repository](https://gitlab.com/liferooter/textpieces). This repository will never receive updates.\n\n# What is it?\n\nText 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.\n\n# Features\n- Base64 encoding and decoding\n- SHA-1, SHA-2 and MD5 checksums\n- Prettify and minify JSON\n- Convert JSON to YAML and vice versa\n- Count lines, symbols and words\n- Escape and unescape string, URL and HTML\n- Remove leading and trailing whitespaces\n- Sort and reverse sort lines\n- Reverse lines and whole text\n- Extendable with third-party scripts and custom tools\n\n# Installation\n\n## From Flathub\n> **Recommended**\n\n<a href=\"https://flathub.org/apps/details/com.github.liferooter.textpieces\">Click here</a> to install app from Flathub.\n\n## Build from source\n### Via GNOME Builder\nText Pieces can be built with GNOME Builder >= 3.38. Clone this repo and click run button.\n### Via Flatpak\nText Pieces has a Flatpak manifest to enable it to be <a href=\"https://docs.flatpak.org/en/latest/building-introduction.html\">built with Flatpak</a>.\n### Via Meson\nText Pieces can be built directly via Meson:\n```bash\ngit clone https://github.com/liferooter/textpieces\ncd textpieces\nmeson _build\ncd _build\nmeson compile\n```\nNext, it can be installed by `meson install`.\n\n# Dependencies\nIf you use GNOME Builder or Flatpak, dependencies will be installed automatically. If you use pure Meson, dependencies will be:\n- vala >= 0.52\n- gtk >= 4.2\n- gtksourceview >= 5.2\n- gio >= 2.50\n- gee >= 0.8\n- json-glib >= 1.6\n- libadwaita >= 1.0\n- python >= 3.8\n- pyyaml >= 5.4\n"
  },
  {
    "path": "build-aux/flatpak/com.github.liferooter.textpieces.yaml",
    "content": "# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\napp-id: com.github.liferooter.textpieces\ncommand: textpieces\n\nruntime: org.gnome.Platform\nruntime-version: '43'\nsdk: org.gnome.Sdk\nsdk-extensions:\n- org.freedesktop.Sdk.Extension.vala\n\nfinish-args:\n  - --share=ipc\n  - --socket=fallback-x11\n  - --socket=wayland\n  - --device=dri\n  - --filesystem=~/.local/share/textpieces:create\n  - --talk-name=org.freedesktop.Flatpak\ncleanup:\n  - /include\n  - /lib/pkgconfig\n  - /man\n  - /share/doc\n  - /share/pkgconfig\n  - /share/vala\n  - '*blueprint*'\n  - '*.la'\n  - '*.a'\n\nmodules:\n\n- name: textpieces\n  builddir: true\n  buildsystem: meson\n  sources:\n  - type: dir\n    path: ../..\n\n  modules:\n  - name: blueprint\n    buildsystem: meson\n    sources:\n    - type: git\n      url: https://gitlab.gnome.org/jwestman/blueprint-compiler\n      branch: main\n\n  - name: python3-pyyaml\n    buildsystem: simple\n    build-commands:\n    - pip3 install --verbose --exists-action=i --no-index --find-links=\"file://${PWD}\"\n      --prefix=${FLATPAK_DEST} \"pyyaml\" --no-build-isolation\n    sources:\n    - type: file\n      url: https://files.pythonhosted.org/packages/36/2b/61d51a2c4f25ef062ae3f74576b01638bebad5e045f747ff12643df63844/PyYAML-6.0.tar.gz\n      sha256: 68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2\n\n  - name: libportal\n    buildsystem: meson\n    config-opts:\n    - -Dbackend-gtk3=disabled\n    - -Dbackend-gtk4=enabled\n    - -Dbackend-qt5=disabled\n    - -Ddocs=false\n    - -Dtests=false\n    sources:\n    - type: git\n      url: https://github.com/flatpak/libportal\n      tag: main\n"
  },
  {
    "path": "build-aux/meson/postinstall.py",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom os import environ, path\nfrom subprocess import call\n\nprefix = environ.get('MESON_INSTALL_PREFIX', '/usr/local')\ndatadir = path.join(prefix, 'share')\ndestdir = environ.get('DESTDIR', '')\n\n# Package managers set this so we don't need to run\nif not destdir:\n    print('Updating icon cache...')\n    call(['gtk-update-icon-cache', '-qtf',\n          path.join(datadir, 'icons', 'hicolor')])\n\n    print('Updating desktop database...')\n    call(['update-desktop-database', '-q',\n          path.join(datadir, 'applications')])\n\n    print('Compiling GSettings schemas...')\n    call(['glib-compile-schemas',\n          path.join(datadir, 'glib-2.0', 'schemas')])\n"
  },
  {
    "path": "ci/generate-tools-po.sh",
    "content": "#!/usr/bin/env bash\n\n# SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\ncd \"$(git rev-parse --show-toplevel)\" || exit 1\n\njq '.tools[] | .name, .description, .args[]?' data/tools.json |\n    sort |\n    uniq |\n    sed 's/\"\\(.*\\)\"/\\n#: data\\/tools.json\\nmsgctxt \"tools\"\\nmsgid \"\\1\"\\nmsgstr \"\"/g' > po/tools.pot\n"
  },
  {
    "path": "ci/update-po.sh",
    "content": "#!/usr/bin/env bash\n\n# SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\ncd \"$(git rev-parse --show-toplevel)\" || exit 1\n\necho \"Changes in files related to translations:\"\n\nchanges=\"$(git diff @~ @ -- ci/update-po.sh po/POTFILES $(grep '^\\w' po/POTFILES) || exit 1)\"\necho \"$changes\"\n\nif [[ -z \"$changes\" ]]; then\n    echo \"No changes\"\n    exit\nfi\n\nci/generate-tools-po.sh &&\nmeson _build &&\nninja -C _build textpieces-pot &&\ncat po/tools.pot >>po/textpieces.pot &&\n\nfor file in $(ls po/*.po); do\n    msgmerge --backup=off -U $file po/textpieces.pot -D .\ndone || exit 1\n\ngit add po &&\n    git commit -m 'chore(po): update translations' ||\n    exit 1\n"
  },
  {
    "path": "data/com.github.liferooter.textpieces.appdata.xml.in",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!--\nSPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0\n-->\n<component type=\"desktop-application\">\n  <id>com.github.liferooter.textpieces.desktop</id>\n  <name>Text Pieces</name>\n  <summary>Transform text without using random websites</summary>\n  <metadata_license>CC0-1.0</metadata_license>\n  <project_license>GPL-3.0-or-later</project_license>\n  <description>\n    <p>\n      Powerful scratchpad with ability to perform a lot of text transformations, such as:\n    </p>\n    <ul>\n      <li>Calculate hashes</li>\n      <li>Encode text</li>\n      <li>Decode text</li>\n      <li>Remove trailing spaces and lines</li>\n      <li>Count lines, symbols and words</li>\n      <li>Format JSON and XML</li>\n      <li>Escape and unescape strings</li>\n      <li>Convert JSON to YAML and vice versa</li>\n      <li>Filter lines</li>\n      <li>Replace substrings and regular expressions</li>\n      <li>And so on.</li>\n    </ul>\n    <p>The application is extendable with your own script-based actions.</p>\n  </description>\n  <launchable type=\"desktop-id\">@app-id@.desktop</launchable>\n  <provides>\n    <binary>textpieces</binary>\n  </provides>\n  <developer id=\"com.github.liferooter\">\n    <name>Gleb Smirnov</name>\n  </developer>\n  <url type=\"homepage\">https://github.com/liferooter/textpieces</url>\n  <url type=\"bugtracker\">https://github.com/liferooter/textpieces/issues</url>\n  <supports>\n    <control>pointing</control>\n    <control>touch</control>\n  </supports>\n  <recommends>\n    <control>keyboard</control>\n  </recommends>\n  <requires>\n    <display_length compare=\"ge\">360</display_length>\n    <internet>offline-only</internet>\n  </requires>\n  <screenshots>\n    <screenshot type=\"default\">\n      <image>https://raw.githubusercontent.com/liferooter/textpieces/6b766fbb9956175db454c3266ab3d8c2bfa57b07/screenshots/slide1.png</image>\n    </screenshot>\n    <screenshot>\n      <image>https://raw.githubusercontent.com/liferooter/textpieces/6b766fbb9956175db454c3266ab3d8c2bfa57b07/screenshots/slide2.png</image>\n    </screenshot>\n    <screenshot>\n      <image>https://raw.githubusercontent.com/liferooter/textpieces/6b766fbb9956175db454c3266ab3d8c2bfa57b07/screenshots/slide3.png</image>\n    </screenshot>\n    <screenshot>\n      <image>https://raw.githubusercontent.com/liferooter/textpieces/6b766fbb9956175db454c3266ab3d8c2bfa57b07/screenshots/slide4.png</image>\n    </screenshot>\n    <screenshot>\n      <image>https://raw.githubusercontent.com/liferooter/textpieces/6b766fbb9956175db454c3266ab3d8c2bfa57b07/screenshots/slide5.png</image>\n    </screenshot>\n  </screenshots>\n  <content_rating type=\"oars-1.1\" />\n  <releases>\n    <release version=\"3.4.1\" date=\"2022-12-21\">\n      <description>\n        <p>Bug fixes:</p>\n        <ul>\n          <li>Script files can be correctly opened using an editor of your choice</li>\n          <li>Newly created script names are generated from tool name</li>\n        </ul>\n      </description>\n    </release>\n    <release version=\"3.4.0\" date=\"2022-12-17\">\n      <description>\n        <p>New features:</p>\n        <ul>\n          <li>Text Pieces now can open files</li>\n        </ul>\n        <p>Bug fixes:</p>\n        <ul>\n          <li>Some translation issues are fixed</li>\n          <li>Some tools are fixed and optimized</li>\n          <li>Shortcuts window is updated and fixed</li>\n        </ul>\n      </description>\n    </release>\n    <release version=\"3.3.1\" date=\"2022-11-01\">\n      <description>\n        <p>Bug fixes:</p>\n        <ul>\n          <li>Tool arguments now work as expected</li>\n          <li>Translate some untranslated strings</li>\n        </ul>\n      </description>\n    </release>\n    <release version=\"3.3.0\" date=\"2022-10-24\">\n      <description>\n        <p>New features and improvements:</p>\n        <ul>\n          <li>Add tool for extracting RSS URLs from OPML files</li>\n          <li>Use more verbose error reporting for Base64 decode tool</li>\n          <li>Focus editor on startup</li>\n          <li>Save selected tool between sessions</li>\n          <li>Minor UI improvements</li>\n        </ul>\n      </description>\n    </release>\n    <release version=\"3.2.0\" date=\"2022-09-21\">\n      <description>\n        <p>New features and improvements:</p>\n        <ul>\n          <li>Add hotkey to open search in replace mode</li>\n        </ul>\n      </description>\n    </release>\n    <release version=\"3.1.1\" date=\"2022-09-21\">\n      <description>\n        <p>New features and improvements:</p>\n        <ul>\n          <li>Add some keywords to the application for system search</li>\n        </ul>\n      </description>\n    </release>\n    <release version=\"3.1.0\" date=\"2022-08-04\">\n      <description>\n        <p>New features and improvement:</p>\n        <ul>\n          <li>Add style schemes</li>\n          <li>Add search and replace feature to editor</li>\n          <li>Fix some bugs</li>\n        </ul>\n      </description>\n    </release>\n    <release version=\"3.0.2\" date=\"2021-12-16\">\n      <description>Bug fix</description>\n    </release>\n    <release version=\"3.0.1\" date=\"2021-12-16\">\n      <description>\n        <p>New features and improvements:</p>\n        <ul>\n          <li>Refresh UI</li>\n          <li>Fix a lot of bugs</li>\n          <li>Add powerful settings</li>\n          <li>Add arguments support for custom tools</li>\n          <li>Add translation support</li>\n          <li>Improve search</li>\n        </ul>\n      </description>\n    </release>\n    <release version=\"2.3.2\" date=\"2021-09-30\">\n      Fix theme switcher\n</release>\n    <release version=\"2.3.1\" date=\"2021-09-30\">\n      Add theme switcher for system which don't support color schemes\n</release>\n    <release version=\"2.2.1\" date=\"2021-09-29\">\n      <description>\n        <p>New features and improvements:</p>\n        <ul>\n          <li>Minor UI changes</li>\n          <li>Add XML formatter</li>\n        </ul>\n      </description>\n    </release>\n    <release version=\"2.1.2\" date=\"2021-09-23\">\n      <description>Some bugfixes and improvements</description>\n    </release>\n    <release version=\"2.1.1\" date=\"2021-09-08\">\n      <description>Bring back tools with arguments</description>\n    </release>\n    <release version=\"2.0.1\" date=\"2021-08-18\">\n      <description>\n        <p>New features and improvements:</p>\n        <ul>\n          <li>Redesign the application</li>\n          <li>Add custom tools support</li>\n          <li>Improve search</li>\n          <li>Add ability to text load from file and save to file</li>\n        </ul>\n      </description>\n    </release>\n    <release version=\"1.3.0\" date=\"2021-04-25\">\n      <description>\n        <p>New features and improvements:</p>\n        <ul>\n          <li>Exit by Ctrl+Q</li>\n          <li>Monospace argument entry</li>\n          <li>Update new runtime</li>\n        </ul>\n      </description>\n    </release>\n    <release version=\"1.2.0\" date=\"2021-04-03\">\n      <description>\n        <p>New features and improvements:</p>\n        <ul>\n          <li>Sort tools</li>\n          <li>Filter tools</li>\n          <li>Minify C-like code</li>\n        </ul>\n      </description>\n    </release>\n    <release version=\"1.1.0\" date=\"2021-04-01\">\n      <description>\n        <p>New features and improvements:</p>\n        <ul>\n          <li>Add more tools</li>\n          <li>Make tools popover more powerful</li>\n          <li>Redesign icon</li>\n          <li>General improvements</li>\n        </ul>\n      </description>\n    </release>\n    <release version=\"1.0.2\" date=\"2021-03-13\">\n      <description>Add tools for replacement and removing</description>\n    </release>\n    <release version=\"1.0.0\" date=\"2021-03-10\">\n      <description>Application release</description>\n    </release>\n  </releases>\n</component>\n"
  },
  {
    "path": "data/com.github.liferooter.textpieces.desktop.in",
    "content": "# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\n[Desktop Entry]\nName=Text Pieces\nIcon=com.github.liferooter.textpieces\nExec=textpieces %U\nMimeType=text/plain;\nTerminal=false\nType=Application\nCategories=Utility;TextTools;GTK;GNOME;\nStartupNotify=true\nActions=new-window;\n\n# Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!\nKeywords=GNOME;GTK;Text;Base64;JSON;YAML;Count;SHA1;SHA256;SHA384;SHA512;MD5;Checksum;Escape;Encode;HTML;URL;Prettify;Filter;Reverse;Trim;\n\n[Desktop Action new-window]\nName=New Window\nExec=textpieces\n"
  },
  {
    "path": "data/com.github.liferooter.textpieces.gschema.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\nSPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: GPL-3.0-or-later\n-->\n\n<schemalist gettext-domain=\"textpieces\">\n  <schema id=\"com.github.liferooter.textpieces\" path=\"/com/github/liferooter/textpieces/\">\n    <key name=\"style-scheme\" type=\"s\">\n      <default>'Adwaita-dark'</default>\n      <summary>Style scheme</summary>\n      <description>Which style scheme to use for application interface and editor.</description>\n    </key>\n    <key name=\"font-name\" type=\"s\">\n      <default>'Monospace'</default>\n      <summary>Font name</summary>\n      <description>Name of font used in editor</description>\n    </key>\n    <key name=\"wrap-lines\" type=\"b\">\n      <default>true</default>\n      <summary>Wrap lines</summary>\n      <description>Whether to wrap long lines</description>\n    </key>\n    <key name=\"tabs-to-spaces\" type=\"b\">\n      <default>false</default>\n      <summary>Tabs to spaces</summary>\n      <description>Whether to insert spaces instead of tab</description>\n    </key>\n    <key name=\"spaces-in-tab\" type=\"i\">\n      <default>4</default>\n      <summary>Spaces in tab</summary>\n      <description>Tab width in spaces</description>\n    </key>\n    <key name=\"window-width\" type=\"i\">\n      <default>800</default>\n      <summary>Window width</summary>\n      <description>Saved width of application window</description>\n    </key>\n    <key name=\"window-height\" type=\"i\">\n      <default>500</default>\n      <summary>Window height</summary>\n      <description>Saved height of the window</description>\n    </key>\n    <key name=\"is-maximized\" type=\"b\">\n      <default>false</default>\n      <summary>Is maximized</summary>\n      <description>Whether the application window is maximized</description>\n    </key>\n    <key name=\"selected-tool\" type=\"s\">\n      <default>''</default>\n      <summary>Selected tool</summary>\n      <description>Used to restore tool choice on restart. Its value must be a name of tool's script.</description>\n    </key>\n  </schema>\n</schemalist>\n"
  },
  {
    "path": "data/icons/com.github.liferooter.textpieces-symbolic.svg.license",
    "content": "SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0"
  },
  {
    "path": "data/icons/com.github.liferooter.textpieces.Source.svg.license",
    "content": "SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0"
  },
  {
    "path": "data/icons/com.github.liferooter.textpieces.svg.license",
    "content": "SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0"
  },
  {
    "path": "data/meson.build",
    "content": "# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\ndesktop_file = i18n.merge_file(\n  input: 'com.github.liferooter.textpieces.desktop.in',\n  output: 'com.github.liferooter.textpieces.desktop',\n  type: 'desktop',\n  po_dir: '../po',\n  install: true,\n  install_dir: join_paths(get_option('datadir'), 'applications')\n)\n\ndesktop_utils = find_program('desktop-file-validate', required: false)\nif desktop_utils.found()\n  test('Validate desktop file', desktop_utils,\n    args: [desktop_file]\n  )\nendif\n\nappstream_file = i18n.merge_file(\n  input: 'com.github.liferooter.textpieces.appdata.xml.in',\n  output: 'com.github.liferooter.textpieces.appdata.xml',\n  po_dir: '../po',\n  install: true,\n  install_dir: join_paths(get_option('datadir'), 'appdata')\n)\n\nappstreamcli = find_program('appstreamcli', required: false)\nif appstreamcli.found()\n  test('Validate appstream file', appstreamcli,\n    args: ['validate', appstream_file]\n  )\nendif\n\ncompile_schemas = find_program('glib-compile-schemas', required: false)\nif compile_schemas.found()\n  test('Validate schema file', compile_schemas,\n    args: ['--strict', '--dry-run', meson.current_source_dir()]\n  )\nendif\n\ninstall_data('com.github.liferooter.textpieces.gschema.xml',\n  install_dir: join_paths(get_option('datadir'), 'glib-2.0/schemas')\n)\n\n\n# Icons\ninstall_data(\n  join_paths('icons', 'com.github.liferooter.textpieces.svg'),\n  install_dir: join_paths (get_option('datadir'), 'icons', 'hicolor', 'scalable', 'apps')\n)\ninstall_data(\n  join_paths('icons', 'com.github.liferooter.textpieces-symbolic.svg'),\n  install_dir: join_paths (get_option('datadir'), 'icons', 'hicolor', 'symbolic', 'apps')\n)\n\n# Tools' JSON\ninstall_data (\n  'tools.json',\n  install_dir: pkgdata_dir\n)\n"
  },
  {
    "path": "data/tools.json",
    "content": "{\n  \"is_system\": true,\n  \"tools\": [\n    {\n      \"name\": \"SHA1 Checksum\",\n      \"description\": \"Compute SHA1 hash sum\",\n      \"icon\": \"fingerprint-symbolic\",\n      \"script\": \"sha1\"\n    },\n    {\n      \"name\": \"SHA256 Checksum\",\n      \"description\": \"Compute SHA256 hash sum\",\n      \"icon\": \"fingerprint-symbolic\",\n      \"script\": \"sha256\"\n    },\n    {\n      \"name\": \"SHA384 Checksum\",\n      \"description\": \"Compute SHA384 hash sum\",\n      \"icon\": \"fingerprint-symbolic\",\n      \"script\": \"sha384\"\n    },\n    {\n      \"name\": \"SHA512 Checksum\",\n      \"description\": \"Compute SHA512 hash sum\",\n      \"icon\": \"fingerprint-symbolic\",\n      \"script\": \"sha512\"\n    },\n    {\n      \"name\": \"MD5 Checksum\",\n      \"description\": \"Compute MD5 hash sum\",\n      \"icon\": \"fingerprint-symbolic\",\n      \"script\": \"md5\"\n    },\n    {\n      \"name\": \"Base64 Encode\",\n      \"description\": \"Encode text to Base64\",\n      \"icon\": \"lock-symbolic\",\n      \"script\": \"base64-encode\"\n    },\n    {\n      \"name\": \"Base64 Decode\",\n      \"description\": \"Decode text from Base64\",\n      \"icon\": \"unlock-symbolic\",\n      \"script\": \"base64-decode\"\n    },\n    {\n      \"name\": \"Remove Trailing\",\n      \"description\": \"Remove trailing whitespaces and newlines\",\n      \"icon\": \"edit-cut-symbolic\",\n      \"script\": \"remove-trailing\"\n    },\n    {\n      \"name\": \"Trim Lines\",\n      \"description\": \"Remove leading and trailing whitespaces and newlines\",\n      \"icon\": \"edit-cut-symbolic\",\n      \"script\": \"trim\"\n    },\n    {\n      \"name\": \"OPML to RSS links\",\n      \"description\": \"Extract RSS links from OPML outline\",\n      \"icon\": \"rss-symbolic\",\n      \"script\": \"rss-from-opml\"\n    },\n    {\n      \"name\": \"Count Symbols\",\n      \"description\": \"Count how many symbols are in text\",\n      \"icon\": \"view-list-ordered-symbolic\",\n      \"script\": \"count-symbols\"\n    },\n    {\n      \"name\": \"Count Lines\",\n      \"description\": \"Count how many lines are in text\",\n      \"icon\": \"view-list-ordered-symbolic\",\n      \"script\": \"count-lines\"\n    },\n    {\n      \"name\": \"Count Words\",\n      \"description\": \"Count how many words are in text\",\n      \"icon\": \"view-list-ordered-symbolic\",\n      \"script\": \"count-words\"\n    },\n    {\n      \"name\": \"Prettify JSON\",\n      \"description\": \"Parse JSON and prettify it\",\n      \"icon\": \"format-justify-left-symbolic\",\n      \"script\": \"format-json\"\n    },\n    {\n      \"name\": \"Minify JSON\",\n      \"description\": \"Parse JSON and minify it\",\n      \"icon\": \"format-justify-fill-symbolic\",\n      \"script\": \"minify-json\"\n    },\n    {\n      \"name\": \"Escape String\",\n      \"description\": \"Replace special characters with escape sequences\",\n      \"icon\": \"security-high-symbolic\",\n      \"script\": \"escape-string\"\n    },\n    {\n      \"name\": \"Escape HTML\",\n      \"description\": \"Replace special characters with HTML-safe sequences\",\n      \"icon\": \"security-high-symbolic\",\n      \"script\": \"escape-html\"\n    },\n    {\n      \"name\": \"Escape URL\",\n      \"description\": \"Replace special characters with URL-safe sequences\",\n      \"icon\": \"security-high-symbolic\",\n      \"script\": \"escape-url\"\n    },\n    {\n      \"name\": \"Unescape String\",\n      \"description\": \"Replace escape sequences with characters\",\n      \"icon\": \"security-low-symbolic\",\n      \"script\": \"unescape-string\"\n    },\n    {\n      \"name\": \"Unescape HTML\",\n      \"description\": \"Replace HTML sequences with characters\",\n      \"icon\": \"security-low-symbolic\",\n      \"script\": \"unescape-html\"\n    },\n    {\n      \"name\": \"Unescape URL\",\n      \"description\": \"Replace URL sequences with characters\",\n      \"icon\": \"security-low-symbolic\",\n      \"script\": \"unescape-url\"\n    },\n    {\n      \"name\": \"Convert JSON to YAML\",\n      \"description\": \"Parse JSON and convert it to YAML document\",\n      \"icon\": \"network-transmit-symbolic\",\n      \"script\": \"json-to-yaml\"\n    },\n    {\n      \"name\": \"Convert YAML to JSON\",\n      \"description\": \"Parse YAML document and convert it to JSON\",\n      \"icon\": \"network-transmit-symbolic\",\n      \"script\": \"yaml-to-json\"\n    },\n    {\n      \"name\": \"Reverse Lines\",\n      \"description\": \"Reoder lines in reverse order\",\n      \"icon\": \"object-flip-vertical-symbolic\",\n      \"script\": \"reverse-lines\"\n    },\n    {\n      \"name\": \"Reverse Text\",\n      \"description\": \"Reorder characters in reverse order\",\n      \"icon\": \"object-flip-horizontal-symbolic\",\n      \"script\": \"reverse-string\"\n    },\n    {\n      \"name\": \"Sort Lines\",\n      \"description\": \"Sort lines in text in alphabetical order\",\n      \"icon\": \"view-sort-ascending-symbolic\",\n      \"script\": \"sort-lines\"\n    },\n    {\n      \"name\": \"Reverse Sort Lines\",\n      \"description\": \"Sort lines in text in reverse alphabetical order\",\n      \"icon\": \"view-sort-descending-symbolic\",\n      \"script\": \"sort-lines-reverse\"\n    },\n    {\n      \"name\": \"Filter Lines by Regex\",\n      \"description\": \"Remove all lines which don't match given regular expression\",\n      \"icon\": \"funnel-symbolic\",\n      \"script\": \"filter-regex\",\n      \"args\": [\n        \"Regex\"\n      ]\n    },\n    {\n      \"name\": \"Filter Lines by Substring\",\n      \"description\": \"Remove all lines which don't contain given substring\",\n      \"icon\": \"funnel-symbolic\",\n      \"script\": \"filter-substring\",\n      \"args\": [\n        \"Substring\"\n      ]\n    },\n    {\n      \"name\": \"Reverse Filter Lines by Regex\",\n      \"description\": \"Remove all lines which match given regular expression\",\n      \"icon\": \"funnel-symbolic\",\n      \"script\": \"reverse-filter-regex\",\n      \"args\": [\n        \"Regex\"\n      ]\n    },\n    {\n      \"name\": \"Reverse Filter Lines by Substring\",\n      \"description\": \"Remove all lines which contain given substring\",\n      \"icon\": \"funnel-symbolic\",\n      \"script\": \"reverse-filter-substring\",\n      \"args\": [\n        \"Substring\"\n      ]\n    },\n    {\n      \"name\": \"Remove Regex\",\n      \"description\": \"Remove all matches of regular expression\",\n      \"icon\": \"edit-cut-symbolic\",\n      \"script\": \"remove-regex\",\n      \"args\": [\n        \"Regex\"\n      ]\n    },\n    {\n      \"name\": \"Remove Substring\",\n      \"description\": \"Remove all occurrences of given substring in text\",\n      \"icon\": \"edit-cut-symbolic\",\n      \"script\": \"remove-substring\",\n      \"args\": [\n        \"Substring\"\n      ]\n    },\n    {\n      \"name\": \"Replace Substring\",\n      \"description\": \"Replace all occurrences of given substring in text with other string\",\n      \"icon\": \"edit-find-replace-symbolic\",\n      \"script\": \"replace-substring\",\n      \"args\": [\n        \"Find\",\n        \"Replace With\"\n      ]\n    },\n    {\n      \"name\": \"Replace Regex\",\n      \"description\": \"Replace all given regex matches in text with given string\",\n      \"icon\": \"edit-find-replace-symbolic\",\n      \"script\": \"replace-regex\",\n      \"args\": [\n        \"Find (regex)\",\n        \"Replace With\"\n      ]\n    },\n    {\n      \"name\": \"Prettify XML\",\n      \"description\": \"Parse XML and prettify it\",\n      \"icon\": \"format-justify-left-symbolic\",\n      \"script\": \"format-xml\"\n    }\n  ]\n}\n"
  },
  {
    "path": "data/tools.json.license",
    "content": "SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\nSPDX-FileCopyrightText: 2022 Adrián Bíro\n\nSPDX-License-Identifier: GPL-3.0-or-later\n"
  },
  {
    "path": "icon.png.license",
    "content": "SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0"
  },
  {
    "path": "meson.build",
    "content": "# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nproject('textpieces', 'vala',\n          version: '3.4.1',\n    meson_version: '>= 0.59.0',\n  default_options: [ 'warning_level=2',\n                   ],\n)\n\n# Get package data directory (usually /usr/share/textpieces)\npkgdata_dir = get_option('prefix') / get_option('datadir') / meson.project_name()\n\ntextpieces_sources = []\n\ncc = meson.get_compiler('c')\nvalac = meson.get_compiler('vala')\n\nvapi_dir = join_paths (meson.project_source_root(), 'src', 'vapi')\n\nadd_project_arguments(\n  [\n    '--target-glib=2.50',\n    '--enable-experimental',\n    '--vapidir', vapi_dir,\n  ],\n  language: 'vala'\n)\n\n# Dependencies {\n\nlibadwaita_dep = dependency('libadwaita-1', version: '>= 1.0.0-alpha.2', required: false)\nif not libadwaita_dep.found()\n  libadwaita = subproject(\n    'libadwaita',\n    default_options: [\n      'examples=false',\n      'package_subdir=@0@'.format(meson.project_name()),\n      'tests=false'\n    ]\n  )\n  libadwaita_dep = declare_dependency(\n    dependencies: [\n      libadwaita.get_variable('libadwaita_dep'),\n      libadwaita.get_variable('libadwaita_vapi')\n    ]\n  )\nendif\n\ngtksourceview_dep = dependency('gtksourceview-5', version: '>= 5.3.0', required: false)\nif not gtksourceview_dep.found()\n  gtksourceview = subproject(\n    'gtksourceview',\n    default_options: [\n      'package_subdir=@0@'.format(meson.project_name()),\n      'tests=false'\n    ]\n  )\n  gtksourceview_dep = declare_dependency(\n    dependencies: [\n      gtksourceview.get_variable('gtksource_dep'),\n      gtksourceview.get_variable('gtksource_vapi')\n    ]\n  )\nendif\n\njson_glib_dep = dependency('json-glib-1.0', version: '>= 1.6.0', required: false)\nif not json_glib_dep.found()\n  json_glib = subproject(\n    'json-glib',\n    default_options: [\n      'package_subdir=@0@'.format(meson.project_name()),\n      'tests=false',\n      'gtk_doc=disabled',\n      'introspection=enabled'\n    ]\n  )\n  json_glib_dep = declare_dependency(\n    dependencies: [\n      json_glib.get_variable('json_glib_dep'),\n      json_glib.get_variable('json_glib_gir')\n    ]\n  )\nendif\n\nlibm_dep = cc.find_library('m')\n\ntextpieces_deps = [\n  dependency('gio-2.0', version: '>= 2.50'),\n  dependency('gio-unix-2.0', version: '>= 2.50'),\n  dependency('gtk4', version: '>= 4.2'),\n  dependency('gee-0.8', version: '>= 0.8'),\n  dependency('libportal', version: '>= 0.6'),\n  dependency('libportal-gtk4', version: '>= 0.6'),\n  json_glib_dep,\n  gtksourceview_dep,\n  libadwaita_dep,\n  libm_dep,\n\n  valac.find_library('config', dirs: vapi_dir),\n]\n\n# }\n\ni18n = import('i18n')\ngnome = import('gnome')\n\nsubdir('data')\nsubdir('resources')\nsubdir('src')\nsubdir('po')\n\ninstall_subdir('scripts', install_dir: pkgdata_dir)\n\ngnome.post_install(\n  glib_compile_schemas: true,\n  gtk_update_icon_cache: true,\n  update_desktop_database: true\n)\n"
  },
  {
    "path": "po/LINGUAS",
    "content": "# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: CC0-1.0\n\nes\nnl\npt_BR\n"
  },
  {
    "path": "po/POTFILES",
    "content": "# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: CC0-1.0\n\ndata/com.github.liferooter.textpieces.desktop.in\ndata/com.github.liferooter.textpieces.appdata.xml.in\ndata/com.github.liferooter.textpieces.gschema.xml\n\nsrc/widgets/Editor.vala\nsrc/widgets/Window.vala\nsrc/widgets/SearchEntry.vala\nsrc/widgets/preferences/Preferences.vala\nsrc/widgets/preferences/ToolSettings.vala\nsrc/widgets/preferences/pages/NewToolPage.vala\n\nresources/ui/CustomToolPage.blp\nresources/ui/Search.blp\nresources/ui/Editor.blp\nresources/ui/NewToolPage.blp\nresources/ui/Preferences.blp\nresources/ui/SearchBar.blp\nresources/ui/SearchEntry.blp\nresources/ui/ShortcutsWindow.blp\nresources/ui/ToolSettings.blp\nresources/ui/Window.blp\n"
  },
  {
    "path": "po/es.po",
    "content": "# SPDX-FileCopyrightText: 2022 Óscar Fernández Díaz <oscfdezdz@tuta.io>\n#\n# SPDX-License-Identifier: CC0-1.0\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: textpieces\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-12-21 08:25+0000\\n\"\n\"PO-Revision-Date: 2022-01-24 14:00+0100\\n\"\n\"Last-Translator: Automatically generated\\n\"\n\"Language-Team: none\\n\"\n\"Language: es\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Generator: Poedit 3.0.1\\n\"\n\n#: data/com.github.liferooter.textpieces.desktop.in:7\n#: data/com.github.liferooter.textpieces.appdata.xml.in:9\n#: src/widgets/Window.vala:241 resources/ui/Window.blp:9\nmsgid \"Text Pieces\"\nmsgstr \"Text Pieces\"\n\n#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!\n#: data/com.github.liferooter.textpieces.desktop.in:18\nmsgid \"\"\n\"GNOME;GTK;Text;Base64;JSON;YAML;Count;SHA1;SHA256;SHA384;SHA512;MD5;Checksum;\"\n\"Escape;Encode;HTML;URL;Prettify;Filter;Reverse;Trim;\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.desktop.in:21\nmsgid \"New Window\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:10\n#: src/widgets/Window.vala:242\nmsgid \"Transform text without using random websites\"\nmsgstr \"Transformar el texto sin usar sitios web aleatorios\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:14\nmsgid \"Do a lot of text transformations, such as:\"\nmsgstr \"Hacer un montón de transformaciones de texto, tales como:\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:16\nmsgid \"Calculate hashes\"\nmsgstr \"Calcular los hashes\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:17\nmsgid \"Encode text\"\nmsgstr \"Codificar texto\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:18\nmsgid \"Decode text\"\nmsgstr \"Decodificar texto\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:19\nmsgid \"Remove trailing spaces and lines\"\nmsgstr \"Eliminar los espacios y líneas finales\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:20\nmsgid \"Count lines, symbols and words\"\nmsgstr \"Contar líneas, símbolos y palabras\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:21\nmsgid \"Format JSON and XML\"\nmsgstr \"Formato JSON y XML\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:22\nmsgid \"Escape and unescape strings\"\nmsgstr \"Cadenas de escape y desescape\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:23\nmsgid \"Convert JSON to YAML and vice versa\"\nmsgstr \"Convertir JSON a YAML y viceversa\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:24\nmsgid \"Filter lines\"\nmsgstr \"Filtrar líneas\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:25\nmsgid \"Replace substrings and regular expressions\"\nmsgstr \"Reemplazar subcadenas y expresiones regulares\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:27\nmsgid \"...and so on.\"\nmsgstr \"...y más.\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:32\n#: src/widgets/Window.vala:252\nmsgid \"Gleb Smirnov\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:64\n#: data/com.github.liferooter.textpieces.appdata.xml.in:77\n#: data/com.github.liferooter.textpieces.appdata.xml.in:87\nmsgid \"Bug fixes:\"\nmsgstr \"Corrección de errores\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:66\nmsgid \"Script files can be correctly opened using an editor of your choice\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:67\nmsgid \"Newly created script names are generated from tool name\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:73\n#, fuzzy\nmsgid \"New features:\"\nmsgstr \"Algunas correcciones de errores y mejoras\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:75\nmsgid \"Text Pieces now can open files\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:79\nmsgid \"Some translation issues are fixed\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:80\nmsgid \"Some tools are fixed and optimized\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:81\nmsgid \"Shortcuts window is updated and fixed\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:89\nmsgid \"Tool arguments now work as expected\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:90\nmsgid \"Translate some untranslated strings\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:96\n#: data/com.github.liferooter.textpieces.appdata.xml.in:108\n#: data/com.github.liferooter.textpieces.appdata.xml.in:116\n#: data/com.github.liferooter.textpieces.appdata.xml.in:137\n#: data/com.github.liferooter.textpieces.appdata.xml.in:156\n#: data/com.github.liferooter.textpieces.appdata.xml.in:171\n#: data/com.github.liferooter.textpieces.appdata.xml.in:182\n#: data/com.github.liferooter.textpieces.appdata.xml.in:192\n#: data/com.github.liferooter.textpieces.appdata.xml.in:202\nmsgid \"New features and improvements:\"\nmsgstr \"Algunas correcciones de errores y mejoras\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:98\nmsgid \"Add tool for extracting RSS URLs from OPML files\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:99\nmsgid \"Use more verbose error reporting for Base64 decode tool\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:100\nmsgid \"Focus editor on startup\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:101\nmsgid \"Save selected tool between sessions\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:102\nmsgid \"Minor UI improvements\"\nmsgstr \"Mejoras generales\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:110\nmsgid \"Add hotkey to open search in replace mode\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:118\nmsgid \"Add some keywords to the application for system search\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:124\nmsgid \"New features and improvement:\"\nmsgstr \"Algunas correcciones de errores y mejoras\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:126\nmsgid \"Add style schemes\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:127\nmsgid \"Add search and replace feature to editor\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:128\nmsgid \"Fix some bugs\"\nmsgstr \"Corrección de un montón de errores\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:133\nmsgid \"Bug fix\"\nmsgstr \"Corrección de errores\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:139\nmsgid \"Refresh UI\"\nmsgstr \"Actualización de la interfaz de usuario\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:140\nmsgid \"Fix a lot of bugs\"\nmsgstr \"Corrección de un montón de errores\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:141\nmsgid \"Add powerful settings\"\nmsgstr \"Añade ajustes potentes\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:142\nmsgid \"Add arguments support for custom tools\"\nmsgstr \"Añade soporte de argumentos para herramientas personalizadas\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:143\nmsgid \"Add translation support\"\nmsgstr \"Añade soporte de traducción\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:144\n#: data/com.github.liferooter.textpieces.appdata.xml.in:175\nmsgid \"Improve search\"\nmsgstr \"Mejora de la búsqueda\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:158\nmsgid \"Minor UI changes\"\nmsgstr \"Cambios menores en la interfaz de usuario\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:159\nmsgid \"Add XML formatter\"\nmsgstr \"Añade un formateador XML\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:164\nmsgid \"Some bugfixes and improvements\"\nmsgstr \"Algunas correcciones de errores y mejoras\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:167\nmsgid \"Bring back tools with arguments\"\nmsgstr \"Vuelven las herramientas con argumentos\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:173\nmsgid \"Redesign the application\"\nmsgstr \"Rediseño de la aplicación\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:174\nmsgid \"Add custom tools support\"\nmsgstr \"Añade soporte para herramientas personalizadas\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:176\nmsgid \"Add ability to text load from file and save to file\"\nmsgstr \"\"\n\"Añade la posibilidad de cargar texto desde un archivo y guardarlo en un \"\n\"archivo\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:184\nmsgid \"Exit by Ctrl+Q\"\nmsgstr \"Salir con Ctrl+Q\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:185\nmsgid \"Monospace argument entry\"\nmsgstr \"Entrada de argumentos en monoespaciado\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:186\nmsgid \"Update new runtime\"\nmsgstr \"Actualizar el nuevo tiempo de ejecución\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:194\nmsgid \"Sort tools\"\nmsgstr \"Herramientas de clasificación\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:195\nmsgid \"Filter tools\"\nmsgstr \"Herramientas de filtrado\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:196\nmsgid \"Minify C-like code\"\nmsgstr \"Reducir el código de tipo C\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:204\nmsgid \"Add more tools\"\nmsgstr \"Añade más herramientas\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:205\nmsgid \"Make tools popover more powerful\"\nmsgstr \"Potencia el menú emergente de herramientas\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:206\nmsgid \"Redesign icon\"\nmsgstr \"Rediseño del icono\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:207\nmsgid \"General improvements\"\nmsgstr \"Mejoras generales\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:212\nmsgid \"Add tools for replacement and removing\"\nmsgstr \"Añade herramientas para la sustitución y el desmontaje\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:215\nmsgid \"Application release\"\nmsgstr \"Publicación de la aplicación\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:13\nmsgid \"Style scheme\"\nmsgstr \"Esquema de colores\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:14\nmsgid \"Which style scheme to use for application interface and editor.\"\nmsgstr \"Qué combinación de colores usar para la interfaz de la aplicación.\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:18\nmsgid \"Font name\"\nmsgstr \"Nombre de la fuente\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:19\nmsgid \"Name of font used in editor\"\nmsgstr \"Nombre de la fuente usada en el editor\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:23\nmsgid \"Wrap lines\"\nmsgstr \"Ajuste de líneas\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:24\nmsgid \"Whether to wrap long lines\"\nmsgstr \"Si las líneas largas continúan en la siguiente línea\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:28\nmsgid \"Tabs to spaces\"\nmsgstr \"Tabulaciones a espacios\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:29\nmsgid \"Whether to insert spaces instead of tab\"\nmsgstr \"Si se insertan espacios en lugar del tabulador\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:33\nmsgid \"Spaces in tab\"\nmsgstr \"Espacios en tabulador\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:34\nmsgid \"Tab width in spaces\"\nmsgstr \"Ancho de tabulado en espacios\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:38\nmsgid \"Window width\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:39\nmsgid \"Saved width of application window\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:43\nmsgid \"Window height\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:44\nmsgid \"Saved height of the window\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:48\nmsgid \"Is maximized\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:49\nmsgid \"Whether the application window is maximized\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:53\nmsgid \"Selected tool\"\nmsgstr \"Seleccionar herramienta\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:54\nmsgid \"\"\n\"Used to restore tool choice on restart. Its value must be a name of tool's \"\n\"script.\"\nmsgstr \"\"\n\n#: src/widgets/Editor.vala:145\nmsgid \"Text is copied\"\nmsgstr \"Texto copiado al portapapeles\"\n\n#. *\n#. * Text used instead of tool name\n#. * when where is no tool selected\n#.\n#: src/widgets/Window.vala:21 resources/ui/ShortcutsWindow.blp:50\nmsgid \"Select tool\"\nmsgstr \"Seleccionar herramienta\"\n\n#: src/widgets/Window.vala:161\n#, c-format\nmsgid \"Can't load from file: %s\"\nmsgstr \"No se puede cargar desde el archivo: %s\"\n\n#: src/widgets/Window.vala:249\nmsgid \"Tobias Bernard https://tobiasbernard.com\"\nmsgstr \"\"\n\n#: src/widgets/Window.vala:250\nmsgid \"Gleb Smirnov <glebsmirnov0708@gmail.com>\"\nmsgstr \"\"\n\n#: src/widgets/Window.vala:253\nmsgid \"translator-credits\"\nmsgstr \"Óscar Fernández Díaz <oscfdezdz@tuta.io>\"\n\n#: src/widgets/Window.vala:280 resources/ui/Window.blp:78\nmsgid \"Save to File\"\nmsgstr \"Guardar en archivo\"\n\n#: src/widgets/Window.vala:305\n#, c-format\nmsgid \"Can't save to file: %s\"\nmsgstr \"No se puede guardar en el archivo: %s\"\n\n#: src/widgets/Window.vala:321 resources/ui/Window.blp:85\nmsgid \"Load from File\"\nmsgstr \"Cargar desde archivo\"\n\n#: src/widgets/SearchEntry.vala:93\n#, c-format\nmsgid \"%u of %d\"\nmsgstr \"\"\n\n#. Create dialog\n#: src/widgets/preferences/Preferences.vala:128\nmsgid \"Select font\"\nmsgstr \"Seleccionar tipografía\"\n\n#: src/widgets/preferences/ToolSettings.vala:187\n#, c-format\nmsgid \"Argument %d\"\nmsgstr \"Argumento %d\"\n\n#: src/widgets/preferences/pages/NewToolPage.vala:113\n#, c-format\nmsgid \"Error occured: %s\"\nmsgstr \"Se ha producido un error: %s\"\n\n#: resources/ui/CustomToolPage.blp:27\nmsgid \"Edit Tool\"\nmsgstr \"Editor\"\n\n#: resources/ui/CustomToolPage.blp:38\nmsgid \"D_elete\"\nmsgstr \"_Eliminar\"\n\n#: resources/ui/Search.blp:41\nmsgid \"No Tools Found\"\nmsgstr \"No se han encontrado herramientas\"\n\n#: resources/ui/Search.blp:53\nmsgid \"_Add Custom Tool\"\nmsgstr \"Añadir herramienta personalizada\"\n\n#: resources/ui/NewToolPage.blp:27\nmsgid \"New Tool\"\nmsgstr \"Herramientas\"\n\n#: resources/ui/NewToolPage.blp:42\nmsgid \"_Create\"\nmsgstr \"_Crear\"\n\n#: resources/ui/Preferences.blp:13\nmsgid \"_General\"\nmsgstr \"_General\"\n\n#: resources/ui/Preferences.blp:19\nmsgid \"Editor\"\nmsgstr \"Editor\"\n\n#: resources/ui/Preferences.blp:22\nmsgid \"Editor _Font\"\nmsgstr \"_Tipografía del editor\"\n\n#: resources/ui/Preferences.blp:40\nmsgid \"_Wrap Lines\"\nmsgstr \"Ajustar _líneas\"\n\n#: resources/ui/Preferences.blp:51\nmsgid \"Tabs to _Spaces\"\nmsgstr \"Tabulaciones a _espacios\"\n\n#: resources/ui/Preferences.blp:62\nmsgid \"_Tab Width in Spaces\"\nmsgstr \"Ancho del _tabulado en espacios\"\n\n#: resources/ui/Preferences.blp:82\nmsgid \"Appearance\"\nmsgstr \"\"\n\n#: resources/ui/Preferences.blp:94\nmsgid \"T_ools\"\nmsgstr \"_Herramientas\"\n\n#: resources/ui/Preferences.blp:100\nmsgid \"Custom tools\"\nmsgstr \"Herramientas personalizadas\"\n\n#: resources/ui/Preferences.blp:112\nmsgid \"_Add custom tool\"\nmsgstr \"Añadir herramienta personalizada\"\n\n#: resources/ui/Preferences.blp:128\nmsgid \"No Custom Tools\"\nmsgstr \"Herramientas personalizadas\"\n\n#: resources/ui/Preferences.blp:129\nmsgid \"You can create one\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:40 resources/ui/ShortcutsWindow.blp:102\nmsgid \"Move to previous match\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:46 resources/ui/ShortcutsWindow.blp:107\nmsgid \"Move to next match\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:58\nmsgid \"Find and replace\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:68\nmsgid \"Toggle search options\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:84\nmsgid \"Close search\"\nmsgstr \"Mejora de la búsqueda\"\n\n#: resources/ui/SearchBar.blp:95 resources/ui/ShortcutsWindow.blp:97\nmsgid \"Replace\"\nmsgstr \"Reemplazar con\"\n\n#: resources/ui/SearchBar.blp:106\nmsgid \"_Replace\"\nmsgstr \"Reemplazar con\"\n\n#: resources/ui/SearchBar.blp:119\nmsgid \"Replace _All\"\nmsgstr \"Reemplazar con\"\n\n#: resources/ui/SearchBar.blp:138\nmsgid \"Re_gular expressions\"\nmsgstr \"Reemplazar subcadenas y expresiones regulares\"\n\n#: resources/ui/SearchBar.blp:144\nmsgid \"_Case sensitive\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:150\nmsgid \"Match whole _word only\"\nmsgstr \"\"\n\n#: resources/ui/SearchEntry.blp:15\nmsgid \"Find\"\nmsgstr \"Buscar\"\n\n#: resources/ui/ShortcutsWindow.blp:13\nmsgid \"General\"\nmsgstr \"General\"\n\n#: resources/ui/ShortcutsWindow.blp:16\nmsgid \"New window\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:21\nmsgid \"Keyboard shortcuts\"\nmsgstr \"Atajos del teclado\"\n\n#: resources/ui/ShortcutsWindow.blp:26\nmsgid \"Preferences\"\nmsgstr \"Preferencias\"\n\n#: resources/ui/ShortcutsWindow.blp:31\nmsgid \"Close window\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:36\nmsgid \"Quit\"\nmsgstr \"Salir\"\n\n#: resources/ui/ShortcutsWindow.blp:42\nmsgid \"Tools\"\nmsgstr \"Herramientas\"\n\n#: resources/ui/ShortcutsWindow.blp:45\nmsgid \"Apply\"\nmsgstr \"Aplicar\"\n\n#: resources/ui/ShortcutsWindow.blp:55\nmsgid \"Just to the tool options\"\nmsgstr \"Saltar a las opciones de la herramienta\"\n\n#: resources/ui/ShortcutsWindow.blp:61\nmsgid \"Undo and Redo\"\nmsgstr \"Deshacer y rehacer\"\n\n#: resources/ui/ShortcutsWindow.blp:64\nmsgid \"Undo\"\nmsgstr \"Deshacer\"\n\n#: resources/ui/ShortcutsWindow.blp:69\nmsgid \"Redo\"\nmsgstr \"Rehacer\"\n\n#: resources/ui/ShortcutsWindow.blp:75\nmsgid \"Files\"\nmsgstr \"Archivos\"\n\n#: resources/ui/ShortcutsWindow.blp:78\nmsgid \"Load from file\"\nmsgstr \"Cargar desde archivo\"\n\n#: resources/ui/ShortcutsWindow.blp:83\nmsgid \"Save to file\"\nmsgstr \"Guardar en archivo\"\n\n#: resources/ui/ShortcutsWindow.blp:89\nmsgid \"Search and Replace\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:92\nmsgid \"Search\"\nmsgstr \"\"\n\n#: resources/ui/ToolSettings.blp:31\nmsgid \"_Name\"\nmsgstr \"_Nombre\"\n\n#: resources/ui/ToolSettings.blp:42\nmsgid \"_Description\"\nmsgstr \"_Descripción\"\n\n#: resources/ui/ToolSettings.blp:52\nmsgid \"_Arguments\"\nmsgstr \"_Argumentos\"\n\n#: resources/ui/ToolSettings.blp:80\nmsgid \"Edit _Script\"\nmsgstr \"\"\n\n#: resources/ui/Window.blp:32\nmsgid \"_Apply\"\nmsgstr \"_Aplicar\"\n\n#: resources/ui/Window.blp:35\nmsgid \"Apply Selected Tool\"\nmsgstr \"Aplicar la herramienta seleccionada\"\n\n#: resources/ui/Window.blp:45\nmsgid \"Copy\"\nmsgstr \"Copiar\"\n\n#: resources/ui/Window.blp:71\nmsgid \"Main Menu\"\nmsgstr \"Menú principal\"\n\n#: resources/ui/Window.blp:123\nmsgid \"_New Window\"\nmsgstr \"\"\n\n#: resources/ui/Window.blp:131\nmsgid \"_Find/Replace…\"\nmsgstr \"\"\n\n#: resources/ui/Window.blp:139\nmsgid \"_Preferences\"\nmsgstr \"_Preferencias\"\n\n#: resources/ui/Window.blp:145\nmsgid \"_Keyboard Shortcuts\"\nmsgstr \"Atajos del _teclado\"\n\n#: resources/ui/Window.blp:151\nmsgid \"_About Text Pieces\"\nmsgstr \"_Acerca de Text Pieces\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Base64 Decode\"\nmsgstr \"Decodificación en Base64\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Base64 Encode\"\nmsgstr \"Codificación en Base64\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute MD5 hash sum\"\nmsgstr \"Calcular la suma del hash MD5\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA1 hash sum\"\nmsgstr \"Calcular la suma del hash SHA1\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA256 hash sum\"\nmsgstr \"Calcular la suma del hash SHA256\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA384 hash sum\"\nmsgstr \"Calcular la suma del hash SHA384\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA512 hash sum\"\nmsgstr \"Calcular la suma del hash SHA512\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Convert JSON to YAML\"\nmsgstr \"Convertir JSON en YAML\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Convert YAML to JSON\"\nmsgstr \"Convertir YAML en JSON\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count how many lines are in text\"\nmsgstr \"Contar cuántas líneas hay en el texto\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count how many symbols are in text\"\nmsgstr \"Contar cuántos símbolos hay en el texto\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count how many words are in text\"\nmsgstr \"Contar cuántas palabras hay en el texto\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count Lines\"\nmsgstr \"Contar líneas\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count Symbols\"\nmsgstr \"Contar símbolos\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count Words\"\nmsgstr \"Contar palabras\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Decode text from Base64\"\nmsgstr \"Decodificación de texto en Base64\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Encode text to Base64\"\nmsgstr \"Codificar texto en Base64\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Escape HTML\"\nmsgstr \"Escape HTML\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Escape String\"\nmsgstr \"Cadena de escape\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Escape URL\"\nmsgstr \"Escape URL\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Extract RSS links from OPML outline\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Filter Lines by Regex\"\nmsgstr \"Filtrar líneas por regex\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Filter Lines by Substring\"\nmsgstr \"Filtrar líneas por subcadena\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Find\"\nmsgstr \"Buscar\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Find (regex)\"\nmsgstr \"Buscar (regex)\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"MD5 Checksum\"\nmsgstr \"Suma de verificación MD5\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Minify JSON\"\nmsgstr \"Minimizar JSON\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"OPML to RSS links\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse JSON and convert it to YAML document\"\nmsgstr \"Analizar JSON y convertirlo en un documento YAML\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse JSON and minify it\"\nmsgstr \"Analizar JSON y minimizarlo\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse JSON and prettify it\"\nmsgstr \"Analizar JSON y mejorarlo\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse XML and prettify it\"\nmsgstr \"Analizar XML y mejorarlo\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse YAML document and convert it to JSON\"\nmsgstr \"Analizar el documento YAML y convertirlo en JSON\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Prettify JSON\"\nmsgstr \"Mejorar JSON\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Prettify XML\"\nmsgstr \"Mejorar XML\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Regex\"\nmsgstr \"Regex\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which contain given substring\"\nmsgstr \"Quitar todas las líneas que contengan la subcadena dada\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which don't contain given substring\"\nmsgstr \"Quitar todas las líneas que no contengan la subcadena dada\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which don't match given regular expression\"\nmsgstr \"Quitar todas las líneas que no coincidan con la expresión regular dada\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which match given regular expression\"\nmsgstr \"Quitar todas las líneas que coincidan con la expresión regular dada\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all matches of regular expression\"\nmsgstr \"Quitar todas las coincidencias de la expresión regular\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all occurrences of given substring in text\"\nmsgstr \"Quitar todas las ocurrencias de la subcadena dada en el texto\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove leading and trailing whitespaces and newlines\"\nmsgstr \"Quitar los espacios en blanco iniciales y finales y las líneas nuevas\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove Regex\"\nmsgstr \"Quitar regex\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove Substring\"\nmsgstr \"Quitar la subcadena\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove Trailing\"\nmsgstr \"Quitar los restos\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove trailing whitespaces and newlines\"\nmsgstr \"Quitar los espacios en blanco al final y los saltos de línea\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reoder lines in reverse order\"\nmsgstr \"Reordenar las líneas en orden inverso\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reorder characters in reverse order\"\nmsgstr \"Reordenar los caracteres en orden inverso\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace all given regex matches in text with given string\"\nmsgstr \"\"\n\"Reemplazar todas las coincidencias regex dadas en el texto con la cadena dada\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace all occurrences of given substring in text with other string\"\nmsgstr \"\"\n\"Reemplazar todas las ocurrencias de la subcadena dada en el texto con otra \"\n\"cadena\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace escape sequences with characters\"\nmsgstr \"Reemplazar las secuencias de escape por caracteres\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace HTML sequences with characters\"\nmsgstr \"Reemplazar las secuencias HTML por caracteres\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace Regex\"\nmsgstr \"Reemplazar regex\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace special characters with escape sequences\"\nmsgstr \"Reemplazar los caracteres especiales por secuencias de escape\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace special characters with HTML-safe sequences\"\nmsgstr \"Reemplazar los caracteres especiales por secuencias seguras para HTML\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace special characters with URL-safe sequences\"\nmsgstr \"\"\n\"Reemplazar los caracteres especiales por secuencias seguras para la URL\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace Substring\"\nmsgstr \"Reemplazar subcadena\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace URL sequences with characters\"\nmsgstr \"Reemplazar las secuencias de URL con caracteres\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace With\"\nmsgstr \"Reemplazar con\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Filter Lines by Regex\"\nmsgstr \"Invertir las líneas de filtrado por regex\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Filter Lines by Substring\"\nmsgstr \"Filtrar líneas por subcadena de forma inversa\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Lines\"\nmsgstr \"Invertir líneas\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Sort Lines\"\nmsgstr \"Ordenar las líneas de forma inversa\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Text\"\nmsgstr \"Invertir texto\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA1 Checksum\"\nmsgstr \"Suma de verificación SHA1\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA256 Checksum\"\nmsgstr \"Suma de verificación SHA256\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA384 Checksum\"\nmsgstr \"Suma de verificación SHA384\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA512 Checksum\"\nmsgstr \"Suma de verificación SHA512\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Sort Lines\"\nmsgstr \"Ordenar líneas\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Sort lines in text in alphabetical order\"\nmsgstr \"Ordenar las líneas del texto por orden alfabético\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Sort lines in text in reverse alphabetical order\"\nmsgstr \"Ordenar las líneas del texto en orden alfabético inverso\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Substring\"\nmsgstr \"Subcadena\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Trim Lines\"\nmsgstr \"Recortar líneas\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Unescape HTML\"\nmsgstr \"Desescape HTML\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Unescape String\"\nmsgstr \"Cadena de desescape\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Unescape URL\"\nmsgstr \"Desescape URL\"\n\n#~ msgid \"Color Scheme\"\n#~ msgstr \"Esquema de colores\"\n\n#~ msgid \"_Light\"\n#~ msgstr \"_Claro\"\n\n#~ msgid \"_Dark\"\n#~ msgstr \"_Oscuro\"\n\n#~ msgid \"S_ystem Default\"\n#~ msgstr \"_Predeterminado del sistema\"\n\n#~ msgid \"Copy and Paste\"\n#~ msgstr \"Copiar y pegar\"\n\n#~ msgid \"Paste\"\n#~ msgstr \"Pegar\"\n\n#~ msgid \"Copy all\"\n#~ msgstr \"Pegar todo\"\n"
  },
  {
    "path": "po/meson.build",
    "content": "# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\ni18n.gettext(meson.project_name(), preset: 'glib')\n"
  },
  {
    "path": "po/nl.po",
    "content": "# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: CC0-1.0\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: \\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-12-21 08:25+0000\\n\"\n\"PO-Revision-Date: 2022-03-24 14:33+0100\\n\"\n\"Last-Translator: Heimen Stoffels <vistausss@fastmail.com>\\n\"\n\"Language-Team: \\n\"\n\"Language: nl\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=(n != 1);\\n\"\n\"X-Generator: Poedit 3.0.1\\n\"\n\n#: data/com.github.liferooter.textpieces.desktop.in:7\n#: data/com.github.liferooter.textpieces.appdata.xml.in:9\n#: src/widgets/Window.vala:241 resources/ui/Window.blp:9\nmsgid \"Text Pieces\"\nmsgstr \"Tekstdelen\"\n\n#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!\n#: data/com.github.liferooter.textpieces.desktop.in:18\nmsgid \"\"\n\"GNOME;GTK;Text;Base64;JSON;YAML;Count;SHA1;SHA256;SHA384;SHA512;MD5;Checksum;\"\n\"Escape;Encode;HTML;URL;Prettify;Filter;Reverse;Trim;\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.desktop.in:21\nmsgid \"New Window\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:10\n#: src/widgets/Window.vala:242\nmsgid \"Transform text without using random websites\"\nmsgstr \"Zet tekst om zonder het gebruik van willekeurige websites\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:14\nmsgid \"Do a lot of text transformations, such as:\"\nmsgstr \"Voer diverse tekstbewerkingen uit, waaronder:\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:16\nmsgid \"Calculate hashes\"\nmsgstr \"Sommen berekenen\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:17\nmsgid \"Encode text\"\nmsgstr \"Tekst versleutelen\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:18\nmsgid \"Decode text\"\nmsgstr \"Tekst ontsleutelen\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:19\nmsgid \"Remove trailing spaces and lines\"\nmsgstr \"Witruimtes verwijderen\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:20\nmsgid \"Count lines, symbols and words\"\nmsgstr \"Regels, symbolen en woorden tellen\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:21\nmsgid \"Format JSON and XML\"\nmsgstr \"JSON en XML opmaken\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:22\nmsgid \"Escape and unescape strings\"\nmsgstr \"Tekenreeksen markeren als aanpassen/niet-aanpassen\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:23\nmsgid \"Convert JSON to YAML and vice versa\"\nmsgstr \"JSON naar YAML converteren en vice-versa\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:24\nmsgid \"Filter lines\"\nmsgstr \"Regels filteren\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:25\nmsgid \"Replace substrings and regular expressions\"\nmsgstr \"Bijzinnen en reguliere uitdrukkingen vervangen\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:27\nmsgid \"...and so on.\"\nmsgstr \"…en nog veel meer.\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:32\n#: src/widgets/Window.vala:252\nmsgid \"Gleb Smirnov\"\nmsgstr \"Gleb Smirnov\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:64\n#: data/com.github.liferooter.textpieces.appdata.xml.in:77\n#: data/com.github.liferooter.textpieces.appdata.xml.in:87\nmsgid \"Bug fixes:\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:66\nmsgid \"Script files can be correctly opened using an editor of your choice\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:67\nmsgid \"Newly created script names are generated from tool name\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:73\n#, fuzzy\nmsgid \"New features:\"\nmsgstr \"Enkele bugs opgelost en andere kleine verbeteringen aangebracht\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:75\nmsgid \"Text Pieces now can open files\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:79\nmsgid \"Some translation issues are fixed\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:80\nmsgid \"Some tools are fixed and optimized\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:81\nmsgid \"Shortcuts window is updated and fixed\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:89\nmsgid \"Tool arguments now work as expected\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:90\nmsgid \"Translate some untranslated strings\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:96\n#: data/com.github.liferooter.textpieces.appdata.xml.in:108\n#: data/com.github.liferooter.textpieces.appdata.xml.in:116\n#: data/com.github.liferooter.textpieces.appdata.xml.in:137\n#: data/com.github.liferooter.textpieces.appdata.xml.in:156\n#: data/com.github.liferooter.textpieces.appdata.xml.in:171\n#: data/com.github.liferooter.textpieces.appdata.xml.in:182\n#: data/com.github.liferooter.textpieces.appdata.xml.in:192\n#: data/com.github.liferooter.textpieces.appdata.xml.in:202\nmsgid \"New features and improvements:\"\nmsgstr \"Enkele bugs opgelost en andere kleine verbeteringen aangebracht\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:98\nmsgid \"Add tool for extracting RSS URLs from OPML files\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:99\nmsgid \"Use more verbose error reporting for Base64 decode tool\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:100\nmsgid \"Focus editor on startup\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:101\nmsgid \"Save selected tool between sessions\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:102\nmsgid \"Minor UI improvements\"\nmsgstr \"Algemene verbeteringen\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:110\nmsgid \"Add hotkey to open search in replace mode\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:118\nmsgid \"Add some keywords to the application for system search\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:124\nmsgid \"New features and improvement:\"\nmsgstr \"Enkele bugs opgelost en andere kleine verbeteringen aangebracht\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:126\nmsgid \"Add style schemes\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:127\nmsgid \"Add search and replace feature to editor\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:128\nmsgid \"Fix some bugs\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:133\nmsgid \"Bug fix\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:139\nmsgid \"Refresh UI\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:140\nmsgid \"Fix a lot of bugs\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:141\nmsgid \"Add powerful settings\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:142\nmsgid \"Add arguments support for custom tools\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:143\nmsgid \"Add translation support\"\nmsgstr \"Nieuw: ondersteuning voor eigen gereedschap\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:144\n#: data/com.github.liferooter.textpieces.appdata.xml.in:175\nmsgid \"Improve search\"\nmsgstr \"Verbeterd: zoekfunctie\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:158\nmsgid \"Minor UI changes\"\nmsgstr \"Kleine aanpassingen aan de vormgeving\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:159\nmsgid \"Add XML formatter\"\nmsgstr \"Nieuw: xml-opmaakgereedschap\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:164\nmsgid \"Some bugfixes and improvements\"\nmsgstr \"Enkele bugs opgelost en andere kleine verbeteringen aangebracht\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:167\nmsgid \"Bring back tools with arguments\"\nmsgstr \"Gereedschap kan worden voorzien van opties\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:173\nmsgid \"Redesign the application\"\nmsgstr \"De toepassing is opnieuw ontworpen\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:174\nmsgid \"Add custom tools support\"\nmsgstr \"Nieuw: ondersteuning voor eigen gereedschap\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:176\nmsgid \"Add ability to text load from file and save to file\"\nmsgstr \"\"\n\"Nieuw: mogelijkheid om tekst te laden uit een bestand en te bewaren als \"\n\"bestand\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:184\nmsgid \"Exit by Ctrl+Q\"\nmsgstr \"Sluit af met Ctrl + Q\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:185\nmsgid \"Monospace argument entry\"\nmsgstr \"Vastebreedtetekst kan worden voorzien van opties\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:186\nmsgid \"Update new runtime\"\nmsgstr \"Aandrijving bijgewerkt\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:194\nmsgid \"Sort tools\"\nmsgstr \"Sorteer gereedschap\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:195\nmsgid \"Filter tools\"\nmsgstr \"Filter gereedschap\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:196\nmsgid \"Minify C-like code\"\nmsgstr \"Verklein C-achtige code\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:204\nmsgid \"Add more tools\"\nmsgstr \"Nieuw: meer gereedschap\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:205\nmsgid \"Make tools popover more powerful\"\nmsgstr \"Gereedschapsmenu uitgebreid\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:206\nmsgid \"Redesign icon\"\nmsgstr \"Het pictogram is opnieuw ontworpen\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:207\nmsgid \"General improvements\"\nmsgstr \"Algemene verbeteringen\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:212\nmsgid \"Add tools for replacement and removing\"\nmsgstr \"Nieuw: gereedschap voor vervangen en wissen\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:215\nmsgid \"Application release\"\nmsgstr \"Eerste uitgave\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:13\nmsgid \"Style scheme\"\nmsgstr \"Thema\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:14\nmsgid \"Which style scheme to use for application interface and editor.\"\nmsgstr \"Kies het thema dat de toepassing dient te gebruiken.\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:18\nmsgid \"Font name\"\nmsgstr \"Lettertype\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:19\nmsgid \"Name of font used in editor\"\nmsgstr \"Kies het in de bewerker te gebruiken lettertype.\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:23\nmsgid \"Wrap lines\"\nmsgstr \"Regels afbreken\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:24\nmsgid \"Whether to wrap long lines\"\nmsgstr \"Schakel in om lange regels af te breken.\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:28\nmsgid \"Tabs to spaces\"\nmsgstr \"Tabs vervangen door spaties\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:29\nmsgid \"Whether to insert spaces instead of tab\"\nmsgstr \"Schakel in om spaties in te voegen in plaats van tabs.\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:33\nmsgid \"Spaces in tab\"\nmsgstr \"Aantal spaties\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:34\nmsgid \"Tab width in spaces\"\nmsgstr \"De tabbreedte in spaties.\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:38\nmsgid \"Window width\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:39\nmsgid \"Saved width of application window\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:43\nmsgid \"Window height\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:44\nmsgid \"Saved height of the window\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:48\nmsgid \"Is maximized\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:49\nmsgid \"Whether the application window is maximized\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:53\nmsgid \"Selected tool\"\nmsgstr \"Kies een gereedschap\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:54\nmsgid \"\"\n\"Used to restore tool choice on restart. Its value must be a name of tool's \"\n\"script.\"\nmsgstr \"\"\n\n#: src/widgets/Editor.vala:145\nmsgid \"Text is copied\"\nmsgstr \"De tekst is gekopieerd naar het klembord\"\n\n#. *\n#. * Text used instead of tool name\n#. * when where is no tool selected\n#.\n#: src/widgets/Window.vala:21 resources/ui/ShortcutsWindow.blp:50\nmsgid \"Select tool\"\nmsgstr \"Kies een gereedschap\"\n\n#: src/widgets/Window.vala:161\n#, c-format\nmsgid \"Can't load from file: %s\"\nmsgstr \"Het bestand kan niet worden geladen: %s\"\n\n#: src/widgets/Window.vala:249\nmsgid \"Tobias Bernard https://tobiasbernard.com\"\nmsgstr \"Tobias Bernard https://tobiasbernard.com\"\n\n#: src/widgets/Window.vala:250\nmsgid \"Gleb Smirnov <glebsmirnov0708@gmail.com>\"\nmsgstr \"Gleb Smirnov <glebsmirnov0708@gmail.com>\"\n\n#: src/widgets/Window.vala:253\nmsgid \"translator-credits\"\nmsgstr \"Heimen Stoffels <vistausss@fastmail.com>\"\n\n#: src/widgets/Window.vala:280 resources/ui/Window.blp:78\nmsgid \"Save to File\"\nmsgstr \"Opslaan als bestand\"\n\n#: src/widgets/Window.vala:305\n#, c-format\nmsgid \"Can't save to file: %s\"\nmsgstr \"Het bestand kan niet worden opgeslagen: %s\"\n\n#: src/widgets/Window.vala:321 resources/ui/Window.blp:85\nmsgid \"Load from File\"\nmsgstr \"Laden uit bestand\"\n\n#: src/widgets/SearchEntry.vala:93\n#, c-format\nmsgid \"%u of %d\"\nmsgstr \"\"\n\n#. Create dialog\n#: src/widgets/preferences/Preferences.vala:128\nmsgid \"Select font\"\nmsgstr \"Kies een lettertype\"\n\n#: src/widgets/preferences/ToolSettings.vala:187\n#, c-format\nmsgid \"Argument %d\"\nmsgstr \"Optie %d\"\n\n#: src/widgets/preferences/pages/NewToolPage.vala:113\n#, c-format\nmsgid \"Error occured: %s\"\nmsgstr \"Er is een fout opgetreden: %s\"\n\n#: resources/ui/CustomToolPage.blp:27\nmsgid \"Edit Tool\"\nmsgstr \"Bewerker\"\n\n#: resources/ui/CustomToolPage.blp:38\nmsgid \"D_elete\"\nmsgstr \"Wiss_en\"\n\n#: resources/ui/Search.blp:41\nmsgid \"No Tools Found\"\nmsgstr \"Er is geen gereedschap aangetroffen\"\n\n#: resources/ui/Search.blp:53\nmsgid \"_Add Custom Tool\"\nmsgstr \"Eigen gereedschap toevoegen\"\n\n#: resources/ui/NewToolPage.blp:27\nmsgid \"New Tool\"\nmsgstr \"Gereedschap\"\n\n#: resources/ui/NewToolPage.blp:42\nmsgid \"_Create\"\nmsgstr \"_Aanmaken\"\n\n#: resources/ui/Preferences.blp:13\nmsgid \"_General\"\nmsgstr \"Al_gemeen\"\n\n#: resources/ui/Preferences.blp:19\nmsgid \"Editor\"\nmsgstr \"Bewerker\"\n\n#: resources/ui/Preferences.blp:22\nmsgid \"Editor _Font\"\nmsgstr \"Letter_type van bewerker\"\n\n#: resources/ui/Preferences.blp:40\nmsgid \"_Wrap Lines\"\nmsgstr \"Regels af_breken\"\n\n#: resources/ui/Preferences.blp:51\nmsgid \"Tabs to _Spaces\"\nmsgstr \"Tabs vervangen door _spaties\"\n\n#: resources/ui/Preferences.blp:62\nmsgid \"_Tab Width in Spaces\"\nmsgstr \"_Tabbreedte in spaties\"\n\n#: resources/ui/Preferences.blp:82\nmsgid \"Appearance\"\nmsgstr \"\"\n\n#: resources/ui/Preferences.blp:94\nmsgid \"T_ools\"\nmsgstr \"Gereedsch_ap\"\n\n#: resources/ui/Preferences.blp:100\nmsgid \"Custom tools\"\nmsgstr \"Eigen gereedschap\"\n\n#: resources/ui/Preferences.blp:112\nmsgid \"_Add custom tool\"\nmsgstr \"Eigen gereedschap toevoegen\"\n\n#: resources/ui/Preferences.blp:128\nmsgid \"No Custom Tools\"\nmsgstr \"Eigen gereedschap\"\n\n#: resources/ui/Preferences.blp:129\nmsgid \"You can create one\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:40 resources/ui/ShortcutsWindow.blp:102\nmsgid \"Move to previous match\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:46 resources/ui/ShortcutsWindow.blp:107\nmsgid \"Move to next match\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:58\nmsgid \"Find and replace\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:68\nmsgid \"Toggle search options\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:84\nmsgid \"Close search\"\nmsgstr \"Verbeterd: zoekfunctie\"\n\n#: resources/ui/SearchBar.blp:95 resources/ui/ShortcutsWindow.blp:97\nmsgid \"Replace\"\nmsgstr \"Vervangen door\"\n\n#: resources/ui/SearchBar.blp:106\nmsgid \"_Replace\"\nmsgstr \"Vervangen door\"\n\n#: resources/ui/SearchBar.blp:119\nmsgid \"Replace _All\"\nmsgstr \"Vervangen door\"\n\n#: resources/ui/SearchBar.blp:138\nmsgid \"Re_gular expressions\"\nmsgstr \"Bijzinnen en reguliere uitdrukkingen vervangen\"\n\n#: resources/ui/SearchBar.blp:144\nmsgid \"_Case sensitive\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:150\nmsgid \"Match whole _word only\"\nmsgstr \"\"\n\n#: resources/ui/SearchEntry.blp:15\nmsgid \"Find\"\nmsgstr \"Zoeken\"\n\n#: resources/ui/ShortcutsWindow.blp:13\nmsgid \"General\"\nmsgstr \"Algemeen\"\n\n#: resources/ui/ShortcutsWindow.blp:16\nmsgid \"New window\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:21\nmsgid \"Keyboard shortcuts\"\nmsgstr \"Sneltoetsen\"\n\n#: resources/ui/ShortcutsWindow.blp:26\nmsgid \"Preferences\"\nmsgstr \"Voorkeuren\"\n\n#: resources/ui/ShortcutsWindow.blp:31\nmsgid \"Close window\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:36\nmsgid \"Quit\"\nmsgstr \"Afsluiten\"\n\n#: resources/ui/ShortcutsWindow.blp:42\nmsgid \"Tools\"\nmsgstr \"Gereedschap\"\n\n#: resources/ui/ShortcutsWindow.blp:45\nmsgid \"Apply\"\nmsgstr \"Toepassen\"\n\n#: resources/ui/ShortcutsWindow.blp:55\nmsgid \"Just to the tool options\"\nmsgstr \"Open het gereedschapsoptiemenu\"\n\n#: resources/ui/ShortcutsWindow.blp:61\nmsgid \"Undo and Redo\"\nmsgstr \"Ongedaan maken/Opnieuw\"\n\n#: resources/ui/ShortcutsWindow.blp:64\nmsgid \"Undo\"\nmsgstr \"Ongedaan maken\"\n\n#: resources/ui/ShortcutsWindow.blp:69\nmsgid \"Redo\"\nmsgstr \"Opnieuw\"\n\n#: resources/ui/ShortcutsWindow.blp:75\nmsgid \"Files\"\nmsgstr \"Bestanden\"\n\n#: resources/ui/ShortcutsWindow.blp:78\nmsgid \"Load from file\"\nmsgstr \"Laden uit bestand\"\n\n#: resources/ui/ShortcutsWindow.blp:83\nmsgid \"Save to file\"\nmsgstr \"Opslaan als bestand\"\n\n#: resources/ui/ShortcutsWindow.blp:89\nmsgid \"Search and Replace\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:92\nmsgid \"Search\"\nmsgstr \"\"\n\n#: resources/ui/ToolSettings.blp:31\nmsgid \"_Name\"\nmsgstr \"_Naam\"\n\n#: resources/ui/ToolSettings.blp:42\nmsgid \"_Description\"\nmsgstr \"_Omschrijving\"\n\n#: resources/ui/ToolSettings.blp:52\nmsgid \"_Arguments\"\nmsgstr \"Opdr_achtregelopties\"\n\n#: resources/ui/ToolSettings.blp:80\nmsgid \"Edit _Script\"\nmsgstr \"\"\n\n#: resources/ui/Window.blp:32\nmsgid \"_Apply\"\nmsgstr \"Toep_assen\"\n\n#: resources/ui/Window.blp:35\nmsgid \"Apply Selected Tool\"\nmsgstr \"Gebruik het gekozen gereedschap\"\n\n#: resources/ui/Window.blp:45\nmsgid \"Copy\"\nmsgstr \"Kopiëren\"\n\n#: resources/ui/Window.blp:71\nmsgid \"Main Menu\"\nmsgstr \"Hoofdmenu\"\n\n#: resources/ui/Window.blp:123\nmsgid \"_New Window\"\nmsgstr \"\"\n\n#: resources/ui/Window.blp:131\nmsgid \"_Find/Replace…\"\nmsgstr \"\"\n\n#: resources/ui/Window.blp:139\nmsgid \"_Preferences\"\nmsgstr \"_Voorkeuren\"\n\n#: resources/ui/Window.blp:145\nmsgid \"_Keyboard Shortcuts\"\nmsgstr \"_Sneltoetsen\"\n\n#: resources/ui/Window.blp:151\nmsgid \"_About Text Pieces\"\nmsgstr \"_Over Tekstdelen\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Base64 Decode\"\nmsgstr \"Base64-ontsleuteling\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Base64 Encode\"\nmsgstr \"Base64-versleuteling\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute MD5 hash sum\"\nmsgstr \"Bereken de md5-controlesom\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA1 hash sum\"\nmsgstr \"Bereken de sha1-controlesom\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA256 hash sum\"\nmsgstr \"Bereken de sha256-controlesom\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA384 hash sum\"\nmsgstr \"Bereken de sha384-controlesom\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA512 hash sum\"\nmsgstr \"Bereken de sha512-controlesom\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Convert JSON to YAML\"\nmsgstr \"JSON naar YAML converteren\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Convert YAML to JSON\"\nmsgstr \"YAML naar JSON converteren\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count how many lines are in text\"\nmsgstr \"Tel het aantal regels in de tekst\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count how many symbols are in text\"\nmsgstr \"Tel het aantal symbolen in de tekst\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count how many words are in text\"\nmsgstr \"Tel het aantal woorden in de tekst\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count Lines\"\nmsgstr \"Regels tellen\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count Symbols\"\nmsgstr \"Symbolen tellen\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count Words\"\nmsgstr \"Woorden tellen\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Decode text from Base64\"\nmsgstr \"Ontsleutel met Base64 versleutelde tekst\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Encode text to Base64\"\nmsgstr \"Versleutel tekst met Base64\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Escape HTML\"\nmsgstr \"Html aanpassen\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Escape String\"\nmsgstr \"Tekenreeks aanpassen\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Escape URL\"\nmsgstr \"Url aanpassen\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Extract RSS links from OPML outline\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Filter Lines by Regex\"\nmsgstr \"Regels filteren op reguliere uitdrukkingen\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Filter Lines by Substring\"\nmsgstr \"Regels filteren op onderliggende tekenreeks\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Find\"\nmsgstr \"Zoeken\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Find (regex)\"\nmsgstr \"Zoeken op reguliere uitdrukking\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"MD5 Checksum\"\nmsgstr \"Md5-controlesom\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Minify JSON\"\nmsgstr \"JSON verkleinen\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"OPML to RSS links\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse JSON and convert it to YAML document\"\nmsgstr \"Verwerk json en converteer het naar yaml\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse JSON and minify it\"\nmsgstr \"Verwerk json en verklein het\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse JSON and prettify it\"\nmsgstr \"Verwerk json en voorzie het van een mooie opmaak\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse XML and prettify it\"\nmsgstr \"Verwerk xml en voorzie het van een mooie opmaak\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse YAML document and convert it to JSON\"\nmsgstr \"Verwerk yaml en converteer het naar json\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Prettify JSON\"\nmsgstr \"JSON opmaken\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Prettify XML\"\nmsgstr \"XML opmaken\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Regex\"\nmsgstr \"Reguliere uitdrukking\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which contain given substring\"\nmsgstr \"Verwijder alle regels die de opgegeven tekenreeks bevatten\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which don't contain given substring\"\nmsgstr \"Verwijder alle regels die niet de opgegeven tekenreeks bevatten\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which don't match given regular expression\"\nmsgstr \"\"\n\"Verwijder alle regels die niet overeenkomen met de opgegeven reguliere \"\n\"uitdrukking\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which match given regular expression\"\nmsgstr \"\"\n\"Verwijder alle regels die overeenkomen met de opgegeven reguliere uitdrukking\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all matches of regular expression\"\nmsgstr \"Verwijder alle overeenkomende reguliere uitdrukkingen\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all occurrences of given substring in text\"\nmsgstr \"Verwijder alle regels die de opgegeven tekenreeks bevatten\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove leading and trailing whitespaces and newlines\"\nmsgstr \"Verwijder witruimtes voor- en achteraan tekst en nieuwe regels\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove Regex\"\nmsgstr \"Reguliere uitdrukking verwijderen\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove Substring\"\nmsgstr \"Onderliggende tekenreeks verwijderen\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove Trailing\"\nmsgstr \"Witruimtes verwijderen\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove trailing whitespaces and newlines\"\nmsgstr \"Verwijder witruimtes en nieuwe regels\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reoder lines in reverse order\"\nmsgstr \"Plaats alle regels in omgekeerde volgorde\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reorder characters in reverse order\"\nmsgstr \"Plaats alle tekens in omgekeerde volgorde\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace all given regex matches in text with given string\"\nmsgstr \"Vervang alle overeenkomende reguliere uitdrukkingen door een andere\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace all occurrences of given substring in text with other string\"\nmsgstr \"Vervang alle overeenkomende tekenreeksen door een andere\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace escape sequences with characters\"\nmsgstr \"Vervang ‘escape’tekens door reguliere tekens\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace HTML sequences with characters\"\nmsgstr \"Vervang html-tekens door reguliere tekens\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace Regex\"\nmsgstr \"Reguliere uitdrukking vervangen\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace special characters with escape sequences\"\nmsgstr \"Vervang speciale tekens door ‘escape’tekens\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace special characters with HTML-safe sequences\"\nmsgstr \"Vervang speciale tekens door html-tekens\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace special characters with URL-safe sequences\"\nmsgstr \"Vervang speciale tekens door url-tekens\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace Substring\"\nmsgstr \"Onderliggende tekenreeks vervangen\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace URL sequences with characters\"\nmsgstr \"Vervang url-tekens door reguliere tekens\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace With\"\nmsgstr \"Vervangen door\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Filter Lines by Regex\"\nmsgstr \"Regels omgekeerd filteren op reguliere uitdrukkingen\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Filter Lines by Substring\"\nmsgstr \"Regels omgekeerd filteren op onderliggende tekenreeks\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Lines\"\nmsgstr \"Regels omkeren\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Sort Lines\"\nmsgstr \"Regels omgekeerd sorteren\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Text\"\nmsgstr \"Tekst omkeren\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA1 Checksum\"\nmsgstr \"Sha1-controlesom\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA256 Checksum\"\nmsgstr \"Sha256-controlesom\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA384 Checksum\"\nmsgstr \"Sha384-controlesom\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA512 Checksum\"\nmsgstr \"Sha512-controlesom\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Sort Lines\"\nmsgstr \"Regels sorteren\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Sort lines in text in alphabetical order\"\nmsgstr \"Sorteer alle regels op alfabetische volgorde\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Sort lines in text in reverse alphabetical order\"\nmsgstr \"Sorteer alle regels op omgekeerde alfabetische volgorde\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Substring\"\nmsgstr \"Onderliggende tekenreeks\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Trim Lines\"\nmsgstr \"Regels afbreken\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Unescape HTML\"\nmsgstr \"Html niet aanpassen\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Unescape String\"\nmsgstr \"Tekenreeksen niet aanpassen\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Unescape URL\"\nmsgstr \"Url niet aanpassen\"\n\n#~ msgid \"Color Scheme\"\n#~ msgstr \"Thema\"\n\n#~ msgid \"_Light\"\n#~ msgstr \"_Licht\"\n\n#~ msgid \"_Dark\"\n#~ msgstr \"_Donker\"\n\n#~ msgid \"S_ystem Default\"\n#~ msgstr \"S_ysteemthema\"\n\n#~ msgid \"Copy and Paste\"\n#~ msgstr \"Kopiëren en plakken\"\n\n#~ msgid \"Paste\"\n#~ msgstr \"Plakken\"\n\n#~ msgid \"Copy all\"\n#~ msgstr \"Alles kopiëren\"\n"
  },
  {
    "path": "po/pt_BR.po",
    "content": "# SPDX-FileCopyrightText: 2022 Éden Alencar <eden.alencar@gmail.com>\n#\n# SPDX-License-Identifier: CC0-1.0\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: textpieces\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-12-21 08:25+0000\\n\"\n\"PO-Revision-Date: 2022-02-03 14:21-0300\\n\"\n\"Last-Translator: Éden Alencar <eden.alencar@gmail.com>\\n\"\n\"Language-Team: Brazilian Portuguese <eden.alencar@gmail.com>\\n\"\n\"Language: pt_BR\\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\"Plural-Forms: nplurals=2; plural=(n > 1)\\n\"\n\"X-Generator: Gtranslator 41.0\\n\"\n\n#: data/com.github.liferooter.textpieces.desktop.in:7\n#: data/com.github.liferooter.textpieces.appdata.xml.in:9\n#: src/widgets/Window.vala:241 resources/ui/Window.blp:9\nmsgid \"Text Pieces\"\nmsgstr \"Text Pieces\"\n\n#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!\n#: data/com.github.liferooter.textpieces.desktop.in:18\nmsgid \"\"\n\"GNOME;GTK;Text;Base64;JSON;YAML;Count;SHA1;SHA256;SHA384;SHA512;MD5;Checksum;\"\n\"Escape;Encode;HTML;URL;Prettify;Filter;Reverse;Trim;\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.desktop.in:21\nmsgid \"New Window\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:10\n#: src/widgets/Window.vala:242\nmsgid \"Transform text without using random websites\"\nmsgstr \"Transforme texto sem usar sites aleatórios\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:14\nmsgid \"Do a lot of text transformations, such as:\"\nmsgstr \"Faça muitas transformações de texto, como:\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:16\nmsgid \"Calculate hashes\"\nmsgstr \"Calcular hashes\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:17\nmsgid \"Encode text\"\nmsgstr \"Codificar texto\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:18\nmsgid \"Decode text\"\nmsgstr \"Decodificar texto\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:19\nmsgid \"Remove trailing spaces and lines\"\nmsgstr \"Remova espaços e linhas à direita\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:20\nmsgid \"Count lines, symbols and words\"\nmsgstr \"Contar linhas, símbolos e palavras\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:21\nmsgid \"Format JSON and XML\"\nmsgstr \"Formate JSON e XML\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:22\nmsgid \"Escape and unescape strings\"\nmsgstr \"Escape e não escape de strings\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:23\nmsgid \"Convert JSON to YAML and vice versa\"\nmsgstr \"Converter JSON para YAML e vice-versa\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:24\nmsgid \"Filter lines\"\nmsgstr \"Filtrar linhas\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:25\nmsgid \"Replace substrings and regular expressions\"\nmsgstr \"Substituir substrings e expressões regulares\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:27\nmsgid \"...and so on.\"\nmsgstr \"...e assim por diante.\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:32\n#: src/widgets/Window.vala:252\nmsgid \"Gleb Smirnov\"\nmsgstr \"Gleb Smirnov\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:64\n#: data/com.github.liferooter.textpieces.appdata.xml.in:77\n#: data/com.github.liferooter.textpieces.appdata.xml.in:87\nmsgid \"Bug fixes:\"\nmsgstr \"Correção de bug\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:66\nmsgid \"Script files can be correctly opened using an editor of your choice\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:67\nmsgid \"Newly created script names are generated from tool name\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:73\n#, fuzzy\nmsgid \"New features:\"\nmsgstr \"Algumas correções de bugs e melhorias\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:75\nmsgid \"Text Pieces now can open files\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:79\nmsgid \"Some translation issues are fixed\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:80\nmsgid \"Some tools are fixed and optimized\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:81\nmsgid \"Shortcuts window is updated and fixed\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:89\nmsgid \"Tool arguments now work as expected\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:90\nmsgid \"Translate some untranslated strings\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:96\n#: data/com.github.liferooter.textpieces.appdata.xml.in:108\n#: data/com.github.liferooter.textpieces.appdata.xml.in:116\n#: data/com.github.liferooter.textpieces.appdata.xml.in:137\n#: data/com.github.liferooter.textpieces.appdata.xml.in:156\n#: data/com.github.liferooter.textpieces.appdata.xml.in:171\n#: data/com.github.liferooter.textpieces.appdata.xml.in:182\n#: data/com.github.liferooter.textpieces.appdata.xml.in:192\n#: data/com.github.liferooter.textpieces.appdata.xml.in:202\nmsgid \"New features and improvements:\"\nmsgstr \"Algumas correções de bugs e melhorias\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:98\nmsgid \"Add tool for extracting RSS URLs from OPML files\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:99\nmsgid \"Use more verbose error reporting for Base64 decode tool\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:100\nmsgid \"Focus editor on startup\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:101\nmsgid \"Save selected tool between sessions\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:102\nmsgid \"Minor UI improvements\"\nmsgstr \"Melhorias gerais\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:110\nmsgid \"Add hotkey to open search in replace mode\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:118\nmsgid \"Add some keywords to the application for system search\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:124\nmsgid \"New features and improvement:\"\nmsgstr \"Algumas correções de bugs e melhorias\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:126\nmsgid \"Add style schemes\"\nmsgstr \"Esquema de estilo\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:127\nmsgid \"Add search and replace feature to editor\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:128\nmsgid \"Fix some bugs\"\nmsgstr \"Corrija muitos bugs\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:133\nmsgid \"Bug fix\"\nmsgstr \"Correção de bug\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:139\nmsgid \"Refresh UI\"\nmsgstr \"Atualizar interface do usuário\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:140\nmsgid \"Fix a lot of bugs\"\nmsgstr \"Corrija muitos bugs\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:141\nmsgid \"Add powerful settings\"\nmsgstr \"Adicione configurações poderosas\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:142\nmsgid \"Add arguments support for custom tools\"\nmsgstr \"Adicionar suporte a argumentos para ferramentas personalizadas\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:143\nmsgid \"Add translation support\"\nmsgstr \"Adicionar suporte de tradução\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:144\n#: data/com.github.liferooter.textpieces.appdata.xml.in:175\nmsgid \"Improve search\"\nmsgstr \"Melhorar a pesquisa\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:158\nmsgid \"Minor UI changes\"\nmsgstr \"Pequenas alterações na interface do usuário\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:159\nmsgid \"Add XML formatter\"\nmsgstr \"Adicionar formatador XML\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:164\nmsgid \"Some bugfixes and improvements\"\nmsgstr \"Algumas correções de bugs e melhorias\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:167\nmsgid \"Bring back tools with arguments\"\nmsgstr \"Traga de volta ferramentas com argumentos\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:173\nmsgid \"Redesign the application\"\nmsgstr \"Redesenhe o aplicativo\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:174\nmsgid \"Add custom tools support\"\nmsgstr \"Adicionar suporte a ferramentas personalizadas\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:176\nmsgid \"Add ability to text load from file and save to file\"\nmsgstr \"Adicione a capacidade de carregar texto do arquivo e salvar no arquivo\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:184\nmsgid \"Exit by Ctrl+Q\"\nmsgstr \"Sair com Ctrl+Q\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:185\nmsgid \"Monospace argument entry\"\nmsgstr \"Entrada de argumento monoespaçado\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:186\nmsgid \"Update new runtime\"\nmsgstr \"Atualizar novo tempo de execução\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:194\nmsgid \"Sort tools\"\nmsgstr \"Ferramentas de classificação\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:195\nmsgid \"Filter tools\"\nmsgstr \"Ferramentas de filtro\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:196\nmsgid \"Minify C-like code\"\nmsgstr \"Minificar código semelhante a C\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:204\nmsgid \"Add more tools\"\nmsgstr \"Adicionar mais ferramentas\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:205\nmsgid \"Make tools popover more powerful\"\nmsgstr \"Torne as ferramentas popover mais poderosas\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:206\nmsgid \"Redesign icon\"\nmsgstr \"Ícone de redesenho\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:207\nmsgid \"General improvements\"\nmsgstr \"Melhorias gerais\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:212\nmsgid \"Add tools for replacement and removing\"\nmsgstr \"Adicione ferramentas para substituição e remoção\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:215\nmsgid \"Application release\"\nmsgstr \"Versão do aplicativo\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:13\nmsgid \"Style scheme\"\nmsgstr \"Esquema de estilo\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:14\nmsgid \"Which style scheme to use for application interface and editor.\"\nmsgstr \"Qual esquema de estilo usar para a interface e o editor do aplicativo.\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:18\nmsgid \"Font name\"\nmsgstr \"Nome da fonte\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:19\nmsgid \"Name of font used in editor\"\nmsgstr \"Nome da fonte usada no editor\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:23\nmsgid \"Wrap lines\"\nmsgstr \"Quebra de linhas\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:24\nmsgid \"Whether to wrap long lines\"\nmsgstr \"Se deve envolver linhas longas\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:28\nmsgid \"Tabs to spaces\"\nmsgstr \"Guias para _Espaços\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:29\nmsgid \"Whether to insert spaces instead of tab\"\nmsgstr \"Se deseja inserir espaços em vez de tabulação\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:33\nmsgid \"Spaces in tab\"\nmsgstr \"Espaços na guia\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:34\nmsgid \"Tab width in spaces\"\nmsgstr \"Largura da guia em espaços\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:38\nmsgid \"Window width\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:39\nmsgid \"Saved width of application window\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:43\nmsgid \"Window height\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:44\nmsgid \"Saved height of the window\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:48\nmsgid \"Is maximized\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:49\nmsgid \"Whether the application window is maximized\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:53\nmsgid \"Selected tool\"\nmsgstr \"Selecionar ferramenta\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:54\nmsgid \"\"\n\"Used to restore tool choice on restart. Its value must be a name of tool's \"\n\"script.\"\nmsgstr \"\"\n\n#: src/widgets/Editor.vala:145\nmsgid \"Text is copied\"\nmsgstr \"O texto é copiado para a área de transferência\"\n\n#. *\n#. * Text used instead of tool name\n#. * when where is no tool selected\n#.\n#: src/widgets/Window.vala:21 resources/ui/ShortcutsWindow.blp:50\nmsgid \"Select tool\"\nmsgstr \"Selecionar ferramenta\"\n\n#: src/widgets/Window.vala:161\n#, c-format\nmsgid \"Can't load from file: %s\"\nmsgstr \"Não é possível carregar do arquivo: %s\"\n\n#: src/widgets/Window.vala:249\nmsgid \"Tobias Bernard https://tobiasbernard.com\"\nmsgstr \"\"\n\n#: src/widgets/Window.vala:250\nmsgid \"Gleb Smirnov <glebsmirnov0708@gmail.com>\"\nmsgstr \"Gleb Smirnov <glebsmirnov0708@gmail.com>\"\n\n#: src/widgets/Window.vala:253\nmsgid \"translator-credits\"\nmsgstr \"créditos do tradutor\"\n\n#: src/widgets/Window.vala:280 resources/ui/Window.blp:78\nmsgid \"Save to File\"\nmsgstr \"Salvar em arquivo\"\n\n#: src/widgets/Window.vala:305\n#, c-format\nmsgid \"Can't save to file: %s\"\nmsgstr \"Não é possível salvar no arquivo: %s\"\n\n#: src/widgets/Window.vala:321 resources/ui/Window.blp:85\nmsgid \"Load from File\"\nmsgstr \"Carregar do arquivo\"\n\n#: src/widgets/SearchEntry.vala:93\n#, c-format\nmsgid \"%u of %d\"\nmsgstr \"\"\n\n#. Create dialog\n#: src/widgets/preferences/Preferences.vala:128\nmsgid \"Select font\"\nmsgstr \"Selecionar fonte\"\n\n#: src/widgets/preferences/ToolSettings.vala:187\n#, c-format\nmsgid \"Argument %d\"\nmsgstr \"Argumento %d\"\n\n#: src/widgets/preferences/pages/NewToolPage.vala:113\n#, c-format\nmsgid \"Error occured: %s\"\nmsgstr \"Ocorreu um erro: %s\"\n\n#: resources/ui/CustomToolPage.blp:27\nmsgid \"Edit Tool\"\nmsgstr \"Editor\"\n\n#: resources/ui/CustomToolPage.blp:38\nmsgid \"D_elete\"\nmsgstr \"E_xcluir\"\n\n#: resources/ui/Search.blp:41\nmsgid \"No Tools Found\"\nmsgstr \"Nenhuma ferramenta encontrada\"\n\n#: resources/ui/Search.blp:53\nmsgid \"_Add Custom Tool\"\nmsgstr \"Adicionar ferramenta personalizada\"\n\n#: resources/ui/NewToolPage.blp:27\nmsgid \"New Tool\"\nmsgstr \"Ferramentas\"\n\n#: resources/ui/NewToolPage.blp:42\nmsgid \"_Create\"\nmsgstr \"_Criar\"\n\n#: resources/ui/Preferences.blp:13\nmsgid \"_General\"\nmsgstr \"_Geral\"\n\n#: resources/ui/Preferences.blp:19\nmsgid \"Editor\"\nmsgstr \"Editor\"\n\n#: resources/ui/Preferences.blp:22\nmsgid \"Editor _Font\"\nmsgstr \"Editor _Fonte\"\n\n#: resources/ui/Preferences.blp:40\nmsgid \"_Wrap Lines\"\nmsgstr \"_Linhas de quebra\"\n\n#: resources/ui/Preferences.blp:51\nmsgid \"Tabs to _Spaces\"\nmsgstr \"Guias para _Espaços\"\n\n#: resources/ui/Preferences.blp:62\nmsgid \"_Tab Width in Spaces\"\nmsgstr \"_Largura da Tabulação nos Espaços\"\n\n#: resources/ui/Preferences.blp:82\nmsgid \"Appearance\"\nmsgstr \"Aparência\"\n\n#: resources/ui/Preferences.blp:94\nmsgid \"T_ools\"\nmsgstr \"F_erramentas\"\n\n#: resources/ui/Preferences.blp:100\nmsgid \"Custom tools\"\nmsgstr \"Ferramentas personalizadas\"\n\n#: resources/ui/Preferences.blp:112\nmsgid \"_Add custom tool\"\nmsgstr \"Adicionar ferramenta personalizada\"\n\n#: resources/ui/Preferences.blp:128\nmsgid \"No Custom Tools\"\nmsgstr \"Ferramentas personalizadas\"\n\n#: resources/ui/Preferences.blp:129\nmsgid \"You can create one\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:40 resources/ui/ShortcutsWindow.blp:102\nmsgid \"Move to previous match\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:46 resources/ui/ShortcutsWindow.blp:107\nmsgid \"Move to next match\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:58\nmsgid \"Find and replace\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:68\nmsgid \"Toggle search options\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:84\nmsgid \"Close search\"\nmsgstr \"Melhorar a pesquisa\"\n\n#: resources/ui/SearchBar.blp:95 resources/ui/ShortcutsWindow.blp:97\nmsgid \"Replace\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:106\nmsgid \"_Replace\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:119\nmsgid \"Replace _All\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:138\nmsgid \"Re_gular expressions\"\nmsgstr \"Substituir substrings e expressões regulares\"\n\n#: resources/ui/SearchBar.blp:144\nmsgid \"_Case sensitive\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:150\nmsgid \"Match whole _word only\"\nmsgstr \"\"\n\n#: resources/ui/SearchEntry.blp:15\nmsgid \"Find\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:13\nmsgid \"General\"\nmsgstr \"Geral\"\n\n#: resources/ui/ShortcutsWindow.blp:16\nmsgid \"New window\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:21\nmsgid \"Keyboard shortcuts\"\nmsgstr \"Atalhos do teclado\"\n\n#: resources/ui/ShortcutsWindow.blp:26\nmsgid \"Preferences\"\nmsgstr \"Preferências\"\n\n#: resources/ui/ShortcutsWindow.blp:31\nmsgid \"Close window\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:36\nmsgid \"Quit\"\nmsgstr \"Sair\"\n\n#: resources/ui/ShortcutsWindow.blp:42\nmsgid \"Tools\"\nmsgstr \"Ferramentas\"\n\n#: resources/ui/ShortcutsWindow.blp:45\nmsgid \"Apply\"\nmsgstr \"Aplicar\"\n\n#: resources/ui/ShortcutsWindow.blp:55\nmsgid \"Just to the tool options\"\nmsgstr \"Ir para as opções da ferramenta\"\n\n#: resources/ui/ShortcutsWindow.blp:61\nmsgid \"Undo and Redo\"\nmsgstr \"Desfazer e refazer\"\n\n#: resources/ui/ShortcutsWindow.blp:64\nmsgid \"Undo\"\nmsgstr \"Desfazer\"\n\n#: resources/ui/ShortcutsWindow.blp:69\nmsgid \"Redo\"\nmsgstr \"Refazer\"\n\n#: resources/ui/ShortcutsWindow.blp:75\nmsgid \"Files\"\nmsgstr \"Arquivos\"\n\n#: resources/ui/ShortcutsWindow.blp:78\nmsgid \"Load from file\"\nmsgstr \"Carregar do arquivo\"\n\n#: resources/ui/ShortcutsWindow.blp:83\nmsgid \"Save to file\"\nmsgstr \"Salvar em arquivo\"\n\n#: resources/ui/ShortcutsWindow.blp:89\nmsgid \"Search and Replace\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:92\nmsgid \"Search\"\nmsgstr \"\"\n\n#: resources/ui/ToolSettings.blp:31\nmsgid \"_Name\"\nmsgstr \"_Nome\"\n\n#: resources/ui/ToolSettings.blp:42\nmsgid \"_Description\"\nmsgstr \"_Descrição\"\n\n#: resources/ui/ToolSettings.blp:52\nmsgid \"_Arguments\"\nmsgstr \"_Argumentos\"\n\n#: resources/ui/ToolSettings.blp:80\nmsgid \"Edit _Script\"\nmsgstr \"\"\n\n#: resources/ui/Window.blp:32\nmsgid \"_Apply\"\nmsgstr \"_Aplicar\"\n\n#: resources/ui/Window.blp:35\nmsgid \"Apply Selected Tool\"\nmsgstr \"Aplicar ferramenta selecionada\"\n\n#: resources/ui/Window.blp:45\nmsgid \"Copy\"\nmsgstr \"Copiar\"\n\n#: resources/ui/Window.blp:71\nmsgid \"Main Menu\"\nmsgstr \"Menu principal\"\n\n#: resources/ui/Window.blp:123\nmsgid \"_New Window\"\nmsgstr \"\"\n\n#: resources/ui/Window.blp:131\nmsgid \"_Find/Replace…\"\nmsgstr \"\"\n\n#: resources/ui/Window.blp:139\nmsgid \"_Preferences\"\nmsgstr \"_Preferências\"\n\n#: resources/ui/Window.blp:145\nmsgid \"_Keyboard Shortcuts\"\nmsgstr \"_Atalhos do teclado\"\n\n#: resources/ui/Window.blp:151\nmsgid \"_About Text Pieces\"\nmsgstr \"_Sobre Text Pieces\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Base64 Decode\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Base64 Encode\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute MD5 hash sum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA1 hash sum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA256 hash sum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA384 hash sum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA512 hash sum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Convert JSON to YAML\"\nmsgstr \"Converter JSON para YAML e vice-versa\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Convert YAML to JSON\"\nmsgstr \"Converter JSON para YAML e vice-versa\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count how many lines are in text\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count how many symbols are in text\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count how many words are in text\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count Lines\"\nmsgstr \"_Linhas de quebra\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count Symbols\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count Words\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Decode text from Base64\"\nmsgstr \"Decodificar texto\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Encode text to Base64\"\nmsgstr \"Codificar texto\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Escape HTML\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Escape String\"\nmsgstr \"Escape e não escape de strings\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Escape URL\"\nmsgstr \"Escape e não escape de strings\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Extract RSS links from OPML outline\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Filter Lines by Regex\"\nmsgstr \"Filtrar linhas\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Filter Lines by Substring\"\nmsgstr \"Filtrar linhas\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Find\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Find (regex)\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"MD5 Checksum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Minify JSON\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"OPML to RSS links\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse JSON and convert it to YAML document\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse JSON and minify it\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse JSON and prettify it\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse XML and prettify it\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse YAML document and convert it to JSON\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Prettify JSON\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Prettify XML\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Regex\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which contain given substring\"\nmsgstr \"Substituir substrings e expressões regulares\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which don't contain given substring\"\nmsgstr \"Substituir substrings e expressões regulares\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which don't match given regular expression\"\nmsgstr \"Substituir substrings e expressões regulares\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which match given regular expression\"\nmsgstr \"Substituir substrings e expressões regulares\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all matches of regular expression\"\nmsgstr \"Substituir substrings e expressões regulares\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all occurrences of given substring in text\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove leading and trailing whitespaces and newlines\"\nmsgstr \"Remova espaços e linhas à direita\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove Regex\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove Substring\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove Trailing\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove trailing whitespaces and newlines\"\nmsgstr \"Remova espaços e linhas à direita\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reoder lines in reverse order\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reorder characters in reverse order\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace all given regex matches in text with given string\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace all occurrences of given substring in text with other string\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace escape sequences with characters\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace HTML sequences with characters\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace Regex\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace special characters with escape sequences\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace special characters with HTML-safe sequences\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace special characters with URL-safe sequences\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace Substring\"\nmsgstr \"Escape e não escape de strings\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace URL sequences with characters\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace With\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Filter Lines by Regex\"\nmsgstr \"Filtrar linhas\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Filter Lines by Substring\"\nmsgstr \"Filtrar linhas\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Lines\"\nmsgstr \"_Linhas de quebra\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Sort Lines\"\nmsgstr \"_Linhas de quebra\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Text\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA1 Checksum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA256 Checksum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA384 Checksum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA512 Checksum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Sort Lines\"\nmsgstr \"_Linhas de quebra\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Sort lines in text in alphabetical order\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Sort lines in text in reverse alphabetical order\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Substring\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Trim Lines\"\nmsgstr \"_Linhas de quebra\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Unescape HTML\"\nmsgstr \"Escape e não escape de strings\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Unescape String\"\nmsgstr \"Escape e não escape de strings\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Unescape URL\"\nmsgstr \"Escape e não escape de strings\"\n\n#~ msgid \"Copy and Paste\"\n#~ msgstr \"Copiar e colar\"\n\n#~ msgid \"Paste\"\n#~ msgstr \"Colar\"\n\n#~ msgid \"Copy all\"\n#~ msgstr \"Copiar tudo\"\n"
  },
  {
    "path": "po/textpieces.pot",
    "content": "# SOME DESCRIPTIVE TITLE.\n# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER\n# This file is distributed under the same license as the textpieces package.\n# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.\n#\n#, fuzzy\nmsgid \"\"\nmsgstr \"\"\n\"Project-Id-Version: textpieces\\n\"\n\"Report-Msgid-Bugs-To: \\n\"\n\"POT-Creation-Date: 2022-12-21 08:25+0000\\n\"\n\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n\"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n\"\n\"Language-Team: LANGUAGE <LL@li.org>\\n\"\n\"Language: \\n\"\n\"MIME-Version: 1.0\\n\"\n\"Content-Type: text/plain; charset=UTF-8\\n\"\n\"Content-Transfer-Encoding: 8bit\\n\"\n\n#: data/com.github.liferooter.textpieces.desktop.in:7\n#: data/com.github.liferooter.textpieces.appdata.xml.in:9\n#: src/widgets/Window.vala:241 resources/ui/Window.blp:9\nmsgid \"Text Pieces\"\nmsgstr \"\"\n\n#. Translators: Search terms to find this application. Do NOT translate or localize the semicolons! The list MUST also end with a semicolon!\n#: data/com.github.liferooter.textpieces.desktop.in:18\nmsgid \"\"\n\"GNOME;GTK;Text;Base64;JSON;YAML;Count;SHA1;SHA256;SHA384;SHA512;MD5;Checksum;\"\n\"Escape;Encode;HTML;URL;Prettify;Filter;Reverse;Trim;\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.desktop.in:21\nmsgid \"New Window\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:10\n#: src/widgets/Window.vala:242\nmsgid \"Transform text without using random websites\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:14\nmsgid \"Do a lot of text transformations, such as:\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:16\nmsgid \"Calculate hashes\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:17\nmsgid \"Encode text\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:18\nmsgid \"Decode text\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:19\nmsgid \"Remove trailing spaces and lines\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:20\nmsgid \"Count lines, symbols and words\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:21\nmsgid \"Format JSON and XML\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:22\nmsgid \"Escape and unescape strings\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:23\nmsgid \"Convert JSON to YAML and vice versa\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:24\nmsgid \"Filter lines\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:25\nmsgid \"Replace substrings and regular expressions\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:27\nmsgid \"...and so on.\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:32\n#: src/widgets/Window.vala:252\nmsgid \"Gleb Smirnov\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:64\n#: data/com.github.liferooter.textpieces.appdata.xml.in:77\n#: data/com.github.liferooter.textpieces.appdata.xml.in:87\nmsgid \"Bug fixes:\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:66\nmsgid \"Script files can be correctly opened using an editor of your choice\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:67\nmsgid \"Newly created script names are generated from tool name\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:73\nmsgid \"New features:\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:75\nmsgid \"Text Pieces now can open files\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:79\nmsgid \"Some translation issues are fixed\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:80\nmsgid \"Some tools are fixed and optimized\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:81\nmsgid \"Shortcuts window is updated and fixed\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:89\nmsgid \"Tool arguments now work as expected\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:90\nmsgid \"Translate some untranslated strings\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:96\n#: data/com.github.liferooter.textpieces.appdata.xml.in:108\n#: data/com.github.liferooter.textpieces.appdata.xml.in:116\n#: data/com.github.liferooter.textpieces.appdata.xml.in:137\n#: data/com.github.liferooter.textpieces.appdata.xml.in:156\n#: data/com.github.liferooter.textpieces.appdata.xml.in:171\n#: data/com.github.liferooter.textpieces.appdata.xml.in:182\n#: data/com.github.liferooter.textpieces.appdata.xml.in:192\n#: data/com.github.liferooter.textpieces.appdata.xml.in:202\nmsgid \"New features and improvements:\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:98\nmsgid \"Add tool for extracting RSS URLs from OPML files\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:99\nmsgid \"Use more verbose error reporting for Base64 decode tool\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:100\nmsgid \"Focus editor on startup\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:101\nmsgid \"Save selected tool between sessions\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:102\nmsgid \"Minor UI improvements\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:110\nmsgid \"Add hotkey to open search in replace mode\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:118\nmsgid \"Add some keywords to the application for system search\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:124\nmsgid \"New features and improvement:\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:126\nmsgid \"Add style schemes\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:127\nmsgid \"Add search and replace feature to editor\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:128\nmsgid \"Fix some bugs\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:133\nmsgid \"Bug fix\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:139\nmsgid \"Refresh UI\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:140\nmsgid \"Fix a lot of bugs\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:141\nmsgid \"Add powerful settings\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:142\nmsgid \"Add arguments support for custom tools\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:143\nmsgid \"Add translation support\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:144\n#: data/com.github.liferooter.textpieces.appdata.xml.in:175\nmsgid \"Improve search\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:158\nmsgid \"Minor UI changes\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:159\nmsgid \"Add XML formatter\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:164\nmsgid \"Some bugfixes and improvements\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:167\nmsgid \"Bring back tools with arguments\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:173\nmsgid \"Redesign the application\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:174\nmsgid \"Add custom tools support\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:176\nmsgid \"Add ability to text load from file and save to file\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:184\nmsgid \"Exit by Ctrl+Q\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:185\nmsgid \"Monospace argument entry\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:186\nmsgid \"Update new runtime\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:194\nmsgid \"Sort tools\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:195\nmsgid \"Filter tools\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:196\nmsgid \"Minify C-like code\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:204\nmsgid \"Add more tools\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:205\nmsgid \"Make tools popover more powerful\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:206\nmsgid \"Redesign icon\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:207\nmsgid \"General improvements\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:212\nmsgid \"Add tools for replacement and removing\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.appdata.xml.in:215\nmsgid \"Application release\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:13\nmsgid \"Style scheme\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:14\nmsgid \"Which style scheme to use for application interface and editor.\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:18\nmsgid \"Font name\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:19\nmsgid \"Name of font used in editor\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:23\nmsgid \"Wrap lines\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:24\nmsgid \"Whether to wrap long lines\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:28\nmsgid \"Tabs to spaces\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:29\nmsgid \"Whether to insert spaces instead of tab\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:33\nmsgid \"Spaces in tab\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:34\nmsgid \"Tab width in spaces\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:38\nmsgid \"Window width\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:39\nmsgid \"Saved width of application window\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:43\nmsgid \"Window height\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:44\nmsgid \"Saved height of the window\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:48\nmsgid \"Is maximized\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:49\nmsgid \"Whether the application window is maximized\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:53\nmsgid \"Selected tool\"\nmsgstr \"\"\n\n#: data/com.github.liferooter.textpieces.gschema.xml:54\nmsgid \"\"\n\"Used to restore tool choice on restart. Its value must be a name of tool's \"\n\"script.\"\nmsgstr \"\"\n\n#: src/widgets/Editor.vala:145\nmsgid \"Text is copied\"\nmsgstr \"\"\n\n#. *\n#. * Text used instead of tool name\n#. * when where is no tool selected\n#.\n#: src/widgets/Window.vala:21 resources/ui/ShortcutsWindow.blp:50\nmsgid \"Select tool\"\nmsgstr \"\"\n\n#: src/widgets/Window.vala:161\n#, c-format\nmsgid \"Can't load from file: %s\"\nmsgstr \"\"\n\n#: src/widgets/Window.vala:249\nmsgid \"Tobias Bernard https://tobiasbernard.com\"\nmsgstr \"\"\n\n#: src/widgets/Window.vala:250\nmsgid \"Gleb Smirnov <glebsmirnov0708@gmail.com>\"\nmsgstr \"\"\n\n#: src/widgets/Window.vala:253\nmsgid \"translator-credits\"\nmsgstr \"\"\n\n#: src/widgets/Window.vala:280 resources/ui/Window.blp:78\nmsgid \"Save to File\"\nmsgstr \"\"\n\n#: src/widgets/Window.vala:305\n#, c-format\nmsgid \"Can't save to file: %s\"\nmsgstr \"\"\n\n#: src/widgets/Window.vala:321 resources/ui/Window.blp:85\nmsgid \"Load from File\"\nmsgstr \"\"\n\n#: src/widgets/SearchEntry.vala:93\n#, c-format\nmsgid \"%u of %d\"\nmsgstr \"\"\n\n#. Create dialog\n#: src/widgets/preferences/Preferences.vala:128\nmsgid \"Select font\"\nmsgstr \"\"\n\n#: src/widgets/preferences/ToolSettings.vala:187\n#, c-format\nmsgid \"Argument %d\"\nmsgstr \"\"\n\n#: src/widgets/preferences/pages/NewToolPage.vala:113\n#, c-format\nmsgid \"Error occured: %s\"\nmsgstr \"\"\n\n#: resources/ui/CustomToolPage.blp:27\nmsgid \"Edit Tool\"\nmsgstr \"\"\n\n#: resources/ui/CustomToolPage.blp:38\nmsgid \"D_elete\"\nmsgstr \"\"\n\n#: resources/ui/Search.blp:41\nmsgid \"No Tools Found\"\nmsgstr \"\"\n\n#: resources/ui/Search.blp:53\nmsgid \"_Add Custom Tool\"\nmsgstr \"\"\n\n#: resources/ui/NewToolPage.blp:27\nmsgid \"New Tool\"\nmsgstr \"\"\n\n#: resources/ui/NewToolPage.blp:42\nmsgid \"_Create\"\nmsgstr \"\"\n\n#: resources/ui/Preferences.blp:13\nmsgid \"_General\"\nmsgstr \"\"\n\n#: resources/ui/Preferences.blp:19\nmsgid \"Editor\"\nmsgstr \"\"\n\n#: resources/ui/Preferences.blp:22\nmsgid \"Editor _Font\"\nmsgstr \"\"\n\n#: resources/ui/Preferences.blp:40\nmsgid \"_Wrap Lines\"\nmsgstr \"\"\n\n#: resources/ui/Preferences.blp:51\nmsgid \"Tabs to _Spaces\"\nmsgstr \"\"\n\n#: resources/ui/Preferences.blp:62\nmsgid \"_Tab Width in Spaces\"\nmsgstr \"\"\n\n#: resources/ui/Preferences.blp:82\nmsgid \"Appearance\"\nmsgstr \"\"\n\n#: resources/ui/Preferences.blp:94\nmsgid \"T_ools\"\nmsgstr \"\"\n\n#: resources/ui/Preferences.blp:100\nmsgid \"Custom tools\"\nmsgstr \"\"\n\n#: resources/ui/Preferences.blp:112\nmsgid \"_Add custom tool\"\nmsgstr \"\"\n\n#: resources/ui/Preferences.blp:128\nmsgid \"No Custom Tools\"\nmsgstr \"\"\n\n#: resources/ui/Preferences.blp:129\nmsgid \"You can create one\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:40 resources/ui/ShortcutsWindow.blp:102\nmsgid \"Move to previous match\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:46 resources/ui/ShortcutsWindow.blp:107\nmsgid \"Move to next match\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:58\nmsgid \"Find and replace\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:68\nmsgid \"Toggle search options\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:84\nmsgid \"Close search\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:95 resources/ui/ShortcutsWindow.blp:97\nmsgid \"Replace\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:106\nmsgid \"_Replace\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:119\nmsgid \"Replace _All\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:138\nmsgid \"Re_gular expressions\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:144\nmsgid \"_Case sensitive\"\nmsgstr \"\"\n\n#: resources/ui/SearchBar.blp:150\nmsgid \"Match whole _word only\"\nmsgstr \"\"\n\n#: resources/ui/SearchEntry.blp:15\nmsgid \"Find\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:13\nmsgid \"General\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:16\nmsgid \"New window\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:21\nmsgid \"Keyboard shortcuts\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:26\nmsgid \"Preferences\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:31\nmsgid \"Close window\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:36\nmsgid \"Quit\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:42\nmsgid \"Tools\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:45\nmsgid \"Apply\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:55\nmsgid \"Just to the tool options\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:61\nmsgid \"Undo and Redo\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:64\nmsgid \"Undo\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:69\nmsgid \"Redo\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:75\nmsgid \"Files\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:78\nmsgid \"Load from file\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:83\nmsgid \"Save to file\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:89\nmsgid \"Search and Replace\"\nmsgstr \"\"\n\n#: resources/ui/ShortcutsWindow.blp:92\nmsgid \"Search\"\nmsgstr \"\"\n\n#: resources/ui/ToolSettings.blp:31\nmsgid \"_Name\"\nmsgstr \"\"\n\n#: resources/ui/ToolSettings.blp:42\nmsgid \"_Description\"\nmsgstr \"\"\n\n#: resources/ui/ToolSettings.blp:52\nmsgid \"_Arguments\"\nmsgstr \"\"\n\n#: resources/ui/ToolSettings.blp:80\nmsgid \"Edit _Script\"\nmsgstr \"\"\n\n#: resources/ui/Window.blp:32\nmsgid \"_Apply\"\nmsgstr \"\"\n\n#: resources/ui/Window.blp:35\nmsgid \"Apply Selected Tool\"\nmsgstr \"\"\n\n#: resources/ui/Window.blp:45\nmsgid \"Copy\"\nmsgstr \"\"\n\n#: resources/ui/Window.blp:71\nmsgid \"Main Menu\"\nmsgstr \"\"\n\n#: resources/ui/Window.blp:123\nmsgid \"_New Window\"\nmsgstr \"\"\n\n#: resources/ui/Window.blp:131\nmsgid \"_Find/Replace…\"\nmsgstr \"\"\n\n#: resources/ui/Window.blp:139\nmsgid \"_Preferences\"\nmsgstr \"\"\n\n#: resources/ui/Window.blp:145\nmsgid \"_Keyboard Shortcuts\"\nmsgstr \"\"\n\n#: resources/ui/Window.blp:151\nmsgid \"_About Text Pieces\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Base64 Decode\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Base64 Encode\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute MD5 hash sum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA1 hash sum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA256 hash sum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA384 hash sum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA512 hash sum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Convert JSON to YAML\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Convert YAML to JSON\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count how many lines are in text\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count how many symbols are in text\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count how many words are in text\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count Lines\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count Symbols\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count Words\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Decode text from Base64\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Encode text to Base64\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Escape HTML\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Escape String\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Escape URL\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Extract RSS links from OPML outline\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Filter Lines by Regex\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Filter Lines by Substring\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Find\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Find (regex)\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"MD5 Checksum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Minify JSON\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"OPML to RSS links\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse JSON and convert it to YAML document\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse JSON and minify it\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse JSON and prettify it\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse XML and prettify it\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse YAML document and convert it to JSON\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Prettify JSON\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Prettify XML\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Regex\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which contain given substring\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which don't contain given substring\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which don't match given regular expression\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which match given regular expression\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all matches of regular expression\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all occurrences of given substring in text\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove leading and trailing whitespaces and newlines\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove Regex\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove Substring\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove Trailing\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove trailing whitespaces and newlines\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reoder lines in reverse order\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reorder characters in reverse order\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace all given regex matches in text with given string\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace all occurrences of given substring in text with other string\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace escape sequences with characters\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace HTML sequences with characters\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace Regex\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace special characters with escape sequences\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace special characters with HTML-safe sequences\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace special characters with URL-safe sequences\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace Substring\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace URL sequences with characters\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace With\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Filter Lines by Regex\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Filter Lines by Substring\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Lines\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Sort Lines\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Text\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA1 Checksum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA256 Checksum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA384 Checksum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA512 Checksum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Sort Lines\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Sort lines in text in alphabetical order\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Sort lines in text in reverse alphabetical order\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Substring\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Trim Lines\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Unescape HTML\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Unescape String\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Unescape URL\"\nmsgstr \"\"\n"
  },
  {
    "path": "po/textpieces.pot.license",
    "content": "SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0"
  },
  {
    "path": "po/tools.pot",
    "content": "\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Base64 Decode\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Base64 Encode\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute MD5 hash sum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA1 hash sum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA256 hash sum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA384 hash sum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Compute SHA512 hash sum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Convert JSON to YAML\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Convert YAML to JSON\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count how many lines are in text\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count how many symbols are in text\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count how many words are in text\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count Lines\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count Symbols\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Count Words\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Decode text from Base64\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Encode text to Base64\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Escape HTML\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Escape String\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Escape URL\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Extract RSS links from OPML outline\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Filter Lines by Regex\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Filter Lines by Substring\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Find\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Find (regex)\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"MD5 Checksum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Minify JSON\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"OPML to RSS links\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse JSON and convert it to YAML document\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse JSON and minify it\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse JSON and prettify it\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse XML and prettify it\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Parse YAML document and convert it to JSON\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Prettify JSON\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Prettify XML\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Regex\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which contain given substring\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which don't contain given substring\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which don't match given regular expression\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all lines which match given regular expression\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all matches of regular expression\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove all occurrences of given substring in text\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove leading and trailing whitespaces and newlines\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove Regex\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove Substring\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove Trailing\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Remove trailing whitespaces and newlines\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reoder lines in reverse order\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reorder characters in reverse order\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace all given regex matches in text with given string\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace all occurrences of given substring in text with other string\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace escape sequences with characters\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace HTML sequences with characters\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace Regex\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace special characters with escape sequences\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace special characters with HTML-safe sequences\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace special characters with URL-safe sequences\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace Substring\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace URL sequences with characters\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Replace With\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Filter Lines by Regex\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Filter Lines by Substring\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Lines\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Sort Lines\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Reverse Text\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA1 Checksum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA256 Checksum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA384 Checksum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"SHA512 Checksum\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Sort Lines\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Sort lines in text in alphabetical order\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Sort lines in text in reverse alphabetical order\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Substring\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Trim Lines\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Unescape HTML\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Unescape String\"\nmsgstr \"\"\n\n#: data/tools.json\nmsgctxt \"tools\"\nmsgid \"Unescape URL\"\nmsgstr \"\"\n"
  },
  {
    "path": "po/tools.pot.license",
    "content": "SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0"
  },
  {
    "path": "resources/icons/scalable/status/applications-utilities-symbolic.svg.license",
    "content": "SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0"
  },
  {
    "path": "resources/icons/scalable/status/fingerprint-symbolic.svg.license",
    "content": "SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0\nSPDX-License-Identifier: GPL-3.0-or-later"
  },
  {
    "path": "resources/icons/scalable/status/funnel-symbolic.svg.license",
    "content": "SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0\nSPDX-License-Identifier: GPL-3.0-or-later"
  },
  {
    "path": "resources/icons/scalable/status/lock-symbolic.svg.license",
    "content": "SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0\nSPDX-License-Identifier: GPL-3.0-or-later"
  },
  {
    "path": "resources/icons/scalable/status/preferences-symbolic.svg.license",
    "content": "SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0\nSPDX-License-Identifier: GPL-3.0-or-later"
  },
  {
    "path": "resources/icons/scalable/status/rss-symbolic.svg.license",
    "content": "SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0\n"
  },
  {
    "path": "resources/icons/scalable/status/unlock-symbolic.svg.license",
    "content": "SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0\nSPDX-License-Identifier: GPL-3.0-or-later"
  },
  {
    "path": "resources/meson.build",
    "content": "# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nblueprints = custom_target('blueprints',\n  input: files(\n    'ui/CustomToolPage.blp',\n    'ui/Editor.blp',\n    'ui/NewToolPage.blp',\n    'ui/Preferences.blp',\n    'ui/SearchBar.blp',\n    'ui/SearchEntry.blp',\n    'ui/ShortcutsWindow.blp',\n    'ui/ToolSettings.blp',\n    'ui/Window.blp',\n    'ui/Search.blp',\n  ),\n  output: '.',\n  command: [find_program('blueprint-compiler'), 'batch-compile', '@OUTPUT@', '@CURRENT_SOURCE_DIR@', '@INPUT@'],\n)\n\nblueprint_vala = 'blueprint.vala'\n\nblueprint_hack = custom_target('blueprint-hack',\n  input: blueprints,\n  output: blueprint_vala,\n  command: [find_program('touch'), '@OUTPUT@']\n)\n\ntextpieces_sources += gnome.compile_resources('textpieces-resources',\n  'textpieces.gresource.xml',\n  source_dir: 'resources',\n  c_name: 'textpieces',\n  dependencies: blueprints\n)\n"
  },
  {
    "path": "resources/style-dark.css",
    "content": "/* style.css\n *\n * Copyright 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n *\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n"
  },
  {
    "path": "resources/style.css",
    "content": "/* style.css\n *\n * Copyright 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n *\n * This program is free software: you can redistribute it and/or modify\n * it under the terms of the GNU General Public License as published by\n * the Free Software Foundation, either version 3 of the License, or\n * (at your option) any later version.\n *\n * This program is distributed in the hope that it will be useful,\n * but WITHOUT ANY WARRANTY; without even the implied warranty of\n * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n * GNU General Public License for more details.\n *\n * You should have received a copy of the GNU General Public License\n * along with this program.  If not, see <http://www.gnu.org/licenses/>.\n *\n * SPDX-License-Identifier: GPL-3.0-or-later\n */\n\n/* Argument view */\n\n.editor-overlay {\n  padding: 6px;\n  background-color: lighter(@window_bg_color);\n  border-radius: 9px;\n  box-shadow: 0 0 0 1px alpha(black, 0.03),\n              0 1px 3px 1px alpha(black, 0.07),\n              0 2px 6px 2px alpha(black, 0.03);\n}\n"
  },
  {
    "path": "resources/textpieces.gresource.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n<!--\nSPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: GPL-3.0-or-later\n-->\n\n<gresources>\n  <gresource prefix=\"/com/github/liferooter/textpieces\">\n    <!-- UI XMLs -->\n    <file preprocess=\"xml-stripblanks\">ui/CustomToolPage.ui</file>\n    <file preprocess=\"xml-stripblanks\">ui/NewToolPage.ui</file>\n    <file preprocess=\"xml-stripblanks\">ui/Preferences.ui</file>\n    <file preprocess=\"xml-stripblanks\">ui/SearchBar.ui</file>\n    <file preprocess=\"xml-stripblanks\">ui/SearchEntry.ui</file>\n    <file preprocess=\"xml-stripblanks\">ui/ToolSettings.ui</file>\n    <file preprocess=\"xml-stripblanks\">ui/Window.ui</file>\n    <file preprocess=\"xml-stripblanks\">ui/Editor.ui</file>\n    <file preprocess=\"xml-stripblanks\">ui/Search.ui</file>\n\n    <!-- Shortcuts window -->\n    <file preprocess=\"xml-stripblanks\" alias=\"gtk/help-overlay.ui\">ui/ShortcutsWindow.ui</file>\n\n    <!-- Style CSS -->\n    <file>style.css</file>\n    <file>style-dark.css</file>\n\n    <!-- Bundled icons -->\n    <file preprocess=\"xml-stripblanks\">icons/scalable/status/fingerprint-symbolic.svg</file>\n    <file preprocess=\"xml-stripblanks\">icons/scalable/status/funnel-symbolic.svg</file>\n    <file preprocess=\"xml-stripblanks\">icons/scalable/status/lock-symbolic.svg</file>\n    <file preprocess=\"xml-stripblanks\">icons/scalable/status/preferences-symbolic.svg</file>\n    <file preprocess=\"xml-stripblanks\">icons/scalable/status/unlock-symbolic.svg</file>\n    <file preprocess=\"xml-stripblanks\">icons/scalable/status/applications-utilities-symbolic.svg</file>\n    <file preprocess=\"xml-stripblanks\">icons/scalable/status/rss-symbolic.svg</file>\n  </gresource>\n</gresources>\n"
  },
  {
    "path": "resources/ui/CustomToolPage.blp",
    "content": "// SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nusing Gtk 4.0;\nusing Adw 1;\n\ntemplate TextPiecesCustomToolPage : Box {\n  orientation: vertical;\n\n  Adw.HeaderBar {\n    show-start-title-buttons: false;\n    show-end-title-buttons: false;\n\n    [start]\n    Button {\n      clicked => go_back();\n      icon-name: \"go-previous-symbolic\";\n    }\n\n    [title]\n    Label {\n      styles [\n        \"heading\",\n      ]\n\n      label: _(\"Edit Tool\");\n    }\n\n    [end]\n    Button {\n      styles [\n        \"destructive-action\",\n      ]\n\n      clicked => delete_tool();\n      clicked => go_back();\n      label: _(\"D_elete\");\n      use-underline: true;\n    }\n  }\n\n  .TextPiecesToolSettings tool_settings {\n    can-edit-script: \"true\";\n  }\n}\n"
  },
  {
    "path": "resources/ui/Editor.blp",
    "content": "// SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nusing Gtk 4.0;\nusing GtkSource 5;\nusing Adw 1;\n\ntemplate TextPiecesEditor : Adw.Bin {\n  child:   Adw.ToastOverlay message_overlay {\n    Overlay {\n      ScrolledWindow {\n        GtkSource.View editor {\n          monospace: true;\n          auto-indent: true;\n          show-line-numbers: true;\n          smart-backspace: true;\n          smart-home-end: before;\n          top-margin: 6;\n        }\n      }\n\n      [overlay]\n      Box {\n        valign: start;\n        halign: end;\n        margin-top: 4;\n        orientation: vertical;\n        spacing: 6;\n\n        .TextPiecesSearchBar search_bar {\n          editor: \"editor\";\n        }\n\n        Box arguments_box {\n          styles [\n            \"linked\",\n            \"editor-overlay\",\n          ]\n\n          halign: end;\n          hexpand: false;\n          width-request: 300;\n          margin-top: 5;\n          margin-bottom: 2.5;\n          margin-start: 5;\n          margin-end: 10;\n          spacing: 3;\n          orientation: vertical;\n        }\n      }\n    }\n  }\n\n  ;\n}\n"
  },
  {
    "path": "resources/ui/NewToolPage.blp",
    "content": "// SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nusing Gtk 4.0;\nusing Adw 1;\n\ntemplate TextPiecesNewToolPage : Box {\n  orientation: vertical;\n\n  Adw.HeaderBar {\n    show-start-title-buttons: false;\n    show-end-title-buttons: false;\n\n    [start]\n    Button {\n      clicked => go_back();\n      icon-name: \"go-previous-symbolic\";\n    }\n\n    [title]\n    Label {\n      styles [\n        \"heading\",\n      ]\n\n      label: _(\"New Tool\");\n    }\n\n    [end]\n    Revealer {\n      transition-type: crossfade;\n      reveal-child: bind tool_settings.is_valid;\n      child: \n      Button {\n        styles [\n          \"suggested-action\",\n        ]\n\n        clicked => create();\n        clicked => go_back();\n        label: _(\"_Create\");\n        use-underline: true;\n      }\n\n      ;\n    }\n  }\n\n  .TextPiecesToolSettings tool_settings {\n  }\n}\n"
  },
  {
    "path": "resources/ui/Preferences.blp",
    "content": "// SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nusing Gtk 4.0;\nusing Adw 1;\n\ntemplate TextPiecesPreferences : Adw.PreferencesWindow {\n  can-navigate-back: true;\n  modal: true;\n\n  Adw.PreferencesPage {\n    title: _(\"_General\");\n    use-underline: true;\n    name: \"general-settings\";\n    icon-name: \"preferences-symbolic\";\n\n    Adw.PreferencesGroup {\n      title: _(\"Editor\");\n\n      Adw.ActionRow {\n        title: _(\"Editor _Font\");\n        use-underline: true;\n        activatable: true;\n        action-name: \"prefs.select-font\";\n\n        Box {\n          spacing: 12;\n\n          Label font_label {\n          }\n\n          Image {\n            icon-name: \"go-next-symbolic\";\n          }\n        }\n      }\n\n      Adw.ActionRow {\n        title: _(\"_Wrap Lines\");\n        use-underline: true;\n        activatable-widget: wrap_lines_switch;\n\n        Switch wrap_lines_switch {\n          valign: center;\n          action-name: \"settings.wrap-lines\";\n        }\n      }\n\n      Adw.ActionRow {\n        title: _(\"Tabs to _Spaces\");\n        use-underline: true;\n        activatable-widget: tabs_to_spaces;\n\n        Switch tabs_to_spaces {\n          valign: center;\n          action-name: \"settings.tabs-to-spaces\";\n        }\n      }\n\n      Adw.ActionRow {\n        title: _(\"_Tab Width in Spaces\");\n        use-underline: true;\n        activatable-widget: spaces_in_tab;\n\n        SpinButton spaces_in_tab {\n          valign: center;\n          climb-rate: 1;\n          adjustment:\n          Adjustment {\n            lower: 1;\n            upper: 21;\n            step-increment: 1;\n          }\n\n          ;\n        }\n      }\n    }\n\n    Adw.PreferencesGroup {\n      title: _(\"Appearance\");\n\n      Adw.Clamp {\n        maximum-size: 450;\n\n        .GtkSourceStyleSchemeChooserWidget style_scheme_chooser {\n        }\n      }\n    }\n  }\n\n  Adw.PreferencesPage {\n    title: _(\"T_ools\");\n    use-underline: true;\n    name: \"custom-tools\";\n    icon-name: \"applications-utilities-symbolic\";\n\n    Adw.PreferencesGroup {\n      title: _(\"Custom tools\");\n\n      [header-suffix]\n      Button {\n        styles [\n          \"flat\",\n        ]\n\n        clicked => add_new_tool();\n\n        Adw.ButtonContent {\n          icon-name: \"list-add-symbolic\";\n          label: _(\"_Add custom tool\");\n          use-underline: true;\n        }\n      }\n\n      ListBox custom_tools_listbox {\n        styles [\n          \"boxed-list\",\n        ]\n\n        row-activated => edit_tool();\n        selection-mode: none;\n      }\n\n      Adw.StatusPage {\n        icon-name: \"applications-utilities-symbolic\";\n        title: _(\"No Custom Tools\");\n        description: _(\"You can create one\");\n        visible: bind custom_tools_listbox.visible inverted;\n        vexpand: true;\n        valign: center;\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "resources/ui/Search.blp",
    "content": "// SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nusing Gtk 4.0;\nusing Adw 1;\n\ntemplate TextPiecesSearch : Adw.Bin {\n  Stack search_stack {\n    transition-type: crossfade;\n\n    StackPage {\n      name: \"search\";\n      child: ScrolledWindow {\n        child: Viewport results_viewport {\n          scroll-to-focus: true;\n\n          Adw.Clamp {\n            maximum-size: 600;\n\n            ListBox results_listbox {\n              styles [\n                \"boxed-list\",\n              ]\n\n              row-activated => on_row_activated();\n              margin-top: 24;\n              margin-bottom: 24;\n              valign: start;\n              selection-mode: browse;\n            }\n          }\n        };\n      };\n    }\n\n    StackPage {\n      name: \"placeholder\";\n      child: Adw.StatusPage {\n        icon-name: \"applications-utilities-symbolic\";\n        title: _(\"No Tools Found\");\n\n        Button {\n          styles [\n            \"flat\",\n          ]\n\n          halign: center;\n          action-name: \"win.tools-settings\";\n\n          Adw.ButtonContent {\n            icon-name: \"list-add-symbolic\";\n            label: _(\"_Add Custom Tool\");\n            use-underline: true;\n          }\n        }\n      };\n    }\n  }\n}\n"
  },
  {
    "path": "resources/ui/SearchBar.blp",
    "content": "// SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nusing Gtk 4.0;\nusing Adw 1;\n\ntemplate TextPiecesSearchBar : Adw.Bin {\n  Revealer search_revealer {\n    transition-type: slide_down;\n\n    Grid {\n      styles [\n        \"editor-overlay\",\n      ]\n\n      margin-top: 5;\n      margin-bottom: 2.5;\n      margin-start: 5;\n      margin-end: 10;\n      row-spacing: 6;\n      column-spacing: 6;\n\n      .TextPiecesSearchEntry search_entry {\n        layout {\n          row: 0;\n          column: 0;\n        }\n      }\n\n      Box {\n        styles [\n          \"linked\",\n        ]\n\n        homogeneous: true;\n\n        Button {\n          icon-name: \"go-up-symbolic\";\n          tooltip-text: _(\"Move to previous match\");\n          action-name: \"search.prev-match\";\n        }\n\n        Button {\n          icon-name: \"go-down-symbolic\";\n          tooltip-text: _(\"Move to next match\");\n          action-name: \"search.next-match\";\n        }\n\n        layout {\n          row: 0;\n          column: 1;\n        }\n      }\n\n      ToggleButton search_replace {\n        icon-name: \"edit-find-replace-symbolic\";\n        tooltip-text: _(\"Find and replace\");\n\n        layout {\n          row: 0;\n          column: 2;\n        }\n      }\n\n      ToggleButton search_options {\n        icon-name: \"preferences-symbolic\";\n        tooltip-text: _(\"Toggle search options\");\n\n        layout {\n          row: 0;\n          column: 3;\n        }\n      }\n\n      Button {\n        styles [\n          \"flat\",\n          \"circular\",\n        ]\n\n        icon-name: \"window-close-symbolic\";\n        action-name: \"search.hide\";\n        tooltip-text: _(\"Close search\");\n\n        layout {\n          row: 0;\n          column: 4;\n        }\n      }\n\n      Entry replace_entry {\n        primary-icon-name: \"edit-find-replace-symbolic\";\n        primary-icon-activatable: false;\n        placeholder-text: _(\"Replace\");\n        visible: bind search_replace.active;\n        changed => remove_error_style();\n\n        layout {\n          row: 1;\n          column: 0;\n        }\n      }\n\n      Button {\n        label: _(\"_Replace\");\n        use-underline: true;\n        visible: bind search_replace.active;\n        sensitive: bind search_entry.occurrence_position;\n        action-name: \"search.replace\";\n\n        layout {\n          row: 1;\n          column: 1;\n        }\n      }\n\n      Button {\n        label: _(\"Replace _All\");\n        use-underline: true;\n        visible: bind search_replace.active;\n        sensitive: bind search_entry.occurrences_count;\n        action-name: \"search.replace-all\";\n\n        layout {\n          row: 1;\n          column: 2;\n          column-span: 3;\n        }\n      }\n\n      Box {\n        spacing: 6;\n        visible: bind search_options.active;\n\n        CheckButton {\n          use-underline: true;\n          label: _(\"Re_gular expressions\");\n          active: bind TextPiecesSearchBar.use-regex bidirectional;\n        }\n\n        CheckButton {\n          use-underline: true;\n          label: _(\"_Case sensitive\");\n          active: bind TextPiecesSearchBar.case-sensitive bidirectional;\n        }\n\n        CheckButton {\n          use-underline: true;\n          label: _(\"Match whole _word only\");\n          active: bind TextPiecesSearchBar.whole-words bidirectional;\n        }\n\n        layout {\n          row: 2;\n          column: 0;\n          column-span: 5;\n        }\n      }\n    }\n\n    ShortcutController {\n      scope: managed;\n\n      Shortcut {\n        trigger: \"Escape\";\n        action: \"action(search.hide)\";\n      }\n    }\n  }\n\n  ShortcutController {\n    scope: managed;\n\n    Shortcut {\n      trigger: \"<Ctrl>f\";\n      action: \"action(search.show)\";\n    }\n\n    Shortcut {\n      trigger: \"<Ctrl>h\";\n      action: \"action(search.show-replace)\";\n    }\n  }\n}\n"
  },
  {
    "path": "resources/ui/SearchEntry.blp",
    "content": "// SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nusing Gtk 4.0;\n\ntemplate TextPiecesSearchEntry : Widget {\n  width-request: 300;\n\n  Image {\n    icon-name: \"edit-find-symbolic\";\n  }\n\n  Text text {\n    placeholder-text: _(\"Find\");\n    hexpand: true;\n    vexpand: true;\n    width-chars: 12;\n    max-width-chars: 12;\n\n    ShortcutController {\n      scope: local;\n\n      Shortcut {\n        action: \"action(search.prev-match)\";\n        trigger: \"<Shift>Return\";\n      }\n\n      Shortcut {\n        action: \"action(search.next-match)\";\n        trigger: \"Return\";\n      }\n    }\n  }\n\n  Label {\n    xalign: 1;\n    opacity: 0.3;\n    label: bind TextPiecesSearchEntry.occurrences_info;\n  }\n}\n"
  },
  {
    "path": "resources/ui/ShortcutsWindow.blp",
    "content": "// SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nusing Gtk 4.0;\n\nShortcutsWindow help_overlay {\n  ShortcutsSection {\n    section-name: \"shortcuts\";\n    max-height: 12;\n\n    ShortcutsGroup {\n      title: _(\"General\");\n\n      ShortcutsShortcut {\n        title: _(\"New window\");\n        action-name: \"app.new-window\";\n      }\n\n      ShortcutsShortcut {\n        title: _(\"Keyboard shortcuts\");\n        action-name: \"win.show-help-overlay\";\n      }\n\n      ShortcutsShortcut {\n        title: _(\"Preferences\");\n        action-name: \"win.open-preferences\";\n      }\n\n      ShortcutsShortcut {\n        title: _(\"Close window\");\n        action-name: \"window.close\";\n      }\n\n      ShortcutsShortcut {\n        title: _(\"Quit\");\n        action-name: \"app.quit\";\n      }\n    }\n\n    ShortcutsGroup {\n      title: _(\"Tools\");\n\n      ShortcutsShortcut {\n        title: _(\"Apply\");\n        action-name: \"win.apply\";\n      }\n\n      ShortcutsShortcut {\n        title: _(\"Select tool\");\n        action-name: \"win.toggle-search\";\n      }\n\n      ShortcutsShortcut {\n        title: _(\"Just to the tool options\");\n        action-name: \"win.jump-to-args\";\n      }\n    }\n\n    ShortcutsGroup {\n      title: _(\"Undo and Redo\");\n\n      ShortcutsShortcut {\n        title: _(\"Undo\");\n        accelerator: \"<ctrl>z\";\n      }\n\n      ShortcutsShortcut {\n        title: _(\"Redo\");\n        accelerator: \"<ctrl><shift>z\";\n      }\n    }\n\n    ShortcutsGroup {\n      title: _(\"Files\");\n\n      ShortcutsShortcut {\n        title: _(\"Load from file\");\n        action-name: \"win.load-file\";\n      }\n\n      ShortcutsShortcut {\n        title: _(\"Save to file\");\n        action-name: \"win.save-as\";\n      }\n    }\n\n    ShortcutsGroup {\n      title: _(\"Search and Replace\");\n\n      ShortcutsShortcut {\n        title: _(\"Search\");\n        accelerator: \"<ctrl>f\";\n      }\n\n      ShortcutsShortcut {\n        title: _(\"Replace\");\n        accelerator: \"<ctrl>h\";\n      }\n\n      ShortcutsShortcut {\n        title: _(\"Move to previous match\");\n        accelerator: \"<Shift>Return\";\n      }\n\n      ShortcutsShortcut {\n        title: _(\"Move to next match\");\n        accelerator: \"Return\";\n      }\n    }\n\n  }\n}\n"
  },
  {
    "path": "resources/ui/ToolSettings.blp",
    "content": "// SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nusing Gtk 4.0;\nusing Adw 1;\n\ntemplate TextPiecesToolSettings : Adw.Bin {\n  ScrolledWindow {\n    Viewport {\n      vexpand: true;\n      scroll-to-focus: true;\n\n      Adw.Clamp {\n        maximum-size: 400;\n\n        Box {\n          orientation: vertical;\n          margin-top: 20;\n          margin-bottom: 20;\n          spacing: 20;\n\n          ListBox {\n            styles [\n              \"boxed-list\",\n            ]\n\n            selection-mode: none;\n\n            Adw.ActionRow {\n              title: _(\"_Name\");\n              use-underline: true;\n              activatable-widget: name_entry;\n\n              Entry name_entry {\n                activate => gtk_widget_grab_focus(description_entry);\n                valign: center;\n              }\n            }\n\n            Adw.ActionRow {\n              title: _(\"_Description\");\n              use-underline: true;\n              activatable-widget: description_entry;\n\n              Entry description_entry {\n                valign: center;\n              }\n            }\n\n            Adw.ActionRow {\n              title: _(\"_Arguments\");\n              use-underline: true;\n              activatable-widget: arguments_number;\n\n              SpinButton arguments_number {\n                valign: center;\n                adjustment: \n                Adjustment {\n                  lower: 0;\n                  upper: 100;\n                  step-increment: 1;\n                  value: 0;\n                }\n\n                ;\n              }\n            }\n          }\n\n          ListBox edit_script_list {\n            styles [\n              \"boxed-list\",\n            ]\n\n            selection-mode: none;\n\n            Adw.ActionRow {\n              activated => edit_script();\n              title: _(\"Edit _Script\");\n              use-underline: true;\n              activatable: true;\n\n              Image {\n                valign: center;\n                icon-name: \"document-edit-symbolic\";\n              }\n            }\n          }\n\n          ListBox argument_list {\n            styles [\n              \"boxed-list\",\n            ]\n\n            visible: false;\n            selection-mode: none;\n          }\n        }\n      }\n    }\n  }\n}\n"
  },
  {
    "path": "resources/ui/Window.blp",
    "content": "// SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nusing Gtk 4.0;\nusing Adw 1;\n\ntemplate TextPiecesWindow : Adw.ApplicationWindow {\n  title: _(\"Text Pieces\");\n  focus-widget: editor;\n  icon-name: \"com.github.liferooter.textpieces\";\n\n  Box {\n    orientation: vertical;\n\n    Adw.HeaderBar headerbar {\n      [start]\n      Revealer {\n        transition-type: crossfade;\n        reveal-child: bind search_bar.search-mode-enabled inverted;\n        child:\n        Box {\n          styles [\n            \"linked\",\n          ]\n\n          Button {\n            styles [\n              \"suggested-action\",\n            ]\n\n            label: _(\"_Apply\");\n            use-underline: true;\n            action-name: \"win.apply\";\n            tooltip-text: _(\"Apply Selected Tool\");\n          }\n\n          Button {\n            styles [\n              \"suggested-action\",\n            ]\n\n            icon-name: \"edit-copy-symbolic\";\n            action-name: \"win.copy\";\n            tooltip-text: _(\"Copy\");\n          }\n        }\n\n        ;\n      }\n\n      [title]\n      ToggleButton tool_button {\n        styles [\n          \"flat\",\n          \"heading\",\n        ]\n\n        toggled => on_search_toggled();\n        child:\n        Adw.ButtonContent tool_button_content {\n        }\n\n        ;\n      }\n\n      [end]\n      MenuButton {\n        icon-name: \"open-menu-symbolic\";\n        menu-model: app-menu;\n        tooltip-text: _(\"Main Menu\");\n        primary: true;\n      }\n\n      [end]\n      Button {\n        icon-name: \"document-save-symbolic\";\n        tooltip-text: _(\"Save to File\");\n        action-name: \"win.save-as\";\n      }\n\n      [end]\n      Button {\n        icon-name: \"document-open-symbolic\";\n        tooltip-text: _(\"Load from File\");\n        action-name: \"win.load-file\";\n      }\n    }\n\n    SearchBar search_bar {\n      search-mode-enabled: bind tool_button.active bidirectional;\n      child: SearchEntry search_entry {};\n    }\n\n    Stack content_stack {\n      vexpand: true;\n      transition-type: crossfade;\n\n      StackPage {\n        name: \"editor\";\n        child:\n        .TextPiecesEditor editor {\n          vexpand: \"true\";\n        }\n\n        ;\n      }\n\n      StackPage {\n        name: \"search\";\n        child: .TextPiecesSearch search {\n          search-entry: search_entry;\n          tool-selected => on_tool_selected();\n        };\n      }\n    }\n  }\n}\n\nmenu app-menu {\n  section {\n    item {\n      label: _(\"_New Window\");\n      action: \"app.new-window\";\n      use-underline: \"true\";\n    }\n  }\n\n  section {\n    item {\n      label: _(\"_Find/Replace…\");\n      action: \"win.find\";\n      use-underline: \"true\";\n    }\n  }\n\n  section {\n    item {\n      label: _(\"_Preferences\");\n      action: \"win.open-preferences\";\n      use-underline: \"true\";\n    }\n\n    item {\n      label: _(\"_Keyboard Shortcuts\");\n      action: \"win.show-help-overlay\";\n      use-underline: \"true\";\n    }\n\n    item {\n      label: _(\"_About Text Pieces\");\n      action: \"win.about\";\n      use-underline: \"true\";\n    }\n  }\n}\n"
  },
  {
    "path": "screenshots/screenshot.png.license",
    "content": "SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0"
  },
  {
    "path": "screenshots/slide1.png.license",
    "content": "SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0"
  },
  {
    "path": "screenshots/slide2.png.license",
    "content": "SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0"
  },
  {
    "path": "screenshots/slide3.png.license",
    "content": "SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0"
  },
  {
    "path": "screenshots/slide4.png.license",
    "content": "SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0"
  },
  {
    "path": "screenshots/slide5.png.license",
    "content": "SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0"
  },
  {
    "path": "scripts/base64-decode",
    "content": "#!/usr/bin/env bash\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nbase64 -d\n"
  },
  {
    "path": "scripts/base64-encode",
    "content": "#!/usr/bin/env bash\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nbase64\n"
  },
  {
    "path": "scripts/count-lines",
    "content": "#!/usr/bin/env bash\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nwc -l\n"
  },
  {
    "path": "scripts/count-symbols",
    "content": "#!/usr/bin/env bash\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nwc -m\n"
  },
  {
    "path": "scripts/count-words",
    "content": "#!/usr/bin/env bash\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nwc -w\n"
  },
  {
    "path": "scripts/escape-html",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom html import escape\nfrom sys import stdin, stdout\n\nstdout.write(escape(stdin.read()))\n"
  },
  {
    "path": "scripts/escape-string",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom sys import stdin, stdout\n\nstdout.write(repr(stdin.read())[1:-1].replace('\"', r'\\\"'))\n"
  },
  {
    "path": "scripts/escape-url",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom sys import stdin, stdout\nfrom urllib.parse import quote_plus\n\nstdout.write(quote_plus(stdin.read()))\n"
  },
  {
    "path": "scripts/filter-regex",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nimport re\nimport warnings\nfrom sys import argv, stderr, stdin, stdout\n\nwarnings.simplefilter(action='ignore', category=FutureWarning)\n\npattern = argv[1]\n\ntry:\n    regex = re.compile(pattern)\nexcept re.error as err:\n    stderr.write(f'Invalid regex: {err}')\n    exit(1)\n\nfor line in stdin:\n    if regex.search(line):\n        stdout.write(line)\n"
  },
  {
    "path": "scripts/filter-substring",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom sys import argv, stdin, stdout\n\nfor line in stdin:\n    if argv[1] in line:\n        stdout.write(line)\n"
  },
  {
    "path": "scripts/format-json",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nimport json\nfrom sys import stderr, stdin, stdout\n\ntry:\n    _dict = json.load(stdin)\nexcept json.JSONDecodeError as err:\n    stderr.write(f'Invalid JSON: {err}')\n    exit(1)\n\njson.dump(\n    _dict,\n    stdout,\n    indent='  ',\n    ensure_ascii=False\n)\n"
  },
  {
    "path": "scripts/format-xml",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nimport xml.etree.ElementTree as xml\nfrom sys import stderr, stdin, stdout\n\netree = xml.ElementTree()\ntry:\n    etree.parse(stdin)\nexcept xml.ParseError as err:\n    stderr.write(f\"Can't parse XML: {err}\")\n    exit(1)\nxml.indent(etree)\netree.write(\n    stdout,\n    xml_declaration=True,\n    encoding='unicode'\n)\n"
  },
  {
    "path": "scripts/json-to-yaml",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nimport json\nfrom sys import stderr, stdin, stdout\n\nimport yaml\n\ntry:\n    _dict = json.load(stdin)\nexcept json.JSONDecodeError as err:\n    stderr.write(f'Invalid JSON: {err}')\n    exit(1)\n\nyaml.dump(\n    _dict,\n    stdout,\n    sort_keys=False,\n    allow_unicode=True\n)\n"
  },
  {
    "path": "scripts/md5",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom hashlib import md5\nfrom sys import stdin, stdout\n\nstdout.write(md5(stdin.read().encode()).hexdigest())\n"
  },
  {
    "path": "scripts/minify-json",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nimport json\nfrom sys import stderr, stdin, stdout\n\ntry:\n    _dict = json.load(stdin)\nexcept json.JSONDecodeError as err:\n    stderr.write(f'Invalid JSON: {err}')\n    exit(1)\njson.dump(\n    _dict,\n    stdout,\n    separators=(',', ':'),\n    ensure_ascii=False\n)\n"
  },
  {
    "path": "scripts/remove-regex",
    "content": "#!/usr/bin/python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nimport re\nimport warnings\nfrom sys import argv, stderr, stdin, stdout\n\nwarnings.simplefilter(action='ignore', category=FutureWarning)\n\n\ntry:\n    stdout.write(re.sub(argv[1], '', stdin.read(), flags=re.MULTILINE))\nexcept re.error as err:\n    stderr.write(f'Invalid regex: {err}')\n"
  },
  {
    "path": "scripts/remove-substring",
    "content": "#!/usr/bin/python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom sys import argv, stdin, stdout\n\nstdout.write(stdin.read().replace(argv[1], ''))\n"
  },
  {
    "path": "scripts/remove-trailing",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom sys import stdin, stdout\n\nstdout.write('\\n'.join([\n    line.rstrip()\n    for line in stdin.read().rstrip().split('\\n')\n]))\n"
  },
  {
    "path": "scripts/replace-regex",
    "content": "#!/usr/bin/python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nimport re\nimport warnings\nfrom sys import argv, stderr, stdin, stdout\n\nwarnings.simplefilter(action='ignore', category=FutureWarning)\n\ntry:\n    stdout.write(re.sub(argv[1], argv[2], stdin.read(), flags=re.MULTILINE))\nexcept re.error as err:\n    stderr.write(f'Invalid regex: {err}')\n    exit(1)\n"
  },
  {
    "path": "scripts/replace-substring",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom sys import argv, stdin, stdout\n\nstdout.write(stdin.read().replace(argv[1], argv[2]))\n"
  },
  {
    "path": "scripts/reverse-filter-regex",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nimport re\nimport warnings\nfrom sys import argv, stderr, stdin, stdout\n\nwarnings.simplefilter(action='ignore', category=FutureWarning)\n\npattern = argv[1]\n\ntry:\n    regex = re.compile(pattern, flags=re.MULTILINE)\nexcept re.error as err:\n    stderr.write(f'Invalid regex: {err}')\n    exit(1)\n\nfor line in stdin:\n    if not regex.search(line):\n        stdout.write(line)\n"
  },
  {
    "path": "scripts/reverse-filter-substring",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom sys import stdin, stdout, argv\n\nfor line in stdin:\n    if argv[1] not in line:\n        stdout.write(line)\n"
  },
  {
    "path": "scripts/reverse-lines",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom sys import stdin, stdout\n\nstdout.write('\\n'.join(stdin.read().split('\\n')[::-1]))\n"
  },
  {
    "path": "scripts/reverse-string",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom sys import stdin, stdout\n\nstdout.write(stdin.read()[::-1])\n"
  },
  {
    "path": "scripts/rss-from-opml",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2022 Adrián Bíro\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom sys import stderr, stdin, stdout\nfrom xml.etree import ElementTree\n\ntry:\n    tree = ElementTree.parse(stdin)\nexcept ElementTree.ParseError as err:\n    stderr.write(f\"Can't parse OPML-XML: {err}\")\n    exit(1)\n\ntitles_urls = {}\nfor i in tree.findall('.//outline'):\n    url = i.attrib.get('xmlUrl')\n    title = i.attrib.get('title')\n    titles_urls.update({title: url})\n\nfor v in titles_urls.values():\n    # Some RSS readers tend to export invalid duplicities, this takes care of that.\n    if v is not None:\n        stdout.write(f'{v.strip()}\\n')\n"
  },
  {
    "path": "scripts/sha1",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom hashlib import sha1\nfrom sys import stdin, stdout\n\nstdout.write(sha1(stdin.read().encode()).hexdigest())\n"
  },
  {
    "path": "scripts/sha256",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom hashlib import sha256\nfrom sys import stdin, stdout\n\nstdout.write(sha256(stdin.read().encode()).hexdigest())\n"
  },
  {
    "path": "scripts/sha384",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom hashlib import sha384\nfrom sys import stdin, stdout\n\nstdout.write(sha384(stdin.read().encode()).hexdigest())\n"
  },
  {
    "path": "scripts/sha512",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom hashlib import sha512\nfrom sys import stdin, stdout\n\nstdout.write(sha512(stdin.read().encode()).hexdigest())\n"
  },
  {
    "path": "scripts/sort-lines",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom sys import stdin, stdout\n\nstdout.write('\\n'.join(sorted(stdin.read().split('\\n'))))\n"
  },
  {
    "path": "scripts/sort-lines-reverse",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom sys import stdin, stdout\n\nstdout.write('\\n'.join(sorted(stdin.read().split('\\n'), reverse=True)))\n"
  },
  {
    "path": "scripts/template",
    "content": "#!/usr/bin/env python3\n\n# Hey, this is the script of your new tool.\n\n# It's easy to create tool.\n\n# - Get selected text from stdin\n# - Write result to stdout\n# - Write errors to stderr\n# - Exit with non-zero exit code to prevent applying on error\n# - Get arguments from command line\n\n# Here is example:\n\nfrom sys import stdin, stdout, stderr, argv\n\n# Read from stdin...\ninput_string = stdin.read()\n\n# ...process string...\noutput_string = 'You said \"' + input_string + '\"'\n\n# IMPORTANT: trailing newline isn't ignored, so you shouldn't\n# print it if you don't really want it to be in result string.\n\n# ...handle errors...\nif len(output_string) > 30:\n    stderr.write('The message is too long')\n    exit(1)\n\n# ...and write result to stdout\nstdout.write(output_string)\n\n# You can use any language, not only Python.\n# Just replace comand in first line with\n# other the interpreter. (https://en.wikipedia.org/wiki/Shebang_(Unix))\n\n# Compiled languages is not supported, but\n# if you *really* want, you can put any\n# binary to this script location\n"
  },
  {
    "path": "scripts/template.license",
    "content": "SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: GPL-3.0-or-later"
  },
  {
    "path": "scripts/trim",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom sys import stdin, stdout\n\nstdout.write('\\n'.join([\n    line.strip()\n    for line in stdin.read().strip().split('\\n')\n]))\n"
  },
  {
    "path": "scripts/unescape-html",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom html import unescape\nfrom sys import stdin, stdout\n\nstdout.write(unescape(stdin.read()))\n"
  },
  {
    "path": "scripts/unescape-string",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nimport warnings\nfrom sys import stderr, stdin, stdout\n\nwith warnings.catch_warnings(record=True) as w:\n    stdout.write(stdin.read().encode('utf-8').decode('unicode_escape'))\n    if w:\n        for warning in map(lambda warning: warning.message, w):\n            if isinstance(warning, DeprecationWarning):\n                stderr.write(f\"Can't unescape string: {str(warning)}\")\n                exit(1)\n"
  },
  {
    "path": "scripts/unescape-url",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nfrom sys import stdin, stdout\nfrom urllib.parse import unquote_plus\n\nstdout.write(unquote_plus(stdin.read()))\n"
  },
  {
    "path": "scripts/yaml-to-json",
    "content": "#!/usr/bin/env python3\n\n# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\nimport json\nfrom sys import stderr, stdin, stdout\n\nimport yaml\n\ntry:\n    _dict = yaml.load(stdin, Loader=yaml.SafeLoader)\nexcept Exception as err:\n    stderr.write(f'Invalid YAML: {err}')\n    exit(1)\n\njson.dump(\n    _dict,\n    stdout,\n    indent='  ',\n    ensure_ascii=False\n)\n"
  },
  {
    "path": "src/Application.vala",
    "content": "// Copyright 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n// SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nnamespace TextPieces {\n    /**\n     * Action accelerator\n     */\n    struct ActionAccel {\n        string action;\n        string accel;\n    }\n\n    /**\n     * Text Pieces application\n     */\n    class Application : Adw.Application {\n        /**\n         * Application instance\n         */\n        public static TextPieces.Application instance;\n\n        /**\n         * Text Pieces settings\n         */\n        public static GLib.Settings settings = new GLib.Settings (\"com.github.liferooter.textpieces\");\n\n        /**\n         * Tools controller\n         */\n        public static ToolsController tools = new ToolsController ();\n\n        /**\n         * Application actions\n         *\n         * Every entry has this form:\n         * {{{\n         *   { \"application-name\", action_callback }\n         * }}}\n         */\n        private static ActionEntry[] ACTIONS = {\n            { \"quit\", quit },\n            { \"new-window\", new_window }\n        };\n\n        /**\n         * Shortcuts for actions\n         *\n         * Every entry has this form:\n         * {{{\n         *   { \"prefix.action\", \"shortcut\" }\n         * }}}\n         */\n        private static ActionAccel[] ACTION_ACCELS = {\n            /*              Window actions              */\n            { \"win.apply\"             , \"<Ctrl>Return\"   },\n            { \"win.copy\"              , \"<Ctrl><Shift>c\" },\n            { \"win.open-preferences\"  , \"<Ctrl>comma\"    },\n            { \"win.load-file\"         , \"<Ctrl>o\"        },\n            { \"win.save-as\"           , \"<Ctrl>s\"        },\n            { \"win.toggle-search\"     , \"<Alt>s\"         },\n            { \"win.jump-to-args\"      , \"<Ctrl>e\"        },\n            { \"window.close\"          , \"<Ctrl>w\"        },\n\n            /*            Application actions           */\n            { \"app.quit\"              , \"<Ctrl>q\"        },\n            { \"app.new-window\"        , \"<Ctrl>n\"        }\n        };\n\n        /**\n         * CSS provider used to set style scheme\n         */\n        private Gtk.CssProvider style_scheme_css_provider = new Gtk.CssProvider ();\n\n         /**\n          * Style scheme\n          */\n        public GtkSource.StyleScheme style_scheme {\n            set {\n                _style_scheme = value;\n\n                /* Apply style scheme to the application */\n                var is_dark = Recoloring.is_scheme_dark (value);\n                Adw.StyleManager.get_default ()\n                    .color_scheme = is_dark\n                        ? Adw.ColorScheme.FORCE_DARK\n                        : Adw.ColorScheme.FORCE_LIGHT;\n                style_scheme_css_provider.load_from_data (\n                    Recoloring.generate_css (value).data\n                );\n\n                settings.set_string (\"style-scheme\", value.id);\n            } get {\n                return _style_scheme;\n            }\n        }\n        private GtkSource.StyleScheme _style_scheme = GtkSource.StyleSchemeManager\n            .get_default ()\n            .get_scheme (settings.get_string (\"style-scheme\"));\n\n        public Application () {\n            ApplicationFlags flags = FLAGS_NONE | HANDLES_OPEN;\n\n            Object (\n                flags: flags,\n                application_id: \"com.github.liferooter.textpieces\"\n            );\n        }\n\n        /**\n         * Startup method\n         *\n         * This method is called once at application\n         * startup. Do here everything that have to be\n         * done only once. Think of it as another `main`.\n         */\n        protected override void startup () {\n            base.startup ();\n\n            /* Place an instance to static field\n               to bring it into global scope */\n            instance = this;\n\n            /* Initialize localization */\n            Intl.setlocale ();\n            Intl.bindtextdomain (Config.GETTEXT_PACKAGE, Config.GNOMELOCALEDIR);\n            Intl.bind_textdomain_codeset (Config.GETTEXT_PACKAGE, \"UTF-8\");\n            Intl.textdomain (Config.GETTEXT_PACKAGE);\n\n            /* Setup style scheme */\n            Gtk.StyleContext.add_provider_for_display (\n                Gdk.Display.get_default (),\n                style_scheme_css_provider,\n                Gtk.STYLE_PROVIDER_PRIORITY_USER\n            );\n\n            /* Initialize libs */\n            GtkSource.init ();\n\n            /* Initialize recoloring mechanism */\n            style_scheme = style_scheme;\n\n            /* Setup actions */\n            add_action_entries (ACTIONS, this);\n\n            /* Setup accels */\n            foreach (var action_accel in ACTION_ACCELS) {\n                set_accels_for_action (\n                    action_accel.action,\n                    { action_accel.accel }\n                );\n            }\n\n            /* Load tools */\n            tools.load.begin ();\n        }\n\n        /**\n         * Activate method\n         *\n         * This method is called every time\n         * when application is activated.\n         * Depending on system it may be done\n         * once or many times. Think of it as\n         * an application entry point.\n         */\n        protected override void activate () {\n            /* Create window window */\n            new_window ();\n        }\n\n        /**\n         * Open file method\n         *\n         * This method is called when user\n         * opens a file with the application.\n         */\n        protected override void open (File[] files, string hint) {\n            foreach (var file in files) {\n                var win = new TextPieces.Window (this);\n                win.load_from (file);\n                win.present();\n            }\n        }\n\n        /**\n         * Shutdown method\n         *\n         * This method is called when app\n         * is being terminated.\n         */\n        protected override void shutdown () {\n            /* Save window geometry if can */\n            var window = get_active_window ();\n            if (window is TextPieces.Window) {\n                ((TextPieces.Window) window).save_window_size ();\n            } else {\n                (window?.get_transient_for () as TextPieces.Window)?.save_window_size ();\n            }\n\n            base.shutdown ();\n        }\n\n        /**\n         * New window action\n         */\n        public void new_window () {\n            var win = new TextPieces.Window (this);\n\n            win.present ();\n        }\n\n        /**\n         * Program entry point\n         *\n         * Do nothing here except creating\n         * application instance. For everything\n         * else use `Application::startup` and\n         * `Application::activate`.\n         */\n        public static int main (string[] args) {\n            ensure_types ();\n\n            var app = new TextPieces.Application ();\n            return app.run (args);\n        }\n\n        /**\n         * Ensure needed types are registered\n         */\n        private static void ensure_types () {\n            typeof (TextPieces.SearchBar).ensure ();\n        }\n    }\n}\n"
  },
  {
    "path": "src/meson.build",
    "content": "# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: GPL-3.0-or-later\n\ntextpieces_sources += [\n  'Application.vala',\n\n  'tools/Tool.vala',\n  'tools/ToolsController.vala',\n\n  'widgets/Window.vala',\n  'widgets/Editor.vala',\n  'widgets/Search.vala',\n  'widgets/SearchBar.vala',\n  'widgets/SearchEntry.vala',\n\n  'widgets/preferences/Preferences.vala',\n  'widgets/preferences/ToolSettings.vala',\n\n  'widgets/preferences/pages/CustomToolPage.vala',\n  'widgets/preferences/pages/NewToolPage.vala',\n\n  'utils/Utils.vala',\n  'utils/Recoloring.vala',\n\n]\n\nexecutable('textpieces', [blueprint_hack, textpieces_sources],\n  c_args: [\n    '-DVERSION=\"' + meson.project_version() + '\"',\n    '-DPKGDATADIR=\"' + pkgdata_dir + '\"',\n    '-DSCRIPTDIR=\"' +  pkgdata_dir / 'scripts' + '\"',\n    '-DGETTEXT_PACKAGE=\"' + meson.project_name() + '\"',\n    '-DGNOMELOCALEDIR=\"' + get_option('prefix') / get_option('localedir') + '\"',\n  ],\n  vala_args: [\n    '--gresourcesdir=' + blueprints.full_path(),\n  ],\n  dependencies: textpieces_deps,\n  install: true,\n)\n"
  },
  {
    "path": "src/tools/Tool.vala",
    "content": "// Copyright 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n// SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nusing Gee;\n\nnamespace TextPieces {\n    /**\n     * Script result\n     */\n    public struct ScriptResult {\n        string? output;\n        string? error;\n    }\n\n    /**\n     * Tool object class\n     */\n    public class Tool : Object {\n        /**\n         * Whether the application\n         * is running in Flatpak\n         */\n        public static Lazy<bool> IN_FLATPAK = new Lazy<bool> (\n            () => File.new_for_path (\"/.flatpak-info\").query_exists (null)\n        );\n\n        /**\n         * Path to the directory containing\n         * custom tool scripts\n         */\n        public static Lazy<string> CUSTOM_TOOLS_DIR = new Lazy<string> (\n            /* Custom tools are stored in ~/.local/share/textpieces/scripts.\n               They are there even in Flatpak because Flatpak-ed apps can't\n               write to files from other apps' directories even through portals */\n            () => Path.build_filename (\n                Environment.get_home_dir (), \".local\", \"share\", \"textpieces\", \"scripts\"\n            )\n        );\n\n        /**\n         * Name of the tool\n         */\n        public string name { get; set; }\n        public string translated_name {\n            get {\n                return dpgettext2(null, \"tools\", name);\n            }\n        }\n\n        /**\n         * Description of the tool\n         */\n        public string description { get; set; }\n        public string translated_description {\n            get {\n                return dpgettext2(null, \"tools\", description);\n            }\n        }\n\n        /**\n         * Tool script's command\n         * line aguments\n         */\n        public string[] arguments;\n        public string[] translated_arguments {\n            owned get {\n                string[] res = {};\n                foreach (var arg in arguments) {\n                    res += dpgettext2(null, \"tools\", arg);\n                }\n\n                return res;\n            }\n        }\n\n        /**\n         * The icon of the tool\n         */\n        public string icon = \"applications-utilities-symbolic\";\n\n        /**\n         * Tool script filename\n         */\n        public string script;\n\n        /**\n         * Whether the tool is pre-installed\n         */\n        public bool is_system;\n\n        static construct {\n            var tools_dir = File.new_for_path (CUSTOM_TOOLS_DIR.get ());\n\n            /* Create tools directory */\n            Utils.ensure_directory_exists.begin (tools_dir);\n        }\n\n        /**\n         * Apply tool on text\n         *\n         * @param input input text\n         * @param args  command line args tool script\n         *\n         * @return result of script executing\n         */\n        public ScriptResult apply (string input, string[] args) {\n            /* Get directory containing script */\n            var scriptdir = is_system\n                ? Config.SCRIPTDIR\n                : CUSTOM_TOOLS_DIR.get ();\n\n            /* Build script command line */\n\n            string[] cmdline = {};\n            /* Run script on host via\n               `flatpak-spawn --host`\n               if running in Flatpak */\n            if (!is_system && IN_FLATPAK.get ()) {\n                cmdline += \"flatpak-spawn\";\n                cmdline += \"--host\";\n            }\n            cmdline += Path.build_filename (scriptdir, script);\n\n            /* Append args to command line */\n            foreach (var arg in args)\n                cmdline += arg;\n\n            try {\n                /* Run script */\n                var process = new Subprocess.newv (\n                    cmdline,\n                    STDIN_PIPE  |\n                    STDOUT_PIPE |\n                    STDERR_PIPE\n                );\n\n                string stdout;\n                string stderr;\n                /* Send input to script, catch stdout and stderr */\n                process.communicate_utf8 (input, null, out stdout, out stderr);\n\n                bool success = process.get_successful ();\n\n                return {\n                    /* Return output if\n                       script successed */\n                    success\n                        ? stdout\n                        : null,\n                    /* Return error if stderr\n                       is not empty */\n                    (stderr ?? \"\") != \"\"\n                        ? Markup.escape_text (stderr, -1)\n                        : null\n                };\n            } catch (Error e) {\n                critical (\"Internal error: %s\", e.message);\n                return {\n                    e.message,\n                    null\n                };\n            }\n        }\n\n        /**\n         * Edit tool script with external editor\n         */\n        public void open (Gtk.Window? window)\n                requires (!this.is_system) {\n            new Xdp.Portal ().open_uri.begin (\n                Xdp.parent_new_gtk(window),\n                File.new_build_filename (\n                    Tool.CUSTOM_TOOLS_DIR.get (), this.script\n                ).get_uri (),\n                ASK | WRITABLE,\n                null\n            );\n        }\n\n        /**\n         * Generate filename for tool script\n         */\n        public static string generate_filename (string name) {\n            /* Generate salt */\n            var salt = Checksum.compute_for_string (\n                SHA256,\n                Random.next_int  ()\n                      .to_string ()\n            ).slice (0, 8);\n\n            /* Generate filename in form:\n               \"filename-salt\", where salt\n               is eight random characters */\n            return \"%s-%s\".printf (\n                name.down ()\n                    .replace (\" \", \"_\")\n                    .replace (\"?\", \"x\"),\n                salt\n            );\n        }\n    }\n\n    /**\n     * Build tool row\n     */\n    Gtk.Widget build_list_row (Object item) {\n        var tool = (Tool) item;\n\n        return new Adw.ActionRow () {\n            title = tool.translated_name,\n            subtitle = tool.translated_description,\n            icon_name = tool.icon,\n            activatable = true\n        };\n    }\n}\n"
  },
  {
    "path": "src/tools/ToolsController.vala",
    "content": "// Copyright 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n// SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nusing Gee;\n\nnamespace TextPieces {\n    /**\n     * Tools controller\n     */\n    class ToolsController : Object {\n        /**\n         * Configuration directory\n         */\n        static Lazy<string> CONFIG_DIR = new Lazy<string> (\n            () => Path.build_filename (\n                Environment.get_user_config_dir (), \"textpieces\"\n            )\n        );\n\n        /**\n         * File containing\n         * pre-installed tools metadata\n         */\n        static Lazy<File> SYSTEM_TOOLS_FILE = new Lazy<File> (\n            () => File.new_build_filename (\n                Config.PKGDATADIR, \"tools.json\"\n            )\n        );\n\n        /**\n         * File containing\n         * custom tools metadata\n         */\n        static Lazy<File> CUSTOM_TOOLS_FILE = new Lazy<File> (\n            () => File.new_build_filename (\n                CONFIG_DIR.get (), \"tools.json\"\n            )\n        );\n\n        /**\n         * List model of pre-installed tools\n         */\n        public ListStore system_tools = new ListStore (typeof (Tool));\n\n        /**\n         * List model of custom tools\n         */\n        public ListStore custom_tools = new ListStore (typeof (Tool));\n\n        /**\n         * List model of all tools\n         */\n        public ListStore all_tools {\n            get; private set; default = new ListStore (typeof (Tool));\n        }\n\n        /**\n         * Queue of deleted tools\n         * pending script removal\n         */\n        private GLib.Queue<Tool> removed_tools = new GLib.Queue<Tool> ();\n\n        public async void load () {\n            /* Create custom tools index if not exists */\n            if (!CUSTOM_TOOLS_FILE.get ().query_exists ()) {\n                try {\n                    yield save_custom_tools ();\n                } catch (Error e) {\n                    critical (\"Can't create custom tools index: %s\", e.message);\n                }\n            }\n\n            yield update_tools ();\n        }\n\n        /**\n         * Update tools\n         */\n        public async void update_tools () {\n            /* Remove and load pre-installed tools */\n            system_tools.remove_all ();\n            var new_system_tools = load_tools_from_file (SYSTEM_TOOLS_FILE.get ())\n                ?? new ListStore (typeof (Tool));\n            for (var i = 0; i < new_system_tools.get_n_items (); i++)\n                system_tools.append (new_system_tools.get_item (i));\n\n            /* Remove and load custom tools */\n            custom_tools.remove_all ();\n            var new_custom_tools = load_tools_from_file (CUSTOM_TOOLS_FILE.get ())\n                ?? new ListStore (typeof (Tool));\n            for (var i = 0; i < new_custom_tools.get_n_items (); i++)\n                custom_tools.append (new_custom_tools.get_item (i));\n\n            /* Update model containing all tools */\n            update_all_tools ();\n        }\n\n        /**\n         * Update list model of all tools\n         */\n        void update_all_tools () {\n            /* Remove all tools */\n            all_tools.remove_all ();\n\n            /* Append pre-installed tools */\n            for (var i = 0; i < system_tools.get_n_items (); i++)\n                all_tools.append (system_tools.get_item (i));\n\n            /* Append custom tools */\n            for (var i = 0; i < custom_tools.get_n_items (); i++)\n                all_tools.append (custom_tools.get_item (i));\n        }\n\n        /**\n         * Load tools from JSON file with tools metadata\n         *\n         * @param file metadata file\n         *\n         * @return list model containing loaded tools\n         */\n        private static ListStore load_tools_from_file (File file) {\n            var tools = new ListStore (typeof (Tool));\n\n            /* Return empty list model\n               if file doesn't exist */\n            if (!file.query_exists ()) {\n                critical (\"Can't load tools from non-existing file \\\"%s\\\"\", file.get_path ());\n                return tools;\n            }\n\n            /* Load JSON to parser */\n            var parser = new Json.Parser ();\n            try {\n                parser.load_from_file (file.get_path ());\n            } catch (Error e) {\n                critical (\"Can't load tools from \\\"%s\\\": %s\", file.get_path (), e.message);\n                return tools;\n            }\n\n            /* Get root object */\n            var root = parser.get_root ();\n            var obj = root?.get_object ();\n\n            /* Return empty list if\n               there are no root object */\n            if (obj == null) {\n                critical (\"Can't load tools: file doesn't contain valid JSON object\");\n                return tools;\n            }\n\n            /* Get whether file contains\n               pre-installed tools metadata */\n            var is_system = obj.get_boolean_member_with_default (\n                \"is_system\", false\n            );\n\n            /* Get tools array */\n            var json_tools = obj.get_array_member (\"tools\");\n\n            /* Return empty list model\n               if there are no tools list */\n            if (json_tools == null) {\n                critical (\"Can't load tools: file doesn't contain list of tools\");\n                return tools;\n            }\n\n            foreach (var json_tool in json_tools.get_elements ()) {\n                /* Get tool object */\n                var tool = json_tool.get_object ();\n\n                if (tool == null) {\n                    critical (\"Error write loading tools: tool is not JSON object\");\n                    continue;\n                }\n\n                if (!tool.has_member (\"script\")) {\n                    critical (\"Error write loading tools: tool has no script\");\n                    continue;\n                }\n\n                string[] arguments = {};\n                if (tool.has_member (\"args\"))\n                    tool.get_array_member (\"args\").foreach_element ((a, i, node) => {\n                        if (node.get_node_type () == VALUE\n                            && node.get_value_type () == typeof (string)) {\n                            arguments += node.get_string ();\n                        } else {\n                            critical (\"Arguments of tools must be string\");\n                        }\n                    });\n\n                var new_tool = new Tool () {\n                    name = tool.has_member (\"name\")\n                        ? tool.get_string_member (\"name\")\n                        : \"\",\n                    description = tool.has_member (\"description\")\n                        ? tool.get_string_member (\"description\")\n                        : \"\",\n                    icon = tool.has_member (\"icon\")\n                        ? tool.get_string_member (\"icon\")\n                        : \"applications-utilities-symbolic\",\n                    script = tool.get_string_member (\"script\"),\n                    is_system = is_system,\n                    arguments = arguments\n                };\n\n                tools.append (new_tool);\n            }\n\n            return tools;\n        }\n\n        /**\n         * Save custom tools' metadata\n         */\n        async void save_custom_tools ()\n                throws Error {\n            /* Ensure configuration directory exists */\n            yield Utils.ensure_directory_exists (File.new_for_path (CONFIG_DIR.get ()));\n\n            /* Create JSON builder */\n            var builder = new Json.Builder ();\n\n            // {\n            builder.begin_object ();\n            // \"tools\":\n            builder.set_member_name (\"tools\");\n            // [\n            builder.begin_array ();\n\n            /* Dump tools */\n            for (var i = 0; i < custom_tools.get_n_items (); i++) {\n                Tool tool = (Tool) custom_tools.get_item (i);\n\n                // {\n                builder\n                    .begin_object ()\n                // \"name\": `tool.name`,\n                    .set_member_name (\"name\")\n                    .add_string_value (tool.name)\n                // \"description\": `tool.description`,\n                    .set_member_name (\"description\")\n                    .add_string_value (tool.description)\n                // \"script\": `tool.script`,\n                    .set_member_name (\"script\")\n                    .add_string_value (tool.script)\n                // \"args\": [\n                    .set_member_name (\"args\")\n                    .begin_array ();\n\n                foreach (var arg in tool.arguments) {\n                    // `arg`,\n                    builder\n                    .add_string_value (arg ?? \"\");\n                }\n\n                // ]\n                builder\n                    .end_array ();\n                // },\n                builder\n                    .end_object ();\n            }\n\n            // ]\n            builder.end_array ();\n            // }\n            builder.end_object ();\n\n            /* Convert JSON builder to string... */\n            var generator = new Json.Generator ();\n            var node = builder.get_root ();\n            generator.set_root (node);\n\n            /* ...and save to file */\n            try {\n                generator.to_file (CUSTOM_TOOLS_FILE.get ().get_path ());\n            } catch (Error e) {\n                error (\"Can't save custom tools: %s\", e.message);\n            }\n        }\n\n        /**\n         * Commit changed in custom tools\n         */\n        public async void commit ()\n                throws Error {\n            /* Save metadata on disk */\n            yield save_custom_tools ();\n\n            /* Update model of all tools */\n            update_all_tools ();\n\n            /* Delete scripts of removed tools */\n            while (!removed_tools.is_empty ()) {\n                var tool = removed_tools.pop_head ();\n                /* Delete tool script */\n                FileUtils.remove (\n                    Path.build_filename (\n                        Tool.CUSTOM_TOOLS_DIR.get (),\n                        tool.script\n                    )\n                );\n            }\n        }\n\n        /**\n         * Add and save tool\n         *\n         * @param tool custom tool to add\n         */\n        public signal void add_tool (Tool tool) {\n            /* Append tool to model */\n            custom_tools.append (tool);\n        }\n\n        public signal void delete_tool (Tool tool) {\n            /* Remove tool from tools */\n            uint pos;\n            if (custom_tools.find (tool, out pos))\n                custom_tools.remove (pos);\n\n            removed_tools.push_tail (tool);\n        }\n    }\n}\n"
  },
  {
    "path": "src/utils/Recoloring.vala",
    "content": "// SPDX-FileCopyrightText: 2021 Christian Hergert <chergert@redhat.com>\n// SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nusing GtkSource;\nusing Gdk;\n\n/**\n * CSS that should be loaded for any style scheme\n * except Adwaita or Adwaita-dark.\n */\nconst string SHARED_CSS = \"\"\"\n@define-color card_fg_color @window_fg_color;\n@define-color headerbar_fg_color @window_fg_color;\n@define-color headerbar_border_color @window_fg_color;\n@define-color popover_fg_color @window_fg_color;\n@define-color dark_fill_bg_color @headerbar_bg_color;\n@define-color view_bg_color @card_bg_color;\n@define-color view_fg_color @window_fg_color;\n\"\"\";\n\n/**\n * CSS that should be loaded for any light style\n * schemes except Adwaita.\n */\nconst string LIGHT_CSS_SUFFIX = \"\"\"\n@define-color popover_bg_color mix(@window_bg_color, white, .1);\n@define-color card_bg_color alpha(white, .6);\n\"\"\";\n\n/**\n * CSS that should be loaded for any dark style\n * schemes except Adwaita-dark.\n */\nconst string DARK_CSS_SUFFIX = \"\"\"\n@define-color popover_bg_color mix(@window_bg_color, white, 0.07);\n@define-color card_bg_color @popover_bg_color;\n@define-color view_bg_color darker(@window_bg_color);\n\"\"\";\n\n/**\n * Implementation of application-wide style schemes\n */\nnamespace Recoloring {\n    /**\n     * Color kind\n     *\n     * Can be background or foreground.\n     */\n    enum ColorKind {\n        FOREGROUND,\n        BACKGROUND,\n    }\n\n    /**\n     * Try to parse color string\n     *\n     * Returns `null` if string is invalid.\n     *\n     * @param color color string\n     *\n     * @return color got from string, or `null` if string is invalid\n     */\n    RGBA? parse_color (string color) {\n        var rgba = RGBA ();\n\n        if (rgba.parse (color))\n            return rgba;\n        else\n            return null;\n    }\n\n    /**\n     * Get color from style scheme\n     *\n     * @param scheme style scheme\n     * @param style_name style name that color is belong to\n     * @param kind color kind\n     *\n     * @return color\n     */\n    RGBA? get_color (StyleScheme scheme, string style_name, ColorKind kind) {\n        var style = scheme.get_style (style_name);\n\n        if (style == null)\n            return null;\n\n        var fg = style.foreground;\n        var fg_set = style.foreground_set;\n        var bg = style.background;\n        var bg_set = style.background_set;\n\n        var color = RGBA ();\n\n        if (kind == FOREGROUND && fg != null && fg_set)\n            color.parse (fg);\n        else if (kind == BACKGROUND && bg != null && bg_set)\n            color.parse (bg);\n        else\n            return null;\n\n        if (color.alpha >= 0.1)\n            return color;\n        else\n            return null;\n    }\n\n    /**\n     * Get color from style scheme metadata\n     *\n     * @param scheme style scheme\n     * @param key color key\n     *\n     * @return color, or `null` if not defined\n     */\n    RGBA? get_metadata_color (StyleScheme scheme, string key) {\n        var str = scheme.get_metadata (key);\n\n        if (str == null)\n            return null;\n        else\n            return parse_color (str);\n    }\n\n    /**\n     * Define color in CSS string builder\n     *\n     * @param str CSS string builder\n     * @param name color name\n     * @param color color to define\n     */\n    void define_color (StringBuilder str, string name, RGBA? color) {\n        if (color == null)\n            return;\n\n        var opaque = color.copy ();\n        opaque.alpha = 1.0f;\n\n        var color_str = opaque.to_string ();\n        str.append_printf (\"@define-color %s %s;\\n\", name, color_str);\n    }\n\n    /**\n     * Define mixed color in CSS string builder\n     *\n     * @param str CSS string builder\n     * @param name color name\n     * @param a first color\n     * @param b second color\n     * @param level mix level\n     */\n    void define_color_mixed (StringBuilder str,\n                             string name,\n                             RGBA a,\n                             RGBA b,\n                             double level) {\n        var a_str = a.to_string ();\n        var b_str = b.to_string ();\n        var level_string = level.to_string ();\n\n        str.append_printf (\n            \"@define-color %s mix(%s, %s, %s);\\n\",\n            name,\n            a_str,\n            b_str,\n            level_string\n        );\n    }\n\n    /**\n     * Get whether style scheme is dark\n     *\n     * @param scheme style scheme\n     *\n     * @return whether the style scheme is dark\n     */\n     public bool is_scheme_dark (StyleScheme scheme) {\n        string id = scheme.get_id ();\n        string variant = scheme.get_metadata (\"variant\");\n\n\n        if (variant == \"light\")\n            return false;\n        else if (variant == \"dark\")\n            return true;\n        else if (id.has_suffix (\"-dark\"))\n            return true;\n\n        var text_bg = get_color (scheme, \"text\", BACKGROUND);\n        if (text_bg != null) {\n            /* http://alienryderflex.com/hsp.html */\n            double r = text_bg.red * 255.0;\n            double g = text_bg.green * 255.0;\n            double b = text_bg.blue * 255.0;\n            double hsp = Math.sqrt (0.299 * (r * r) +\n                                    0.587 * (g * g) +\n                                    0.114 * (b * b));\n\n            return hsp <= 127.5;\n        }\n\n        return false;\n    }\n\n    /**\n     * Generate CSS for style scheme\n     *\n     * This function is used to apply `gtksourceview`'s\n     * style schemes to whole application.\n     *\n     * @param style_scheme style scheme\n     *\n     * @return CSS string to apply on application\n     */\n    public string generate_css (StyleScheme style_scheme) {\n        RGBA black = {0, 0, 0, 1};\n        RGBA white = {1, 1, 1, 1};\n\n        /* Don't restyle Adwaita as we already have it */\n        var id = style_scheme.get_name ();\n        if (id.has_prefix (\"Adwaita\"))\n            return \"\";\n\n        var name = style_scheme.get_name ();\n        var is_dark = is_scheme_dark (style_scheme);\n        var alt = is_dark ? white : black;\n\n        var str = new StringBuilder (SHARED_CSS);\n        str.append_printf (\"/* %s */\\n\", name);\n\n        /* TODO: Improve error checking and fallbacks */\n\n        var text_bg = get_color (style_scheme, \"text\", BACKGROUND)\n            ?? (is_dark\n                ? black\n                : white);\n\n        var text_fg = get_color (style_scheme, \"text\", FOREGROUND)\n            ?? (is_dark\n                ? white\n                : black);\n\n        var right_margin = get_color (style_scheme, \"right-margin\", BACKGROUND)\n            ?? text_bg;\n        right_margin.alpha = 1;\n\n        if (is_dark) {\n            define_color_mixed (str, \"window_bg_color\", text_bg, alt, 0.025);\n        } else {\n            define_color_mixed (str, \"window_bg_color\", text_bg, white, 0.1);\n        }\n        define_color_mixed (str, \"headerbar_bg_color\", text_bg, alt, 0.05);\n\n        define_color_mixed (str, \"window_fg_color\", text_fg, alt, 0.1);\n        define_color (str, \"headerbar_fg_color\", text_fg);\n\n        define_color_mixed (str, \"view_bg_color\", text_bg, white, is_dark ? 0.1 : 0.3);\n        define_color (str, \"view_fg_color\", text_fg);\n\n\n        define_color (\n            str,\n            \"accent_bg_color\",\n            get_metadata_color (style_scheme, \"accent_bg_color\")\n                ?? get_color (style_scheme, \"selection\", BACKGROUND)\n        );\n\n        define_color (\n            str,\n            \"accent_fg_color\",\n            get_metadata_color (style_scheme, \"accent_fg_color\")\n                ?? get_color (style_scheme, \"selection\", FOREGROUND)\n        );\n\n        var accent_color = get_metadata_color (style_scheme, \"accent_color\");\n        if (accent_color != null) {\n            define_color (str, \"accent_color\", accent_color);\n        } else {\n            accent_color = get_metadata_color (style_scheme, \"accent_bg_color\")\n                ?? get_color (style_scheme, \"selection\", BACKGROUND);\n\n            if (accent_color != null) {\n                accent_color.alpha = 1;\n                define_color_mixed (str, \"accent_color\", accent_color, alt, 0.1);\n            }\n        }\n\n        if (is_dark)\n            str.append(DARK_CSS_SUFFIX);\n        else\n            str.append(LIGHT_CSS_SUFFIX);\n\n        return str.str;\n    }\n}\n"
  },
  {
    "path": "src/utils/Utils.vala",
    "content": "// Copyright 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n// SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nnamespace TextPieces.Utils {\n    /**\n     * Create directory if not exists\n     *\n     * @param dir directory to ensure\n     */\n    public async void ensure_directory_exists (File dir)\n            throws Error {\n        /* Get parent directory */\n        var parent_dir = dir.get_parent ();\n\n        /* Create parent directory\n           if not exists */\n        if (!parent_dir.query_exists ())\n            yield ensure_directory_exists (parent_dir);\n\n        /* Create target directory\n           if not exists */\n        if (!dir.query_exists ())\n            yield dir.make_directory_async ();\n    }\n}\n"
  },
  {
    "path": "src/vapi/config.vapi",
    "content": "// SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\n[CCode (cprefix = \"\", lower_case_cprefix = \"\")]\nnamespace Config {\n\tpublic const string VERSION;\n\tpublic const string PKGDATADIR;\n\tpublic const string SCRIPTDIR;\n\tpublic const string GETTEXT_PACKAGE;\n\tpublic const string GNOMELOCALEDIR;\n}\n"
  },
  {
    "path": "src/widgets/Editor.vala",
    "content": "// SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nnamespace TextPieces {\n    /**\n     * Message toast timeout, in seconds\n     */\n    const uint TOAST_TIMEOUT = 2;\n\n    /**\n     * Text Pieces' editor widget\n     */\n    [GtkTemplate (ui = \"/com/github/liferooter/textpieces/ui/Editor.ui\")]\n    class Editor : Adw.Bin {\n        [GtkChild] unowned GtkSource.View editor;\n        [GtkChild] unowned Gtk.Box arguments_box;\n        [GtkChild] unowned Adw.ToastOverlay message_overlay;\n\n        [GtkChild] public unowned TextPieces.SearchBar search_bar;\n\n        /**\n         * Whether to wrap lines\n         */\n        public bool wrap_lines {\n            set {\n                editor.wrap_mode = value\n                    ? Gtk.WrapMode.WORD_CHAR\n                    : Gtk.WrapMode.NONE;\n            } get {\n                return editor.wrap_mode == WORD_CHAR;\n            }\n        }\n\n        /**\n         * CSS provider used to set editor font\n         */\n        Gtk.CssProvider editor_font_css_provider = new Gtk.CssProvider ();\n\n        /**\n         * Editor font\n        */\n        public string? editor_font {\n            set {\n                if (value != null) {\n                    editor_font_css_provider.load_from_data (\"\"\"\n                        .monospace {\n                            font-family: %s;\n                        }\n                    \"\"\".printf (value).data);\n                } else {\n                    editor_font_css_provider.load_from_data ({});\n                }\n            }\n        }\n\n        /**\n         * Editor's application\n         *\n         * Used to bind editor style scheme\n         * to global style scheme.\n         */\n        public TextPieces.Application application {\n            get {\n                return Application.instance;\n            }\n        }\n\n        construct {\n            /* Bind some values to settings */\n            with (Application.settings) {\n                bind (\"tabs-to-spaces\", editor, \"insert-spaces-instead-of-tabs\", GET);\n                bind (\"spaces-in-tab\",  editor, \"tab-width\",                     GET);\n                bind (\"font-name\",      this,   \"editor-font\",                   GET);\n                bind (\"wrap-lines\",     this,   \"wrap-lines\",                    GET);\n            }\n\n            /* Add style provider with editor font */\n            get_style_context ().add_provider (\n                editor_font_css_provider,\n                Gtk.STYLE_PROVIDER_PRIORITY_USER\n            );\n\n            /* Bind editor style scheme\n               to application style scheme */\n            application.bind_property (\n                \"style-scheme\",\n                editor.buffer,\n                \"style-scheme\",\n                SYNC_CREATE\n            );\n\n            /* Enable drag-n-drop to open file content */\n            var drop_target = new Gtk.DropTarget (typeof (File), COPY);\n            drop_target.on_drop.connect ((file) => {\n                var path = ((File) file).get_path ();\n                string contents;\n\n                try {\n                    FileUtils.get_contents (path, out contents);\n                    editor.buffer.text = contents;\n\n                    return true;\n                } catch (Error err) {\n                    critical (\"failed to load file %s: %s\", path, err.message);\n\n                    return false;\n                }\n            });\n            editor.add_controller (drop_target);\n        }\n\n        /**\n         * Move focus to the editor\n         */\n        public override bool grab_focus () {\n            return editor.grab_focus ();\n        }\n\n        /**\n         * Get selection or whole text if there are no selection\n         */\n        public string get_selection () {\n            var buffer = editor.buffer;\n\n            Gtk.TextIter start, end;\n\n            if (buffer.has_selection) {\n                buffer.get_selection_bounds (out start, out end);\n            } else {\n                buffer.get_bounds (out start, out end);\n            }\n\n            return buffer.get_text (start, end, false);\n        }\n\n        /**\n         * Copy text from editor\n         */\n        public void copy_text () {\n            Gdk.Display\n                .get_default ()\n                .get_clipboard ()\n                .set_text (editor.buffer.text);\n            show_message (_(\"Text is copied\"));\n        }\n\n        /**\n         * Replace selection or whole text if there are no selection\n         */\n        public void replace_selection (string text) {\n            var buffer = editor.buffer;\n\n            Gtk.TextIter start, end;\n\n            var has_selection = buffer.has_selection;\n\n            if (has_selection) {\n                buffer.get_selection_bounds (out start, out end);\n            } else {\n                buffer.get_bounds (out start, out end);\n            }\n\n            var start_offset = start.get_offset();\n\n            buffer.begin_user_action ();\n\n            /* Replace selection by given text */\n            buffer.delete (ref start, ref end);\n            buffer.insert (ref start, text, -1);\n\n            buffer.end_user_action ();\n\n            if (has_selection) {\n                buffer.get_iter_at_offset (\n                    out start,\n                    start_offset\n                );\n                buffer.get_iter_at_offset (\n                    out end,\n                    start_offset + text.length\n                );\n\n                buffer.select_range (start, end);\n            } else {\n                /* TODO: Invent better behavior of cursor\n                         after whole-text transformations */\n                buffer.place_cursor (start);\n            }\n        }\n\n        /**\n         * Set argument list\n         */\n        public void set_arguments (string[] arguments) {\n            var children = arguments_box.observe_children ();\n            for (int i = (int) children.get_n_items () - 1; i >= 0; i--) {\n                var entry = (Gtk.Widget) children.get_item (i);\n                arguments_box.remove (entry);\n            }\n\n            foreach (var argument in arguments) {\n                var entry = new Gtk.Entry () {\n                    placeholder_text = argument,\n                    css_classes = {\"monospace\"}\n                };\n\n                entry.activate.connect (() => {\n                    move_focus (TAB_FORWARD);\n                });\n                arguments_box.append (entry);\n            }\n\n            arguments_box.visible = arguments.length != 0;\n        }\n\n        /**\n         * Get argument values\n         */\n        public string[] get_arguments () {\n            var args = new string[0];\n\n            var children = arguments_box.observe_children ();\n            for (var i = 0; i < children.get_n_items (); i++) {\n                args += ((Gtk.Entry) children.get_item (i)).text;\n            }\n\n            return args;\n        }\n\n        /**\n         * Move focus to the first argument entry\n         */\n        public void jump_to_args () {\n            if (arguments_box.visible)\n                ((Gtk.Widget) arguments_box.observe_children ().get_item (0))\n                    .grab_focus ();\n        }\n\n        /**\n         * Save text to file\n         */\n        public void save_as (File file) throws FileError {\n            /* Try to save text to file */\n            FileUtils.set_contents (\n                file.get_path (),\n                editor.buffer.text\n            );\n        }\n\n        /**\n         * Load text from file\n         */\n        public void load_from (File file) throws FileError {\n            /* Try to load text from file */\n            string text;\n\n            FileUtils.get_contents (\n                file.get_path (),\n                out text\n            );\n\n            editor.buffer.text = text;\n        }\n\n        /**\n         * Show message in a toast\n         */\n        public void show_message (string message) {\n            message_overlay.add_toast (new Adw.Toast (message) {\n                priority = HIGH,\n                timeout = TOAST_TIMEOUT\n            });\n        }\n\n        /**\n         * Update editor's top margin\n         */\n        public bool update_editor_margin () {\n            /* Calculate new margin */\n            Graphene.Rect bounds;\n            assert (arguments_box.compute_bounds (this, out bounds));\n\n            var old_margin = editor.top_margin;\n            var new_margin = arguments_box.visible\n                ? (int) Math.ceil (bounds.get_height ())\n                : 6;\n\n            /* Set margin */\n            editor.top_margin = new_margin;\n\n            /* Restore scroll position */\n            editor.vadjustment.value = double.max(editor.vadjustment.value - old_margin + new_margin, 0);\n\n            return Source.REMOVE;\n        }\n    }\n}\n"
  },
  {
    "path": "src/widgets/Search.vala",
    "content": "// SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nusing Gtk;\nusing Gdk;\nusing Adw;\n\n[GtkTemplate(ui = \"/com/github/liferooter/textpieces/ui/Search.ui\")]\nclass TextPieces.Search : Bin {\n    [GtkChild] unowned Viewport results_viewport;\n    [GtkChild] unowned ListBox results_listbox;\n    [GtkChild] unowned Stack search_stack;\n\n    /**\n     * Search entry to use\n     */\n    public Gtk.SearchEntry search_entry { get; construct; default = null; }\n\n    /**\n     * Tool list model\n     */\n    GLib.ListModel results_model;\n\n    /**\n     * Sorter for search results\n     */\n    Sorter sorter;\n\n    /**\n     * Filter for search results\n     */\n    Filter filter;\n\n    /**\n     * Search entry key event controller\n     */\n    EventControllerKey search_event_controller;\n\n    construct {\n        /* Create model for search results */\n        sorter = new CustomSorter ((a, b) => tool_sort_func ((Tool) a, (Tool) b));\n        filter = new CustomFilter (tool_filter_func);\n        results_model = new SortListModel (\n            new FilterListModel (\n                Application.tools.all_tools,\n                filter\n            ),\n            sorter\n        );\n\n        /* Bind model to list box */\n        results_listbox.bind_model (\n            results_model,\n            build_list_row\n        );\n\n        /* Update search results on query changes */\n        search_entry.changed.connect (update_search_results);\n\n        /* Activate current row on activate */\n        search_entry.activate.connect (activate_selected_result);\n\n        /* Pass arrow keys events from\n           search entry to list box */\n        search_event_controller = new EventControllerKey ();\n        search_event_controller.key_pressed.connect (on_search_entry_key);\n        search_entry.add_controller (search_event_controller);\n    }\n\n    /**\n     * Emitted when a tool is selected\n     *\n     * @param tool selected tool\n     */\n    public signal void tool_selected (Tool tool);\n\n    /**\n     * Reset search\n     *\n     * Clear the search entry, scroll to the top, reset selection\n     */\n    public void reset () {\n        /* Clear the search entry */\n        search_entry.text = \"\";\n        /* Scroll to the top */\n        results_viewport.vadjustment.value = 0;\n        /* Select the first row */\n        var row = results_listbox.get_row_at_index (0);\n        results_listbox.select_row (row);\n    }\n\n    /**\n     * Choose selected result\n     */\n    void activate_selected_result () {\n        var row = results_listbox.get_selected_row ()\n            ?? results_listbox.get_row_at_index (0);\n        if (row != null)\n            results_listbox.row_activated (row);\n    }\n\n    /**\n     * Process key pressed in search entry\n     *\n     * Move focus to the first search result\n     * if `Gdk.Key.Down` is pressed\n     *\n     * @param keyval    pressed key value\n     * @param keycode   pressed key code\n     * @param modifiers pressed modifiers\n     *\n     * @returnif whether the key press was handled\n     */\n    bool on_search_entry_key (uint keyval,\n                              uint keycode,\n                              ModifierType modifiers) {\n        switch (keyval) {\n            case (Key.Down):\n            case (Key.Up):\n                /* Move focus to listbox */\n                results_listbox.get_selected_row()?.grab_focus ();\n                /* Forward event to listbox */\n                search_event_controller.forward (results_listbox);\n                /* Grab focus back when done */\n                search_entry.grab_focus ();\n\n                return true;\n            default:\n                return false;\n        }\n    }\n\n    /**\n     * Emit `tool-selected` signal when tool is selected\n     */\n    [GtkCallback]\n    void on_row_activated (ListBoxRow row) {\n        var index = row.get_index ();\n        var tool = (Tool) results_model.get_item (index);\n        tool_selected (tool);\n    }\n\n    /**\n     * Update list of search results\n     */\n    void update_search_results () {\n        /* Invalidate search sorter and filter */\n        sorter.changed (DIFFERENT);\n        filter.changed (DIFFERENT);\n\n        /* Select the first row if no row is selected */\n        if (results_listbox.get_selected_row () == null) {\n            results_listbox.select_row (\n                results_listbox.get_row_at_index (0)\n            );\n        }\n\n        /* Show placeholder if\n           there are no tools found */\n        search_stack.set_visible_child_name (\n            results_model.get_n_items () == 0\n                ? \"placeholder\"\n                : \"search\"\n        );\n    }\n\n    /**\n     * Compare tools by search irrelevance\n     *\n     * Less relevant tool should be\n     * larger than more relevant\n     * to be below more relevant tool\n     * in search results. If tools are\n     * equal, compare tools' names.\n     *\n     * @param a one tool\n     * @param b another tool\n     *\n     * @return zero if tools are equal, positive if `a` is larger, negative otherwise\n     */\n    public int tool_sort_func (Tool a, Tool b) {\n        var query = search_entry.text;\n\n        var res = calculate_irrelevance (a, query)\n                - calculate_irrelevance (b, query);\n        if (res == 0)\n            return strcmp (a.translated_name, b.translated_name);\n        return res;\n    }\n\n    /**\n     * Functions used to filter tools by search relevance\n     *\n     * Don't show tool if its irrelevance is infinite\n     *\n     * @param tool  the tool\n     * @param query search query\n     *\n     * @return whether to show tool in search results\n     */\n    public bool tool_filter_func (Object tool) {\n        return calculate_irrelevance ((Tool) tool, search_entry.text) != int.MAX;\n    }\n\n    /**\n     * Calculate tool irrelevance\n     *\n     * This method is used to filter\n     * and sort search results\n     *\n     * @param tool  the tool\n     * @param query search query\n     *\n     * @return tool's search irrelevance\n     */\n    static int calculate_irrelevance (Tool tool, string query) {\n        /* Get case-independent form of search query */\n        var casefolded_query = query.casefold ();\n\n        return int.min (\n            /* Calculate non-translated tool's irrelevance */\n            calculate_irrelevance_for_fields ({\n                tool.name       .casefold (),\n                tool.description.casefold ()\n            }, casefolded_query),\n            /* Calculate translated tool's irrelevance */\n            calculate_irrelevance_for_fields ({\n                tool.translated_name       .casefold (),\n                tool.translated_description.casefold ()\n            }, casefolded_query)\n        );\n    }\n\n    /**\n     * Calculate fields' irrelevance\n     *\n     * This method gets list of fields\n     * and search query and returns fields'\n     * search irrelevance. The alghorythm is as follows:\n     *\n     * 1. If query is empty string, algorythm finishes,\n     *    irrelevance is zero. It's used to fallback\n     *    to alphabetical sort.\n     * 2. At the start of the alghorythm irrelevance\n     *    is equals to zero\n     * 3. Query is divided into words, called terms\n     * 4. Algorythm iterates over terms\n     * 5. I search for term in fields, from the first\n     *    field to the last field\n     * 6. If term is not found, algorythm finishes,\n     *    irrelevance is infinite\n     * 7. If term is found in the field, increase\n     *    irrelevance by the difference from the index of\n     *    the match start and then cut out part of field\n     *    from the start of field to the end of match\n     * 8. At the end of the algorythm, increase\n     *    irrelevance by sum of lengths of first fields\n     *    which don't contain any terms\n     *\n     * @param fields list of the fields\n     * @param query  search query\n     *\n     * @return tool's search irrelevance\n     */\n    static int calculate_irrelevance_for_fields (string[] fields, string query) {\n        /* If query is empty, return zero */\n        if (query == \"\")\n            return 0;\n\n        /* Split query to terms */\n        var terms = query.split (\" \");\n\n        /* Create array of field begginings.\n           It's used to easily cut fields as said\n           in the algorythm */\n        var field_beginning = new int[fields.length];\n\n        /* Initial irrelevance is zero */\n        var irrelevance = 0;\n\n        /* Iterate over terms */\n        foreach (var term in terms) {\n            /* Find first field containing the term */\n            var matching_field = 0;\n            var match = 0;\n\n            while (matching_field < fields.length\n                   && (match = fields[matching_field].index_of (term, field_beginning[matching_field])) == -1) {\n                matching_field++;\n            }\n\n            /* If there are no such field,\n               return infinity */\n            if (match == -1)\n                return int.MAX;\n\n            /* Increase irrelevance by\n               match beginning index */\n            irrelevance += match - field_beginning[matching_field];\n\n            /* Cut the field */\n            field_beginning[matching_field] = match + term.length;\n        }\n\n        /* Increase irrelevance by sum of\n           lengths of first non-matched fields */\n        for (var i = 0; i < field_beginning.length && field_beginning[i] == 0; i++)\n            irrelevance += fields[i].length;\n\n        /* Return the result */\n        return irrelevance;\n    }\n}\n"
  },
  {
    "path": "src/widgets/SearchBar.vala",
    "content": "// SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\n[GtkTemplate(ui = \"/com/github/liferooter/textpieces/ui/SearchBar.ui\")]\nclass TextPieces.SearchBar : Adw.Bin {\n    [GtkChild] unowned SearchEntry      search_entry;\n    [GtkChild] unowned Gtk.Entry        replace_entry;\n    [GtkChild] unowned Gtk.Revealer     search_revealer;\n    [GtkChild] unowned Gtk.ToggleButton search_replace;\n\n    /**\n     * Source view that this search bar is associated with\n     */\n    public GtkSource.View editor { get; set construct; }\n\n    /**\n     * Whether to parse query as regular expression\n     */\n    public bool use_regex { get; set; default = false; }\n\n    /**\n     * Whether the search is case sensitive.\n     */\n    public bool case_sensitive { get; set; default = false; }\n\n    /**\n     * Whether the search must match whole words only\n     */\n    public bool whole_words { get; set; default = false; }\n\n    /**\n     * Search settings\n     */\n    private GtkSource.SearchSettings search_settings = new GtkSource.SearchSettings () {\n        wrap_around = true\n    };\n\n    /**\n     * Search context\n     */\n    private GtkSource.SearchContext search_context;\n\n    /**\n     * Search cancelable\n     */\n    private Cancellable search_cancelable = new Cancellable ();\n\n    /**\n     * Get action entries\n     */\n    private ActionEntry[] action_entries () {\n        return {\n            { \"hide\"        , hide_search                    },\n            { \"show\"        , show_search                    },\n            { \"show-replace\", show_replace                   },\n            { \"next-match\"  , () => { next_match.begin (); } },\n            { \"prev-match\"  , () => { prev_match.begin (); } },\n            { \"replace\"     , replace                        },\n            { \"replace-all\" , replace_all                    },\n        };\n    }\n\n    construct {\n        search_context = new GtkSource.SearchContext (\n            (GtkSource.Buffer) editor.buffer,\n            search_settings\n        );\n\n        /* Bind properties */\n        search_entry.bind_property (\n            \"query\",\n            search_settings,\n            \"search-text\"\n        );\n\n        search_revealer.bind_property (\n            \"child-revealed\",\n            search_context,\n            \"highlight\"\n        );\n\n        this.bind_property (\n            \"use-regex\",\n            search_settings,\n            \"regex-enabled\"\n        );\n\n        this.bind_property (\n            \"case-sensitive\",\n            search_settings,\n            \"case-sensitive\"\n        );\n\n        this.bind_property (\n            \"whole-words\",\n            search_settings,\n            \"at-word-boundaries\"\n        );\n\n        /* Setup search entry */\n        search_entry.set_context (search_context);\n\n        /* Setup action entries */\n        var action_group = new SimpleActionGroup ();\n        action_group.add_action_entries (action_entries (), this);\n        insert_action_group (\"search\", action_group);\n    }\n\n    /**\n     * Show search\n     */\n    public void show_search () {\n        search_entry.query = \"\";\n        search_revealer.reveal_child = true;\n        search_cancelable.reset ();\n        search_entry.grab_focus ();\n    }\n\n    /**\n     * Show search in replace mode\n     */\n    public void show_replace () {\n        show_search ();\n        search_replace.active = true;\n    }\n\n    /**\n     * Hide search\n     */\n    private void hide_search () {\n        search_revealer.reveal_child = false;\n        search_replace.active = false;\n        search_cancelable.cancel ();\n    }\n\n    /**\n     * Move to next match\n     */\n    public async void next_match () {\n        var buffer = editor.buffer;\n        Gtk.TextIter begin, end;\n\n        buffer.get_selection_bounds (out begin, out end);\n        begin.order (ref end);\n        var cursor = end.copy ();\n\n        try {\n            bool has_wrapped;\n\n            var found = yield search_context.forward_async (\n                cursor,\n                search_cancelable,\n                out begin,\n                out end,\n                out has_wrapped\n            );\n\n            if (!found) {\n                return;\n            }\n\n            buffer.select_range (begin, end);\n        } catch (Error e) {\n            critical (\"failed to move to next search match: %s\", e.message);\n        }\n\n        editor.scroll_mark_onscreen (buffer.get_insert ());\n    }\n\n    /**\n     * Move to previous match\n     */\n    public async void prev_match () {\n        var buffer = editor.buffer;\n        Gtk.TextIter begin, end;\n\n        buffer.get_selection_bounds (out begin, out end);\n        begin.order (ref end);\n        var cursor = begin.copy ();\n\n        try {\n            bool has_wrapped;\n\n            var found = yield search_context.backward_async (\n                cursor,\n                search_cancelable,\n                out begin,\n                out end,\n                out has_wrapped\n            );\n\n            if (!found) {\n                return;\n            }\n\n            buffer.select_range (begin, end);\n        } catch (Error e) {\n            critical (\"failed to move to next search match: %s\", e.message);\n        }\n\n        editor.scroll_mark_onscreen (buffer.get_insert ());\n    }\n\n    /**\n     * Replace current occurrence\n     */\n    public void replace ()\n        requires (search_entry.occurrence_position > 0)\n    {\n        var replacement = replace_entry.text;\n\n        Gtk.TextIter start, end;\n        editor.buffer.get_selection_bounds (out start, out end);\n\n        try {\n            search_context.replace (\n                start,\n                end,\n                replacement,\n                -1\n            );\n        } catch (Error e) {\n            replace_entry.add_css_class (\"error\");\n            replace_entry.error_bell ();\n        }\n\n        if (search_context.occurrences_count != 0)\n            next_match.begin ();\n    }\n\n    /**\n     * Replace all occurrences\n     */\n    public void replace_all () {\n        var replacement = replace_entry.text;\n\n        try {\n            search_context.replace_all (\n                replacement,\n                -1\n            );\n        } catch (Error e) {\n            replace_entry.add_css_class (\"error\");\n            replace_entry.error_bell ();\n        }\n    }\n\n    /**\n     * Remove error style\n     */\n    [GtkCallback]\n    void remove_error_style (Gtk.Widget widget) {\n        widget.remove_css_class (\"error\");\n    }\n\n}\n"
  },
  {
    "path": "src/widgets/SearchEntry.vala",
    "content": "// SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\n\n[GtkTemplate(ui = \"/com/github/liferooter/textpieces/ui/SearchEntry.ui\")]\nclass TextPieces.SearchEntry : Gtk.Widget {\n    [GtkChild] unowned Gtk.Text text;\n\n    /**\n     * Current search query\n     */\n    public string query { get; set; }\n\n    /**\n     * Current information string about occurrences count and position\n     */\n    public string occurrences_info { get; set; }\n\n    /**\n     * occurrences count\n     */\n    public int occurrences_count { get; set; }\n\n    /**\n     * Position of current occurrence\n     */\n    public uint occurrence_position { get; set; }\n\n    class construct {\n        set_layout_manager_type (typeof (Gtk.BoxLayout));\n        set_css_name (\"entry\");\n        set_accessible_role (TEXT_BOX);\n    }\n\n    construct {\n        text.bind_property (\n            \"text\",\n            this,\n            \"query\"\n        );\n\n        notify[\"occurrences-count\"].connect (update_info);\n        notify[\"occurrence-position\"].connect (update_info);\n        update_info ();\n    }\n\n    public void set_context (GtkSource.SearchContext context) {\n        /* Bind occurrences count */\n        context.bind_property (\n            \"occurrences-count\",\n            this,\n            \"occurrences-count\"\n        );\n\n        /* Bind occurrence position */\n        var buffer = context.buffer;\n        buffer.changed.connect (() => {\n            update_occurrence_position (context);\n        });\n\n        buffer.notify[\"cursor-position\"].connect (() => {\n            update_occurrence_position (context);\n        });\n\n        text.changed.connect (() => {\n            update_occurrence_position (context);\n        });\n\n        context.notify.connect(() => {\n            update_occurrence_position (context);\n        });\n    }\n\n    private void update_occurrence_position (GtkSource.SearchContext context) {\n        Gtk.TextIter? start, end;\n        context.buffer.get_selection_bounds (out start, out end);\n        if (start != null && end != null) {\n            var position = context.get_occurrence_position (start, end);\n            occurrence_position = position >= 0\n                ? position\n                : 0;\n        }\n    }\n\n    public override bool grab_focus () {\n        return text.grab_focus ();\n    }\n\n    public void update_info () {\n        var info = \"\";\n        if (occurrences_count != 0) {\n            info = _(\"%u of %d\").printf (\n                occurrence_position,\n                occurrences_count\n            );\n        }\n\n        this.occurrences_info = info;\n    }\n}\n"
  },
  {
    "path": "src/widgets/Window.vala",
    "content": "// SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nnamespace TextPieces {\n    /**\n     * Main window class\n     */\n    [GtkTemplate (ui = \"/com/github/liferooter/textpieces/ui/Window.ui\")]\n    class Window : Adw.ApplicationWindow {\n        [GtkChild] unowned Adw.ButtonContent tool_button_content;\n        [GtkChild] unowned TextPieces.Editor editor;\n        [GtkChild] unowned Gtk.Stack content_stack;\n        [GtkChild] unowned Gtk.ToggleButton tool_button;\n        [GtkChild] unowned Search search;\n\n        /**\n         * Text used instead of tool name\n         * when where is no tool selected\n         */\n        const string NO_TOOL_LABEL = _(\"Select tool\");\n\n        /**\n         * Name of icon used\n         * instead of tool icon\n         * where is no tool selected\n         */\n        const string NO_TOOL_ICON = \"applications-utilities-symbolic\";\n\n        /**\n         * Selected tool\n         */\n        private Tool? _selected_tool;\n        public Tool? selected_tool {\n            get {\n                return _selected_tool;\n            } set {\n                /* Disconnect callback frow old tool */\n                if (_selected_tool != null)\n                    _selected_tool.notify\n                        .disconnect (tool_changed);\n\n                /* Save new tool */\n                _selected_tool = value;\n\n                /* Connect callback to tool changes */\n                if (_selected_tool != null)\n                    _selected_tool.notify\n                        .connect (tool_changed);\n\n                /* Save selected tool in GSettings */\n                Application.settings.set_string (\n                    \"selected-tool\",\n                    value?.script ?? \"\"\n                );\n\n                /* Trigger tool change callback */\n                tool_changed ();\n            }\n        }\n\n        /**\n         * Window actions\n         */\n        private const ActionEntry[] ACTION_ENTRIES = {\n            { \"apply\"            , action_apply            },\n            { \"open-preferences\" , action_open_preferences },\n            { \"about\"            , action_about            },\n            { \"copy\"             , action_copy             },\n            { \"tools-settings\"   , action_tools_settings   },\n            { \"toggle-search\"    , action_toggle_search    },\n            { \"save-as\"          , action_save_as          },\n            { \"load-file\"        , action_load_file        },\n            { \"jump-to-args\"     , action_jump_to_args     },\n            { \"find\"             , action_find             },\n        };\n\n        public Window (Application application) {\n            Object (\n                application: application\n            );\n        }\n\n        construct {\n            /* Restore window geometry from settings */\n            with (Application.settings) {\n                default_width = get_int (\"window-width\");\n                default_height = get_int (\"window-height\");\n                maximized = get_boolean (\"is-maximized\");\n            }\n\n            /* Load actions */\n            add_action_entries (ACTION_ENTRIES, this);\n\n            /* Unselect tool if it's deleted */\n            Application.tools.delete_tool.connect ((tool) => {\n                if (tool == selected_tool)\n                    selected_tool = null;\n            });\n\n            /* Initialize selected\n               tool property and run\n               its callback */\n            var selected_tool_script = Application.settings.get_string (\"selected-tool\");\n            var tool_found = false;\n\n            if (selected_tool_script != \"\") {\n                for (uint i = 0; i < Application.tools.all_tools.get_n_items (); i++) {\n                    var tool = (Tool) Application.tools.all_tools.get_item (i);\n                    if (tool.script == selected_tool_script) {\n                        selected_tool = tool;\n                        tool_found = true;\n                        break;\n                    }\n                }\n\n                if (!tool_found) {\n                    message (\"Previously selected tool is not found, so will be reset\");\n                    selected_tool = null;\n                }\n            } else {\n                selected_tool = null;\n            }\n        }\n\n        /**\n         * Close request callback\n         *\n         * Saves window geometry.\n         */\n        protected override bool close_request () {\n            save_window_size ();\n\n            return false;\n        }\n\n        /**\n         * Save window size\n         */\n        public void save_window_size () {\n            with (Application.settings) {\n                set_int (\"window-width\", default_width);\n                set_int (\"window-height\", default_height);\n                set_boolean (\"is-maximized\", maximized);\n            }\n        }\n\n        /**\n         * Load content from file\n         */\n        public void load_from (File file) {\n            try {\n                editor.load_from (file);\n            } catch (FileError e) {\n                /* Show error dialog if error occurs */\n                var dialog = new Gtk.MessageDialog (\n                    this,\n                    MODAL | DESTROY_WITH_PARENT,\n                    WARNING,\n                    CLOSE,\n                    _(\"Can't load from file: %s\"),\n                    e.message\n                );\n                dialog.response.connect (dialog.destroy);\n                dialog.show ();\n            }\n        }\n\n        /**\n         * Apply tool\n         */\n        void action_apply () {\n            /* Don't apply non-existing tool */\n            if (selected_tool == null)\n                return;\n\n            /* Get arguments from\n               argument entries */\n            var args = editor.get_arguments ();\n\n            /* Apply tool on text */\n            var result = selected_tool.apply (\n                editor.get_selection (),\n                args\n            );\n\n            string result_text;\n\n            /* Send error notification\n               if stderr is not null */\n            if (result.error != null) {\n                editor.show_message (result.error);\n            }\n\n            /* Set new text if stdout\n               is not null */\n            if (result.output != null) {\n                result_text = result.output;\n            } else {\n                return;\n            }\n\n            editor.replace_selection (result_text);\n        }\n\n        /**\n         * Open preferences\n         */\n        void action_open_preferences () {\n            /* Create preferences window */\n            var prefs = new Preferences (this) {\n                transient_for = this,\n                /* Pass application to the window\n                   to get application's shortcuts */\n                application = application\n            };\n            prefs.present ();\n        }\n\n        /**\n         * Open custom tools settings\n         */\n         void action_tools_settings () {\n            var prefs = new Preferences (this) {\n                transient_for = this,\n                application = application,\n                visible_page_name = \"custom-tools\"\n            };\n            prefs.present ();\n        }\n\n        /**\n         * Show about dialog\n         */\n        void action_about () {\n            var about_window = new Adw.AboutWindow () {\n                transient_for = this,\n                application = application,\n\n                application_icon = \"com.github.liferooter.textpieces\",\n                application_name = _(\"Text Pieces\"),\n                comments = _(\"Transform text without using random websites\"),\n                version = Config.VERSION,\n                license_type = Gtk.License.GPL_3_0,\n\n                website = \"https://github.com/liferooter/textpieces\",\n                issue_url = \"https://github.com/liferooter/textpieces/issues/new\",\n\n                artists = {_(\"Tobias Bernard https://tobiasbernard.com\")},\n                developers = {_(\"Gleb Smirnov <glebsmirnov0708@gmail.com>\")},\n\n                developer_name = _(\"Gleb Smirnov\"),\n                translator_credits = _(\"translator-credits\")\n            };\n\n            about_window.present ();\n        }\n\n        /**\n         * Toggle search\n         */\n         void action_toggle_search () {\n            tool_button.active = !tool_button.active;\n        }\n\n        /**\n         * Copy all text from the\n         * editor to clipboard\n         */\n        void action_copy () {\n            editor.copy_text ();\n        }\n\n        /**\n         * Save editor content to file\n         */\n        void action_save_as () {\n            /* Create file chooser dialog */\n            var file_chooser = new Gtk.FileChooserNative (\n                _(\"Save to File\"),\n                this,\n                SAVE,\n                null,\n                null\n            ) {\n                transient_for = this,\n                modal = true\n            };\n\n            file_chooser.response.connect (() => {\n                /* Get selected file */\n                var file = file_chooser.get_file ();\n                if (file == null)\n                    return;\n\n                try {\n                    editor.save_as (file);\n                } catch (FileError e) {\n                    /* Show error dialog if error occurs */\n                    var dialog = new Gtk.MessageDialog (\n                        this,\n                        MODAL | DESTROY_WITH_PARENT,\n                        WARNING,\n                        CLOSE,\n                        _(\"Can't save to file: %s\"),\n                        e.message\n                    );\n                    dialog.response.connect (dialog.destroy);\n                    dialog.show ();\n                }\n            });\n            file_chooser.show ();\n        }\n\n        /**\n         * Load editor content from file\n         */\n        void action_load_file () {\n            /* Create file chooser dialog */\n            var file_chooser = new Gtk.FileChooserNative (\n                _(\"Load from File\"),\n                this,\n                OPEN,\n                null,\n                null\n            ) {\n                transient_for = this,\n                modal = true\n            };\n\n            file_chooser.response.connect (() => {\n                /* Get selected file */\n                var file = file_chooser.get_file ();\n                if (file == null)\n                    return;\n\n                load_from (file);\n            });\n            file_chooser.show ();\n        }\n\n        /**\n         * Move focus to the first arguments entry\n         */\n        void action_jump_to_args () {\n            editor.jump_to_args ();\n        }\n\n        /**\n         * Show search overlay\n         */\n        void action_find () {\n            editor.search_bar.show_search ();\n        }\n\n        /**\n         * Tool change callback\n         *\n         * Set tool button's\n         * label and icon\n         */\n        void tool_changed () {\n            /* Update tool button */\n            with (tool_button_content) {\n                label = selected_tool?.translated_name\n                    ?? NO_TOOL_LABEL;\n                icon_name = selected_tool?.icon\n                    ?? NO_TOOL_ICON;\n            }\n\n            /* Disable applying if there are no tool */\n            ((SimpleAction) lookup_action (\"apply\"))\n                .set_enabled (selected_tool != null);\n\n            editor.set_arguments (selected_tool?.translated_arguments ?? new string[0]);\n\n            /* Update editor's top margin */\n            Idle.add (() => {\n                editor.update_editor_margin ();\n\n                return Source.REMOVE;\n            });\n        }\n\n        /**\n         * Select tool and close the search\n         */\n        [GtkCallback]\n        void on_tool_selected (Tool tool) {\n            selected_tool = tool;\n            tool_button.active = false;\n        }\n\n        /**\n         * Change application state\n         * according to new search state\n         */\n        [GtkCallback]\n        void on_search_toggled () {\n            if (tool_button.active) {\n                /* Show search */\n                content_stack.visible_child_name = \"search\";\n            } else {\n                /* Show editor */\n                content_stack.visible_child_name = \"editor\";\n                search.reset ();\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "src/widgets/preferences/Preferences.vala",
    "content": "// Copyright 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n// SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nnamespace TextPieces {\n    /**\n     * Preferences window\n     */\n    [GtkTemplate (ui = \"/com/github/liferooter/textpieces/ui/Preferences.ui\")]\n    class Preferences : Adw.PreferencesWindow {\n        [GtkChild] unowned Gtk.ListBox custom_tools_listbox;\n        [GtkChild] unowned Gtk.Label font_label;\n        [GtkChild] unowned Gtk.SpinButton spaces_in_tab;\n        [GtkChild] unowned GtkSource.StyleSchemeChooserWidget style_scheme_chooser;\n\n        /**\n         * Preferences window actions\n         */\n        const ActionEntry[] ACTION_ENTRIES = {\n            { \"select-font\", action_select_font }\n        };\n\n        /**\n         * List of settings keys\n         * binded to the actions\n         */\n        const string[] SETTINGS_ACTIONS = {\n            \"wrap-lines\",\n            \"tabs-to-spaces\"\n        };\n\n        public Preferences (TextPieces.Window win) {\n            Application.instance.bind_property (\n                \"style-scheme\",\n                style_scheme_chooser,\n                \"style-scheme\",\n                SYNC_CREATE | BIDIRECTIONAL\n            );\n        }\n\n        construct {\n            /* Create actions from entries */\n            var action_group = new SimpleActionGroup ();\n            action_group.add_action_entries (ACTION_ENTRIES, this);\n\n            insert_action_group (\"prefs\", action_group);\n\n            /* Create settings actions */\n            var settings_group = new SimpleActionGroup ();\n            foreach (var setting in SETTINGS_ACTIONS) {\n                var action = Application.settings\n                    .create_action (setting);\n                settings_group.add_action (action);\n            }\n\n            insert_action_group (\"settings\", settings_group);\n\n            /* Bind settings to corresponding widgets */\n            with (Application.settings) {\n                bind (\n                    \"font-name\",\n                    font_label,\n                    \"label\",\n                    DEFAULT\n                );\n                bind (\n                    \"spaces-in-tab\",\n                    spaces_in_tab,\n                    \"value\",\n                    DEFAULT\n                );\n            }\n\n            /* Bind list of custom tools to `custom_tools_listbox` */\n            var tools = Application.tools.custom_tools;\n            custom_tools_listbox.bind_model (\n                tools,\n                (obj) => {\n                    var item = (Tool) obj;\n                    var row = new Adw.ActionRow () {\n                        title = item.translated_name,\n                        subtitle = item.translated_description,\n                        activatable = true\n                    };\n                    row.add_suffix (new Gtk.Image () {\n                        icon_name = \"go-next-symbolic\"\n                    });\n\n                    return row;\n                }\n            );\n\n            /* Don't show empty list */\n            update_tools_visibility ();\n            tools.items_changed.connect (update_tools_visibility);\n        }\n\n        ~Preferences () {\n            /* Unbind list visibility */\n            Application\n                .tools\n                .custom_tools\n                .items_changed\n                .disconnect (update_tools_visibility);\n        }\n\n        /**\n         * Update tools listbox visibility\n         *\n         * Hide if there are no custom tools,\n         * show if there are any custom tools\n         */\n        void update_tools_visibility () {\n            custom_tools_listbox.visible\n                = Application.tools.custom_tools.get_n_items () > 0;\n        }\n\n        /**\n         * Select font action\n         *\n         * Show dialog to select\n         * font for editor and\n         * argument entries\n         */\n        void action_select_font () {\n            /* Create dialog */\n            var dialog = new Gtk.FontChooserDialog (_(\"Select font\"), this) {\n                modal = true,\n                transient_for = this,\n                font = Application.settings.get_string (\"font-name\"),\n                level = FAMILY\n            };\n\n            /* Set font and close dialog on response */\n            dialog.response.connect ((res) => {\n                if (res == Gtk.ResponseType.OK) {\n                    Application.settings.set_string (\"font-name\", dialog.font_desc.get_family ());\n                }\n\n                dialog.close ();\n            });\n\n            /* Show dialog */\n            dialog.present ();\n        }\n\n        /**\n         * Open tool creating page\n         */\n        [GtkCallback]\n        void add_new_tool () {\n            present_subpage (new NewToolPage (this));\n        }\n\n        /**\n         * Open tool settings page\n         */\n        [GtkCallback]\n        void edit_tool (Gtk.ListBoxRow row) {\n            var tool = (Tool) Application\n                .tools\n                .custom_tools\n                .get_item (row.get_index ());\n            present_subpage (new CustomToolPage (this, tool));\n        }\n    }\n}\n"
  },
  {
    "path": "src/widgets/preferences/ToolSettings.vala",
    "content": "// Copyright 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n// SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nnamespace TextPieces {\n    [GtkTemplate (ui = \"/com/github/liferooter/textpieces/ui/ToolSettings.ui\")]\n    class ToolSettings : Adw.Bin {\n        [GtkChild] unowned Gtk.Entry name_entry;\n        [GtkChild] unowned Gtk.Entry description_entry;\n        [GtkChild] unowned Gtk.SpinButton arguments_number;\n        [GtkChild] unowned Gtk.ListBox argument_list;\n        [GtkChild] unowned Gtk.ListBox edit_script_list;\n\n        public Gtk.Window? window = null;\n\n        /**\n         * Whether all entered data is valid\n         */\n        public bool is_valid { get; set; }\n\n        /**\n         * Whether to allow edit script\n         */\n        public bool can_edit_script { get; construct; default = false; }\n\n        /**\n         * Tool which this settings is of\n         */\n        private Tool? tool = null;\n\n        /**\n         * Model of tool arguments\n         */\n        private ListStore arguments =\n            new ListStore (typeof (Argument));\n\n        /**\n         * Tool argument class\n         */\n        private class Argument : Object {\n            /**\n             * Index of the argument\n             */\n            public int index { get; construct set; }\n\n            /**\n             * Tool which the argument belongs to\n             */\n            public Tool tool { get; construct; }\n\n            /**\n             * Name of the argument\n             */\n            public string name { get; construct set; }\n\n            /**\n             * Translated name of the argument\n             */\n            public string translated_name { get { return _(name); } }\n\n            /**\n             * Create argument object\n             *\n             * @param index index of the argument\n             * @param tool tool which the argument belongs to\n             */\n            public Argument (int index, Tool tool) {\n                Object (\n                    index: index,\n                    tool: tool\n                );\n            }\n\n            construct {\n                /* Get argument name from tool */\n                name = tool.arguments[index] ?? \"\";\n\n                /* Update tool argument name\n                   on argument's name changes */\n                notify[\"name\"].connect_after (() => {\n                    tool.arguments[index] = name;\n                });\n            }\n        }\n\n        construct {\n            edit_script_list.visible = can_edit_script;\n\n            name_entry.bind_property (\n                \"text\",\n                this,\n                \"is-valid\",\n                SYNC_CREATE,\n                (_, from, ref to) => {\n                    to.set_boolean (from.get_string () != \"\");\n\n                    return true;\n                }\n            );\n\n            arguments_number.value_changed.connect (() => {\n                /* Add arguments */\n                while (arguments_number.value > arguments.get_n_items ()) {\n                    var arg = new Argument (\n                        (int) arguments.get_n_items (),\n                        tool\n                    );\n                    arguments.append (arg);\n                }\n\n                /* Remove arguments */\n                while (arguments_number.value < arguments.get_n_items ()) {\n                    arguments.remove (arguments.get_n_items () - 1);\n                }\n            });\n        }\n\n        /**\n         * Set tool for tool settings.\n         *\n         * Can be called only when\n         * tool is not set.\n         *\n         * @param tool tool to set\n         */\n        public void set_tool (Tool tool)\n                requires (this.tool == null) {\n            /* Save tool in private field */\n            this.tool = tool;\n\n            /* Bind tool name */\n            tool.bind_property (\n                \"name\",\n                name_entry,\n                \"text\",\n                BIDIRECTIONAL | SYNC_CREATE,\n                null,\n                (_, from, ref to) => {\n                    var name = from.get_string ();\n\n                    if (name != \"\") {\n                        to.set_string (name);\n                        name_entry.remove_css_class (\"error\");\n                        return true;\n                    } else {\n                        name_entry.add_css_class (\"error\");\n                        return false;\n                    }\n                }\n            );\n\n            /* Bind tool description */\n            tool.bind_property (\n                \"description\",\n                description_entry,\n                \"text\",\n                BIDIRECTIONAL | SYNC_CREATE\n            );\n\n            /* Load tool arguments to model */\n            arguments_number.value = tool.arguments.length;\n            arguments_number.value_changed ();\n\n            /* Show arguments list\n               if there are any arguments */\n            argument_list.visible = arguments.get_n_items () != 0;\n\n            /* Setup model rendering */\n            argument_list.bind_model (\n                arguments,\n                (obj) => {\n                    var arg = obj as Argument;\n\n                    /* Create entry for argument name,\n                       align it to the center to don't\n                       stretch to the full height of row */\n                    var entry = new Gtk.Entry () {\n                        valign = CENTER\n                    };\n\n                    /* Create row itself\n                       set title \"Argument N\",\n                       make entry focusable\n                       by click on row */\n                    var row = new Adw.ActionRow () {\n                        title = _(\"Argument %d\").printf (arg.index + 1),\n                        activatable_widget = entry\n                    };\n\n                    /* Add entry to the row */\n                    row.add_suffix (entry);\n\n                    /* Bind entry text to\n                       argument name */\n                    arg.bind_property (\n                        \"name\",\n                        entry,\n                        \"text\",\n                        BIDIRECTIONAL | SYNC_CREATE\n                    );\n\n                    return row;\n                }\n            );\n\n            /* Update tool arguments on model chages */\n            arguments.items_changed.connect ((pos, removed, added) => {\n                /* Get new arguments */\n                var new_args = new string[added];\n                for (var i = 0; i < added; i++) {\n                    var arg = arguments.get_item (pos + i) as Argument;\n                    new_args[i] = arg.translated_name;\n                }\n\n                /* Update tool arguments */\n                var args = tool.arguments[:pos];\n                foreach (var new_arg in new_args)\n                    args += new_arg;\n                foreach (var old_arg in tool.arguments[pos + removed:])\n                    args += old_arg;\n                tool.arguments = args;\n\n                /* Update arguments' indexes */\n                for (var i = pos + added; i < arguments.get_n_items (); i++) {\n                    var arg = arguments.get_item (i) as Argument;\n                    arg.index = (int) i;\n                }\n\n                /* Show list if there are any tools */\n                argument_list.visible = arguments.get_n_items () != 0;\n            });\n        }\n\n        [GtkCallback]\n        async void edit_script () {\n            tool.open (window);\n        }\n    }\n}\n"
  },
  {
    "path": "src/widgets/preferences/pages/CustomToolPage.vala",
    "content": "// Copyright 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n// SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nnamespace TextPieces {\n    /**\n     * Custom tool settings page\n     */\n    [GtkTemplate (ui = \"/com/github/liferooter/textpieces/ui/CustomToolPage.ui\")]\n    class CustomToolPage : Gtk.Box {\n        [GtkChild] unowned ToolSettings tool_settings;\n\n        /**\n         * Tool which this settings is of\n         */\n        public Tool tool { get; construct; }\n\n        /**\n         * Parent preferences window\n         */\n        public Preferences prefs { get; construct; }\n\n        public CustomToolPage (Preferences prefs, Tool tool) {\n            Object (\n                tool: tool,\n                prefs: prefs\n            );\n        }\n\n        /**\n         * Unmap signal override used to\n         * save changes when page is closed\n         *\n         * It's called when page is no\n         * longer visible\n         */\n        public override void unmap () {\n            base.unmap ();\n\n            var custom_tools = Application.tools.custom_tools;\n\n            /* Find tool index */\n            uint pos;\n            custom_tools.find (tool, out pos);\n\n            /* Trigger tools update\n               to apply changes */\n            custom_tools.items_changed (pos, 1, 1);\n\n            /* Save changes */\n            Application.tools.commit.begin ((obj, res) => {\n                try {\n                    Application.tools.commit.end (res);\n                } catch (Error e) {\n                    critical (\"Can't commit tools: %s\", e.message);\n                    prefs.add_toast (new Adw.Toast (\n                        (\"Error occured: %s\").printf (e.message)\n                    ));\n                }\n            });\n        }\n\n        construct {\n            tool_settings.set_tool (tool);\n            tool_settings.window = prefs;\n        }\n\n        /**\n         * Go back and close the page\n         */\n        [GtkCallback]\n        void go_back () {\n            prefs.close_subpage ();\n        }\n\n        /**\n         * Delete tool\n         */\n        [GtkCallback]\n        void delete_tool () {\n            Application.tools.delete_tool (tool);\n        }\n    }\n}\n"
  },
  {
    "path": "src/widgets/preferences/pages/NewToolPage.vala",
    "content": "// Copyright 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n// SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n//\n// SPDX-License-Identifier: GPL-3.0-or-later\n\nnamespace TextPieces {\n    /**\n     * Custom tool create page\n     */\n    [GtkTemplate (ui = \"/com/github/liferooter/textpieces/ui/NewToolPage.ui\")]\n    class NewToolPage : Gtk.Box {\n        [GtkChild] unowned ToolSettings tool_settings;\n\n        /**\n         * Parent preferences window\n         */\n        public Preferences prefs { get; construct; }\n\n        /**\n         * Pre-created tool\n         *\n         * This tool isn't presented\n         * in tools model and added to\n         * the model on create\n         */\n        private Tool new_tool;\n\n        public NewToolPage (Preferences prefs) {\n            Object (\n                prefs: prefs\n            );\n        }\n\n        construct {\n            /* Initialize tool */\n            new_tool = new Tool () {\n                name = \"\",\n                description = \"\",\n                arguments = {},\n                script = \"\",\n                is_system = false\n            };\n\n            /* Setup tool settings widget */\n            tool_settings.set_tool (new_tool);\n            tool_settings.window = prefs;\n        }\n\n        [GtkCallback]\n        void go_back () {\n            prefs.close_subpage ();\n        }\n\n        /**\n         * Create tool\n         *\n         * This method is called when\n         * create button is clicked.\n         * It saves tool and exits.\n         */\n        [GtkCallback]\n        async void create () {\n            /* Create tool directory if not exists */\n            var dir = File.new_for_path (Tool.CUSTOM_TOOLS_DIR.get ());\n            if (!dir.query_exists ()) {\n                try {\n                    yield Utils.ensure_directory_exists (dir);\n                } catch (Error e) {\n                    error (\"Can't create directory for tool scripts: %s\", e.message);\n                }\n            }\n\n            /* Generate script filename with salt */\n            var filename = Tool.generate_filename (new_tool.name);\n\n            /* Get script file */\n            var script_file = File.new_build_filename (\n                Tool.CUSTOM_TOOLS_DIR.get (),\n                filename\n            );\n\n            /* Get template file */\n            var template_file = File.new_build_filename (\n                Config.SCRIPTDIR,\n                \"template\"\n            );\n\n            /* Copy template to file */\n            try {\n                yield template_file.copy_async (\n                    script_file,\n                    OVERWRITE\n                );\n            } catch (Error err) {\n                error (\"Can't copy script template to file: %s\".printf (err.message));\n            }\n\n            /* Change file permissions */\n            FileUtils.chmod (script_file.get_path (), 0750); // rwxr-x---\n\n            /* Set script filename to the tool */\n            new_tool.script = filename;\n\n            /* Add new tool to tools */\n            Application.tools.add_tool (\n                new_tool\n            );\n            try {\n                yield Application.tools.commit ();\n            } catch (Error e) {\n                critical (\"Can't commit tools: %s\", e.message);\n                prefs.add_toast (new Adw.Toast (\n                    _(\"Error occured: %s\").printf (e.message)\n                ));\n            }\n\n            /* Open tool script in\n               your favorite editor */\n            new_tool.open (prefs);\n        }\n    }\n}\n"
  },
  {
    "path": "subprojects/blueprint-compiler.wrap",
    "content": "# SPDX-FileCopyrightText: 2022 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: CC0-1.0\n\n[wrap-git]\ndirectory = blueprint-compiler\nurl = https://gitlab.gnome.org/jwestman/blueprint-compiler.git\nrevision = main\ndepth = 1\n\n[provide]\nprogram_names = blueprint-compiler\n"
  },
  {
    "path": "subprojects/gtksourceview.wrap",
    "content": "# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: CC0-1.0\n\n[wrap-file]\ndirectory = gtksourceview-5.3.2\n\nsource_url = https://download.gnome.org/sources/gtksourceview/5.3/gtksourceview-5.3.2.tar.xz\nsource_filename = gtksourceview-5.3.2.tar.xz\nsource_hash = af7736e2ee3cdbc1013090e8caf35fb89d65cf41c9c399cac5d8992d955ded30\n\n[provide]\ndependency_names = gtksourceview-5"
  },
  {
    "path": "subprojects/json-glib.wrap",
    "content": "# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: CC0-1.0\n\n[wrap-file]\ndirectory = json-glib-1.6.6\n\nsource_url = https://download.gnome.org/sources/json-glib/1.6/json-glib-1.6.6.tar.xz\nsource_filename = json-glib-1.6.6.tar.xz\nsource_hash = 96ec98be7a91f6dde33636720e3da2ff6ecbb90e76ccaa49497f31a6855a490e"
  },
  {
    "path": "subprojects/libadwaita.wrap",
    "content": "# SPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n#\n# SPDX-License-Identifier: CC0-1.0\n\n[wrap-file]\ndirectory = libadwaita-1.0.1\n\nsource_url = https://download.gnome.org/sources/libadwaita/1.0/libadwaita-1.0.1.tar.xz\nsource_filename = libadwaita-1.0.1.tar.xz\nsource_hash = bb49cf5a09d2e8bc144946c2c3272aecd611667fd027f3808b95d7101ed473d6\n\n[provide]\ndependency_names = libadwaita-1\n"
  },
  {
    "path": "textpieces.doap",
    "content": "<!--\nSPDX-FileCopyrightText: 2021 Gleb Smirnov <glebsmirnov0708@gmail.com>\n\nSPDX-License-Identifier: CC0-1.0\n-->\n\n<Project\n  xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\"\n  xmlns:rdfs=\"http://www.w3.org/2000/01/rdf-schema#\"\n  xmlns:foaf=\"http://xmlns.com/foaf/0.1/\"\n  xmlns:gnome=\"http://api.gnome.org/doap-extensions#\"\n  xmlns=\"http://usefulinc.com/ns/doap#\"\n>\n  <name>Text Pieces</name>\n  <shortdesc xml:lang=\"en\">Transform text without using random websites</shortdesc>\n  <description xml:lang=\"en\">\n    Calculate hashes, encode text, decode text, remove trailing spaces and lines,\n    count lines, symbols and words, format JSON, escape and unescape strings,\n    convert JSON to YAML and vice versa, filter lines, replace substrings and regular expressions\n    and so on. You can use predefined tools to transform text and even create your own.\n  </description>\n\n  <homepage rdf:resource=\"https://github.com/liferooter/textpieces/\" />\n  <license rdf:resource=\"http://usefulinc.com/doap/licenses/gpl\" />\n  <bug-database rdf:resource=\"https://github.com/liferooter/textpieces/issues\" />\n  <programming-language>Vala</programming-language>\n\n  <repository>\n    <GitRepository>\n      <location rdf:resource=\"https://github.com/liferooter/textpieces.git\" />\n      <browse rdf:resource=\"https://github.com/liferooter/textpieces\" />\n    </GitRepository>\n  </repository>\n\n  <maintainer>\n    <foaf:Person>\n      <foaf:name>Gleb Smirnov</foaf:name>\n      <foaf:mbox rdf:resource=\"mailto:glebsmirnov0708@gmail.com\" />\n      <foaf:account>\n        <foaf:OnlineAccount>\n            <foaf:accountServiceHomepage rdf:resource=\"https://github.com/\" />\n            <foaf:accountName>liferooter</foaf:accountName>\n        </foaf:OnlineAccount>\n      </foaf:account>\n      <foaf:account>\n        <foaf:OnlineAccount>\n            <foaf:accountServiceHomepage rdf:resource=\"https://gitlab.com/\" />\n            <foaf:accountName>liferooter</foaf:accountName>\n        </foaf:OnlineAccount>\n      </foaf:account>\n      <foaf:account>\n        <foaf:OnlineAccount>\n          <foaf:accountServiceHomepage\n            rdf:resource=\"https://gitlab.gnome.org\"\n          />\n          <foaf:accountName>liferooter</foaf:accountName>\n        </foaf:OnlineAccount>\n      </foaf:account>\n    </foaf:Person>\n  </maintainer>\n</Project>"
  }
]