pax_global_header00006660000000000000000000000064132565324240014520gustar00rootroot0000000000000052 comment=926298b5ba2f33b6cf784f7a1b4cd4043bfe30c7 FeatherPad-0.8/000077500000000000000000000000001325653242400133725ustar00rootroot00000000000000FeatherPad-0.8/COPYING000066400000000000000000001045141325653242400144320ustar00rootroot00000000000000 GNU GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: (1) assert copyright on the software, and (2) offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. TERMS AND CONDITIONS 0. Definitions. "This License" refers to version 3 of the GNU General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. "The Program" refers to any copyrightable work licensed under this License. Each licensee is addressed as "you". "Licensees" and "recipients" may be individuals or organizations. To "modify" a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a "modified version" of the earlier work or a work "based on" the earlier work. A "covered work" means either the unmodified Program or a work based on the Program. To "propagate" a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To "convey" a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays "Appropriate Legal Notices" to the extent that it includes a convenient and prominently visible feature that (1) displays an appropriate copyright notice, and (2) tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. 1. Source Code. The "source code" for a work means the preferred form of the work for making modifications to it. "Object code" means any non-source form of a work. A "Standard Interface" means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The "System Libraries" of an executable work include anything, other than the work as a whole, that (a) is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and (b) serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A "Major Component", in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The "Corresponding Source" for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. 2. Basic Permissions. All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. 3. Protecting Users' Legal Rights From Anti-Circumvention Law. No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. 4. Conveying Verbatim Copies. You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. 5. Conveying Modified Source Versions. You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: a) The work must carry prominent notices stating that you modified it, and giving a relevant date. b) The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to "keep intact all notices". c) You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. d) If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an "aggregate" if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. 6. Conveying Non-Source Forms. You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: a) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. b) Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either (1) a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or (2) access to copy the Corresponding Source from a network server at no charge. c) Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. d) Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. e) Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A "User Product" is either (1) a "consumer product", which means any tangible personal property which is normally used for personal, family, or household purposes, or (2) anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, "normally used" refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. "Installation Information" for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. 7. Additional Terms. "Additional permissions" are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: a) Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or b) Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or c) Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or d) Limiting the use for publicity purposes of names of licensors or authors of the material; or e) Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or f) Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered "further restrictions" within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. 8. Termination. You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated (a) provisionally, unless and until the copyright holder explicitly and finally terminates your license, and (b) permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. 9. Acceptance Not Required for Having Copies. You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. 10. Automatic Licensing of Downstream Recipients. Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An "entity transaction" is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. 11. Patents. A "contributor" is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's "contributor version". A contributor's "essential patent claims" are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, "control" includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a "patent license" is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To "grant" such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either (1) cause the Corresponding Source to be so available, or (2) arrange to deprive yourself of the benefit of the patent license for this particular work, or (3) arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. "Knowingly relying" means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is "discriminatory" if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license (a) in connection with copies of the covered work conveyed by you (or copies made from those copies), or (b) primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. 12. No Surrender of Others' Freedom. If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. 13. Use with the GNU Affero General Public License. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. 15. Disclaimer of Warranty. THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 16. Limitation of Liability. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 17. Interpretation of Sections 15 and 16. If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: Copyright (C) This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an "about box". You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see . The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read . FeatherPad-0.8/ChangeLog000066400000000000000000000565221325653242400151560ustar00rootroot00000000000000V0.8 --------- * Close the warning-bar when the text is scrolled. * Fixed the workaround for the RTL bug in QPlainTextEdit (it included an odd line and the indentation lines were disabled for RTL). * Some fixes for markdown and sh syntax highlighting. * Fixed highlighting of here-docs for languages other than sh. * Fixed highlighting of bash command substitution by using a completely different approach with a simple logic. * Added the symlink "fpad". * Added an options for auto-saving of opened files (disabled by default). * Fixed a potential cause of crash in the function "formatOnTextChange()" by using the object "this" in the singleShot timer. * Close tabs on releasing the middle mouse button instead of pressing it. * Add a new tab on double clicking an empty space of the tabbar. * Added a warning about the lack of permission to read. * Show warnings on focusing the window or changing the tab when a file is modified in another way or removed. * An option for showing the cursor position on the statusbar (disabled by default). * Support syntax override (disabled by default). * An option for vertical position lines. * Don't Activate the window repeatedly on opening multiple files because, otherwise, the input might be blocked. * Fixed a mistake in localization that didn't let Simplified Chinese be detected. * Added a configurable date and time pasting. * Open hyperlinks of highlighted texts by left-clikcing them while the Ctrl key is pressed and also with an item in the context menu. * Put the cursor at the right-click position if it has no selection. * Replaced QRegExp with QRegularExpression. * Added an option for removing trailing spaces on saving (Markdown's trailing double spaces are taken care of). * Added Undo/Redo to custom shortcuts, although their default shortcuts are hard-coded in Qt. * Don't show the shortcut strings of the editor context menu because they can be misleading. * Fixed an old bug in font setting when the document was zoomed. * Show whitespaces or line/document ends with normal texts too (by using the URL syntax highlighting). * Never show the horizontal scrollbar on wrapping (when whitespaces are visible). * Instead of QSharedMemory, use a lock file to create a single instance application because QSharedMemory not only is unsafe in case a crash happens (catching a crash is not an option) but also may persist without a crash and even after being attached and detached, resulting in an unchangeable state. Also, for whatever reason, QSharedMemory may cause random delays. * Never use QCoreApplication::processEvents() because the waiting object may be deleted meanwhile. * Support scss (without syntax error but with its keywords). * Added translators' names/links to About dialog. Also added the license info. * Handle """, ">" and "<", especially in XML. * Support hard-coded symbolic icons that change their colors appropriately (adapted from Cantata). * Don't let QPlainTextEdit's built-in undo/redo shortcuts cause a scroll jump when there's nothing to do (such scroll jumps are a Qt bug, in my opinion). V0.7 --------- * Remember cursor positions for files that are managed by the session dialog (thanks to AcarBurak at GitHub for the idea). * Side-pane mode with filtering. * French translation by Cyril Cottet. * Also apply all settings when Escape is pressed to close the Preferences dialog. (Some settings were applied only when the Close button was pressed). * Added an option to save a session only from the currect window. * An option for always opening files in separate windows. * Fixed a memory leak in making the cursor busy on closing a window with a tab (found by valgrind). * Fixed a bug in bracket matching, which happened when a selected text was removed while the cursor was at its start. * Get red of "foreach" because it is deprecated and will be removed sooner or later. * Fixed double connections with syntax highlighting of dropped pages. * Don't set the right-clicked side-pane item as the current item. * Added items for closing next/previous tabs to side-pane context menus. * Fixed incomplete syntax highlighting after the active tab is changed quickly several times. * Distinguish opened symlinks visually. Also, add context menu items for copying their target paths and opening their targets. * When checking if a file is already opened, also consider symlink targets. * Syntax highlighting for regular expressions of JavaScript (/.../). * Fixes for the syntax highlighting of HTML, especially its style and embedded JavaScript. * Reduced the maximum file size to 100 MiB. * Truncate all lines with more than 500,000 characters while loading a text because they would take a lot of CPU time if shown. Append this string to the truncated lines: " HUGE LINE TRUNCATED: NO LINE WITH MORE THAN 500000 CHARACTERS". Also, don't highlight the syntax of documents with such lines. * Don't format huge lines (with more than 50,000 characters) in CSS. This is especially useful with HTML syntax highlighting. (Kate avoids this problem by "jumping" over huge lines while scrolling but that isn't a good solution, in my opinion.) * Open non-text files as well as files with huge lines as completely uneditable documents to prevent accidental overwritings. * A workaround for a Qt5 bug that will cause a crash if "QPlainTextEdit::setTextCursor()" comes before "QTextCursor::endEditBlock()", near the end of a very long line. Similar bugs were reported but, apparently, there is a buggy code at the root of all Qt5 crashes related to "QPlainTextEdit::setTextCursor()". V0.6.3 --------- * Optional inertial wheel scrolling when the cursor is inside the text view. * Use the native font dialog as a workaround under GTK environments. V0.6.2 --------- * Added playlists to syntax highlighting. * Fixed single-line comments when the comment sign is repeated after being quoted. * Fixed two bad typos in "session.cpp". V0.6.1 --------- * Improved encoding detection. * Always open non-text files as UTF-8 (and detect UTF-16 and UTF-32 better). * Multiple-line indentation with Tab and de-indentation BackTab (Shift+Tab). * Ctrl+Tab for a 4-space tabulation and Ctrl+Meta+Tab for a 2-space one. * Middle-click closes the tab. * Don't open files > 500 MiB. * Added a warning-bar for non-prompt messages. * Added markdown to syntax highlighting (thanks to AcarBurak at GitHub). * Highlight the syntax of sh double quoted code substitution variables "$(...)". * Russian translation by Vladimir Sharonin (Survolog at GitHub). * Polish translation by Marcin Mikołajczak (m4sk1n at GitHub). * Danish translation by scootergrisen at GitHub. * Made translation support optional. * Warn the user about a previous crash. * Added an option for native file dialogs. * Restore the scrollbar position, in addition to the cursor, on reloading. * Consider "next-line" comments (with an ending back-slash) in c/c++. * Workaround for "the hover bug" in Qt5 (will it ever be fixed?!). * Highlight the syntax on text change too (because otherwise, the text won't be formatted if the whole visible text is pasted). * Auto-bracket support. * A workaround for the RTL bug in QPlainTextEdit. * A workaround for the scrollbar range bug in QPlainTextEdit (the range wasn't updated correctly on chaanging font size). * Don't let the statusbar text change the window width. * Added options for showing whitespaces and line/document ends with syntax highlighting. * Match brackets too (in addition to parentheses and braces). * Added config files to syntax highlighting. * Support Shift+Enter (useful e.g. to write code comments). * Addded a shortcut editor to Preferences for customizing some shortcuts. * Filtering for the session manager dialog. V0.6.0 --------- * FeatherPad deserves a new version. * Made the number of the "Recently Modified" menu items optional. * Give each tab a separate search bar to preserve search undo/redo history on switching tabs and also to have completely independent searches. * Removed the remainder of the old C structure of the code. * Change the title of the Replacement dock appropriately on tab switching. * Workarounds for some KDE bugs (automatic mnemonics and double slashes). * An option to show the recently opened files instead of the recently modified ones. * Added a prompt label to the Preferences dialog. * Support Perls's POD (in syntax highlighting). * Session management. * Set the maximum tab length to half the tabbar size and elide tab text dynamically. * Always ensure enough contrast between the active/inactive text selection color and the line highlight color. * Added an option that, by default, will append an empty line on saving if the last line of the document isn't empty. * Iconless mode. V0.5.9 --------- * Added support for FreeBSD. * Added support for text zooming with Ctrl+wheel. * Added an option for the tab navigation with keyboard to wrap around. * Translation support (also added an Esperanto translation). * Support for opening files with newline in their names. * Support for hiding menubar. * Added a lot of CMake and Python keywords to syntax highlighting. * A workaround for Qt's backward search bug. * Removed the translucency workaround because Qt-5.7 translucency problems are fixed in Kvantum (QtCurve should do the same thing). * Set an optional end limit to the forward search (for faster highlighting of found matches, especially on scrolling a large text with sytax highlighting). * An option to set the color value of the background. * An option to hide a single tab. * Add the home folder name to the singleton string because the user may not change as root. * Single-line single quote for c and c++. * Preliminary Wayland support. * Added an option for running executable script files. * Remember whether hidden files are shown in file dialogs. * Rehighlight the syntax on saving if the programming language is changed. * An option to remember last saved files on an empty startup. * "Recently Modified" menu. V0.5.8 --------- * The here-doc delimiter shouldn't be commented out. * Format note and url patterns inside Python multiline quotes. * When reformatting single-line comments inside Highlighter::multiLineComment(), consider note and url patterns too. * Added a separate option for working around the translucency bug of Qt-5.7. * When highlighting the syntax, do the main (single-line) formatting only in the visible part of the text. In this way, syntax highlighting becomes very fast for most languages. * A very simple logic for syntax highlighting of here-docs. * Remove search (and replacement) highlights on hiding the search bar. * Always show the search bar on showing the replacement dock. Also hide the latter on hiding the former. * Added an option for using dark color scheme. * An option to set a fixed start size (700x500 is still the default size). * Added an option for tab position. * Elide long tab texts. * Added two items to tab context menu for copying file name and path. * Made tab drag-and-drop independent from X11. * Fixed an old bug in opening multiple files (it was created after using threads). * Support selection of multiple files in the open file dialog. * Added a time buffer for the currentChanged() signal of QTabWidget. * Made all dialogs window-modal without letting two dialogs at the same time (as a workaround for a Qt issue). * Set the cursor to busy after one second of unfinished activity. * Explicitly disable all window shortcuts when a dialog is shown (Qt should disable all shortcuts of the main window when a modal dialog is shown. This is a Qt bug). * While files are being loaded, disable dangerous shortcuts and don't let any dialog be shown. * Added tab switch menu-items (and shortcuts). V0.5.7 --------- * Added libqt5svg5 as a dependency to 'INSTALL' (but it isn't a build dependency). * Added an option for choosing between own and system icons. * Fixed a memory leak which was created when QtSingleApplication was removed. V0.5.6 --------- * Because of a Qt5 bug, an app may crash on logging out (https://bugreports.qt.io/browse/QTBUG-50829). As a workaround, replaced all theme icons with own icons. * Added RDF, QML, docbook and xhtml to syntax highlighting. * Enable C++11. * Get unix quit signals, namely SIGQUIT, SIGINT (Ctrl-C), SIGTERM and SIGHUP (terminal closed). * When deciding to open a new window or tab, also consider the current viewport in addition to the current desktop. For example, Compiz has only one desktop, which may be divided into multiple viewports. * Explicitly call updateGeometry() for the text-edit on layout change if the main window is translucent. (Workaround for a regression in Qt-5.7?) V0.5.5 --------- * Use a separate thread to get text. (Maybe useful later.) * Set size limit for syntax highlighting. * Put the source on GitHub. * Added QtGlobal types and functions to syntax highlighting. V0.5.4 --------- * Code clean-up. * Replaced the C++-only methods for validating UTF-8 with QTextCodec::toUnicode(). * Added HDPI support. * Transformed signal-slot connections into the new syntax. V0.5.3 --------- * Reversed the change that was made for bypassing the old KGlobal-locale warning message. * Implemented auto-scrolling to selected file in file dialogs. * Replaced search push-buttons with tool-buttons. V0.5.2 --------- * At last, found a simple workaround for Qt5's scroll jump bug. * Use singleShot instead of processEvents. * Never use native dialogs (since KFileDialog can't be trusted). * Removed a ridiculous cause of crash from closeEvent(). * Fixed an old bug in auto-indentation. V0.5.1 --------- * Removed own icons. * No context menu outside all tabs. V0.5.0 --------- * Exported to Qt5 because Qt 5.4.2 seems OK. * Workaround for a Qt5 bug, which prevents insertion of the non-breaking space (zero-width non-joiner, ZWNJ) with SHIFT+SPACE. V0.4.9 --------- * Replaced "%U" with "%F" in the desktop file and used toLocalFile() if the file path is a URL. * In log files, the day number may only have one digit. V0.4.8 --------- * Use QSharedMemory, QLocalServer and QLocalSocket instead of QtSingleApplication to have a single-instance app with a simpler code. * Instead of adding a member variable to the window class, use the qApp macro to get the app. * Set window modality for the subdialogs of the print dialog too. * Don't open new tabs in minimized windows; respect the minimized state! * Use X11 functions for detecting the shaded state and unshading again because isMinimized() may not return true for shaded windows under all WMs. * Format urls and email addresses inside single-line comments too. * Format special notes inside comments. * Format qmake paths. * Some languages need backslash as the line ending for multiline quotes. * Remove server instance from previous crash. * Use documentMode. V0.4.7 --------- * Take into account some DEs that don't support _NET_CURRENT_DESKTOP. * Always use absoluteFilePath() when opening a file. * Use absolutePath() instead of dir().path() to show the path of the opened file as tooltip. * Do nothing in keyPressEvent() if the text is readonly. * Guarantee a fixed height for lineedits. * Instead of hiding and showing a window to resize it to the default size, reparent it with a dummy widget and take it out again to guarantee resizing under all DEs. V0.4.6 --------- * Faster mouse wheel scrolling when the mouse cursor is on the vertical scrollbar. * Use changeEvent() to get the window state because not all window managers trigger resizeEvent() when a window is unmaximized. * Rehighlight found matches after zooming out or changing font. * For returning to the default size to be possible under all circumstances, hide the window before resizing and show it again afterward. V0.4.5 --------- * Added syntax highlighting for HTML embedded JavaScript. * Added syntax highlighting for extra bash keywords. * Added syntax highlighting for srt subtitles and gtkrc. * A workaround for the scrollbar range(s) not being updated after zooming in the text. * To unshade the active window, it's enough to consider it minimized and then, restore and activate it. V0.4.4 --------- * Added HTML multiline comment highlighting. * Added syntax highlighting for makefile, cmake, qmake, troff, theme files and Ruby. V0.4.3 --------- * Code clean-up. * Unshade the active window when a tab is created or a file is opened in it. * Remove the settings for "max" and "fullscreen" when the window size isn't saved. V0.4.2 --------- * Added '/usr/lib/x86_64-linux-gnu/libX11.so' as a system library to the project to fix the new error messages "x11.o: undefined reference to symbol...". * Corrected highlighting of Bash keywords and also url patterns. * Show tab tooltips without delay. * Don't use setFixedSize() with dialogs because it may prevent them from being centered when shown. * Under KDE, when text is selected for the first time after starting FeatherPad, it isn't copied to the selection clipboard. Although I still don't know the cause (Klipper?), I found a workaround. V0.4.1 -------- * Added tab context menus for closing other tabs. * When text is selected, use the left and right arrow keys to go to the start and end of the selection respectively. * Use hasUrls() instead of hasFormat("text/plain") in dragEnterEvent() for DnD to work with all file managers. * When an empty file is opened in the current tab, open the next file in a new tab. V0.4.0 -------- * Made FeatherPad independent from any icon set. * Use an extra setTextCursor() in find() for the signal selectionChanged() be emitted (it's needed by the status bar). * Set the toolbar icon size explicitly to 22x22. * Made the status bar info selectable with mouse. * More complete email address pattern in quotation highlighting. * Fixed bugs in searching for multiline strings. V0.3.6 -------- * When highlighting found matches, search for them only inside the visible part of the text. * Fixed bugs in HTML highlighting. * No need to enforce a central position for dialogs anymore. V0.3.5 -------- * Restructured syntax highlighting. * Fixed a bug common to Highlighter::isQuoted() and Highlighter::isMLCommented(). * Fixed HTML highlighting. * Fixed a minor bug in CSS highlighting. * Fixed some bugs related to quotation and commenting out. * Don't disconnect the syntax highlighting signal in a QTextDocument when doing tab DND. V0.3.4 -------- * A more advanced way of CSS syntax highlighting (with error handling). V0.3.3 -------- * Fixed the CSS syntax highlighting. V0.3.2 -------- * Rehighlight found matches after text replacement because they may need correction. * Set style sheet only for the viewport of QPlainTextEdit. * Added icons for GTK+ based environments and also for root. * Always use raise() after activateWindow(). * Corrected a mistake, which had occurred during replacement of QLists with QHash. V0.3.1 -------- * Corrected the C single-line comment. * When opening a file, use the name of the currently opened file in the dialog. * When saving a deleted file, show the save-as dialog only if the file isn't restorable (because its containing folder is removed). * Disconnect everything connected to the signal modificationChanged() before setting text and reconnect them afterward. V0.3.0 -------- * Use QHash instead of QLists to set and get the needed info on each tab. * After a disastrous overwriting, I decided to use filters for saving as far as possible. V0.2.10 -------- * Added an option to Preferences for always showing the statusbar. * Format Apt's sources.list. * Removed duplicate QActions. V0.2.9 -------- * Yet faster highlighting of here-docs. * Don't reset the chosen encoding of a new document to UTF-8 with tab switching. * Offer saving before changing the encoding if the text of an opened file is modified. V0.2.8 -------- * Better and faster highlighting of here-docs. * To be able to open system files as well, use read() instead of getChar() for file opening. V0.2.7 -------- * It seems that highlighting of here-docs works now, although not efficiently. V0.2.6 -------- * A slightly better way for highlighting of here-documents. * Cleaned up the detection of escaped quotes. * Use link target to get mime type. V0.2.5 -------- * TextBlockData needed a destructor. * Syntax highlighting for Perl. * Only an odd number of backslashes before a quotation mark means that it's escaped. V0.2.4 --------- * Added parentheses and braces matching. V0.2.3 --------- * Suppose that only the script languages (Bash, Lua and Python) have both single and double quotes. V0.2.2 --------- * Added formatting of Bash "here documnents". V0.2.1 --------- * Debugged and polished syntax highlighting. V0.2.0 --------- * Dropped using of source-highlight-qt and source-highlight; devised a heuristic method instead. V0.1.11 --------- * Included what was needed from source-highlight-qt in the source. * Show the number of replaced occurrences. V0.1.10 --------- * Fixed bugs in text highlighting. V0.1.9 --------- * If a tab with a readonly text is dropped, don't activate cut and delete menuitems. * No need to set the visibility of actionEdit when dropping tabs as it's set by tabChange(). * The tab detachment keyboard shortcut was ambiguous. * Set the tab width proportional to the font size. * Added 'About' dialog and 'Help'. * Use path of containing folder as tab tootip and insert ellipsis for very long tooltips. * Fixed a silly bug in tab DND. * Write size to the config file only if necessary. * Fixed a little bug in size preference. * Enabled tooltips for inactive windows. V0.1.8 --------- * A better way of giving window modality to open and save dialogs, which bypasses the KDE bug related to the KGlobal-locale warning message. * No need to delete child widgets as they're deleted with their parents. * Added Preferences. V0.1.7 --------- * Added the number of selected characters to the status bar. V0.1.6 --------- * Use a docked window instead of replace dialog. * Fixed two bugs related to the clear shortcut and the clear button of LineEdit. V0.1.5 --------- * Added calculation of word numbers to the status bar. * It's more logical to keep a tab with empty document when another tab is dropped into its window. * Fixed a bug related to the enabled/disabled states of the reload button and menu item. V0.1.4 --------- * Simplified an X11 function (no need to get a list of all windows in the order of creation). * Always use QX11Info::display() instead of XOpenDisplay(NULL), otherwise there would be a memory leak. V0.1.3 --------- * Use closeTab() in detachTab(). * Prevent any possibility of memory leak by using appropriate destructors and returning QList instead of "Window*" from the X11 function List(). * Don't drop tab if the drop target has a modal dialog. * Open a window and not a tab if a FeatherPad window is on the same desktop but has a modal dialog. V0.1.2 --------- * Drop a dragged tab into the topmost FeatherPad window if the drop point is inside more than one window. * Don't add a new tab if the current tab of the drop target has no text and isn't modified. * Fixed a memory leak in the function whichDesktops(). V0.1.1 --------- * Tab drag-and-drop, including tab detachment and window merging, without using of Qt's DnD mechanism. * Fixed disappearance of green highlights when the yellow ones were repainted. * Better X11 functions. * Use window modality and not application modality for all dialogs. * Other minor fixes. V0.1.0 --------- * FeatherPad is reborn in Qt :) I tried to make it similar to its GTK+ predecessor but it became even better. FeatherPad-0.8/INSTALL000066400000000000000000000030101325653242400144150ustar00rootroot00000000000000************************************ * Compilation And Installation * ************************************ First install build dependencies. In Debian-based systems, they are: * g++ >= 5 * libx11-dev and libxext-dev (for X11) * qtbase5-dev and libqt5x11extras5-dev (for Qt5) * libqt5svg5 (for showing own icons) * qttools5-dev-tools (for localization) In Arch-based systems, the required package are: * gcc (or gcc-multilib for multilib systems) * libx11 and libxext (for X11) * qt5-base and qt5-x11extras (for Qt5) * qt5-svg (for showing own icons) * qt5-tools (for localization) Then open a terminal inside this folder and issue the following commands: qmake && make sudo make install If your default Qt installation is not Qt5, put the full path of Qt5 qmake in the first command but before that, put the full path of Qt5's "lrelease" binary into 'featherpad/featherpad.pro' (only one place). Afterward, you could use this command for cleaning the source directory: make distclean ********************************** * Translation (Localization) * ********************************** The file 'featherpad/data/translations/featherpad.ts' can serve as the basis for translation. The translated file should be saved in the same directory as "featherpad_LN.ts", where "LN" is the abbreviation for the target language, like "de", "fr", etc. If you have translated FeatherPad's GUI into your language, please make a "Pull Request" (PR) at https://github.com/tsujan/FeatherPad for your work to be merged into FeatherPad! FeatherPad-0.8/NEWS000066400000000000000000000001021325653242400140620ustar00rootroot00000000000000Latest version: 27 Mar 2018, V0.8 See "ChangeLog" for changes. FeatherPad-0.8/README.md000066400000000000000000000030041325653242400146460ustar00rootroot00000000000000# FeatherPad ## Overview FeatherPad (by Pedram Pourang, a.k.a. Tsu Jan ) is a lightweight Qt5 plain-text editor for Linux. It is independent of any desktop environment and has: * Drag-and-drop support, including tab detachment and attachment; * X11 virtual desktop awareness (using tabs on current desktop but opening a new window on another); * An optionally permanent search-bar with a different search entry for each tab; * Instant highlighting of found matches when searching; * A docked window for text replacement; * Support for showing line numbers and jumping to a specific line; * Automatic detection of text encoding as far as possible and optional saving with encoding; * Syntax highlighting for common programming languages; * Session management; * Side-pane mode; * Auto-saving; * Printing; * Text zooming; * Appropriate but non-interrupting prompts; and * Other features that can be found in its settings, on its menus or when it is actually used. Please see INSTALL for instructions on compilation, installation and translation! FeatherPad was written in GTK+ at first, then ported to Qt with more features. Its homepage is . ## Screenshots The active Qt widget style determines the look and feel of every Qt application. The following screenshots are taken with a Kvantum theme and the dark color scheme of FeatherPad: ![Tabs](screenshots/Tabs.png?raw=true "Tabs") ![Side-Pane](screenshots/Side-Pane.png?raw=true "Side-Pane") FeatherPad-0.8/featherpad/000077500000000000000000000000001325653242400154755ustar00rootroot00000000000000FeatherPad-0.8/featherpad/about.ui000066400000000000000000000235401325653242400171520ustar00rootroot00000000000000 FeatherPad::AboutDialog 0 0 400 300 Dialog 0 Qt::AlignCenter Qt::AlignCenter Qt::AlignCenter Qt::Vertical 20 5 Qt::Vertical 20 5 Qt::NoFocus <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Danish:</span> <a href="https://github.com/scootergrisen"><span;>scootergrisen at GitHub</span></a></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; text-decoration: underline; color:#2eb8e6;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Esperanto:</span> Tsu Jan (author)</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">French:</span> <a href="mailto:cyrilusber2001@yahoo.fr?Subject=My%20Subject"><span;>Cottet Cyril</span></a></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Polish:</span> Marcin Mikołajczak (<a href="https://github.com/m4sk1n"><span;>m4sk1n at GitHub)</span></a></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Russian:</span> Vladimir Sharonin (<a href="https://github.com/Survolog"><span;>Survolog at GitHub</span></a>)</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Simplified Chinese:</span> <a href="https://github.com/notname000"><span;>notname000 at GitHub</span></a></p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> </style></head><body style=" font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-weight:600;">Spanish:</span> <a href="https://github.com/micrococo"><span;>micrococo at GitHub</span></a></p></body></html> true License Qt::NoFocus <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">FeatherPad</span> 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 <a href="https://spdx.org/licenses/GPL-3.0+.html"><span;>version 3 of the License</span></a>, or (at your option) any later version.</p> <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><br /></p> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-style:italic;">FeatherPad</span> 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.</p></body></html> true Qt::Horizontal QDialogButtonBox::Close buttonBox accepted() AboutDialog accept() 199 279 199 149 buttonBox rejected() AboutDialog reject() 199 279 199 149 FeatherPad-0.8/featherpad/brackets.cpp000066400000000000000000000263651325653242400200130ustar00rootroot00000000000000/* * Copyright (C) Pedram Pourang (aka Tsu Jan) 2014 * * FeatherPad 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. * * FeatherPad is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * @license GPL-3.0+ */ #include "fpwin.h" #include "ui_fp.h" namespace FeatherPad { void FPwin::matchBrackets() { int index = ui->tabWidget->currentIndex(); if (index == -1) return; TextEdit *textEdit = qobject_cast< TabPage *>(ui->tabWidget->widget (index))->textEdit(); QTextCursor cur = textEdit->textCursor(); TextBlockData *data = static_cast(cur.block().userData()); if (!data) return; QList es = textEdit->extraSelections(); int n = textEdit->getRedSel().count(); while (n > 0 && !es.isEmpty()) // just a precaution against crash { es.removeLast(); --n; } textEdit->setRedSel (QList()); textEdit->setExtraSelections (es); QTextDocument *doc = textEdit->document(); int curPos = cur.position(); /* position of block's first character */ int blockPos = cur.block().position(); /* position of cursor in block */ int curBlockPos = curPos - blockPos; /* parenthesis */ bool isAtLeft (doc->characterAt (curPos) == '('); bool isAtRight (doc->characterAt (curPos - 1) == ')'); bool findNextBrace (!isAtLeft || !isAtRight); if (isAtLeft || isAtRight) { QVector infos = data->parentheses(); for (int i = 0; i < infos.size(); ++i) { ParenthesisInfo *info = infos.at (i); if (isAtLeft && info->position == curBlockPos && info->character == '(') { if (matchLeftParenthesis (cur.block(), i + 1, 0)) { createSelection (blockPos + info->position); if (isAtRight) isAtLeft = false; else break; } } if (isAtRight && info->position == curBlockPos - 1 && info->character == ')') { if (matchRightParenthesis (cur.block(), infos.size() - i, 0)) { createSelection (blockPos + info->position); if (isAtLeft) isAtRight = false; else break; } } } } if (!findNextBrace) return; /* brace */ isAtLeft = (doc->characterAt (curPos) == '{'); isAtRight = (doc->characterAt (curPos - 1) == '}'); findNextBrace = !isAtLeft || !isAtRight; if (isAtLeft || isAtRight) { QVector braceInfos = data->braces(); for (int i = 0; i < braceInfos.size(); ++i) { BraceInfo *info = braceInfos.at (i); if (isAtLeft && info->position == curBlockPos && info->character == '{') { if (matchLeftBrace (cur.block(), i + 1, 0)) { createSelection (blockPos + info->position); if (isAtRight) isAtLeft = false; else break; } } if (isAtRight && info->position == curBlockPos - 1 && info->character == '}') { if (matchRightBrace (cur.block(), braceInfos.size() - i, 0)) { createSelection (blockPos + info->position); if (isAtLeft) isAtRight = false; else break; } } } } if (!findNextBrace) return; /* bracket */ isAtLeft = (doc->characterAt (curPos) == '['); isAtRight = (doc->characterAt (curPos - 1) == ']'); if (isAtLeft || isAtRight) { QVector bracketInfos = data->brackets(); for (int i = 0; i < bracketInfos.size(); ++i) { BracketInfo *info = bracketInfos.at (i); if (isAtLeft && info->position == curBlockPos && info->character == '[') { if (matchLeftBracket (cur.block(), i + 1, 0)) { createSelection (blockPos + info->position); if (isAtRight) isAtLeft = false; else break; } } if (isAtRight && info->position == curBlockPos - 1 && info->character == ']') { if (matchRightBracket (cur.block(), bracketInfos.size() - i, 0)) { createSelection (blockPos + info->position); if (isAtLeft) isAtRight = false; else break; } } } } } /*************************/ bool FPwin::matchLeftParenthesis (QTextBlock currentBlock, int i, int numLeftParentheses) { TextBlockData *data = static_cast(currentBlock.userData()); if (!data) return false; QVector infos = data->parentheses(); int docPos = currentBlock.position(); for (; i < infos.size(); ++i) { ParenthesisInfo *info = infos.at (i); if (info->character == '(') { ++numLeftParentheses; continue; } if (info->character == ')' && numLeftParentheses == 0) { createSelection (docPos + info->position); return true; } else --numLeftParentheses; } currentBlock = currentBlock.next(); if (currentBlock.isValid()) return matchLeftParenthesis (currentBlock, 0, numLeftParentheses); return false; } /*************************/ bool FPwin::matchRightParenthesis (QTextBlock currentBlock, int i, int numRightParentheses) { TextBlockData *data = static_cast(currentBlock.userData()); if (!data) return false; QVector infos = data->parentheses(); int docPos = currentBlock.position(); for (; i < infos.size(); ++i) { ParenthesisInfo *info = infos.at (infos.size() - 1 - i); if (info->character == ')') { ++numRightParentheses; continue; } if (info->character == '(' && numRightParentheses == 0) { createSelection (docPos + info->position); return true; } else --numRightParentheses; } currentBlock = currentBlock.previous(); if (currentBlock.isValid()) return matchRightParenthesis (currentBlock, 0, numRightParentheses); return false; } /*************************/ bool FPwin::matchLeftBrace (QTextBlock currentBlock, int i, int numRightBraces) { TextBlockData *data = static_cast(currentBlock.userData()); if (!data) return false; QVector infos = data->braces(); int docPos = currentBlock.position(); for (; i < infos.size(); ++i) { BraceInfo *info = infos.at (i); if (info->character == '{') { ++numRightBraces; continue; } if (info->character == '}' && numRightBraces == 0) { createSelection (docPos + info->position); return true; } else --numRightBraces; } currentBlock = currentBlock.next(); if (currentBlock.isValid()) return matchLeftBrace (currentBlock, 0, numRightBraces); return false; } /*************************/ bool FPwin::matchRightBrace (QTextBlock currentBlock, int i, int numLeftBraces) { TextBlockData *data = static_cast(currentBlock.userData()); if (!data) return false; QVector infos = data->braces(); int docPos = currentBlock.position(); for (; i < infos.size(); ++i) { BraceInfo *info = infos.at (infos.size() - 1 - i); if (info->character == '}') { ++numLeftBraces; continue; } if (info->character == '{' && numLeftBraces == 0) { createSelection (docPos + info->position); return true; } else --numLeftBraces; } currentBlock = currentBlock.previous(); if (currentBlock.isValid()) return matchRightBrace (currentBlock, 0, numLeftBraces); return false; } /*************************/ bool FPwin::matchLeftBracket (QTextBlock currentBlock, int i, int numRightBrackets) { TextBlockData *data = static_cast(currentBlock.userData()); if (!data) return false; QVector infos = data->brackets(); int docPos = currentBlock.position(); for (; i < infos.size(); ++i) { BracketInfo *info = infos.at (i); if (info->character == '[') { ++numRightBrackets; continue; } if (info->character == ']' && numRightBrackets == 0) { createSelection (docPos + info->position); return true; } else --numRightBrackets; } currentBlock = currentBlock.next(); if (currentBlock.isValid()) return matchLeftBracket (currentBlock, 0, numRightBrackets); return false; } /*************************/ bool FPwin::matchRightBracket (QTextBlock currentBlock, int i, int numLeftBrackets) { TextBlockData *data = static_cast(currentBlock.userData()); if (!data) return false; QVector infos = data->brackets(); int docPos = currentBlock.position(); for (; i < infos.size(); ++i) { BracketInfo *info = infos.at (infos.size() - 1 - i); if (info->character == ']') { ++numLeftBrackets; continue; } if (info->character == '[' && numLeftBrackets == 0) { createSelection (docPos + info->position); return true; } else --numLeftBrackets; } currentBlock = currentBlock.previous(); if (currentBlock.isValid()) return matchRightBracket (currentBlock, 0, numLeftBrackets); return false; } /*************************/ void FPwin::createSelection (int pos) { int index = ui->tabWidget->currentIndex(); if (index == -1) return; TextEdit *textEdit = qobject_cast< TabPage *>(ui->tabWidget->widget (index))->textEdit(); QList es = textEdit->extraSelections(); QTextCursor cursor = textEdit->textCursor(); cursor.setPosition (pos); cursor.movePosition (QTextCursor::NextCharacter, QTextCursor::KeepAnchor); QTextEdit::ExtraSelection extra; extra.format.setBackground (textEdit->hasDarkScheme() ? QColor (190, 0, 3) : QColor (255, 150, 150)); extra.cursor = cursor; QList rsel = textEdit->getRedSel(); rsel.append (extra); textEdit->setRedSel (rsel); es.append (extra); textEdit->setExtraSelections (es); } } FeatherPad-0.8/featherpad/config.cpp000066400000000000000000000340531325653242400174530ustar00rootroot00000000000000/* * Copyright (C) Pedram Pourang (aka Tsu Jan) 2014 * * FeatherPad 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. * * FeatherPad is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * @license GPL-3.0+ */ #include "config.h" #include #include namespace FeatherPad { Config::Config(): remSize_ (true), remSplitterPos_ (true), iconless_ (false), sysIcon_ (false), noToolbar_ (false), noMenubar_ (false), hideSearchbar_ (false), showStatusbar_ (false), showCursorPos_ (false), showLangSelector_ (false), sidePaneMode_ (false), remFont_ (true), wrapByDefault_ (true), indentByDefault_ (true), autoBracket_ (false), lineByDefault_ (false), syntaxByDefault_ (true), showWhiteSpace_ (false), showEndings_ (false), isMaxed_ (false), isFull_ (false), darkColScheme_ (false), tabWrapAround_ (false), hideSingleTab_ (false), executeScripts_ (false), appendEmptyLine_ (true), removeTrailingSpaces_ (false), openInWindows_ (false), nativeDialog_ (false), inertialScrolling_ (false), autoSave_ (false), scrollJumpWorkaround_ (false), vLineDistance_ (-80), tabPosition_ (0), maxSHSize_ (2), lightBgColorValue_ (255), darkBgColorValue_ (15), recentFilesNumber_ (10), curRecentFilesNumber_ (10), // not needed autoSaveInterval_ (1), // not needed winSize_ (QSize (700, 500)), startSize_ (QSize (700, 500)), splitterPos_ (20), // percentage font_ (QFont ("Monospace", 9)), openRecentFiles_ (0), recentOpened_ (false), cursorPosRetrieved_ (false) {} /*************************/ Config::~Config() {} /*************************/ void Config::readConfig() { Settings settings ("featherpad", "fp"); /************** *** Window *** **************/ settings.beginGroup ("window"); if (settings.value ("size") == "none") remSize_ = false; // true by default else { winSize_ = settings.value ("size", QSize(700, 500)).toSize(); if (!winSize_.isValid() || winSize_.isNull()) winSize_ = QSize (700, 500); isMaxed_ = settings.value ("max", false).toBool(); isFull_ = settings.value ("fullscreen", false).toBool(); } startSize_ = settings.value ("startSize", QSize(700, 500)).toSize(); if (!startSize_.isValid() || startSize_.isNull()) startSize_ = QSize (700, 500); if (settings.value ("splitterPos") == "none") remSplitterPos_ = false; // true by default else splitterPos_ = qMin (qMax (settings.value ("splitterPos", 20).toInt(), 0), 100); if (settings.value ("iconless").toBool()) iconless_ = true; // false by default if (settings.value ("sysIcon").toBool()) sysIcon_ = true; // false by default if (settings.value ("noToolbar").toBool()) noToolbar_ = true; // false by default if (settings.value ("noMenubar").toBool()) noMenubar_ = true; // false by default if (noToolbar_ && noMenubar_) { // we don't want to hide all actions noToolbar_ = false; noMenubar_ = true; } if (settings.value ("hideSearchbar").toBool()) hideSearchbar_ = true; // false by default if (settings.value ("showStatusbar").toBool()) showStatusbar_ = true; // false by default if (settings.value ("showCursorPos").toBool()) showCursorPos_ = true; // false by default if (settings.value ("showLangSelector").toBool()) showLangSelector_ = true; // false by default if (settings.value ("sidePaneMode").toBool()) sidePaneMode_ = true; // false by default int pos = settings.value ("tabPosition").toInt(); if (pos > 0 && pos <= 3) tabPosition_ = pos; // 0 by default if (settings.value ("tabWrapAround").toBool()) tabWrapAround_ = true; // false by default if (settings.value ("hideSingleTab").toBool()) hideSingleTab_ = true; // false by default if (settings.value ("openInWindows").toBool()) openInWindows_ = true; // false by default if (settings.value ("nativeDialog").toBool()) nativeDialog_ = true; // false by default settings.endGroup(); /************ *** Text *** ************/ settings.beginGroup ("text"); if (settings.value ("font") == "none") remFont_ = false; // true by default else font_.fromString ((settings.value ("font", "Monospace,9,-1,5,50,0,0,0,0,0")).toString()); if (settings.value ("noWrap").toBool()) wrapByDefault_ = false; // true by default if (settings.value ("noIndent").toBool()) indentByDefault_ = false; // true by default if (settings.value ("autoBracket").toBool()) autoBracket_ = true; // false by default if (settings.value ("lineNumbers").toBool()) lineByDefault_ = true; // false by default if (settings.value ("noSyntaxHighlighting").toBool()) syntaxByDefault_ = false; // true by default if (settings.value ("showWhiteSpace").toBool()) showWhiteSpace_ = true; // false by default if (settings.value ("showEndings").toBool()) showEndings_ = true; // false by default if (settings.value ("darkColorScheme").toBool()) darkColScheme_ = true; // false by default if (settings.value ("inertialScrolling").toBool()) inertialScrolling_ = true; // false by default if (settings.value ("autoSave").toBool()) autoSave_ = true; // false by default int distance = settings.value ("vLineDistance").toInt(); if (qAbs (distance) >= 10 && qAbs (distance) < 1000) vLineDistance_ = distance; // -80 by default if (settings.value ("scrollJumpWorkaround").toBool()) scrollJumpWorkaround_ = true; // false by default maxSHSize_ = qBound (1, settings.value ("maxSHSize", 2).toInt(), 10); /* don't let the dark bg be darker than #e6e6e6 */ lightBgColorValue_ = qBound (230, settings.value ("lightBgColorValue", 255).toInt(), 255); /* don't let the dark bg be lighter than #323232 */ darkBgColorValue_ = qBound (0, settings.value ("darkBgColorValue", 15).toInt(), 50); dateFormat_ = settings.value ("dateFormat").toString(); if (settings.value ("executeScripts").toBool()) executeScripts_ = true; // false by default executeCommand_ = settings.value ("executeCommand").toString(); QVariant v = settings.value ("appendEmptyLine"); if (v.isValid()) // true by default appendEmptyLine_ = v.toBool(); if (settings.value ("removeTrailingSpaces").toBool()) removeTrailingSpaces_ = true; // false by default recentFilesNumber_ = qBound (1, settings.value ("recentFilesNumber", 10).toInt(), 20); curRecentFilesNumber_ = recentFilesNumber_; // fixed recentFiles_ = settings.value ("recentFiles").toStringList(); recentFiles_.removeAll (""); recentFiles_.removeDuplicates(); while (recentFiles_.count() > recentFilesNumber_) recentFiles_.removeLast(); openRecentFiles_ = qBound (0, settings.value ("openRecentFiles", 0).toInt(), recentFilesNumber_); if (settings.value ("recentOpened").toBool()) recentOpened_ = true; // false by default autoSaveInterval_ = qBound (1, settings.value ("autoSaveInterval", 1).toInt(), 60); settings.endGroup(); } /*************************/ void Config::readShortcuts() { Settings settings ("featherpad", "fp"); settings.beginGroup ("shortcuts"); QStringList actions = settings.childKeys(); for (int i = 0; i < actions.size(); ++i) { QVariant v = settings.value (actions.at (i)); if (isValidShortCut (v)) setActionShortcut (actions.at (i), v.toString()); else // remove the key on writing config removedActions_ << actions.at (i); } settings.endGroup(); } /*************************/ void Config::writeConfig() { Settings settings ("featherpad", "fp"); if (!settings.isWritable()) return; /************** *** Window *** **************/ settings.beginGroup ("window"); if (remSize_) { settings.setValue ("size", winSize_); settings.setValue ("max", isMaxed_); settings.setValue ("fullscreen", isFull_); } else { settings.setValue ("size", "none"); settings.remove ("max"); settings.remove ("fullscreen"); } if (remSplitterPos_) settings.setValue ("splitterPos", splitterPos_); else settings.setValue ("splitterPos", "none"); settings.setValue ("startSize", startSize_); settings.setValue ("iconless", iconless_); settings.setValue ("sysIcon", sysIcon_); settings.setValue ("noToolbar", noToolbar_); settings.setValue ("noMenubar", noMenubar_); settings.setValue ("hideSearchbar", hideSearchbar_); settings.setValue ("showStatusbar", showStatusbar_); settings.setValue ("showCursorPos", showCursorPos_); settings.setValue ("showLangSelector", showLangSelector_); settings.setValue ("sidePaneMode", sidePaneMode_); settings.setValue ("tabPosition", tabPosition_); settings.setValue ("tabWrapAround", tabWrapAround_); settings.setValue ("hideSingleTab", hideSingleTab_); settings.setValue ("openInWindows", openInWindows_); settings.setValue ("nativeDialog", nativeDialog_); settings.endGroup(); /************ *** Text *** ************/ settings.beginGroup ("text"); if (remFont_) settings.setValue ("font", font_.toString()); else settings.setValue ("font", "none"); settings.setValue ("noWrap", !wrapByDefault_); settings.setValue ("noIndent", !indentByDefault_); settings.setValue ("autoBracket", autoBracket_); settings.setValue ("lineNumbers", lineByDefault_); settings.setValue ("noSyntaxHighlighting", !syntaxByDefault_); settings.setValue ("showWhiteSpace", showWhiteSpace_); settings.setValue ("showEndings", showEndings_); settings.setValue ("darkColorScheme", darkColScheme_); settings.setValue ("inertialScrolling", inertialScrolling_); settings.setValue ("autoSave", autoSave_); settings.setValue ("scrollJumpWorkaround", scrollJumpWorkaround_); settings.setValue ("maxSHSize", maxSHSize_); settings.setValue ("lightBgColorValue", lightBgColorValue_); settings.setValue ("dateFormat", dateFormat_); settings.setValue ("darkBgColorValue", darkBgColorValue_); settings.setValue ("executeScripts", executeScripts_); settings.setValue ("appendEmptyLine", appendEmptyLine_); settings.setValue ("removeTrailingSpaces", removeTrailingSpaces_); settings.setValue ("vLineDistance", vLineDistance_); settings.setValue ("recentFilesNumber", recentFilesNumber_); settings.setValue ("executeCommand", executeCommand_); while (recentFiles_.count() > recentFilesNumber_) // recentFilesNumber_ may have decreased recentFiles_.removeLast(); if (recentFiles_.isEmpty()) // don't save "@Invalid()" settings.remove("recentFiles"); else settings.setValue ("recentFiles", recentFiles_); settings.setValue ("openRecentFiles", openRecentFiles_); settings.setValue ("recentOpened", recentOpened_); settings.setValue ("autoSaveInterval", autoSaveInterval_); settings.endGroup(); /***************** *** Shortcuts *** *****************/ settings.beginGroup ("shortcuts"); for (int i = 0; i < removedActions_.size(); ++i) settings.remove (removedActions_.at (i)); QHash::const_iterator it = actions_.constBegin(); while (it != actions_.constEnd()) { settings.setValue (it.key(), it.value()); ++it; } settings.endGroup(); writeCursorPos(); } /*************************/ void Config::readCursorPos() { if (!cursorPosRetrieved_) { Settings settings ("featherpad", "fp_cursor_pos"); cursorPos_ = settings.value ("cursorPositions").toHash(); cursorPosRetrieved_ = true; } } /*************************/ void Config::writeCursorPos() { if (!cursorPos_.isEmpty()) { /* no need to clean up the config file here because it's more or less cleaned by the session dialog */ /*QHash::iterator it = cursorPos_.begin(); while (it != cursorPos_.end()) { if (!QFileInfo (it.key()).isFile()) it = cursorPos_.erase (it); else ++it; }*/ Settings settings ("featherpad", "fp_cursor_pos"); if (settings.isWritable()) settings.setValue ("cursorPositions", cursorPos_); } } /*************************/ void Config::addRecentFile (QString file) { recentFiles_.removeAll (file); recentFiles_.prepend (file); while (recentFiles_.count() > curRecentFilesNumber_) recentFiles_.removeLast(); } /*************************/ // Used only at the session start QStringList Config::getLastFiles() const { QStringList res; int n = qMin (openRecentFiles_, recentFiles_.count()); for (int i = 0; i < n; ++i) res << recentFiles_.at (i); return res; } /*************************/ bool Config::isValidShortCut (const QVariant v) { static QStringList added; if (v.isValid()) { QString str = v.toString(); if (!QKeySequence (str).toString().isEmpty()) { if (!reservedShortcuts_.contains (str) // prevent ambiguous shortcuts at startup as far as possible && !added.contains (str)) { added << str; return true; } } } return false; } } FeatherPad-0.8/featherpad/config.h000066400000000000000000000267651325653242400171330ustar00rootroot00000000000000/* * Copyright (C) Pedram Pourang (aka Tsu Jan) 2014 * * FeatherPad 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. * * FeatherPad is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * @license GPL-3.0+ */ #ifndef CONFIG_H #define CONFIG_H #include #include #include namespace FeatherPad { // Prevent redundant writings! (Why does QSettings write to the config file when no setting is changed?) class Settings : public QSettings { Q_OBJECT public: Settings (const QString &organization, const QString &application = QString(), QObject *parent = nullptr) : QSettings (organization, application, parent) {} void setValue (const QString &key, const QVariant &v) { if (value (key) == v) return; QSettings::setValue (key, v); } }; class Config { public: Config(); ~Config(); void readConfig(); void readShortcuts(); void writeConfig(); void writeCursorPos(); bool getRemSize() const { return remSize_; } void setRemSize (bool rem) { remSize_ = rem; } bool getRemSplitterPos() const { return remSplitterPos_; } void setRemSplitterPos (bool rem) { remSplitterPos_ = rem; } bool getIconless() const { return iconless_; } void setIconless (bool iconless) { iconless_ = iconless; } bool getSysIcon() const { return sysIcon_; } void setSysIcon (bool own) { sysIcon_ = own; } bool getIsMaxed() const { return isMaxed_; } void setIsMaxed (bool isMaxed) { isMaxed_ = isMaxed; } bool getIsFull() const { return isFull_; } void setIsFull (bool isFull) { isFull_ = isFull; } bool getDarkColScheme() const { return darkColScheme_; } void setDarkColScheme (bool dark) { darkColScheme_ = dark; } int getLightBgColorValue() const { return lightBgColorValue_; } void setLightBgColorValue (int lightness) { lightBgColorValue_ = lightness; } int getDarkBgColorValue() const { return darkBgColorValue_; } void setDarkBgColorValue (int darkness) { darkBgColorValue_ = darkness; } QString getDateFormat() const { return dateFormat_; } void setDateFormat (const QString &format) { dateFormat_ = format; } int getRecentFilesNumber() const { return recentFilesNumber_; } void setRecentFilesNumber (int number) { recentFilesNumber_ = number; } int getCurRecentFilesNumber() const { return curRecentFilesNumber_; } bool getTabWrapAround() const { return tabWrapAround_; } void setTabWrapAround (bool wrap) { tabWrapAround_ = wrap; } bool getHideSingleTab() const { return hideSingleTab_; } void setHideSingleTab (bool hide) { hideSingleTab_ = hide; } QSize getWinSize() const { return winSize_; } void setWinSize (QSize s) { winSize_ = s; } QSize getStartSize() const { return startSize_; } void setStartSize (QSize s) { startSize_ = s; } int getSplitterPos() const { return splitterPos_; } void setSplitterPos (int pos) { splitterPos_ = pos; } bool getNoToolbar() const { return noToolbar_; } void setNoToolbar (bool noTB) { noToolbar_ = noTB; } bool getNoMenubar() const { return noMenubar_; } void setNoMenubar (bool noMB) { noMenubar_ = noMB; } bool getHideSearchbar() const { return hideSearchbar_; } void setHideSearchbar (bool hide) { hideSearchbar_ = hide; } bool getShowStatusbar() const { return showStatusbar_; } void setShowStatusbar (bool show) { showStatusbar_ = show; } bool getShowCursorPos() const { return showCursorPos_; } void setShowCursorPos (bool show) { showCursorPos_ = show; } bool getShowLangSelector() const { return showLangSelector_; } void setShowLangSelector (bool show) { showLangSelector_ = show; } bool getSidePaneMode() const { return sidePaneMode_; } void setSidePaneMode (bool sidePane) { sidePaneMode_ = sidePane; } int getTabPosition() const { return tabPosition_; } void setTabPosition (int pos) { tabPosition_ = pos; } QFont getFont() const { return font_; } void setFont (const QFont &font) { font_ = font; } bool getRemFont() const { return remFont_; } void setRemFont (bool rem) { remFont_ = rem; } bool getWrapByDefault() const { return wrapByDefault_; } void setWrapByDefault (bool wrap) { wrapByDefault_ = wrap; } bool getIndentByDefault() const { return indentByDefault_; } void setIndentByDefault (bool indent) { indentByDefault_ = indent; } bool getAutoBracket() const { return autoBracket_; } void setAutoBracket (bool autoB) { autoBracket_ = autoB; } bool getLineByDefault() const { return lineByDefault_; } void setLineByDefault (bool line) { lineByDefault_ = line; } bool getSyntaxByDefault() const { return syntaxByDefault_; } void setSyntaxByDefault (bool syntax) { syntaxByDefault_ = syntax; } bool getShowWhiteSpace() const { return showWhiteSpace_; } void setShowWhiteSpace (bool show) { showWhiteSpace_ = show; } bool getShowEndings() const { return showEndings_; } void setShowEndings (bool show) { showEndings_ = show; } int getVLineDistance() const { return vLineDistance_; } void setVLineDistance (int distance) { vLineDistance_ = distance; } int getMaxSHSize() const { return maxSHSize_; } void setMaxSHSize (int max) { maxSHSize_ = max; } bool getScrollJumpWorkaround() const { return scrollJumpWorkaround_; } void setScrollJumpWorkaround (bool workaround) { scrollJumpWorkaround_ = workaround; } bool getExecuteScripts() const { return executeScripts_; } void setExecuteScripts (bool excute) { executeScripts_ = excute; } QString getExecuteCommand() const { return executeCommand_; } void setExecuteCommand (QString commnad) { executeCommand_ = commnad; } bool getAppendEmptyLine() const { return appendEmptyLine_; } void setAppendEmptyLine (bool append) { appendEmptyLine_ = append; } bool getRemoveTrailingSpaces() const { return removeTrailingSpaces_; } void setRemoveTrailingSpaces (bool remove) { removeTrailingSpaces_ = remove; } bool getOpenInWindows() const { return openInWindows_; } void setOpenInWindows (bool windows) { openInWindows_ = windows; } bool getNativeDialog() const { return nativeDialog_; } void setNativeDialog (bool native) { nativeDialog_ = native; } int getOpenRecentFiles() const { return openRecentFiles_; } void setOpenRecentFiles (int number) { openRecentFiles_ = qBound (0, number, 20); } bool getRecentOpened() const { return recentOpened_; } void setRecentOpened (bool opened) { recentOpened_ = opened; } QStringList getLastFiles() const; QStringList getRecentFiles() const { return recentFiles_; } void clearRecentFiles() { recentFiles_ = QStringList(); } void addRecentFile (QString file); QHash customShortcutActions() const { return actions_; } void setActionShortcut (const QString &action, const QString &shortcut) { actions_.insert (action, shortcut); } void removeShortcut (const QString &action) { actions_.remove (action); removedActions_ << action; } bool hasReservedShortcuts() const { return (!reservedShortcuts_.isEmpty()); } QStringList reservedShortcuts() const { return reservedShortcuts_; } void setReservedShortcuts (const QStringList &s) { reservedShortcuts_ = s; } bool getInertialScrolling() const { return inertialScrolling_; } void setInertialScrolling (bool inertial) { inertialScrolling_ = inertial; } QHash savedCursorPos() { readCursorPos(); return cursorPos_; } void saveCursorPos (const QString& name, int pos) { readCursorPos(); if (removedCursorPos_.contains (name)) removedCursorPos_.removeOne (name); else cursorPos_.insert (name, pos); } void removeCursorPos (const QString& name) { readCursorPos(); cursorPos_.remove (name); removedCursorPos_ << name; } void removeAllCursorPos() { readCursorPos(); removedCursorPos_.append (cursorPos_.keys()); cursorPos_.clear(); } bool getAutoSave() const { return autoSave_; } void setAutoSave (bool as) { autoSave_ = as; } int getAutoSaveInterval() const { return autoSaveInterval_; } void setAutoSaveInterval (int i) { autoSaveInterval_ = i; } private: bool isValidShortCut (const QVariant v); void readCursorPos(); bool remSize_, remSplitterPos_, iconless_, sysIcon_, noToolbar_, noMenubar_, hideSearchbar_, showStatusbar_, showCursorPos_, showLangSelector_, sidePaneMode_, remFont_, wrapByDefault_, indentByDefault_, autoBracket_, lineByDefault_, syntaxByDefault_, showWhiteSpace_, showEndings_, isMaxed_, isFull_, darkColScheme_, tabWrapAround_, hideSingleTab_, executeScripts_, appendEmptyLine_, removeTrailingSpaces_, openInWindows_, nativeDialog_, inertialScrolling_, autoSave_, scrollJumpWorkaround_; // Should a workaround for Qt5's "scroll jump" bug be applied? int vLineDistance_, tabPosition_, maxSHSize_, lightBgColorValue_, darkBgColorValue_, recentFilesNumber_, curRecentFilesNumber_, // the start value of recentFilesNumber_ -- fixed during a session autoSaveInterval_; QString dateFormat_; QSize winSize_, startSize_; int splitterPos_; QFont font_; QString executeCommand_; int openRecentFiles_; bool recentOpened_; QStringList recentFiles_; QHash actions_; QStringList removedActions_, reservedShortcuts_; QHash cursorPos_; QStringList removedCursorPos_; // used only internally for the clean-up bool cursorPosRetrieved_; // used only internally for reading once }; } #endif // CONFIG_H FeatherPad-0.8/featherpad/data/000077500000000000000000000000001325653242400164065ustar00rootroot00000000000000FeatherPad-0.8/featherpad/data/featherpad.desktop000066400000000000000000000011541325653242400221050ustar00rootroot00000000000000[Desktop Entry] Name=FeatherPad GenericName=Text Editor GenericName[da]=Tekstredigering GenericName[eo]=Tekst-Redaktilo GenericName[pl]=Edytor tekstu GenericName[pt]=Editor de texto GenericName[zh_CN]=文本编辑器 Comment=Lightweight Qt5 text editor Comment[da]=Letvægts Qt5-tekstredigering Comment[eo]=Malpeza tekst-redaktilo bazita sur Qt5 Comment[pl]=Lekki edytor tekstu oparty na Qt5 Comment[pt]=Editor de texto em Qt5 Comment[zh_CN]=轻量级 Qt5 文本编辑器 Exec=featherpad %F Icon=featherpad Terminal=false Type=Application MimeType=text/plain; Categories=Qt;Utility;TextEditor; X-KDE-StartupNotify=false FeatherPad-0.8/featherpad/data/featherpad.svg000066400000000000000000000151341325653242400212360ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/fp.qrc000066400000000000000000000030631325653242400175240ustar00rootroot00000000000000 icons/application-exit.svg icons/application-menu.svg icons/arrow-down-double.svg icons/case.svg icons/document-edit.svg icons/document-new.svg icons/document-open.svg icons/document-open-recent.svg icons/document-print.svg icons/document-properties.svg icons/document-save-as.svg icons/document-save.svg icons/edit-clear.svg icons/edit-copy.svg icons/edit-cut.svg icons/edit-delete.svg icons/edit-find-replace.svg icons/edit-find.svg icons/edit-paste.svg icons/edit-redo.svg icons/edit-select-all.svg icons/edit-undo.svg icons/featherpad.svg icons/go-down.svg icons/go-jump.svg icons/go-next.svg icons/go-previous.svg icons/go-up.svg icons/help-about.svg icons/help-contents.svg icons/link.svg icons/preferences-desktop-font.svg icons/preferences-system.svg icons/system-run.svg icons/tab.svg icons/tab-close-other.svg icons/view-refresh.svg icons/whole.svg icons/window-close.svg FeatherPad-0.8/featherpad/data/help000066400000000000000000000253411325653242400172660ustar00rootroot00000000000000********************* * Drag-and-Drop * ********************* Files can be dragged and dropped into FeatherPad windows. Also tabs can be dragged from a window and dropped into another window or outside all FeatherPad windows. In the first case, the dropped tab will appear after the previously active tab; in the second case, a new window containing the dropped tab will be created, i.e. the tab will be detached. ************ * Tabs * ************ Tabs can be reordered or detached by the mouse. If there is more that one tab, each one will have a right-click menu for closing its right or left tabs. With more than one tab, it is also possible to detach the active tab by clicking the related item on the File menu or with Ctrl+T. If a file is opened in a tab, the right-click menu of that tab will also contain two items for copying the name and path of the file. For users' comfort, double clicking on an empty area of tha tab-bar creates a new tab. *********************** * File Management * *********************** If there is a FeatherPad window on the current virtual desktop or viewport, so that more than half of its width as well as its height is visible, files will be opened as new tabs in it; otherwise, a new window will be created. Also when the window on the current desktop/viewport has a modal dialog, another window is created. In this way, FeatherPad is aware of virtual desktops under most Linux desktop environments. However, there are exceptions (like Enlightenment). Files are always opened after the active tab unless it is empty, in which case the first file will be opened in it. If a single file is opened, its tab will be activated but in the case of multiple files, the active tab will not change. If a file is opened multiple times, its second (third, ... ) instance will be uneditable by default and will have a light yellow or dark red background, depending on whether the default or the dark color scheme is used. To make it editable, click on the newly created 'Edit' button on the toolbar or the 'Edit' menu. After that, these two buttons will disappear again. If the opened file is a symbolic link (symlink), the context menu of its tab will have two extra items for copying its target path and opening its target inside the current window. Executable script files could be run from inside FeatherPad if the corresponding option is enabled in the Preferences dialog. Then also a Run button will appear on the tool bar and the File menu whenever needed. If no terminal command is used to run them, their output and error messages will be shown by a popup dialog. FeatherPad remembers recently modified or opened files, depending on which option is enabled in the Preferences dialog. It can also open them on a session startup. However, it has a more advanced session manager, which provides the user with options for saving a session and restoring or removing saved sessions at any time and without limit. *********************************** * Sessions and Side-Pane Mode * *********************************** Sessions can be saved and opened by using the Session Manager dialog. As mentioned above, there is no limit to the number of stored sessions. Each session can have any name and consist of any number of files. All files of a session are opened in the current FeatherPad window and their cursor positions are remembered. FeatherPad also has a side-pane mode, which can be enabled either temporarily or with startup. It is most suitable for working with sessions because its file list is alphabetically ordered and can be filtered. Unlike in the tab mode, when multiple files are opened in the side-pane mode while the active document is empty, the alphabetically first file becomes visible. Each pane item has a right-click menu, which contains menu-items for various jobs when there is more than one page. Items can also be removed by middle-clicking without being selected. Although the side-pane mode does not have the features provided by tabs -- for example, the tab drag-and-drop is missing from it -- it can be used alongside the tab mode. ******************************************* * Encodings and Programming Languages * ******************************************* FeatherPad tries to guess encodings as well as programming languages (for syntax highlighting) when opening files. Although it often guesses encodings right, there is no exact way for that. Therefore, there are some encodings in the Options menu. If you choose one, the text could be saved with it by using the item "Save with Encoding" on the File menu. By default, all texts are saved with UTF-8. The programming language is detected based on the mime type or file name and its syntax will be highlighted if the syntax highlighting is enabled and if it is supported for the language in question. There is an optiion in the Preferences dialog that, if enabled, will add a language button to the status bar for overriding the original syntax or lack of it. Reloading a document restores its original syntax. There are also options for showing whitespaces and vertical position lines with all documents. If, at least, one of them is enabled, it will be possible to open hyperlinks by right clicking them and activating the related menu-item or by pressing the Control key, moving the cursor over them, and clicking them while the cursor is like a pointing hand. ******************************* * Searching and Replacing * ******************************* To remove the yellow highlights after finishing a search, you could * Click on the 'Clear' icon of the search entry, or * Press Ctrl+K while the search entry has focus, or * Empty the search entry and press Enter or F3 in it, or * Hide the search bar by focusing it and then, pressing Ctrl+F. The 'Replace' docked window respects the settings for 'Match Case' and 'Whole Word' on the search bar. It can be detached from and reattached to the the main window at top or bottom. To remove the green highlights after replacing text, you could either hide/close the 'Replace' docked window or do as in the case of removing yellow search highlights (without closing the dock). *********************** * Going to A Line * *********************** The Jump bar can be shown by clicking its item on the toolbar or the Search menu. Jumping will happen after pressing Enter while the Jump spinbox is active. If the checkbox beside it is checked, all the text between the text cursor and the target line will be selected. ****************** * Status Bar * ****************** The status bar shows some information about the opened file and can be toggled with the item "Document Properties" on the File menu. You could also make it visible permanently in the Preferences dialog, in which case, the above-mentioned menu item will be removed. The Preferences dialog also has an option (disabled by default) for showing the current cursor position on the status bar. *********************** * Wheel Scrolling * *********************** For fast mouse wheel scrolling, put the cursor on the scrollbar. If it is inside the text view, the speed of wheel scrolling will be normal. If, in addition, the Shift key is pressed, the text will scroll one line per wheel turn. "Inertial" scrolling can be enabled in the Text section of the Preferences dialog. It creates a kind of inertia with wheel scrolling when the cursor is inside the text view. ******************** * Text Tabbing * ******************** The (selected multi-line) text can be tabbed by the Tab key and untabbed by Shift+Tab. If Ctrl+Tab is used, the tabulation will be done by 4 spaces instead of a tab. With Ctrl+Meta+Tab, the text will be tabbed by 2 spaces. ******************** * Auto-Bracket * ******************** With "auto-bracketing" enabled in Preferences, if a left parenthesis, brace, square bracket or double-quote is typed, a right parenthesis, brace, square bracket or double-quote will respectively be inserted after it and the cursor will be moved between them, provided that the next character is not a letter or number. (Although double-quote is not a bracket and has identical left and right signs, it is included in this.) Also if any part of the text is selected from end to start, typing of a left parenthesis, brace, etc. will add a right one after the selection end, so that the selection will be put inside parentheses, braces, etc. For user convenience, if Enter/Return is pressed after a text selection is auto-bracketed by parentheses "(...)" or braces "{...}", the bracketed text will be put below the left bracket and above the right one. The same holds for RTL (right-to-left) texts but with right and left reversed. ********************************* * Useful Keyboard Shortcuts * ********************************* Ctrl+H This help Ctrl+L Show/hide line numbers Ctrl+W Toggle wrapping state Ctrl+I Toggle auto-indentation Ctrl+Shift+H Toggle syntax highlighting Ctrl+Shift+R Refresh (reload file) Ctrl+J Jump to line Ctrl+Shift+J Select text to line (the Jump bar should be visible) Ctrl+F Focus/hide search bar (it hides only a focused search bar) F3 Forward search (also Enter can be pressed in the search entry) F4 Backward search F5 Match case F6 Whole word Ctrl+R Replacement dock F7 Forward replacement F8 Backward replacement F9 Replace all Ctrl+K Clear the text of an entry when it is focused Ctrl+Shift+P Preferences Ctrl+Shift+D Document statistics (on the statusbar) Ctrl+Shift+Q Close tab Ctrl+Shift+V Paste date and time Ctrl+T Detach tab Alt+Right Next tab (Alt+Left for RTL) Alt+Left Previous tab (Alt+Right for RTL) Alt+Up Last tab Alt+Down First tab Ctrl+P Print Ctrl+E Edit uneditable documents Extra Shortcuts: ================= F11 (Un-)Fullscreen Ctrl+Shift+W Resize window to fixed size (700x500 by default but can be set in Preferences) Ctrl+= Zoom in (also Ctrl++ or Ctrl + mouse wheel) Ctrl+- Zoom out (also Ctrl + mouse wheel) Ctrl+0 Reset zooming Ctrl+E Execute this file (only if this is enabled in Preferences) Ctrl+Alt+E Exit (kill) the above process immediately Ctrl+K Delete to the end of the line when the editor is focused Ctrl+Right/Left Move the cursor one word to the right/left Ctrl+Tab 4-space text tabulation Ctrl+Meta+Tab 2-space text tabulation Shift+Enter Insert newline with the non-letter prefix of the current line (to write code comments easily) The remaining shortcuts are standard. All shortcuts, except for the extra ones above, can be found on menus or as tooltips. Many of them can be customized in the Preferences dialog. FeatherPad-0.8/featherpad/data/icons/000077500000000000000000000000001325653242400175215ustar00rootroot00000000000000FeatherPad-0.8/featherpad/data/icons/application-exit.svg000066400000000000000000000011771325653242400235220ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/application-menu.svg000066400000000000000000000006051325653242400235100ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/arrow-down-double.svg000066400000000000000000000010751325653242400236140ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/case.svg000066400000000000000000000015731325653242400211630ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/document-edit.svg000066400000000000000000000012141325653242400230010ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/document-new.svg000066400000000000000000000006671325653242400226600ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/document-open-recent.svg000066400000000000000000000015101325653242400242720ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/document-open.svg000066400000000000000000000007471325653242400230270ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/document-print.svg000066400000000000000000000007631325653242400232200ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/document-properties.svg000066400000000000000000000026401325653242400242540ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/document-save-as.svg000066400000000000000000000012741325653242400234210ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/document-save.svg000066400000000000000000000010561325653242400230160ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/edit-clear.svg000066400000000000000000000010351325653242400222520ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/edit-copy.svg000066400000000000000000000006421325653242400221410ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/edit-cut.svg000066400000000000000000000024711325653242400217640ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/edit-delete.svg000066400000000000000000000004761325653242400224360ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/edit-find-replace.svg000066400000000000000000000020471325653242400235210ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/edit-find.svg000066400000000000000000000012421325653242400221040ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/edit-paste.svg000066400000000000000000000007031325653242400223010ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/edit-redo.svg000066400000000000000000000011461325653242400221200ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/edit-select-all.svg000066400000000000000000000012171325653242400232130ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/edit-undo.svg000066400000000000000000000011431325653242400221310ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/featherpad.svg000066400000000000000000000125111325653242400223450ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/go-down.svg000066400000000000000000000005641325653242400216210ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/go-jump.svg000066400000000000000000000010621325653242400216170ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/go-next.svg000066400000000000000000000005621325653242400216260ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/go-previous.svg000066400000000000000000000005611325653242400225230ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/go-up.svg000066400000000000000000000005611325653242400212730ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/help-about.svg000066400000000000000000000007471325653242400223120ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/help-contents.svg000066400000000000000000000023331325653242400230260ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/link.svg000066400000000000000000000010231325653242400211730ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/preferences-desktop-font.svg000066400000000000000000000006251325653242400251610ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/preferences-system.svg000066400000000000000000000034071325653242400240710ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/system-run.svg000066400000000000000000000075021325653242400223740ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/tab-close-other.svg000066400000000000000000000011521325653242400232310ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/tab.svg000066400000000000000000000017621325653242400210160ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/view-refresh.svg000066400000000000000000000014251325653242400226520ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/whole.svg000066400000000000000000000007371325653242400213670ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/icons/window-close.svg000066400000000000000000000015721325653242400226610ustar00rootroot00000000000000 FeatherPad-0.8/featherpad/data/translations/000077500000000000000000000000001325653242400211275ustar00rootroot00000000000000FeatherPad-0.8/featherpad/data/translations/featherpad.ts000066400000000000000000002370041325653242400236100ustar00rootroot00000000000000 FeatherPad::AboutDialog License FeatherPad::FPwin Untitled Go to line: Select text from cursor to this line (Ctrl+Shift+J) Select Text Ctrl+Shift+J &File &Edit &Options Encoding &Search &Help Find: To be replaced Replace with: Replacing text F8 F7 F9 &New New tab Ctrl+N &Open Open a file Ctrl+O &Save Save the current tab Ctrl+S &Undo Undo Ctrl+Z &Redo Redo Ctrl+Shift+Z Reload Ctrl+Shift+R &Find Ctrl+F Show/hide replacement dock Ctrl+R Save &As Ctrl+Shift+S &Print Ctrl+P Documen&t Properties Ctrl+Shift+D &Close Ctrl+Shift+Q &Quit Ctrl+Q &Cut Ctrl+X C&opy Ctrl+C &Paste Ctrl+V &Delete &Select All Ctrl+A &Font &Line Numbers Ctrl+L &Wrap Lines Ctrl+W &Auto-Indentation Ctrl+I &Syntax Highlighting Ctrl+Shift+H &Preferences Ctrl+Shift+P Ctrl+H &About Enforce UTF-8 Save with &Encoding &Jump to Show/hide jump bar Ctrl+J Edit text Ctrl+Shift+E &Run Ctrl+E &Clear Clear the list of recently modified files Save/Restore Session Sa&ve/Restore Session Ctrl+M Side-Pane Ctrl+Alt+P Paste Date and Time Ctrl+Shift+V &Detach Tab Ctrl+T Close Ne&xt Tabs Close &Previous Tabs Ne&xt Tab Previous Ta&b Execute Close &All Tabs Recently &Modified &Encoding &Unicode &Western European &East European Ea&st Asian Rep&lacement Focus/hide search bar &Replace Windows Arabic (&CP1256) &Other &UTF-8 UTF-&16 &ISO-8859-1 &Windows-1252 &Cyrillic (CP1251) Cyrillic (&KOI8-U) Cyrillic (&ISO-8859-5) &Chineese (BIG5) Chinese (&GB18030) &Japanese (ISO-2022-JP) Japanese (&ISO-2022-JP-2) Japanese (ISO-&2022-KR) Ja&panese (CP932) Japa&nese (EUC-JP) &Korean (CP949) K&orean (CP1361) Korean (&EUC-KR) ISO-&8859-15 Close &Other Tabs &Copy File Name Copy File &Path Alt+Right Alt+Left &First Tab Alt+Down &Last Tab Alt+Up Menu Calculate number of words (For huge texts, this may be CPU-intensive.) Next Previous Replace all &Recently Opened All Close Save changes? Please attend to that window or just close its dialog! The document has been modified. The file has been removed. Save Discard changes Cancel No to all Unsaved Lines Sel. Chars Words Another process is running in this tab! Script File Another FeatherPad window has a modal dialog! Ctrl+= Ctrl++ Ctrl+- Ctrl+0 F11 Ctrl+Shift+W Ctrl+Alt+E Shift+Ins Shift+Del Ctrl+Ins Ctrl+Left Ctrl+Right Ctrl+Up Ctrl+Down Ctrl+Home Ctrl+End F3 F4 F5 F6 Shift+Enter Ctrl+Tab Ctrl+Meta+Tab Ctrl+K Position: Normal Only one process is allowed per tab. Script Output Clear FeatherPad does not open files larger than 100 MiB. Some file(s) could not be opened! You may not have the permission to read. Uneditable file(s)! Non-text files or files with huge lines cannot be edited. A previous crash detected! Preferably, close all FeatherPad windows and start again! All Files (*) All Files (*);;.%1 Files (*.%1) Open file... .%1 Files (*.%1);;All Files (*) Save as... Keep encoding and save as... Yes No Do you want to use <b>MS Windows</b> end-of-lines? This may be good for readability under MS Windows. Cannot be saved! Help Syntax Huge file(s) not opened! This file has been modified elsewhere or in another way! Please be careful about reloading or saving this document! Print Document Copy Target Path Open Target Here Translators A lightweight, tabbed, plain-text editor based on Qt5 Author aka. About FeatherPad No Replacement One Replacement Replacements FeatherPad::FileDialog Ctrl+H Toggle showing hidden files Alt+. Toggle showing hidden files FeatherPad::LineEdit Clear text (Ctrl+K) Ctrl+K Clear text FeatherPad::PrefDialog Preferences Window Window Settings Remember window &size on closing Window frame is excluded. Start with this size: px × Most suitable with sessions but without tab functionality. Uncheck for 1/5 of the width. Remember splitter position No icons in the main window and its menus. KDE may have a bug that disables search and replace shortcuts with the iconless mode. Needs application restart to take effect. &Iconless mode Do not show &toolbar If the menubar is hidden, a menu button appears on the toolbar. Do not show &menubar Hide search &bar by default Always show st&atus bar Show cursor position on status bar Will take effect after closing this dialog. Tab position: North South West East This means that, for LTR, Alt+Right goes to the first tab after the last tab is activated, and the same for Alt+Left. Tab navigation with mouse wheel is not affected. Tab navigation wraps &around By default, if a FeatherPad window exists on the current desktop, files will be opened in its tabs. However, some desktop environments may not report that they have multiple desktops. Always open in separate windows If this is checked, the file dialog provided by the current desktop environment will be used instead of the Qt file dialog. Some desktop environments, like KDE and LXQt, provide files dialogs. Native file dialog Uncheck for Monospace 9. Remember &font This covers parentheses, braces, brackets and quotes. Auto-&bracket Never highlight syntax for files > This creates a menu button on the status bar for changing the syntax. Support syntax override Show spaces, tabs and tab lines when the syntax is highlighted. Show whitespaces The vertical position lines will be drawn only if the editor font has a fixed pitch (like Monospace). Show vertical lines starting from this position: Also show line and document ends Date and time format: Some text editors cannot open a document whose last line is not empty. Ensure an empty last line on saving Remove trailing spaces on saving Inertial scrolling with mouse wheel Files File Management This can be any starting command with arguments, for example, "xterm -hold" for running the process in XTerm. If the command is left empty, the file will be executed directly. If the script is not run in a terminal emulator, the output and error messages will be shown by a popup dialog. Start with this command: Command + Arguments Show recentl&y modified files Show recentl&y opened files Save changes to opened files every: Help Uncheck this if you want FeatherPad to use system icons! Needs application restart to take effect. Start with side-pane mode &Use own icons If this is checked, not only you will lose the informative tooltip and context menu of a single tab but you could not merge a single tabbed window into another one by tab drag- and-drop either. &Do not show a single tab Text Text Editor &Wrap lines by default Auto-&indent by default Always show line &numbers Highlight s&yntax by default Dark c&olor scheme Background color value: MiB This is not a complete fix but prevents annoying scroll jumps. Workaround for &Qt5's scroll jump bug This only includes executable files written in script languages like Shell and Python. If it is checked and the opened file is executable, the file will be executed by clicking the Run button, that appears on the toolbar/filemenu when needed, or by its shortcut Ctrl+E. Then, the process could be killed by Ctrl+Alt+E. Needs window reopening to take effect. The color value of the background. 255 means white while 0 means black. For the light background, it can be between 230 and 255; for the dark background, between 0 and 50. Needs window reopening to take effect. Run executable scripts The maximum number of recently modified or opened files FeatherPad shows. The default is 10. Needs application restart to take effect. Used for pasting the date and time. Takes effect after closing this dialog. Leave empty for: MMM dd, yyyy, hh:mm:ss Should the mouse wheel scrolling be inertial if the cursor is inside the text view? Number of recent files: The maximum number of recently modified or opened files FeatherPad should open with an empty startup of a session. Start with recent files: Only for files that exist and can be saved. min Shortcuts Action Shortcut Restore default shortcuts. Default Close files file No file Warning: Ambiguous shortcut detected! Application restart is needed for changes to take effect. Window reopening is needed for changes to take effect. &Recently Opened Recently &Modified The typed shortcut was not valid. The typed shortcut was reserved. FeatherPad::SearchBar Search... Match Case Whole Word Next Previous FeatherPad::SessionDialog Session Manager &Remove <b>Save/Restore Session</b> Filter... Open the selected session(s). &Open Del Ctrl+Del By default, all files that are opened in all windows will be included in the saved session. Save only from this &window <b>Warning</b> &Yes &No Re&name Remove &All &Close Save the curent session under the given title. &Save Type a name to save session Nothing saved.<br>No file was opened. No file exists or can be opened. Not all files exist or can be opened. &OK Do you really want to remove all saved sessions? Do you really want to remove the selected sessions? Do you really want to remove the selected session? A session with the same name exists.<br>Do you want to overwrite it? FeatherPad::SidePane Filter... FeatherPad::TextEdit Open Link Copy Link Paste Date and Time FeatherPad::WarningBar Close FeatherPad-0.8/featherpad/data/translations/featherpad_da.ts000066400000000000000000002444651325653242400242650ustar00rootroot00000000000000 FeatherPad::AboutDialog License Licens FeatherPad::FPwin Untitled Unavngivet Go to line: Gå til linje: Select text from cursor to this line (Ctrl+Shift+J) Vælg tekst fra markør til denne linje (Ctrl+Skift+J) Select Text Vælg tekst Ctrl+Shift+J Ctrl+Skift+J &File &Fil &Edit &Rediger &Options &Valgmuligheder Encoding Kodning &Search &Søg &Help &Hjælp Find: Find: To be replaced Som skal erstattes Replace with: Erstat med: Replacing text Erstatter tekst F8 F8 F7 F7 F9 F9 &New &Nyt New tab Nyt faneblad Ctrl+N Ctrl+N &Open &Åbn Open a file Åbn en fil Ctrl+O Ctrl+O &Save &Gem Save the current tab Gem det nuværende faneblad Ctrl+S Ctrl+S &Undo &Fortryd Undo Fortryd Ctrl+Z Ctrl+Z &Redo &Omgør Redo Omgør Ctrl+Shift+Z Ctrl+Skift+Z Reload Genindlæs Ctrl+Shift+R Ctrl+Skift+R &Find &Find Ctrl+F Ctrl+F Show/hide replacement dock Vis/skjul erstatningsdok Ctrl+R Ctrl+R Save &As Gem &som Ctrl+Shift+S Ctrl+Skift+S &Print &Udskriv Ctrl+P Ctrl+P Documen&t Properties Dokument&egenskaber Ctrl+Shift+D Ctrl+Skift+D &Close &Luk Ctrl+Shift+Q Ctrl+Skift+Q &Quit &Afslut Ctrl+Q Ctrl+Q &Cut &Klip Ctrl+X Ctrl+X C&opy &Kopiér Ctrl+C Ctrl+C &Paste &Indsæt Ctrl+V Ctrl+V &Delete &Slet &Select All &Vælg alt Ctrl+A Ctrl+A &Font &Skrifttype &Line Numbers &Linjenumre Ctrl+L Ctrl+L &Wrap Lines &Ombryd linjer Ctrl+W Ctrl+W &Auto-Indentation &Automatisk indrykning Ctrl+I Ctrl+I &Syntax Highlighting &Syntaksfremhævning Ctrl+Shift+H Ctrl+Skift+H &Preferences &Præferencer Ctrl+Shift+P Ctrl+Skift+P Ctrl+H Ctrl+H &About &Om Enforce UTF-8 Tving UTF-8 Save with &Encoding Gem med &kodning &Jump to &Hop til Show/hide jump bar Vis/skjul hoplinje Ctrl+J Ctrl+J Edit text Rediger tekst Ctrl+Shift+E Ctrl+Skift+E &Run &Kør Ctrl+E Ctrl+E &Clear &Ryd Clear the list of recently modified files Ryd listen over seneste ændrede filer Save/Restore Session Gem/gendan session Sa&ve/Restore Session &Gem/gendan session Ctrl+M Ctrl+M Side-Pane Siderude Ctrl+Alt+P Ctrl+Alt+P Paste Date and Time Indsæt dato og klokkeslæt Ctrl+Shift+V Ctrl+Skift+V &Detach Tab &Løsriv faneblad Ctrl+T Ctrl+T Close Ne&xt Tabs Luk &næste faneblade Close &Previous Tabs Luk &forrige faneblade Ne&xt Tab &Næste faneblad Previous Ta&b &Forrige faneblad Execute Eksekver Close &All Tabs Luk &alle faneblade Recently &Modified Seneste &ændrede &Encoding &Kodning &Unicode &Unicode &Western European &Vesteuropæisk &East European &Østeuropæisk Ea&st Asian &Østasiatisk Rep&lacement &Erstatning Focus/hide search bar Fokuser/skjul søgelinje &Replace &Erstat Windows Arabic (&CP1256) Windows arabisk (&CP1256) &Other &Andet &UTF-8 &UTF-8 UTF-&16 UTF-&16 &ISO-8859-1 &ISO-8859-1 &Windows-1252 &Windows-1252 &Cyrillic (CP1251) &Kyrillisk (CP1251) Cyrillic (&KOI8-U) Kyrillisk (&KOI8-U) Cyrillic (&ISO-8859-5) Kyrillisk (&ISO-8859-5) &Chineese (BIG5) &Kinesisk (BIG5) Chinese (&GB18030) Kinesisk (&GB18030) &Japanese (ISO-2022-JP) &Japansk (ISO-2022-JP) Japanese (&ISO-2022-JP-2) Japansk (&ISO-2022-JP-2) Japanese (ISO-&2022-KR) Japansk (ISO-&2022-KR) Ja&panese (CP932) Ja&pansk (CP932) Japa&nese (EUC-JP) Japa&nsk (EUC-JP) &Korean (CP949) &Koreansk (CP949) K&orean (CP1361) K&oreansk (CP1361) Korean (&EUC-KR) Koreansk (&EUC-KR) ISO-&8859-15 ISO-&8859-15 Close &Other Tabs Luk &andre faneblade &Copy File Name &Kopiér filnavn Copy File &Path Kopiér fil&sti Alt+Right Alt+Højre Alt+Left Alt+Venstre &First Tab &Første faneblad Alt+Down Alt+Ned &Last Tab &Sidste faneblad Alt+Up Alt+Op Menu Menu Calculate number of words (For huge texts, this may be CPU-intensive.) Udregn antal ord (dette kan være CPU-intensivt, når der er meget tekst). Next Næste Previous Forrige Replace all Erstat alle &Recently Opened &Seneste åbnede All Alle Close Luk Save changes? Gem ændringer? Please attend to that window or just close its dialog! Tag dig venligst at det vindue eller luk dets dialog! The document has been modified. Dokumentet er blevet ændret. The file has been removed. Filen er blevet fjernet. Save Gem Discard changes Forkast ændringer Cancel Annuller No to all Nej til alle Unsaved Ugemt Lines Linjer Sel. Chars Vælg tegn Words Ord Another process is running in this tab! En anden proces kører i dette faneblad! Script File Script-fil Another FeatherPad window has a modal dialog! Et andet FeatherPad-vindue har en modal dialog! Ctrl+= Ctrl+= Ctrl++ Ctrl++ Ctrl+- Ctrl+- Ctrl+0 Ctrl+0 F11 F11 Ctrl+Shift+W Ctrl+Skift+W Ctrl+Alt+E Ctrl+Alt+E Shift+Ins Skift+Ins Shift+Del Skift+Del Ctrl+Ins Ctrl+Ins Ctrl+Left Ctrl+Venstre Ctrl+Right Ctrl+Højre Ctrl+Up Ctrl+Op Ctrl+Down Ctrl+Ned Ctrl+Home Ctrl+Home Ctrl+End Ctrl+End F3 F3 F4 F4 F5 F5 F6 F6 Shift+Enter Shift+Enter Ctrl+Tab Ctrl+Tab Ctrl+Meta+Tab Ctrl+Meta+Tab Ctrl+K Ctrl+K Position: Placering: Normal Normal Only one process is allowed per tab. Kun en proces pr. faneblad er tilladt. Script Output Script-output Clear Ryd FeatherPad does not open files larger than 100 MiB. FeatherPad åbner ikke filer større end 100 MiB. Some file(s) could not be opened! Nogle fil(er) kunne ikke åbnes! You may not have the permission to read. Du har måske ikke tilladelsen til at læse. Uneditable file(s)! Ikke-redigerbar fil(er)! Non-text files or files with huge lines cannot be edited. Filer som ikke er tekst, eller filer med kæmpe linjer kan ikke redigeres. A previous crash detected! Et tidligere nedbrud er registreret! Preferably, close all FeatherPad windows and start again! Luk helst alle FeatherPad-vinduer og start igen! All Files (*) Alle filer (*) All Files (*);;.%1 Files (*.%1) Alle filer (*);;.%1 filer (*.%1) Open file... Åbn fil... .%1 Files (*.%1);;All Files (*) .%1 filer (*.%1);;Alle filer (*) Save as... Gem som... Keep encoding and save as... Bevar kodning og gem som... Yes Ja No Nej Do you want to use <b>MS Windows</b> end-of-lines? Vil du bruge <b>MS Windows</b>-linjeskift? This may be good for readability under MS Windows. Det kan være godt for læsbarheden i MS Windows. Cannot be saved! Kan ikke gemmes! Help Hjælp Syntax Syntaks Huge file(s) not opened! Kæmpe fil(er) ikke åbnet! This file has been modified elsewhere or in another way! Filen er blevet ændret et andet sted eller på en anden måde! Please be careful about reloading or saving this document! Pas venligst på med at genindlæse eller gemme dokumentet! Print Document Udskriv dokument Copy Target Path Kopiér målsti Open Target Here Åbn mål her Translators Oversættere A lightweight, tabbed, plain-text editor Et letvægts tekstredigeringsprogram med faneblade based on Qt5 baseret på Qt5 Author Forfatter aka. også kendt som About FeatherPad Om FeatherPad No Replacement Ingen erstatning One Replacement En erstatning Replacements Erstatninger FeatherPad::FileDialog Ctrl+H Toggle showing hidden files Ctrl+H Alt+. Toggle showing hidden files Alt+. FeatherPad::LineEdit Clear text (Ctrl+K) Ryd tekst (Ctrl+K) Ctrl+K Clear text Ctrl+K FeatherPad::PrefDialog Preferences Præferencer Window Vindue Window Settings Vinduesindstillinger Remember window &size on closing Husk vinduets &størrelse ved lukning Window frame is excluded. Vinduets ramme er ekskluderet. Start with this size: Start med denne størrelse: px px × × Most suitable with sessions but without tab functionality. Bedst egnet med sessioner uden fanebladsfunktionalitet. Uncheck for 1/5 of the width. Fravælg for 1/5 af bredten. Remember splitter position Husk opdelerens placering No icons in the main window and its menus. KDE may have a bug that disables search and replace shortcuts with the iconless mode. Needs application restart to take effect. Ingen ikoner i hovedvinduet og dets menuer. KDE kan have en fejl som deaktivere søgning og erstatter genveje med den ikonløse tilstand. Programmet skal genstartes før ændringerne træder i kraft. &Iconless mode &Ikonløs tilstand Do not show &toolbar Vis ikke &værktøjslinje If the menubar is hidden, a menu button appears on the toolbar. Hvis menulinjen er skjult, så vises en menuknap på værktøjslinjen. Do not show &menubar Vis ikke &menulinje Hide search &bar by default Skjul &søgelinje som standard Always show st&atus bar Vis altid &statuslinje Show cursor position on status bar Vis markørplacering på statuslinje Will take effect after closing this dialog. Træder i kraft når denne dialog er blevet lukket. Tab position: Tabulator placering: North Nord South Syd West Vest East Øst This means that, for LTR, Alt+Right goes to the first tab after the last tab is activated, and the same for Alt+Left. Tab navigation with mouse wheel is not affected. Dette betyder at for venstre mod højre, går Alt+Højre til det første faneblad efter det sidste faneblad er aktiveret, og det samme for Alt+Venstre. Fanebladsnavigation med musehjul påvirkes ikke. Tab navigation wraps &around Fanebladsnavigation &starter forfra By default, if a FeatherPad window exists on the current desktop, files will be opened in its tabs. However, some desktop environments may not report that they have multiple desktops. Filer åbnes som standard i faneblade, hvis der findes et FeatherPad-vindue på det nuværende skrivebord. Dog rapporterer nogle skrivebordsmiljøer muligvis ikke at de har flere skriveborde. Always open in separate windows Åbn altid i separate vinduer If this is checked, the file dialog provided by the current desktop environment will be used instead of the Qt file dialog. Some desktop environments, like KDE and LXQt, provide files dialogs. Hvis den er valgt til, bruges fildialogen som leveres af det nuværende skrivebordsmiljø i stedet for Qt-fildialogen. Nogle skrivebordsmiljøer, såsom KDE og LXQt leverer fildialoger. Native file dialog Indbygget fildialog Uncheck for Monospace 9. Fravælg for Monospace 9. Remember &font Husk &skrifttype This covers parentheses, braces, brackets and quotes. Dette dækker parenteser, klammer, vinkelparenteser og citationstegn. Auto-&bracket Automatisk &vinkelparenteser Never highlight syntax for files > Fremhæv aldrig syntaks for filer > This creates a menu button on the status bar for changing the syntax. Det opretter en menuknap på statuslinjen til at skifte syntaksen. Support syntax override Understøt tilsidesættelse af syntaks Show spaces, tabs and tab lines when the syntax is highlighted. Vis mellemrum, tabulatorer og linjeskift når syntaksen er fremhævet. Show whitespaces Vis blanktegn The vertical position lines will be drawn only if the editor font has a fixed pitch (like Monospace). De lodrette placeringslinjer tegnes ku hvis editorens skrifttype har en fast tegnbredde (såsom Monospace). Show vertical lines starting from this position: Vis lodrette linjer startende fra denne placering: Also show line and document ends Vis også linje- og dokumentslutninger Date and time format: Dato- og klokkeslætformat: Some text editors cannot open a document whose last line is not empty. Nogle tekstredigeringsprogrammer kan ikke åbne et dokument hvor den sidste linje ikke er tom. Ensure an empty last line on saving Sørg for en tom linje til sidst når der gemmes Remove trailing spaces on saving Fjern efterstillede mellemrum når der gemmes Inertial scrolling with mouse wheel Inertisk rulning med musehjul Files Filer File Management Filhåndtering This can be any starting command with arguments, for example, "xterm -hold" for running the process in XTerm. If the command is left empty, the file will be executed directly. If the script is not run in a terminal emulator, the output and error messages will be shown by a popup dialog. Dette kan være en hvilken som helst startkommando med argumenter, f.eks. "xterm -hold" for at køre processen i XTerm. Hvis kommandoen efterlades tom, så vil filen blive eksekveret direkte. Hvis scriptet ikke køre i en terminalemulator, så vises outputtet og fejlmeddelelserne af en pop op-dialog. Start with this command: Start med denne kommando: Command + Arguments Kommando + Argumenter Show recentl&y modified files &Vis seneste ændrede filer Show recentl&y opened files &Vis seneste åbnede filer Save changes to opened files every: Gem ændringer til åbnede filer hver: Help Hjælp Uncheck this if you want FeatherPad to use system icons! Needs application restart to take effect. Fravælg dette hvis du vil have at FeatherPad skal bruge systemets ikoner! Programmet skal genstartes før ændringerne træder i kraft. Start with side-pane mode Start med siderude-tilstand &Use own icons &Brug egne ikoner If this is checked, not only you will lose the informative tooltip and context menu of a single tab but you could not merge a single tabbed window into another one by tab drag- and-drop either. Hvis den er valgt til, vil du ikke blot miste det informative værktøjstip og genvejsmenuen på enkelte faneblad, men du vil heller ikke samle et enkelt vindue med faneblad med et andet ved at trække og slippe et af dem. &Do not show a single tab &Vis ikke ét faneblad Text Tekst Text Editor Tekstredigering &Wrap lines by default &Ombryd linjer som standard Auto-&indent by default Automatisk &indrykning som standard Always show line &numbers Vis altid &linjenumre Highlight s&yntax by default Fremhæv &syntaks som standard Dark c&olor scheme Mørkt &farveskema Background color value: Baggrundens farveværdi: MiB MiB This is not a complete fix but prevents annoying scroll jumps. Dette er ikke en fuldstændig rette men det forhindre irriterende rullehop. Workaround for &Qt5's scroll jump bug Ret &Qt5's rullehop-fejl This only includes executable files written in script languages like Shell and Python. If it is checked and the opened file is executable, the file will be executed by clicking the Run button, that appears on the toolbar/filemenu when needed, or by its shortcut Ctrl+E. Then, the process could be killed by Ctrl+Alt+E. Dette inkludere eksekverbare filer som er skrevet i script-sprog såsom Shell og Python. Hvis den er valgt til, og den åbnede fil er eksekverbar, så vil filen blive eksekveret når der klikkes på Kør-knappen, som vises på værktøjslinjen/filmenuen når det er nødvendigt, eller med dens genvej Ctrl+E. Så kan processen dræbes med Ctrl+Alt+E. Needs window reopening to take effect. Vinduet skal genåbnes før det træder i kraft. The color value of the background. 255 means white while 0 means black. For the light background, it can be between 230 and 255; for the dark background, between 0 and 50. Needs window reopening to take effect. Baggrundens farveværdi. 255 betyder hvid, mens 0 betyder sort. Lys baggrund er mellem 230 og 255; og mørk baggrund er mellem 0 og 50. Vinduet skal genåbnes før det træder i kraft. Run executable scripts Kør eksekverbare scripts The maximum number of recently modified or opened files FeatherPad shows. The default is 10. Needs application restart to take effect. Det maksimale antal af seneste ændrede eller åbnede filer som FeatherPad viser. Standarden er 10. Programmet skal genstartes før ændringerne træder i kraft. Used for pasting the date and time. Takes effect after closing this dialog. Leave empty for: MMM dd, yyyy, hh:mm:ss Bruges til at indsætte datoen og klokkeslættet. Træder i kraft når dialogen lukkes. Lad være tom for: MMM dd, åååå, tt:mm:ss Should the mouse wheel scrolling be inertial if the cursor is inside the text view? Skal musehjulets rulning være inertisk hvis markøren er inden i tekstvisningen? Number of recent files: Antal seneste filer: The maximum number of recently modified or opened files FeatherPad should open with an empty startup of a session. Det maksimale antal af seneste ændrede eller åbnede filer som FeatherPad skal åbne med en tom opstart af en session. Start with recent files: Start med seneste filer: Only for files that exist and can be saved. Kun for filer som findes og kan gemmes. min min Shortcuts Genveje Action Handling Shortcut Genvej Restore default shortcuts. Gendan standardgenveje. Default Standard Close Luk files filer file fil No file Ingen fil Warning: Ambiguous shortcut detected! Advarsel: Tvetydig genvej registreret! Application restart is needed for changes to take effect. Programmet skal genstartes før ændringerne træder i kraft. Window reopening is needed for changes to take effect. Vinduet skal genåbnes før ændringerne træder i kraft. &Recently Opened &Seneste åbnede Recently &Modified Seneste &ændrede The typed shortcut was not valid. Den indtastede genvej var ikke gyldig. The typed shortcut was reserved. Den indtastede genvej var reserveret. FeatherPad::SearchBar Search... Søg... Match Case Forskel på store og små bogstaver Whole Word Hele ord Next Næste Previous Forrige FeatherPad::SessionDialog Session Manager Sessionshåndtering &Remove &Fjern <b>Save/Restore Session</b> <b>Gem/gendan session</b> Filter... Filter... Open the selected session(s). Åbn den valgte session(er). &Open &Åbn Del Del Ctrl+Del Ctrl+Del By default, all files that are opened in all windows will be included in the saved session. Som standard, gemmes alle filer som er åbnet i alle vinduer i den gemte session. Save only from this &window Gem kun fra dette &vindue <b>Warning</b> <b>Advarsel</b> &Yes &Ja &No &Nej Re&name &Omdøb Remove &All Fjern &alle &Close &Luk Save the curent session under the given title. Gem den nuværende session under den givne titel. &Save &Gem Type a name to save session Indtast et navn for at gemme sessionen Nothing saved.<br>No file was opened. Intet blev gemt.<br>Ingen fil var åbnet. No file exists or can be opened. Der findes ingen fil eller den kan ikke åbnes. Not all files exist or can be opened. Alle filerne findes ikke eller kan ikke åbnes. &OK &OK Do you really want to remove all saved sessions? Vil du virkelig fjerne alle gemte sessioner? Do you really want to remove the selected sessions? Vil du virkelig fjerne de valgte sessioner? Do you really want to remove the selected session? Vil du virkelig fjerne den valgte session? A session with the same name exists.<br>Do you want to overwrite it? Der findes allerede en session med det navn.<br>Vil du overskrive den? FeatherPad::SidePane Filter... Filter... FeatherPad::TextEdit Open Link Åbn link Copy Link Kopiér link Paste Date and Time Indsæt dato og klokkeslæt FeatherPad::WarningBar Close Luk FeatherPad-0.8/featherpad/data/translations/featherpad_eo.ts000066400000000000000000002442641325653242400243010ustar00rootroot00000000000000 FeatherPad::AboutDialog License Licenco FeatherPad::FPwin Untitled Sentitola Go to line: Iru al linio: Select text from cursor to this line (Ctrl+Shift+J) Selekti tekston ekde la kursoro al ĉi tiu linio (Ctrl+Shift+J) Select Text Selekti tekston Ctrl+Shift+J &File &Dosiero &Edit R&edakti &Options A&gordoj Encoding Kodiĝo &Search &Serĉi &Help &Helpo Find: Trovu: To be replaced Anstataŭigota teksto Replace with: Anstataŭigu per: Replacing text Anstataŭiganta teksto F8 F7 F9 &New &Nova New tab Nova folio Ctrl+N &Open &Malfermi Open a file Malfermi dosieron Ctrl+O &Save Kon&servi Save the current tab Konservi nunan folion Ctrl+S &Undo Malfar&u Undo Malfaru Ctrl+Z &Redo Refa&ru Redo Refaru Ctrl+Shift+Z Reload Reŝarĝi Ctrl+Shift+R &Find &Trovi Ctrl+F Show/hide replacement dock Kaŝi/malkaŝi la dokon de anstataŭigo Ctrl+R Save &As Konser&vi Kiel Ctrl+Shift+S &Print &Presi Ctrl+P Documen&t Properties Eco&j Ctrl+Shift+D &Close &Fermi Ctrl+Shift+Q &Quit Forl&asi Ctrl+Q &Cut &Tranĉu Ctrl+X C&opy &Kopiu Ctrl+C &Paste Algl&uu Ctrl+V &Delete &Forviŝu &Select All Selectu &Ĉion Ctrl+A &Font &Tiparo &Line Numbers &Lini-Numeroj Ctrl+L &Wrap Lines Ĉirkaŭ&fermi Liniojn Ctrl+W &Auto-Indentation &Mem-Alinei Ctrl+I &Syntax Highlighting Marki &Sintakson Ctrl+Shift+H &Preferences A&gordoj Ctrl+Shift+P Ctrl+H &About &Pri Enforce UTF-8 Efikigu UTF-8 Save with &Encoding Konsevi Kodigant&e &Jump to &Salti al Show/hide jump bar Kaŝi/malkaŝi la saltobreton Ctrl+J Edit text Radakti tekston Ctrl+Shift+E &Run Ek&zekvi Ctrl+E &Clear &Viŝi Clear the list of recently modified files Viŝu la liston de lastatempe modifitaj dosieroj Save/Restore Session Konservi/Restaŭri Sesion Sa&ve/Restore Session K&onservi/Restaŭri Sesion Ctrl+M Side-Pane Flankpanelo Ctrl+Alt+P Paste Date and Time Algluu la Daton kaj Tempon Ctrl+Shift+V &Detach Tab &Deŝiri Folion Ctrl+T Close Ne&xt Tabs Fermu la &Sekvajn Foliojn Close &Previous Tabs Fermu la &Antaŭajn Foliojn Ne&xt Tab Folio Se&kva Previous Ta&b Folio An&taŭa Execute Ekzekvi Close &All Tabs Fermu Ĉ&iujn Foliojn Recently &Modified Lastatempe Mod&ifitaj &Encoding &Kodiĝo &Unicode &Unikoda &Western European &Okcident-Eŭropa &East European Orient-&Eŭropa Ea&st Asian O&rient-Asia Rep&lacement An&stataŭigo Focus/hide search bar Enfokusigi/malkaŝi la serĉ-breton &Replace &Anstataŭigi Windows Arabic (&CP1256) &Vindozo-Araba (CP1256) &Other &Alia &UTF-8 UTF-&16 &ISO-8859-1 &Windows-1252 &Vindozo-1252 &Cyrillic (CP1251) &Cirila (CP1251) Cyrillic (&KOI8-U) Cirila (&KOI8-U) Cyrillic (&ISO-8859-5) Cirila (&ISO-8859-5) &Chineese (BIG5) Ĉina (BIG&5) Chinese (&GB18030) Chinese (&GB18030) &Japanese (ISO-2022-JP) &Japana (ISO-2022-JP) Japanese (&ISO-2022-JP-2) Japana (&ISO-2022-JP-2) Japanese (ISO-&2022-KR) Japana (ISO-&2022-KR) Ja&panese (CP932) Ja&pana (CP932) Japa&nese (EUC-JP) Japa&na (EUC-JP) &Korean (CP949) &Korea (CP949) K&orean (CP1361) K&orea (CP1361) Korean (&EUC-KR) Korea (&EUC-KR) ISO-&8859-15 Close &Other Tabs Fermu la A&liajn Foliojn &Copy File Name &Kopiu la Dosiernomon Copy File &Path Kopiu la Dosier&padon Alt+Right Alt+Left &First Tab Folio &Unua Alt+Down &Last Tab Folio &Lasta Alt+Up Menu Menuo Calculate number of words (For huge texts, this may be CPU-intensive.) Kalkulu vortnombron (Por grandegaj tekstoj, ĉi tio eblas esti CPU-intensiva.) Next Sekva Previous Antaŭa Replace all Anstataŭigu ĉion &Recently Opened Lastatempe Malfermita&j All Ĉio Please attend to that window or just close its dialog! Bonvole prizorgu tiun fenestron aŭ nur fermu ĝian dialogon! Close Fermi Save Konservi Ctrl+= Ctrl++ Ctrl+- Ctrl+0 F11 Ctrl+Shift+W Ctrl+Alt+E Shift+Ins Shift+Del Ctrl+Ins Ctrl+Left Ctrl+Right Ctrl+Up Ctrl+Down Ctrl+Home Ctrl+End F3 F4 F5 F6 Shift+Enter Ctrl+Tab Ctrl+Meta+Tab Ctrl+K Position: Pozicio: Normal Normala Discard changes Preterlasi ŝanĝojn Cancel Rezigni No to all Ne al ĉio Unsaved Nekonservita Sel. Chars Sel. Signoj Another process is running in this tab! Alia procezo estas plenumata en ĉi tiu folio! Only one process is allowed per tab. Nur unu procezo estas permesita po folio. Script Output Eligo de la Skripto Clear Viŝi FeatherPad does not open files larger than 100 MiB. FeatherPad ne malfermas dosierojn pli grandajn ol 100 MiB. Some file(s) could not be opened! Iu(j) dosiero(j) ne povis esti malfermita(j)! You may not have the permission to read. Vi eble ne havas la permeson por legi. Uneditable file(s)! Neredaktebla(j) dosiero(j)! Non-text files or files with huge lines cannot be edited. Netekstaj dosieroj aŭ tiuj kun longegaj linioj ne redakteblas. A previous crash detected! Antaŭa kraŝo detektita! Preferably, close all FeatherPad windows and start again! Prefere fermu ĉiujn fenestrojn de FeatherPad kaj komencu denove! All Files (*) Ĉiuj Dosieroj (*) All Files (*);;.%1 Files (*.%1) Ĉiuj Dosieroj (*);;.%1-Dosieroj (*.%1) Open file... Malfermi dosieron... Save as... Konservi kiel... Keep encoding and save as... Teni la kodiĝon kaj konservi kiel... Yes Jes No Ne This file has been modified elsewhere or in another way! Ĉi tiu dosiero estas modifita aliloke aŭ alimaniere! Please be careful about reloading or saving this document! Bonvole zorgu pri ĝia reŝarĝado aŭ konservado! Copy Target Path Kopiu la Celpadon Open Target Here Malfermu la Celon Ĉi Tie Translators Tradukintoj A lightweight, tabbed, plain-text editor Malpeza, foliigita tekst-redaktilo based on Qt5 bazita sur Qt5 Author Verkinto aka. akk. Help Helpo Lines Linioj Another FeatherPad window has a modal dialog! Alia fenestro havas modalan dialogon! Save changes? Konservi la ŝanĝojn? The document has been modified. La dokumento estas modifita. The file has been removed. La dosiero estas forigita. Words Vortoj Script File Skripta Dosiero Huge file(s) not opened! Grandega(j) dosiero(j) ne malfermita(j)! .%1 Files (*.%1);;All Files (*) .%1-Dosieroj (*.%1);;Ĉiuj Dosieroj (*) Do you want to use <b>MS Windows</b> end-of-lines? Ĉu vi volas uzi <b>MS Vindozajn</b> lini-finaĵojn? This may be good for readability under MS Windows. Povas esti bone por legindeco ĉe MS-Vindozo. Cannot be saved! Ne eblas konservi! Syntax Sintakso Print Document Presi Dokumenton About FeatherPad Pri FeatherPad No Replacement Neniu Anstataŭigo One Replacement Unu Anstataŭigo Replacements Anstataŭigoj FeatherPad::FileDialog Ctrl+H Toggle showing hidden files Alt+. Toggle showing hidden files FeatherPad::LineEdit Clear text (Ctrl+K) Viŝi la tekston (Ctrl+K) Ctrl+K Clear text FeatherPad::PrefDialog Preferences Agordoj Window Fenestro Window Settings Agordoj de Fenestro Remember window &size on closing Memoru la &grandon de la fenestro Window frame is excluded. La fenestrokadro estas ekskludita. Start with this size: Startu kun ĉi tiu grando: px × Most suitable with sessions but without tab functionality. Pleje uzeblas kun sesioj sed sen la funkcieco de langetoj. Uncheck for 1/5 of the width. Malmarku por 1/5 de la larĝo. Remember splitter position Memoru la pozicion de la dividilo No icons in the main window and its menus. KDE may have a bug that disables search and replace shortcuts with the iconless mode. Needs application restart to take effect. Neniu ikono en la ĉefa fenestro kaj ĝiaj menuoj. KDE eble havas cimon, kiu malaktivigas la serĉajn kaj anstataŭigajn fulmoklavojn ĉe la senikona modalo. Restartigo de la aplikaĵo necesas. &Iconless mode Sen&ikona modalo Do not show &toolbar Kaŝu la ilbre&ton If the menubar is hidden, a menu button appears on the toolbar. Se la menubreto estus kaŝita, menubutono aperus sur la ilbreto. Do not show &menubar Kaŝu la &menubreton Hide search &bar by default Kaŝu la serĉ&breton defaŭlte Always show st&atus bar Ĉiam vidigu la st&atusbreton Show cursor position on status bar Montru la pozicion de kursoro sur la statusbreto Will take effect after closing this dialog. Efektiviĝos post ĉi tiu dialogo estas fermita. Tab position: La pozicio de la foliaro: North Nordo South Sudo West Okcidento East Oriento This means that, for LTR, Alt+Right goes to the first tab after the last tab is activated, and the same for Alt+Left. Tab navigation with mouse wheel is not affected. Tio signifas ke, por LTR, Alt+Dekstren aktivigas la unuan folion post la lasta folio, kaj simile por Alt+Maldekstren. La foliumado per musrado ne estas influita. Tab navigation wraps &around Ĉirkaŭfermu la f&oliumadon By default, if a FeatherPad window exists on the current desktop, files will be opened in its tabs. However, some desktop environments may not report that they have multiple desktops. Defaŭlte, se fenestro de FeatherPad ĉeestus sur la nuna labortablo, la dosieroj estus malfermitaj kiel ĝiaj folioj. Tamen, iuj labortablaj mediaoj eblas ne sciigi tion ke ili havas plurajn labortablojn. Always open in separate windows Ĉiam malfermu en apartaj fenestroj If this is checked, the file dialog provided by the current desktop environment will be used instead of the Qt file dialog. Some desktop environments, like KDE and LXQt, provide files dialogs. Se ĉi tio estus markita, la dosierdialogo provizita de la nuna labortabla medio estus uzita anstataŭ tio de Qt. Iuj labortablaj medioj, kiel KDE kaj LXQt, provizas dosierdialogojn. Native file dialog Indiĝena dosierdialogo Uncheck for Monospace 9. Malmarku por Monospace 9. Remember &font Memoru la ti&paron This covers parentheses, braces, brackets and quotes. Ĉi tio pritraktas la rondan, rektan kaj kunigan krampojn kaj ankaŭ la citmarkojn. Auto-&bracket &Aŭtomata enkrampigo Never highlight syntax for files > Ne marku la sintakson por dosieroj > This creates a menu button on the status bar for changing the syntax. Ĉi tio estigas menubutonon sur la statusbreto por ŝanĝi la sintakson. Support syntax override Ebligu ŝanĝi la sintakson Show spaces, tabs and tab lines when the syntax is highlighted. Montru spacojn, tabojn, kaj tabliniojn kiam la sintakso estas markita. Show whitespaces Montru la blankspacojn The vertical position lines will be drawn only if the editor font has a fixed pitch (like Monospace). La vertikalaj poziciaj linioj estus desegnitaj nur se la redaktila tiparo havus fiksitan larĝon (kiel Monospace). Show vertical lines starting from this position: Montru vertikalajn liniojn komence de ĉ tiu pozicio: Also show line and document ends Ankaŭ montru la liniajn kaj dokumentan finojn Date and time format: Datoprezento: Some text editors cannot open a document whose last line is not empty. Iuj tekst-redaktiloj ne povas malfermi dokumenton, kies fina linio ne estas malplena. Ensure an empty last line on saving Garantiu finan linion malplenan konservante Remove trailing spaces on saving Forviŝu la vostajn spacetojn Inertial scrolling with mouse wheel Inercia rulumado per la musrado Files Dosieroj File Management Dosieradministrado This can be any starting command with arguments, for example, "xterm -hold" for running the process in XTerm. If the command is left empty, the file will be executed directly. If the script is not run in a terminal emulator, the output and error messages will be shown by a popup dialog. Ĉi tio povas esti komencanta signovico kun operando, ekz. "xterm -hold" por ekzekvi la procezon en XTerm. Se la signovico forestus, la dosiero estus ekzekvita senpere. Se la skripto ne estus ekzekvita en terminal- emulatoro, dialogo ekaperus montranta la eligajn kaj erarajn mesaĝojn. Start with this command: Startigu kun ĉi tiu komando: Command + Arguments Komando + Operando Show recentl&y modified files Montru la lastatempe mo&difitajn dosierojn Show recentl&y opened files Montru la lastatempe mal&fermitajn dosierojn Save changes to opened files every: Konservi la ŝanĝojn al la malfermitaj dosieroj ĉiun: Help Helpo Uncheck this if you want FeatherPad to use system icons! Needs application restart to take effect. Malmarku se vi volas ke FeatherPad uzu sistemajn ikonojn! Restartigo de la aplikaĵo necesas. Start with side-pane mode Startu kun la flankpanela reĝimo &Use own icons &Uzu la proprajn ikonojn If this is checked, not only you will lose the informative tooltip and context menu of a single tab but you could not merge a single tabbed window into another one by tab drag- and-drop either. Se ĉi tio estus markita, vi perdus la sciigajn ŝveb-informon kaj kuntekstan menuon de la unuobla folio kaj ne povus kunigi unufolian fenestron al alia per ŝovmetado. &Do not show a single tab &Ne montru la unusolan folion Text Teksto Text Editor Tekstredaktilo &Wrap lines by default Ĉir&kaŭfermu la liniojn defaŭlte Auto-&indent by default Mem-al&ineu defaŭlte Always show line &numbers Ĉiam vidigu la lini-numero&jn Highlight s&yntax by default Marku la sin&takson defaŭlte Dark c&olor scheme Malluma kolo&rskemo Background color value: La kolorvaloro de la fono: MiB This is not a complete fix but prevents annoying scroll jumps. Ĉi tio ne estas kompleta solvo sed detenas ĝenantajn saltojn komence de rulumado. Workaround for &Qt5's scroll jump bug Proviza korekto por la ruluma salto ĉe &Qt5 This only includes executable files written in script languages like Shell and Python. If it is checked and the opened file is executable, the file will be executed by clicking the Run button, that appears on the toolbar/filemenu when needed, or by its shortcut Ctrl+E. Then, the process could be killed by Ctrl+Alt+E. Tio inkluzivas nur ekzekveblajn dosierojn scribitajn en skript-lingvoj kiel Shell kaj Python. Se ĝi estus markita kaj la malfermita dosiero estus ekzekvebla, la dosiero estus plenumita per kliko al la butono "Ekzekvi", kiu aperus taŭge sur la ilbreto/dosiermenuo, aŭ per ĝia fulmoklavo Ctrl+E. Tiam, la procezo povus esti ĉesigita per la fulmoklavo Ctrl+Alt+E. Needs window reopening to take effect. Remalfermado de la fenestro necesas. The color value of the background. 255 means white while 0 means black. For the light background, it can be between 230 and 255; for the dark background, between 0 and 50. Needs window reopening to take effect. La kolorvaloro de la fono. 255 signifas la blankon kaj 0 la nigron. Por la luma fono, ĝi estas inter 230 kaj 255; por la malluma fono, inter 0 and 50. Remalfermado de la fenestro necesas por ke ĉi tio efektivĝu. Run executable scripts Plenumu ekzekveblajn skriptojn The maximum number of recently modified or opened files FeatherPad shows. The default is 10. Needs application restart to take effect. La maksimuma nombro de la lastatempe modifitaj aŭ malfermitaj dosieroj, kiujn FeatherPad montru. La defaŭlto estas 10. Restartigo de la aplikaĵo necesas. Used for pasting the date and time. Takes effect after closing this dialog. Leave empty for: MMM dd, yyyy, hh:mm:ss Uzita por alglui la daton kaj tempon. Efektiviĝos post ĉi tiu dialogo estas fermita. Lasu ĉi tion malplena por: MMM dd, yyyy, hh:mm:ss Should the mouse wheel scrolling be inertial if the cursor is inside the text view? Ĉu la musrada rulumado estu inercia kiam la kursoro estas interne de la tekst-vido? Number of recent files: Nombro de la lastatempaj dosieroj: The maximum number of recently modified or opened files FeatherPad should open with an empty startup of a session. La maksimuma nombro de la lastatempe modifitaj aŭ malfermitaj dosieroj, kiujn FeatherPad malfermu okaze de senenhava startigo de sesio. Start with recent files: Startu kun lastatempaj dosieroj: Only for files that exist and can be saved. Nur por la dosieroj ekzistantaj kaj konserveblaj. min min Shortcuts Fulmoklavoj Action Ago Shortcut Fulmoklavo Restore default shortcuts. Restaŭri defaŭltajn fulmoklavojn. Default Defaŭlto Close Fermi files dosieroj file dosiero No file Neniu dosiero Warning: Ambiguous shortcut detected! Averto: Ambigua fulmoklavo estas detektita! Application restart is needed for changes to take effect. Restartigo de la aplikaĵo necesas por ke la ŝanĝoj efektiviĝu. Window reopening is needed for changes to take effect. Remalfermado de la fenestro necesas por ke la ŝanĝoj efektiviĝu. &Recently Opened Lastatempe Malfermita&j Recently &Modified Lastatempe Mod&ifitaj The typed shortcut was not valid. La fulmoklavo tajpita ne estis valida. The typed shortcut was reserved. La fulmoklavo tajpita estis rezervita. FeatherPad::SearchBar Search... Serĉu... Match Case Uskleciva Whole Word Tuta Vorto Next Sekva Previous Antaŭa FeatherPad::SessionDialog Session Manager Administrilo de Sesio &Remove Fo&rviŝi <b>Save/Restore Session</b> <b>Konservi/Restaŭri Sesion</b> Filter... Filtru... Open the selected session(s). Malfermu la elektita(j)n sesio(j)n. &Open &Malfermi Del Ctrl+Del By default, all files that are opened in all windows will be included in the saved session. Defaŭlte, ĉiuj dosieroj malfermitaj en ĉiuj fenestroj estos inkluzivitaj de la konservota sesio. Save only from this &window Konservi nur el ĉi &tiu fenestro <b>Warning</b> <b>Averto</b> &Yes &Jes &No &Ne Re&name Re&nomi Remove &All Forviŝi Ĉi&on &Close &Fermi Save the curent session under the given title. Konservu la nunan sesion sub la donita titolo. &Save Kon&servi Type a name to save session Tajpu nomon por konservi sesion Nothing saved.<br>No file was opened. Nenio estas konservita.<br>Neniu dosiero estis malfermita. No file exists or can be opened. Neniu dosiero ekzistas aŭ poveas esti malfermita. Not all files exist or can be opened. Ne ĉiuj dosieroj ekzistas aŭ povas esti malfermitaj. &OK &Bone Do you really want to remove all saved sessions? Ĉu vi vere volas forviŝi ĉiujn konservitajn sesiojn? Do you really want to remove the selected sessions? Ĉu vi vere volas forviŝi la elektitajn sesiojn? Do you really want to remove the selected session? Ĉu vi vere volas forviŝi la elektitan sesion? A session with the same name exists.<br>Do you want to overwrite it? Samnama sesio ĉeestas.<br>Ĉu vi volas anstataŭigi ĝin? FeatherPad::SidePane Filter... Filtru... FeatherPad::TextEdit Open Link Malfermu la Ligilon Copy Link Kopiu la Ligilon Paste Date and Time Algluu la Daton kaj Tempon FeatherPad::WarningBar Close Fermi FeatherPad-0.8/featherpad/data/translations/featherpad_es_ES.ts000066400000000000000000002467031325653242400246740ustar00rootroot00000000000000 FeatherPad::AboutDialog License Licencia FeatherPad::FPwin Untitled Sin título Go to line: Ir a la línea: Select text from cursor to this line (Ctrl+Shift+J) Seleccionar texto desde el cursor hasta esta línea (Ctrl+Mayús+J) Select Text Seleccionar texto Ctrl+Shift+J Ctrl+Mayús+J &File &Fichero &Edit &Editar &Options &Opciones Encoding Codificación &Search Bu&scar &Help &Ayuda Find: Buscar: To be replaced A sustituir Replace with: Sustituir con: Replacing text Texto de sustitución F8 F8 F7 F7 F9 F9 &New &Nuevo New tab Nueva pestaña Ctrl+N Ctrl+N &Open &Abrir Open a file Abrir un fichero Ctrl+O Ctrl+O &Save &Guardar Save the current tab Guarda la pestaña actual Ctrl+S Ctrl+S &Undo &Deshacer Undo Deshacer Ctrl+Z Ctrl+Z &Redo &Rehacer Redo Rehacer Ctrl+Shift+Z Ctrl+Mayús+Z Reload Recargar Ctrl+Shift+R Ctrl+Mayús+R &Find &Buscar Ctrl+F Ctrl+F Show/hide replacement dock Mostrar/ocultar el panel de sustituciones Ctrl+R Ctrl+R Save &As G&uardar como Ctrl+Shift+S Ctrl+Mayús+S &Print Im&primir Ctrl+P Ctrl+P Documen&t Properties Propiedades del documen&to Ctrl+Shift+D Ctrl+Mayús+D &Close &Cerrar Ctrl+Shift+Q Ctrl+Mayús+Q &Quit &Salir Ctrl+Q Ctrl+Q &Cut &Cortar Ctrl+X Ctrl+X C&opy C&opiar Ctrl+C Ctrl+C &Paste &Pegar Ctrl+V Ctrl+V &Delete &Borrar &Select All &Seleccionar todo Ctrl+A Ctrl+A &Font &Tipo de letra &Line Numbers Números de &línea Ctrl+L Ctrl+L &Wrap Lines &Dividir las líneas Ctrl+W Ctrl+W &Auto-Indentation S&angría automática Ctrl+I Ctrl+I &Syntax Highlighting Re&saltado de sintaxis Ctrl+Shift+H Ctrl+Mayús+H &Preferences &Preferencias Ctrl+Shift+P Ctrl+Mayús+P Ctrl+H Ctrl+H &About &Acerca de Enforce UTF-8 Forzar UTF-8 Save with &Encoding Guardar con codificación &Jump to &Saltar a Show/hide jump bar Mostrar/ocultar la barra de salto Ctrl+J Ctrl+J Edit text Editar el texto Ctrl+Shift+E Ctrl+Mayús+E &Run Ejecuta&r Ctrl+E Ctrl+E &Clear &Limpiar Clear the list of recently modified files Limpia la lista de ficheros modificados recientemente Save/Restore Session Guardar/restaurar sesión Sa&ve/Restore Session &Guardar/restaurar sesión Ctrl+M Ctrl+M Side-Pane Panel lateral Ctrl+Alt+P Ctrl+Alt+P Paste Date and Time Pegar la fecha y hora Ctrl+Shift+V Ctrl+Shift+V &Detach Tab &Desprender la pestaña Ctrl+T Ctrl+T Close Ne&xt Tabs Cerrar las siguientes pestañas Close &Previous Tabs Cerrar las pestañas anteriores Ne&xt Tab Pestaña siguiente Previous Ta&b Pestaña anterior Execute Ejecutar Close &All Tabs Cerrar todas las pestañas Recently &Modified Modificados recientemente &Encoding Codificación &Unicode &Unicode &Western European Europeo occidental &East European Europeo oriental Ea&st Asian Asiático del este Rep&lacement Sustitución Focus/hide search bar Enfocar/ocultar la barra de búsqueda &Replace &Sustituir Windows Arabic (&CP1256) Arábico de Windows (CP1256) &Other &Otra &UTF-8 &UTF-8 UTF-&16 UTF-&16 &ISO-8859-1 &ISO-8849-1 &Windows-1252 &Windows-1252 &Cyrillic (CP1251) &Cirílico (CP1251) Cyrillic (&KOI8-U) Cirílico (&KOI8-U) Cyrillic (&ISO-8859-5) Cirílico (&ISO-8859-5) &Chineese (BIG5) &Chino (BIG5) Chinese (&GB18030) Chino (&GB18030) &Japanese (ISO-2022-JP) &Japonés (ISO-2022-JP) Japanese (&ISO-2022-JP-2) Japonés (&ISO-2022-JP-2) Japanese (ISO-&2022-KR) Japonés (ISO-&2022-KR) Ja&panese (CP932) Ja&ponés (CP932) Japa&nese (EUC-JP) Japonés (EUC-JP) &Korean (CP949) Coreano (CP949) K&orean (CP1361) Coreano (CP1361) Korean (&EUC-KR) Coreano (&EUC-KR) ISO-&8859-15 ISO-&8859-15 Close &Other Tabs Cerrar las &otras pestañas &Copy File Name &Copiar el nombre del fichero Copy File &Path Copiar la ruta del fichero Alt+Right Alt+Derecha Alt+Left Alt+Izquierda &First Tab Primera pestaña Alt+Down Alt+Abajo &Last Tab Última pestaña Alt+Up Alt+Arriba Menu Menú Calculate number of words (For huge texts, this may be CPU-intensive.) Calcular el número de palabras (Para textos muy grandes, puede usar la CPU intensivamente) Next Siguiente Previous Anterior Replace all Sustituir todo &Recently Opened Abie&rtos recientemente All Todo Close Cerrar Save changes? ¿Guardar los cambios? Please attend to that window or just close its dialog! ¡Haga el favor de atender a esa ventana o cierre su diálogo! The document has been modified. El documento ha sido modificado The file has been removed. El fichero ha sido borrado Save Guardar Discard changes Descartar los cambios Cancel Cancelar No to all No a todo Unsaved Sin guardar Lines Líneas Sel. Chars Caracteres sel. Words Palabras Another process is running in this tab! ¡Otro proceso está en ejecución en esta pestaña! Script File Archivo de script Another FeatherPad window has a modal dialog! ¡Otra ventana de FeatherPad tiene un diálodo modal! Ctrl+= Ctrl+= Ctrl++ Ctrl++ Ctrl+- Ctrl+- Ctrl+0 Ctrl+0 F11 F11 Ctrl+Shift+W Ctrl+Mayús+W Ctrl+Alt+E Ctrl+Alt+E Shift+Ins Mayús+Insertar Shift+Del Mayús+Retroceso Ctrl+Ins Ctrl+Insertar Ctrl+Left Ctrl+Izquierda Ctrl+Right Ctrl+Derecha Ctrl+Up Ctrl+Arriba Ctrl+Down Ctrl+Abajo Ctrl+Home Ctrl+Inicio Ctrl+End Ctrl+Fin F3 F3 F4 F4 F5 F5 F6 F6 Shift+Enter Mayús+Entrar Ctrl+Tab Ctrl+Tab Ctrl+Meta+Tab Ctrl+Meta+Tab Ctrl+K Ctrl+K Position: Posición: Normal Normal Only one process is allowed per tab. Solo se permite un proceso por pestaña. Script Output Salida del script Clear Limpiar FeatherPad does not open files larger than 100 MiB. FeatherPad no abre ficheros de más de 100 MiB Some file(s) could not be opened! ¡No se puede abrir uno o más ficheros! You may not have the permission to read. Puede que no tenga permiso de escritura. Uneditable file(s)! ¡Ficheros no editables! Non-text files or files with huge lines cannot be edited. Los ficheros que no sean de texto o tengan líneas muy grandes no se pueden editar. A previous crash detected! ¡Se ha detectado un cierre inesperado anterior! Preferably, close all FeatherPad windows and start again! ¡Es preferible cerrar todas las ventanas de FeatherPad y empezar de nuevo! All Files (*) Todos los ficheos (*) All Files (*);;.%1 Files (*.%1) Todos los ficheros (*);;.%1 Ficheros (*.%1) Open file... Abrir fichero... .%1 Files (*.%1);;All Files (*) .%1 Ficheos (*.%1);;Todos los ficheros (*) Save as... Guardar como... Keep encoding and save as... Mantener la codificación y guardar como... Yes No No Do you want to use <b>MS Windows</b> end-of-lines? ¿Quiere usar el fin de línea de <b>MS Windows</b>? This may be good for readability under MS Windows. Esto podría ser bueno para la legibilidad en MS Windows. Cannot be saved! ¡No se puede guardar! Help Ayuda Syntax Sintáxis Huge file(s) not opened! ¡Uno o más archivos muy grandes no se han abierto! This file has been modified elsewhere or in another way! ¡El fichero ha sido modificado externamente o de alguna otra forma! Please be careful about reloading or saving this document! ¡Haga el favor de tener cuidado al recargar o guardar el documento! Print Document Imprimir el documento Copy Target Path Copiar la ruta de destino Open Target Here Abrir el destino aquí Translators Traductores A lightweight, tabbed, plain-text editor Un editor de texto simpre ligero y con pestañas based on Qt5 basado en Qt5 Author Autor aka. Alias. About FeatherPad Acerca de FeatherPad No Replacement No hay sustituciones One Replacement Una sustitución Replacements Sustituciones FeatherPad::FileDialog Ctrl+H Toggle showing hidden files Ctrl+H Alt+. Toggle showing hidden files Alt+. FeatherPad::LineEdit Clear text (Ctrl+K) Borrar el texto (Ctrl+K) Ctrl+K Clear text Ctrl+K FeatherPad::PrefDialog Preferences Preferencias Window Ventana Window Settings Ajustes de la ventana Remember window &size on closing Recordar el tamaño de la ventana al cerrar Window frame is excluded. No incluye el borde de la ventana Start with this size: Comenzar con este tamaño: px px × × Most suitable with sessions but without tab functionality. Más adecuado para sesiones pero sin la funcionalidad de pestañas. Uncheck for 1/5 of the width. Desmarcar para 1/5 del ancho. Remember splitter position Recordar la posición del divisor No icons in the main window and its menus. KDE may have a bug that disables search and replace shortcuts with the iconless mode. Needs application restart to take effect. Sin iconos en la ventana principal y sus menús. KDE puede tener un fallo que deshabilita la búsqueda y sustituye los atajos de teclado en el modo sin iconos. Es necesario reiniciar la aplicación para que tenga efecto. &Iconless mode Sin &iconos Do not show &toolbar No mostrar la barra de herramientas If the menubar is hidden, a menu button appears on the toolbar. Si la barra de menú está oculta, un bottón de menú aparece en la barra de herramientas. Do not show &menubar No mostrar la barra de &menús Hide search &bar by default Ocultar la &barra de búsqueda por omisión Always show st&atus bar Mostrar siempre la barra de e&stado Show cursor position on status bar Mostrar la posición del cursor en la barra de estado Will take effect after closing this dialog. Tendrá efecto tras cerrar este diálogo. Tab position: Posición de la pestaña: North Norte South Sur West Oeste East Este This means that, for LTR, Alt+Right goes to the first tab after the last tab is activated, and the same for Alt+Left. Tab navigation with mouse wheel is not affected. Esto implica que, para idiomas escritos de izquierda a derecha, Alt+Derecha va a la primera pestaña tras activar la última, y lo mismo para Alt+Izquierda. La circulación de pestañas con el ratón no se ve afectada. Tab navigation wraps &around La circulación de pestañas es cíclica By default, if a FeatherPad window exists on the current desktop, files will be opened in its tabs. However, some desktop environments may not report that they have multiple desktops. Por defecto, si una ventana de FeatherPad existe en el escritorio actual, los ficheros se abren en sus pestañas. Sin embargo, algunos entornos de escritorio pueden no comunicar que tienen varios escritorios. Always open in separate windows Abrir siempre en ventanas separadas If this is checked, the file dialog provided by the current desktop environment will be used instead of the Qt file dialog. Some desktop environments, like KDE and LXQt, provide files dialogs. Si está marcado, el diálogo de ficheros proporcionado por el entorno de escritorio actual será el que se use en vez del de Qt. Algunos entornos de escritorio, como KDE y LXQt, ofrecen díalogos de ficheros. Native file dialog Diálogo de ficheros nativo Uncheck for Monospace 9. Desmarcar para ancho fijo 9. Remember &font Recordar el tipo de letra This covers parentheses, braces, brackets and quotes. Esto incluye paréntesis, llaves, corchetes y comillas. Auto-&bracket Corchetes automáticos Never highlight syntax for files > No realzar la sintaxis de ficheros > This creates a menu button on the status bar for changing the syntax. Esto crea un botón de menú en la barra de estado para cambiar la sintaxis. Support syntax override Permitir el cambio de sintaxis. Show spaces, tabs and tab lines when the syntax is highlighted. Mostar espacios, tabuladores y líneas de tabulación cuando la sintaxis esté realzada. Show whitespaces Mostrar los espacios en blanco The vertical position lines will be drawn only if the editor font has a fixed pitch (like Monospace). Las líneas de posición vertical se pintarán solo si el tipo de letra del editor es de ancho fijo (como Monospace). Show vertical lines starting from this position: Mostrar líneas verticales empezando en esta posición: Also show line and document ends Mostrar también el final de líneas y del documento Date and time format: Formato de la fecha y hora: Some text editors cannot open a document whose last line is not empty. Algunos editores de texto no pueden abrir un documento cuya última línea no esté vacía. Ensure an empty last line on saving Garantizar una última línea vacía al guardar Remove trailing spaces on saving Borrar los espacios al final de las líneas al guardar Inertial scrolling with mouse wheel Desplazamiento inercial con la rueda del ratón Files Ficheros File Management Gestión de ficheros This can be any starting command with arguments, for example, "xterm -hold" for running the process in XTerm. If the command is left empty, the file will be executed directly. If the script is not run in a terminal emulator, the output and error messages will be shown by a popup dialog. Puede ser cualquier orden de inicio con argumentos, por ejemplo, "xterm -hold" para ejecutar el proceso en XTerm. Si la orden se deja vacía, el fichero será ejecutado directamente. Si el script no se ejecuta en un emulador de terminal, la salida y mensajes de error se muestran en un díalogo emergente. Start with this command: Iniciar con esta orden: Command + Arguments Orden + Argumentos Show recentl&y modified files Mostrar los ficheros modificados recientemente Show recentl&y opened files Mostrar los ficheros abiertos recientemente Save changes to opened files every: Guardar los cambios en los ficheros abiertos cada: Help Ayuda Uncheck this if you want FeatherPad to use system icons! Needs application restart to take effect. ¡Desmárquelo si quiere que FeatherPad use los iconos del sistema! Es necesario reiniciar la aplicación para que tenga efecto. Start with side-pane mode Iniciar en modo con panel lateral &Use own icons &Usar iconos propios If this is checked, not only you will lose the informative tooltip and context menu of a single tab but you could not merge a single tabbed window into another one by tab drag- and-drop either. Si está marcado, no solo perderá los mensajes emergentes informativos y el menú contextual de una pestaña sino que tampoco podrá mover ventanas con una pestaña a otra arrastrando dicha pestaña y soltándola. &Do not show a single tab No mostrar una sola pestaña Text Texto Text Editor Editor de texto &Wrap lines by default Dividir las líneas por omisión Auto-&indent by default Sangría automática por omisión Always show line &numbers Mostrar siempre los números de línea Highlight s&yntax by default Realzar la sintaxis por omisión Dark c&olor scheme Esquema de color oscuro Background color value: Valor del color de fondo: MiB MiB This is not a complete fix but prevents annoying scroll jumps. No es un arreglo completo pero evita saltos de desplazamiento molestos. Workaround for &Qt5's scroll jump bug Solución provisional para el fallo de saltos de desplazamiento de Qt5 This only includes executable files written in script languages like Shell and Python. If it is checked and the opened file is executable, the file will be executed by clicking the Run button, that appears on the toolbar/filemenu when needed, or by its shortcut Ctrl+E. Then, the process could be killed by Ctrl+Alt+E. Esto solo incluye ficheros ejecutales escritos en lenguajes de script como Shell y Python. Si está marcado y el fichero abierto es ejecutable, el fichero será ejecutado al hacer clic en el botón Ejecutar que aparece en la barra de herramientas/menú de ficheros cuando sea necesario, o por su atajo de teclado Ctrl+E. Entonces, el proceso se puede matar con Ctrl+Alt+E. Needs window reopening to take effect. Es necesario reabrir la ventana para que tenga efecto. The color value of the background. 255 means white while 0 means black. For the light background, it can be between 230 and 255; for the dark background, between 0 and 50. Needs window reopening to take effect. El valor del color de fondo. 255 significa blanco, y 0 es negro. Para un fondo claro, puede estar entre 230 y 255; para uno fondo oscuro, entre 0 y 50. Es necesario reabrir la ventana para que tenga efecto. Run executable scripts Ejecutar los script ejecutables The maximum number of recently modified or opened files FeatherPad shows. The default is 10. Needs application restart to take effect. El número máximo de ficheros modificados o abiertos recientemente que muestra FeatherPad. Por omisión es 10. Es necesario reiniciar la aplicación para que tenga efecto. Used for pasting the date and time. Takes effect after closing this dialog. Leave empty for: MMM dd, yyyy, hh:mm:ss Se usa para pegar la fecha y hora. Tiene efecto al cerrar este diálogo. Déjelo vacío para: MMM dd, aaaa, hh:mm:ss Should the mouse wheel scrolling be inertial if the cursor is inside the text view? ¿Usar el desplazamiento inercial con la rueda del ratón si el cursor está dentro del área de texto? Number of recent files: Número de ficheros recientes: The maximum number of recently modified or opened files FeatherPad should open with an empty startup of a session. El número máximo de ficheros modificados o abiertos recientemente que FeatherPad debería abrir en un inicio de sesión vacío. Start with recent files: Iniciar con ficheros recientes: Only for files that exist and can be saved. Solo para ficheros existentes y que pueden guardarse. min min Shortcuts Atajos Action Acción Shortcut Atajo Restore default shortcuts. Restaurar los atajos por omisión Default Por omisión Close Cerrar files ficheros file fichero No file Ninguno Warning: Ambiguous shortcut detected! Aviso: ¡atajo ambiguo detectado! Application restart is needed for changes to take effect. Es necesario reiniciar la aplicación para que los cambios tengan efecto. Window reopening is needed for changes to take effect. Es necesario reabrir la ventana para que los cambios tengan efecto. &Recently Opened Abiertos &recientemente Recently &Modified &Modificados recientemente The typed shortcut was not valid. El atajo pulsado no es válido. The typed shortcut was reserved. El atajo pulsado está reservado. FeatherPad::SearchBar Search... Buscar... Match Case Distinguir mayúsculas de minúsculas Whole Word Palabras completas Next Siguiente Previous Anterior FeatherPad::SessionDialog Session Manager Gestor de sesiones &Remove Bo&rrar <b>Save/Restore Session</b> <b>Guardar/restaurar sesión</b> Filter... Filtrar... Open the selected session(s). Abrir las sesiones seleccionadas &Open Abrir Del Retroceso Ctrl+Del Ctrl+Retroceso By default, all files that are opened in all windows will be included in the saved session. Por omisión, todos los ficheros abiertos en todas las ventanas serán incluidos en la sesión guardada. Save only from this &window Guardar solo de esta ventana. <b>Warning</b> <b>Aviso</b> &Yes &No No Re&name Re&nombrar Remove &All Borr&ar todo &Close &Cerrar Save the curent session under the given title. Guardar la sesión actual con el título dado. &Save Guardar Type a name to save session Escriba un nombre para guardar la sesión Nothing saved.<br>No file was opened. No se ha guardado nada.<br>No había ningún fichero abierto. No file exists or can be opened. No existe ningún fichero o no pueden abrirse. Not all files exist or can be opened. No existen todos los ficheros o no pueden abrirse. &OK Aceptar Do you really want to remove all saved sessions? ¿Realmente quiere borar todas las sesiones guardadas? Do you really want to remove the selected sessions? ¿Realmente quiere borrar las sesiones seleccionadas? Do you really want to remove the selected session? ¿Realmente quiere borrar la sesión seleccionada? A session with the same name exists.<br>Do you want to overwrite it? ¿Ya existe una sesión con el mismo nombe.<br>¿Quiere sobrescribirla? FeatherPad::SidePane Filter... Filtrar... FeatherPad::TextEdit Open Link Abrir el enlace Copy Link Copiar el enlace Paste Date and Time Pegar la fecha y hora FeatherPad::WarningBar Close Cerrar FeatherPad-0.8/featherpad/data/translations/featherpad_fr.ts000066400000000000000000002466711325653242400243110ustar00rootroot00000000000000 FeatherPad::AboutDialog License FeatherPad::FPwin Untitled Sans titre Select text from cursor to this line (Ctrl+Shift+J) Sélectionner le texte depuis le curseur jusqu'à cette ligne (Ctrl+MAJ+J) Select Text Sélectionner le texte Ctrl+Shift+J Ctrl+MAJ+J &File &Fichier &Edit &Éditer &Options &Options Encoding Encodage &Search &Chercher &Help &Aide Find: Trouver: To be replaced À remplacer Replace with: Remplacer par: Replacing text Texte de remplacement F8 F7 F9 &New &Nouveau New tab Nouvel onglet Ctrl+N &Open &Ouvrir Open a file Ouvrir un fichier Ctrl+O &Save &Enregistrer Save the current tab Enregistrer l'onglet actif Ctrl+S &Undo &Annuler Undo Annuler Ctrl+Z &Redo &Recommencer Redo Recommencer Ctrl+Shift+Z Ctrl+MAJ+Z Reload Recharger Ctrl+Shift+R Ctrl+MAJ+R &Find &Trouver Ctrl+F Show/hide replacement dock Afficher/masquer la fenêtre de remplacement Ctrl+R Save &As Enregistrer s&ous Ctrl+Shift+S Ctrl+MAJ+S &Print &Imprimer Ctrl+P Documen&t Properties &Propriétés du document Ctrl+Shift+D Ctrl+MAJ+D &Close &Fermer Ctrl+Shift+Q Ctrl+MAJ+Q &Quit &Quitter Ctrl+Q &Cut &Couper Ctrl+X C&opy C&opier Ctrl+C &Paste Col&ler Ctrl+V &Delete &Effacer &Select All &Tout sélectionner Ctrl+A &Font P&olice &Line Numbers &Numéros de ligne Ctrl+L &Wrap Lines &Retour à la ligne Ctrl+W &Auto-Indentation &Indentation automatique Ctrl+I &Syntax Highlighting &Coloration syntaxique Ctrl+Shift+H Ctrl+MAJ+H &Preferences &Préférences Ctrl+Shift+P Ctrl+MAJ+P Ctrl+H &About À &propos Enforce UTF-8 Forcer UTF-8 Save with &Encoding Enregistrer avec l'en&codage &Jump to &Aller à Show/hide jump bar Afficher/masquer la barre de saut Ctrl+J Edit text Éditer le texte Ctrl+Shift+E Ctrl+MAJ+E Alt+Down Alt+Bas &Run &Lancer Ctrl+E &Clear &Vider Clear the list of recently modified files Vider la liste des fichiers modifiés récemment Save/Restore Session Enregistrer/Restaurer une session Sa&ve/Restore Session En&registrer/Restaurer une session Ctrl+M Side-Pane Panneau latéral Ctrl+Alt+P Paste Date and Time Ctrl+Shift+V Ctrl+MAJ+V &Detach Tab &Détacher un onglet Ctrl+T Close Ne&xt Tabs Fermer les onglets &suivants Close &Previous Tabs Fermer les onglets &précédents Ne&xt Tab Onglet &suivant Previous Ta&b Onglet &précédent Execute Exécuter Close &All Tabs Fermer &tous les onglets Go to line: Aller à la ligne: Recently &Modified Récemment &modifié &Encoding &Encodage &Unicode &Western European Europe &Occidentale &East European &Europe de l'Est Ea&st Asian &Asie de l'Est Rep&lacement Remp&lacement Focus/hide search bar Mettre en évidence/masquer la barre de recherche &Replace &Remplacer Windows Arabic (&CP1256) Arabe Windows (&CP1256) &Other Au&tre &UTF-8 UTF-&16 &ISO-8859-1 &Windows-1252 &Cyrillic (CP1251) &Cyrillique (CP1251) Cyrillic (&KOI8-U) Cyrillique (&KOI8-U) Cyrillic (&ISO-8859-5) Cyrillique (&ISO-8859-5) &Chineese (BIG5) &Chinois (BIG5) Chinese (&GB18030) Chinois (&GB18030) &Japanese (ISO-2022-JP) &Japonais (ISO-2022-JP) Japanese (&ISO-2022-JP-2) Japonais (&ISO-2022-JP-2) Japanese (ISO-&2022-KR) Japonais (ISO-&2022-KR) Ja&panese (CP932) Ja&ponais (CP932) Japa&nese (EUC-JP) Japo&nais (EUC-JP) &Korean (CP949) &Coréen (CP949) K&orean (CP1361) C&oréen (CP1361) Korean (&EUC-KR) Coréen (&EUC-KR) ISO-&8859-15 Close &Other Tabs Fermer les &autres onglets &Copy File Name &Copier le nom du fichier Copy File &Path Copier le chemin du &fichier Alt+Right Alt+Droite Alt+Left Alt+Gauche &First Tab P&remier onglet &Last Tab Dernier on&glet Alt+Up Alt+Haut Menu Calculate number of words (For huge texts, this may be CPU-intensive.) Calculer le nombre de mots (Pour les textes volumineux, peut fortement solliciter le processeur.) Next Suivant Previous Précédent Replace all Tout remplacer &Recently Opened &Ouvert récemment All Tout Close Fermer Save changes? Enregistrer les changements? Please attend to that window or just close its dialog! Veuillez vous occuper de cette fenêtre ou fermez simplement la boîte de dialogue! The file has been removed. Le fichier a été supprimé. Save Enregistrer Discard changes Annuler les changements Cancel Annuler No to all Non à tout Unsaved Non sauvegardé Lines Lignes Sel. Chars Nb carac. sél. Words Mots Another process is running in this tab! Un autre processus a été lancé dans cet onglet! Script File Fichier de script Another FeatherPad window has a modal dialog! Une autre fenêtre de FeatherPad a une boite de dialogue modale! Ctrl+= Ctrl++ Ctrl+- Ctrl+0 F11 Ctrl+Shift+W Ctrl+MAJ+W Ctrl+Alt+E Shift+Ins Shift+Del Ctrl+Ins Ctrl+Left Ctrl+Right Ctrl+Up Ctrl+Down Ctrl+Home Ctrl+End F3 F4 F5 F6 Shift+Enter MAJ+Entrée Ctrl+Tab Ctrl+Meta+Tab Ctrl+K Position: Normal The document has been modified. Le document a été modifié. Only one process is allowed per tab. Un seul processus est autorisé par onglet. Script Output Résultats du script Clear Vider Some file(s) could not be opened! You may not have the permission to read. Uneditable file(s)! Fichier(s) non modifiable(s)! Non-text files or files with huge lines cannot be edited. Les fichiers non textuels ou contenant de trop grandes lignes ne peuvent pas être modifiés. This file has been modified elsewhere or in another way! Please be careful about reloading or saving this document! Copy Target Path Copier le chemin de la cible Open Target Here Ouvrir ici la cible Translators A lightweight, tabbed, plain-text editor Un éditeur de texte simple, léger, à onglets aka. alias FeatherPad does not open files larger than 100 MiB. FeatherPad ne peut pas ouvrir les fichiers de plus de 100 Mio. A previous crash detected! Plantage antérieur détecté! Preferably, close all FeatherPad windows and start again! Si possible, fermez toutes les fenêtres de FeatherPad et redémarrez! All Files (*) Tous les fichiers (*) All Files (*);;.%1 Files (*.%1) Tous les fichiers (*);;.%1 Fichiers (*.%1) Open file... Ouvrir fichier... .%1 Files (*.%1);;All Files (*) .%1 Fichiers (*.%1);;Tous les fichiers (*) Save as... Enregistrer sous... Keep encoding and save as... Conserver l'encodage et enregistrer sous... Yes Oui No Non Do you want to use <b>MS Windows</b> end-of-lines? Voulez-vous utiliser les fins de ligne <b>MS Windows</b>? This may be good for readability under MS Windows. Peut être bénéfique pour la lisibilité sous MS Windows. Cannot be saved! Sauvegarde impossible! Help Aide Syntax Syntaxe Huge file(s) not opened! Fichier(s) volumineux non ouvert(s)! Print Document Imprimer le document based on Qt5 basé sur Qt5 Author Auteur About FeatherPad À propos de FeatherPad No Replacement Aucun remplacement One Replacement Un remplacement Replacements Remplacements FeatherPad::FileDialog Ctrl+H Toggle showing hidden files Alt+. Toggle showing hidden files FeatherPad::LineEdit Clear text (Ctrl+K) Effacer le texte (Ctrl+K) Ctrl+K Clear text Effacer le texte FeatherPad::PrefDialog Preferences Préférences Window Fenêtre Window Settings Paramètres des fenêtres Remember window &size on closing Conserver la taille des &fenêtres à la fermeture Window frame is excluded. Excepté le cadre de la fenêtre. Start with this size: Démarrer avec la taille: px × Most suitable with sessions but without tab functionality. Plus adapté pour les sessions mais sans la fonctionnalité des onglets. Uncheck for 1/5 of the width. Décoché pour 1/5 de la largeur. Remember splitter position Garder en mémoire la position du séparateur No icons in the main window and its menus. KDE may have a bug that disables search and replace shortcuts with the iconless mode. Needs application restart to take effect. Pas d'icônes dans la fenêtre principale et ses menus. KDE semble avoir un bug qui désactive la recherche et remplace les raccourcis par un mode sans icône. Nécessite un redémarrage de l'application pour être activé. &Iconless mode &Mode sans icônes Do not show &toolbar Ne pas afficher la barre d'&outils If the menubar is hidden, a menu button appears on the toolbar. Si la barre de menu est masquée, un bouton menu apparait sur la barre d'outils. Do not show &menubar Ne pas afficher la barre de &menu Hide search &bar by default Masquer la &barre de recherche par défaut Always show st&atus bar Toujours afficher la barre d'ét&at Show cursor position on status bar Will take effect after closing this dialog. Prendra effet après la fermeture de la boîte de dialogue. Tab position: Emplacement des onglets: North Nord South Sud West Ouest East Est This means that, for LTR, Alt+Right goes to the first tab after the last tab is activated, and the same for Alt+Left. Tab navigation with mouse wheel is not affected. Signifie que, de gauche à droite, Alt+Droite amène au premier onglet après activation du dernier onglet, et pareil pour Alt+Gauche. La navigation par onglets avec la souris n'est pas concernée. Tab navigation wraps &around Rot&ation de la navigation par onglets By default, if a FeatherPad window exists on the current desktop, files will be opened in its tabs. However, some desktop environments may not report that they have multiple desktops. Par défaut, si une fenêtre FeatherPad existe dans le bureau actuel, les fichiers seront ouverts dans ses onglets. Cependant, certains environnements de bureau n'indiquent pas qu'ils ont des bureaux multiples. Always open in separate windows Toujours ouvrir dans des fenêtres séparées If this is checked, the file dialog provided by the current desktop environment will be used instead of the Qt file dialog. Some desktop environments, like KDE and LXQt, provide files dialogs. Si cochée, la boîte de dialoge de fichiers fournie par l'environnement de bureau actuel sera utilisée à la place de la boîte de dialogue de Qt. Certains environnnements de bureau, comme KDE et LXQt, disposent de leurs propres boîtes de dialogue. Native file dialog Boîte de dialogue des fichiers native Uncheck for Monospace 9. Décocher pour Monospace 9. Remember &font Garder en mémoire la &police This covers parentheses, braces, brackets and quotes. Concerne les parenthèses, accolades, et guillemets. Auto-&bracket &Parenthésage automatique Never highlight syntax for files > Pas de coloration syntaxique pour les fichiers > This creates a menu button on the status bar for changing the syntax. Support syntax override Show spaces, tabs and tab lines when the syntax is highlighted. Afficher les espaces, les tabulations et lignes de tabulation quand la syntaxe est colorée. Show whitespaces Afficher les espaces blancs The vertical position lines will be drawn only if the editor font has a fixed pitch (like Monospace). Show vertical lines starting from this position: Also show line and document ends Afficher également la fin de ligne et de document Date and time format: Some text editors cannot open a document whose last line is not empty. Certains éditeurs de texte ne peuvent ouvrir un document dont la dernière ligne n'est pas vide. Ensure an empty last line on saving Conserver une dernière ligne vide lors de l'enregistrement Remove trailing spaces on saving Inertial scrolling with mouse wheel Défilement inertiel de la roulette de la souris Files Fichiers File Management Gestion des fichiers This can be any starting command with arguments, for example, "xterm -hold" for running the process in XTerm. If the command is left empty, the file will be executed directly. If the script is not run in a terminal emulator, the output and error messages will be shown by a popup dialog. Il s'agit de n'importe quelle commande de lancement ayant des arguments, par exemple, "xterm -hold" pour lancer le processus dans XTerm. Si la commande est vide, le fichier sera directement executé. Si le script n'est pas lancé dans un émulateur de terminal, le résultat et les messages d'erreur seront affichés dans une boîte de dialogue popup. Start with this command: Lancer avec la commande: Command + Arguments Commande + Arguments Show recentl&y modified files Afficher les fichiers récemment &modifiés Show recentl&y opened files Afficher les fichiers récemment &ouverts Save changes to opened files every: Help Aide Uncheck this if you want FeatherPad to use system icons! Needs application restart to take effect. Décochez si vous voulez que FeatherPad utilise les icônes du système! Nécessite le redémarrage de l'application pour prendre effet. Start with side-pane mode Démarrer avec le mode panneau latéral &Use own icons &Utiliser ses icônes personnelles If this is checked, not only you will lose the informative tooltip and context menu of a single tab but you could not merge a single tabbed window into another one by tab drag- and-drop either. Si coché, non seulement vous perdrez l'infobulle d'information et le menu contextuel d'un onglet unique mais vous ne pourrez pas non plus fusionner une fenêtre à onglet unique avec une autre en glissant-déposant un onglet. &Do not show a single tab &Ne pas afficher un onglet unique Text Texte Text Editor Éditeur de texte &Wrap lines by default &Retour à la ligne par défaut Auto-&indent by default Indentation auto&matique par défaut Always show line &numbers Toujours afficher les &numéros de ligne Highlight s&yntax by default Coloration s&yntaxique par défaut Dark c&olor scheme Jeu de c&ouleur sombre Background color value: Valeur de la couleur d'arrière-plan: MiB Mio This is not a complete fix but prevents annoying scroll jumps. Ce n'est pas un correctif parfait mais il empêche les sauts de défilement énervants. Workaround for &Qt5's scroll jump bug Solution au bug du saut de défilement de &Qt5 This only includes executable files written in script languages like Shell and Python. If it is checked and the opened file is executable, the file will be executed by clicking the Run button, that appears on the toolbar/filemenu when needed, or by its shortcut Ctrl+E. Then, the process could be killed by Ctrl+Alt+E. Comprend uniquement les fichiers exécutables écrits en languages scripts tels que Shell et Python. Si coché et si le fichier ouvert est exécutable, le fichier sera exécuté en cliquant sur le bouton Lancer, qui apparaît sur la barre d'outils/menu fichier lorsque c'est nécessaire, ou par son raccourci Ctrl+E. Par la suite, le processus pourra être arrêté avec Ctrl+Alt+E. Needs window reopening to take effect. Nécessite la réouverture de la fenêtre pour être activé. The color value of the background. 255 means white while 0 means black. For the light background, it can be between 230 and 255; for the dark background, between 0 and 50. Needs window reopening to take effect. La valeur de la couleur d'arrière-plan. 255 pour le blanc et 0 pour le noir. Pour les arrière-plans clairs, elle peut être comprise entre 230 et 255; pour les arrière-plans sombres, entre 0 et 50. Nécessite la réouverture de la fenêtre pour être activé. Run executable scripts Lance des scripts exécutables The maximum number of recently modified or opened files FeatherPad shows. The default is 10. Needs application restart to take effect. Le nombre maximal de fichiers récemment modifiés ou ouverts que FeatherPad affiche. 10 par défaut. Nécessite le redémarrage de l'application pour être activé. Used for pasting the date and time. Takes effect after closing this dialog. Leave empty for: MMM dd, yyyy, hh:mm:ss Should the mouse wheel scrolling be inertial if the cursor is inside the text view? Le défilement de la roulette de la souris doit-il être inertiel si le curseur se trouve dans la vue du texte? Number of recent files: Nombre de fichiers récents: The maximum number of recently modified or opened files FeatherPad should open with an empty startup of a session. Le nombre maximal de fichiers récemment modifiés ou ouverts que FeatherPad doit ouvrir au démarrage à vide d'une session. Start with recent files: Démarrer avec des fichiers récents: Only for files that exist and can be saved. min Shortcuts Raccourcis Action Shortcut Raccourci Restore default shortcuts. Rétablir les raccourcis par défaut. Default Défaut Close Fermer files fichiers file fichier No file Aucun fichier Warning: Ambiguous shortcut detected! Attention: raccourci ambigu détecté! Application restart is needed for changes to take effect. Le redémarrage de l'application est nécessaire pour valider les changements. Window reopening is needed for changes to take effect. La réouverture de la fenêtre est nécessaire pour valider les changements. &Recently Opened &Récemment ouvert Recently &Modified Récemment &Modifié The typed shortcut was not valid. Le raccourci entré est invalide. The typed shortcut was reserved. Le raccourci entré est déjà réservé. FeatherPad::SearchBar Search... Rechercher... Match Case Respecter la casse Whole Word Mot entier Next Suivant Previous Précédent FeatherPad::SessionDialog Session Manager Gestionnaire de session &Remove &Supprimer <b>Save/Restore Session</b> <b>Enregistrer/Restaurer une session</b> Filter... Filtrer... Open the selected session(s). Ouvir la (les) session(s) sélectionnée(s). &Open &Ouvrir Del Suppr Ctrl+Del Ctrl+Suppr By default, all files that are opened in all windows will be included in the saved session. Par défaut, tous les fichiers ouverts dans toutes les fenêtres seront inclus dans la session sauvegardée. Save only from this &window Sauvegarder uniquement depuis cette fenêtre <b>Warning</b> <b>Attention</b> &Yes &Oui &No &Non Re&name &Renommer Remove &All &Tout supprimer &Close &Fermer Save the curent session under the given title. Enregistrer la session en cours en lui donnant un nom. &Save &Enregistrer Type a name to save session Entrer un nom pour enregistrer une session Nothing saved.<br>No file was opened. Aucune sauvegarde.<br>Aucun fichier n'a été ouvert. No file exists or can be opened. Aucun fichier existant ou pouvant être ouvert. Not all files exist or can be opened. Certains fichiers n'existent pas ou ne peuvent être ouverts. &OK O&K Do you really want to remove all saved sessions? Souhaitez-vous vraiment supprimer toutes les sessions sauvegardées? Do you really want to remove the selected sessions? Souhaitez-vous vraiment supprimer les sessions sélectionnées? Do you really want to remove the selected session? Souhaitez-vous vraiment supprimer la session sélectionnée? A session with the same name exists.<br>Do you want to overwrite it? Il existe une session ayant le même nom.<br>Souhaitez-vous l'écraser? FeatherPad::SidePane Filter... Filtrer... FeatherPad::TextEdit Open Link Copy Link Paste Date and Time FeatherPad::WarningBar Close Fermer FeatherPad-0.8/featherpad/data/translations/featherpad_pl.ts000066400000000000000000002427161325653242400243110ustar00rootroot00000000000000 FeatherPad::AboutDialog License FeatherPad::FPwin Untitled Bez tytułu Go to line: Przejdź do wiersza: Select text from cursor to this line (Ctrl+Shift+J) Zaznacz tekst od kursora do tego wiersza (Ctrl+Shift+J) Select Text Zaznacz tekst Ctrl+Shift+J Ctrl+Shift+J &File &Plik &Edit &Edycja &Options &Opcje Encoding Kodowanie &Search &Szukaj &Help Po&moc Find: Szukaj: To be replaced Do zastąpienia Replace with: Zastąp na: Replacing text Zastępowanie tekstu F8 F8 F7 F7 F9 F9 &New &Nowy New tab Nowa karta Ctrl+N Ctrl+N &Open &Otwórz Open a file Otwórz plik Ctrl+O Ctrl+O &Save &Zapisz Save the current tab Zapisz obecną kartę Ctrl+S Ctrl+S &Undo &Cofnij Undo Cofnij Ctrl+Z Ctrl+Z &Redo &Ponów Redo Ponów Ctrl+Shift+Z Ctrl+Shift+Z Reload Przeładuj Ctrl+Shift+R Ctrl+Shift+R &Find &Szukaj Ctrl+F Ctrl+F Show/hide replacement dock Pokaż/ukryj dok zastępowania Ctrl+R Ctrl+R Save &As Z&apisz jako Ctrl+Shift+S Ctrl+Shift+S &Print &Drukuj Ctrl+P Ctrl+P Documen&t Properties Właściwości dokumen&tu Ctrl+Shift+D Ctrl+Shift+D &Close Zam&knij Ctrl+Shift+Q Ctrl+Shift+Q &Quit &Wyjdź Ctrl+Q Ctrl+Q &Cut &Wytnij Ctrl+X Ctrl+X C&opy K&opiuj Ctrl+C Ctrl+C &Paste W&klej Ctrl+V Ctrl+V &Delete &Usuń &Select All Zaznacz w&szystko Ctrl+A Ctrl+A &Font &Czcionka &Line Numbers &Numerowanie wierszy Ctrl+L Ctrl+L &Wrap Lines Za&wijanie wierszy Ctrl+W Ctrl+W &Auto-Indentation &Automatyczne wcięcia Ctrl+I Ctrl+I &Syntax Highlighting Podświetlanie &składni Ctrl+Shift+H Ctrl+Shift+H &Preferences &Preferencje Ctrl+Shift+P Ctrl+Shift+P Ctrl+H Ctrl+H &About O progr&amie Enforce UTF-8 Wymuś UTF-8 Save with &Encoding Zapisz z kodowani&em &Jump to Prze&jdź do Show/hide jump bar Pokaż/ukryj pasek przechodzenia do linii Ctrl+J Ctrl+J Edit text Edytuj tekst Ctrl+Shift+E Ctrl+Shift+E &Run U&ruchom Ctrl+E Ctrl+E &Clear Wy&czyść Clear the list of recently modified files Wyczyść listę ostatnio modyfikowanych plików Save/Restore Session Zapisz/przywróć sesję Sa&ve/Restore Session Zapisz/przy&wróć sesję Ctrl+M Ctrl+M Side-Pane Panel boczny Ctrl+Alt+P Ctrl+Alt+P Paste Date and Time Ctrl+Shift+V Ctrl+Shift+V &Detach Tab O&dłącz kartę Ctrl+T Ctrl+T Close Ne&xt Tabs Zamknij nas&tępne karty Close &Previous Tabs Zamknij &poprzednie karty Ne&xt Tab &Następna karta Previous Ta&b Poprze&dnia karta Execute Wykonaj Close &All Tabs Z&amknij wszystkie karty Recently &Modified Ostatnio z&modyfikowane &Encoding Kodowani&e &Unicode &Unicode &Western European &Zachodnioeuropejskie &East European Wschodnio&europejskie Ea&st Asian W&schodnioazjatyckie Rep&lacement &Zastąpienie Focus/hide search bar Pokaż/ukryj pasek wyszukiwania &Replace &Zastąp Windows Arabic (&CP1256) Windows Arabic (&CP1256) &Other &Inne &UTF-8 &UTF-8 UTF-&16 UTF-&16 &ISO-8859-1 &ISO-8859-1 &Windows-1252 &Windows-1252 &Cyrillic (CP1251) &Cyrylica (CP1251) Cyrillic (&KOI8-U) Cyrylica (&KOI8-U) Cyrillic (&ISO-8859-5) Cyrylica (&ISO-8859-5) &Chineese (BIG5) &Chińskie (BIG5) Chinese (&GB18030) Chińskie (&GB18030) &Japanese (ISO-2022-JP) &Japońskie (ISO-2022-JP) Japanese (&ISO-2022-JP-2) Japońskie (&ISO-2022-JP-2) Japanese (ISO-&2022-KR) Japońskie (ISO-&2022-KR) Ja&panese (CP932) Ja&pońskie (CP932) Japa&nese (EUC-JP) Japoń&skie (EUC-JP) &Korean (CP949) &Koreańskie (CP949) K&orean (CP1361) K&oreańskie (CP1361) Korean (&EUC-KR) Koreańskie (&EUC-KR) ISO-&8859-15 ISO-&8859-15 Close &Other Tabs Zamknij p&ozostałe karty &Copy File Name &Kopiuj nazwę pliku Copy File &Path Ko&piuj ścieżkę do pliku Alt+Right Alt+Strzałka w prawo Alt+Left Alt+Strzałka w lewo &First Tab Pier&wsza karta Alt+Down Alt+Strzałka w dół &Last Tab &Ostatnia karta Alt+Up Alt+Strzałka w górę Menu Menu Calculate number of words (For huge texts, this may be CPU-intensive.) Oblicz liczbę wyrazów (może obciążyć procesor w wypadku większych tekstów) Next Następne Previous Poprzednie Replace all Zastąp wszystko &Recently Opened Ostatnio otwie&rane All Wszystko Close Zamknij Save changes? Zapisać zmiany? Please attend to that window or just close its dialog! Przejdź do tego okna lub zamknij jego dialog! The document has been modified. Dokument został zmodyfikowany. The file has been removed. Plik został usunięty. Save Zapisz Discard changes Odrzuć zmiany Cancel Anuluj No to all Nie dla wszystkich Unsaved Niezapisane Lines Wiersze Sel. Chars Zaznaczone znaki Words Wyrazy Another process is running in this tab! Inny proces jest uruchomiony w tej karcie! Script File Plik skryptu Another FeatherPad window has a modal dialog! Inne okno FeatherPad wyświetliło dialogowe okno modalne! Ctrl+= Ctrl+= Ctrl++ Ctrl++ Ctrl+- Ctrl+- Ctrl+0 Ctrl+0 F11 F11 Ctrl+Shift+W Ctrl+Shift+W Ctrl+Alt+E Ctrl+Alt+E Shift+Ins Shift+Del Ctrl+Ins Ctrl+Left Ctrl+Right Ctrl+Up Ctrl+Down Ctrl+Home Ctrl+End F3 F3 F4 F4 F5 F5 F6 F6 Shift+Enter Shift+Enter Ctrl+Tab Ctrl+Tab Ctrl+Meta+Tab Ctrl+Meta+Tab Ctrl+K Ctrl+K Position: Normal Only one process is allowed per tab. Tylko jeden proces może być aktywny w karcie. Script Output Wynik skryptu Clear Wyczyść FeatherPad does not open files larger than 100 MiB. FeatherPad nie otworzy plików większych niż 500 MB. Some file(s) could not be opened! You may not have the permission to read. Uneditable file(s)! Non-text files or files with huge lines cannot be edited. A previous crash detected! Wykryto wcześniejsze zatrzymanie aplikacji! Preferably, close all FeatherPad windows and start again! Zalecane jest zamknięcie wszystkich okien FeatherPad i ponowne uruchomienie programu! All Files (*) Wszystkie pliki (*) All Files (*);;.%1 Files (*.%1) Wszystkie pliki (*);;.Pliki %1 (*.%1) Open file... Otwórz plik… .%1 Files (*.%1);;All Files (*) Pliki .%1 (*.%1);;Wszystkie pliki (*) Save as... Zapisz jako… Keep encoding and save as... Zachowaj kodowanie i zapisz jako… Yes Tak No Nie Do you want to use <b>MS Windows</b> end-of-lines? Czy chcesz uźyć znaków końca wierszu systemu <b>MS Windows</b>? This may be good for readability under MS Windows. Może to poprawić czytelność pod MS Windows. Cannot be saved! Nie udało się zapisać! Help Pomoc Syntax Składnia Huge file(s) not opened! Nie można otwierać dużych plików! This file has been modified elsewhere or in another way! Please be careful about reloading or saving this document! Print Document Wydrukuj dokument Copy Target Path Kopiuj ścieżkę docelową Open Target Here Otwórz cel tutaj Translators A lightweight, tabbed, plain-text editor Lekki edytor tekstu niesformatowanego z zakładkami based on Qt5 oparty na Qt5 Author Autor aka. aka. About FeatherPad O FeatherPad No Replacement Bez zamiennika One Replacement Jeden zamiennik Replacements Zamieki FeatherPad::FileDialog Ctrl+H Toggle showing hidden files Ctrl+H Alt+. Toggle showing hidden files Alt+. FeatherPad::LineEdit Clear text (Ctrl+K) Wyczyść tekst (Ctrl+K) Ctrl+K Clear text Ctrl+K FeatherPad::PrefDialog Preferences Preferencje Window Okno Window Settings Ustawienia okna Remember window &size on closing Zapamiętaj rozmiar okna podcza&s zamykania Window frame is excluded. Obramowanie okna nie jest uwzględnione. Start with this size: Rozpocznij z rozmiarem: px px × × Most suitable with sessions but without tab functionality. Najbardziej przydatne w sesjach bez możliwości korzystana z kart. Uncheck for 1/5 of the width. Odznacz, aby używać 1/5 szerokości. Remember splitter position Zapamiętaj szerokość panelu No icons in the main window and its menus. KDE may have a bug that disables search and replace shortcuts with the iconless mode. Needs application restart to take effect. Brak ikon w głównym oknie i jego menu. Błąd w KDE może skutkować niedziałającymi skrótami wyszukiwania i zastępowania w trybie bez ikon. Restart jest wymagany, aby uzyskać efekt. &Iconless mode Tryb bez &ikon Do not show &toolbar Nie pokazuj paska &narzędzi If the menubar is hidden, a menu button appears on the toolbar. Jeżeli pasek menu jest ukryty, przycisk menu pojawi się na pasku narzędzi. Do not show &menubar Nie pokazuj paska &menu Hide search &bar by default Ukryj domyślnie &pasek wyszukiwania Always show st&atus bar Zawsze pokazuj pasek st&anu Show cursor position on status bar Will take effect after closing this dialog. Zmiany będą widoczne po zamknięciu tego okna. Tab position: Położenie paska kart: North Góra South Dół West Lewo East Prawo This means that, for LTR, Alt+Right goes to the first tab after the last tab is activated, and the same for Alt+Left. Tab navigation with mouse wheel is not affected. Tab navigation wraps &around By default, if a FeatherPad window exists on the current desktop, files will be opened in its tabs. However, some desktop environments may not report that they have multiple desktops. Always open in separate windows Zawsze otwieraj w oddzielnych oknach If this is checked, the file dialog provided by the current desktop environment will be used instead of the Qt file dialog. Some desktop environments, like KDE and LXQt, provide files dialogs. Jeżeli zaznaczone, zamiast okien dialogowych Qt, zostaną użyte dostarczone przez używane środowisko graficzne. Niektóre środowiska, np. KDE i LXQt dostarczają własne okna dialogowe. Native file dialog Natywne okna dialogowe Uncheck for Monospace 9. Odznacz, aby korzystać z Monospace 9. Remember &font Zapamiętaj &czcionkę This covers parentheses, braces, brackets and quotes. Obejmuje to nawiasy, klamry i cudzysłowie. Auto-&bracket Automatyczne &nawiasy Never highlight syntax for files > Nigdy nie podświetlaj składni w plikach > This creates a menu button on the status bar for changing the syntax. Support syntax override Show spaces, tabs and tab lines when the syntax is highlighted. Pokazuje spacje, tabulatory itp. jeżeli składnia jest podświetlana. Show whitespaces Wyróżniaj białe znaki The vertical position lines will be drawn only if the editor font has a fixed pitch (like Monospace). Show vertical lines starting from this position: Also show line and document ends Pokazuj także koniec dokumentów i wierszy Date and time format: Some text editors cannot open a document whose last line is not empty. Niektóre edytory tekstu nie otworzą dokumentu jeżeli ostatni wiersz nie jest pusty. Ensure an empty last line on saving Sprawdzaj obecność pustego wiersza podczas zapisywania Remove trailing spaces on saving Inertial scrolling with mouse wheel Files Pliki File Management Zarządzanie plikami This can be any starting command with arguments, for example, "xterm -hold" for running the process in XTerm. If the command is left empty, the file will be executed directly. If the script is not run in a terminal emulator, the output and error messages will be shown by a popup dialog. Start with this command: Rozpocznij z komendą: Command + Arguments Komenda + argumenty Show recentl&y modified files Pokazuj ostatnio mod&yfikowane pliki Show recentl&y opened files Pokazuj ostatnio ot&wierane pliki Save changes to opened files every: Help Pomoc Uncheck this if you want FeatherPad to use system icons! Needs application restart to take effect. Odznacz, jeżeli chcesz aby FeatherPad używał ikon systemowych! Restart jest wymagany, aby efekt był widoczny. Start with side-pane mode Uruchamiaj z panelem bocznym &Use own icons &Używaj własnych ikon If this is checked, not only you will lose the informative tooltip and context menu of a single tab but you could not merge a single tabbed window into another one by tab drag- and-drop either. Jeżeli zaznaczone, utracisz menu kontekstowe pojedynczej karty i nie będziesz mógł przenieść dokumentu z jednokartowego okna do drugiego przeciągając kartę. &Do not show a single tab Nie pokazuj poje&dynczej karty Text Tekst Text Editor Edytor tekstu &Wrap lines by default Za&wijaj domyślnie wiersze Auto-&indent by default Twórz automatyczn&ie wcięcia domyślnie Always show line &numbers Zawsze &numeruj wiersze Highlight s&yntax by default Podświetaj składnię dom&yślnie Dark c&olor scheme Ciemny zestaw k&olorów Background color value: Wartość koloru tła: MiB MiB This is not a complete fix but prevents annoying scroll jumps. Nie jest to w pełni działająca poprawka, ale zapobiega irytującemu przeskakiwaniu przy przewijaniu. Workaround for &Qt5's scroll jump bug Obejdź błąd przeskakiwania przy przewijaniu w &Qt5 This only includes executable files written in script languages like Shell and Python. If it is checked and the opened file is executable, the file will be executed by clicking the Run button, that appears on the toolbar/filemenu when needed, or by its shortcut Ctrl+E. Then, the process could be killed by Ctrl+Alt+E. Needs window reopening to take effect. Wymaga ponownego otwarcia okna, aby uzyskać efekt. The color value of the background. 255 means white while 0 means black. For the light background, it can be between 230 and 255; for the dark background, between 0 and 50. Needs window reopening to take effect. Wartość koloru tła. 255 oznacza biel, a 0 – czerń. Dla jasnego tła, może to być wartość pomiędzy 230 a 255; dla ciemnego – od 0 do 50. Wymagane jest ponowne otwarcie okna, aby efekt był widoczny. Run executable scripts Wykonaj skrypty wykonywalne The maximum number of recently modified or opened files FeatherPad shows. The default is 10. Needs application restart to take effect. Maksymalna liczba ostatnio otwieranych plików wyświetlanych przez FeatherPad. Domyślna wartość to 10. Restart jest wymagany, aby uzyskać efekt. Used for pasting the date and time. Takes effect after closing this dialog. Leave empty for: MMM dd, yyyy, hh:mm:ss Should the mouse wheel scrolling be inertial if the cursor is inside the text view? Number of recent files: Liczba ostatnich plików: The maximum number of recently modified or opened files FeatherPad should open with an empty startup of a session. Maksymalna liczba ostatnio otwieranych plików uruchamianych po rozpoczęciu pustej sesji. Start with recent files: Rozpocznij z ostatnimi plikami: Only for files that exist and can be saved. min Shortcuts Skróty Action Działanie Shortcut Skrót Restore default shortcuts. Przywróć domyślne skróty. Default Domyślne Close Zamknij files pliki file plik No file Brak pliku Warning: Ambiguous shortcut detected! Ostrzeżenie: Wykryto niejednoznaczny skrót! Application restart is needed for changes to take effect. Restart aplikacji jest wymagany, aby uzyskać efekt. Window reopening is needed for changes to take effect. Ponowne otwarcie okna jest wymagane, aby uzyskać efekt. &Recently Opened Ostatnio otwie&rane Recently &Modified Ostatnio &modyfikowane The typed shortcut was not valid. Wprowadzony skrót jest nieprawidłowy. The typed shortcut was reserved. Wprowadzony skrót został zarejestrowany. FeatherPad::SearchBar Search... Szukaj… Match Case Uwzględniaj wielkość znaków Whole Word Cały wyraz Next Następne Previous Poprzednie FeatherPad::SessionDialog Session Manager Menedżer sesji &Remove &Usuń <b>Save/Restore Session</b> <b>Zapisz/Przywróć sesję</b> Filter... Filtruj… Open the selected session(s). Otwórz zaznaczone sesje. &Open &Otwórz Del Del Ctrl+Del Ctrl+Del By default, all files that are opened in all windows will be included in the saved session. Domyślnie wszystkie pliki otwarte we wszystkich oknach zostaną uwzględnione w zapisanej sesji. Save only from this &window &Zapisz tylko z tego okna <b>Warning</b> <b>Ostrzeżenie</b> &Yes &Tak &No &Nie Re&name Zmień &nazwę Remove &All &Usuń wszystkie &Close Zam&knij Save the curent session under the given title. Zapisz obecną sesję pod nadanym tytułem. &Save Zapi&sz Type a name to save session Wprowadź nazwę zapisywanej sesji Nothing saved.<br>No file was opened. Nie zapisano niczego.<br>Nie zapisano żadnego pliku. No file exists or can be opened. Plik nie istnieje lub nie może zostać otworzony. Not all files exist or can be opened. Niektóre pliki nie istnieją lub nie udało się ich otworzyć. &OK &OK Do you really want to remove all saved sessions? Czy na pewno chcesz usunąć wszystkie zapisane sesje? Do you really want to remove the selected sessions? Czy na pewno chcesz usunąć wszystkie zaznaczone sesje? Do you really want to remove the selected session? Czy na pewno chcesz usunąć zaznaczoną sesję? A session with the same name exists.<br>Do you want to overwrite it? Sesja o tej nazwie istnieje.<br>Czy chcesz ją zastąpić? FeatherPad::SidePane Filter... Filtruj… FeatherPad::TextEdit Open Link Copy Link Paste Date and Time FeatherPad::WarningBar Close Zamknij FeatherPad-0.8/featherpad/data/translations/featherpad_ru.ts000066400000000000000000002526671325653242400243320ustar00rootroot00000000000000 FeatherPad::AboutDialog License FeatherPad::FPwin Untitled Без названия Go to line: Перейти к строке: Select text from cursor to this line (Ctrl+Shift+J) Выберите текст от курсора до этой строки (Ctrl+Shift+J) Select Text Выберите текст Ctrl+Shift+J &File &Файл Recently &Modified &Недавно изменённый &Edit &Редактировать &Options &Настройки Encoding Кодировка &Search &Поиск &Help &Справка Rep&lacement &Замена Find: Найти: To be replaced Подлежит замене Replace with: Заменить: Replacing text Замена текста F8 F7 F9 &New &Новый New tab Новая вкладка Ctrl+N &Open &Открыть Open a file Открыть файл Ctrl+O &Save &Сохранить Save the current tab Сохранить текущую вкладку Ctrl+S &Undo &Отменить Undo Отменить Ctrl+Z &Redo &Восстановить Redo Восстановить Ctrl+Shift+Z Reload Перезагрузка Ctrl+Shift+R &Find &Найти Ctrl+F Show/hide replacement dock Показать/скрыть заменяющее прикрепление Ctrl+R Sa&ve/Restore Session &Сохранить/Восстановить сеанс Ctrl+Shift+S &Print &Печать Ctrl+P Ctrl+Shift+D &Close &Закрыть Ctrl+Shift+Q &Quit &Выход Ctrl+Q &Cut &Вырезать Ctrl+X Ctrl+C &Paste &Вставить Ctrl+V &Delete &Удалить &Select All &Выбрать все Ctrl+A &Font &Шрифт &Line Numbers &Номер строки Ctrl+L &Wrap Lines &Знаки переноса Ctrl+W &Auto-Indentation &Авто-отступ Ctrl+I &Syntax Highlighting &Подсветка синтаксиса Ctrl+Shift+H &Preferences &Настройки Ctrl+Shift+P Ctrl+H &About &О программе Windows Arabic (&CP1256) Enforce UTF-8 Кодировка UTF-8 &Jump to &Перейти в Show/hide jump bar Показать/скрыть панель перехода Ctrl+J Edit text Редактировать текст Ctrl+Shift+E ISO-&8859-15 Close Ne&xt Tabs &Закрыть следующие вкладки Close &Previous Tabs &Закрыть предыдущие вкладки Close &All Tabs &Закрыть все вкладки Close &Other Tabs &Закрыть другие вкладк Copy File &Path &Путь к файлу копии Ne&xt Tab &Следующая вкладка Previous Ta&b &Предыдущая вкладка &Run &Запустить Ctrl+E &Clear &Очистить Clear the list of recently modified files Очистить список недавно изменённых файлов Save/Restore Session Сохранить/Восстановить сеанс Ctrl+M Side-Pane Ctrl+Alt+P Paste Date and Time Ctrl+Shift+V &Detach Tab &Вставить вкладку Ea&st Asian Save &As &Сохранить как Documen&t Properties &Свойства документа C&opy &Копировать UTF-&16 Cyrillic (&KOI8-U) Кириллица (&KOI8-U) Cyrillic (&ISO-8859-5) Кириллица (&ISO-8859-5) Chinese (&GB18030) Japanese (&ISO-2022-JP-2) Japanese (ISO-&2022-KR) Japa&nese (EUC-JP) K&orean (CP1361) Korean (&EUC-KR) Save with &Encoding Ctrl+T Execute Запустить &Encoding &Кодировка &Unicode &Unicode &Western European &Western European &East European &East European Focus/hide search bar Фокусировка/скрыть панель поиска &Replace &Замена &Other &Другой &UTF-8 &ISO-8859-1 &Windows-1252 &Cyrillic (CP1251) &Кириллица (CP1251) &Chineese (BIG5) &Chineese (BIG5) &Japanese (ISO-2022-JP) &Japanese (ISO-2022-JP) Ja&panese (CP932) &Korean (CP949) &Korean (CP949) &Copy File Name &Имя файла копирования Alt+Right Alt+Left &First Tab &Первая вкладка Alt+Down &Last Tab &Последняя вкладка Alt+Up Menu Меню Calculate number of words (For huge texts, this may be CPU-intensive.) Подсчитать количество слов (для огромных текстов возрастёт нагрузка на ЦПУ) Next Следующий Previous Предыдущий Replace all Заменить все &Recently Opened &Недавно открытый All Все Close Закрыть Save Сохранить Shift+Ins Shift+Del Ctrl+Ins Ctrl+Left Ctrl+Right Ctrl+Up Ctrl+Down Ctrl+Home Ctrl+End Ctrl+K Position: Normal Please attend to that window or just close its dialog! Discard changes Отменить изменения Cancel Отменить No to all Не ко всем Unsaved Несохранённый Sel. Chars Sel. символы Another process is running in this tab! Другой процесс работает на этой вкладке! Only one process is allowed per tab. Только один процесс разрешён на вкладку. Script Output Вывод скрипта Clear Очистить Huge file(s) not opened! FeatherPad does not open files larger than 100 MiB. Some file(s) could not be opened! You may not have the permission to read. Uneditable file(s)! Non-text files or files with huge lines cannot be edited. A previous crash detected! Preferably, close all FeatherPad windows and start again! All Files (*) Все файлы (*) All Files (*);;.%1 Files (*.%1) Все файлы (*);;.%1 Файлы (*.%1) Open file... Открыть файл... Save as... Сохранить как... Yes Да No Нет This file has been modified elsewhere or in another way! Please be careful about reloading or saving this document! Copy Target Path Open Target Here Translators A lightweight, tabbed, plain-text editor Лёгкий, с вкладками, текстовый редактор based on Qt5 на основе Qt5 Author Автор aka. aka. Help Справка Lines Строки Another FeatherPad window has a modal dialog! Другое окно FeatherPad имеет модальный диалог! Ctrl+= Ctrl++ Ctrl+- Ctrl+0 F11 Ctrl+Shift+W Ctrl+Alt+E F3 F4 F5 F6 Shift+Enter Ctrl+Tab Ctrl+Meta+Tab Save changes? Сохранить изменения? The document has been modified. Документ был изменён. The file has been removed. Файл удалён. Words Слова Script File Файл сценария .%1 Files (*.%1);;All Files (*) .%1 Файлы (*.%1);;Все файлы (*) Keep encoding and save as... Do you want to use <b>MS Windows</b> end-of-lines? Вы хотите использовать <b>MS Windows</b> конец строк? This may be good for readability under MS Windows. Это может быть полезно для удобочитаемости под MS Windows. Cannot be saved! Не удаётся сохранить! Syntax Синтаксис Print Document Печать документа About FeatherPad О FeatherPad No Replacement Нет замены One Replacement Одна замена Replacements Замены FeatherPad::FileDialog Ctrl+H Toggle showing hidden files Переключить отображение скрытых файлов Alt+. Toggle showing hidden files Переключить отображение скрытых файлов FeatherPad::LineEdit Clear text (Ctrl+K) Очистить текст (Ctrl+K) Ctrl+K Clear text Очистить текст Ctrl+K FeatherPad::PrefDialog Preferences Предпочтения Window Окно Window Settings Настройки окна Window frame is excluded. Рамка окна исключена. Start with this size: Запуск с этого размера: px × Most suitable with sessions but without tab functionality. Uncheck for 1/5 of the width. Remember splitter position No icons in the main window and its menus. KDE may have a bug that disables search and replace shortcuts with the iconless mode. Needs application restart to take effect. Отключение иконок в главном окне и меню. У KDE может быть ошибка в виде отключения поиска и замены ярлыков в режиме без иконок. Требуется перезапуск приложения для вступления в силу. &Iconless mode &Режим без иконок If the menubar is hidden, a menu button appears on the toolbar. Если панель меню скрыта, на панели инструментов появляется кнопка меню. Show cursor position on status bar Will take effect after closing this dialog. Вступит в силу после закрытия этого диалогового окна. Tab position: Позиция табуляции: North Север South Юг West Запад East Восток This means that, for LTR, Alt+Right goes to the first tab after the last tab is activated, and the same for Alt+Left. Tab navigation with mouse wheel is not affected. Подразумевается, что сочетание Alt+Right перекинет на крайнюю левую вкладку с крайней правой, и наоборот для Alt+Left. На навигацию колесом мыши не повлияет. By default, if a FeatherPad window exists on the current desktop, files will be opened in its tabs. However, some desktop environments may not report that they have multiple desktops. Always open in separate windows If this is checked, the file dialog provided by the current desktop environment will be used instead of the Qt file dialog. Some desktop environments, like KDE and LXQt, provide files dialogs. Native file dialog Uncheck for Monospace 9. Снимите флажок для Monospace 9. This covers parentheses, braces, brackets and quotes. Auto-&bracket This creates a menu button on the status bar for changing the syntax. Support syntax override Show spaces, tabs and tab lines when the syntax is highlighted. Show whitespaces The vertical position lines will be drawn only if the editor font has a fixed pitch (like Monospace). Show vertical lines starting from this position: Also show line and document ends Date and time format: Some text editors cannot open a document whose last line is not empty. Некоторые текстовые редакторы не могут открыть документ, последняя строка которого не пуста. Ensure an empty last line on saving Убедитесь, что пустая последняя строка при сохранении Remove trailing spaces on saving Inertial scrolling with mouse wheel Files Файл File Management Управление файлами This can be any starting command with arguments, for example, "xterm -hold" for running the process in XTerm. If the command is left empty, the file will be executed directly. If the script is not run in a terminal emulator, the output and error messages will be shown by a popup dialog. Это может быть любая стартовая команда с аргументами, например, "xterm -hold" для запуска процесса в XTerm. Если команда оставлена пустой, файл будет выполнен непосредственно. Если сценарий не запущен в эмуляторе терминала, сообщения вывода и ошибки будут отображаться во всплывающем диалоговом окне. Start with this command: Запуск с этой команды: Command + Arguments Команда + Аргументы Only for files that exist and can be saved. min Shortcuts Action Shortcut Restore default shortcuts. Default Help Справка Uncheck this if you want FeatherPad to use system icons! Needs application restart to take effect. Снимите этот флажок, если вы хотите, чтобы FeatherPad использовал значки системы! Требуется перезапуск приложения для вступления в силу. Remember window &size on closing &Запомнить размер окна при закрытии Start with side-pane mode &Use own icons &Использовать собственные значки Do not show &toolbar &Не показывать панель инструментов Do not show &menubar &Не показывать панель меню Hide search &bar by default &Скрыть панель поиска по умолчанию Always show st&atus bar &Всегда показывать строку состояния Tab navigation wraps &around &Повторить навигацию по вкладкам If this is checked, not only you will lose the informative tooltip and context menu of a single tab but you could not merge a single tabbed window into another one by tab drag- and-drop either. Если этот флажок установлен, пропадут не только подсказки и контекстное меню вкладки, но и нельзя будет объединить одно окно с вкладками с другим с помощью перетаскивания вкладки. &Do not show a single tab &Не показывать единственную вкладку Text Текст Text Editor Текстовый редактор Remember &font &Запомнить шрифт &Wrap lines by default &Выделение линий по умолчанию Background color value: Значение цвета фона: MiB This is not a complete fix but prevents annoying scroll jumps. Это не полное исправление, но предотвращает раздражающие скачки прокрутки. This only includes executable files written in script languages like Shell and Python. If it is checked and the opened file is executable, the file will be executed by clicking the Run button, that appears on the toolbar/filemenu when needed, or by its shortcut Ctrl+E. Then, the process could be killed by Ctrl+Alt+E. Это включает только исполняемые файлы, написанные на языках сценариев, таких как Shell и Python. Если это отмечено, и открытый файл исполняемый, то он будет запущен нажатием кнопки «Запустить», которая появляется на панели инструментов в меню файла, когда потребуется, или комбинацией Ctrl+E. Затем процесс можно завершить на Ctrl+Alt+E. Needs window reopening to take effect. Требуется ещё раз открыть окно. The color value of the background. 255 means white while 0 means black. For the light background, it can be between 230 and 255; for the dark background, between 0 and 50. Needs window reopening to take effect. Значение цвета фона. 255 означает белый, а 0 означает чёрный. Для светлого фона оно может быть между 230 и 255; Для тёмного фона - от 0 до 50. Требуется ещё раз открыть окно. Run executable scripts Запуск исполняемых скриптов The maximum number of recently modified or opened files FeatherPad shows. The default is 10. Needs application restart to take effect. Отображается максимальное количество недавно изменённых или открытых файлов FeatherPad. Значение по умолчанию - 10. Требуется перезапуск приложения. Auto-&indent by default &Автоотступ по умолчанию Always show line &numbers &Всегда показывать номера строк Highlight s&yntax by default &Выделить синтаксис по умолчанию Never highlight syntax for files > Никогда не выделять синтаксис для файлов > Dark c&olor scheme &Тёмная цветовая схема Used for pasting the date and time. Takes effect after closing this dialog. Leave empty for: MMM dd, yyyy, hh:mm:ss Should the mouse wheel scrolling be inertial if the cursor is inside the text view? Workaround for &Qt5's scroll jump bug &Устранение дёрганья при прокрутке на Qt5 Number of recent files: Количество последних файлов: Show recentl&y modified files &Показать недавно изменённые файлы Show recentl&y opened files &Показать недавно открытые файлы The maximum number of recently modified or opened files FeatherPad should open with an empty startup of a session. Открывать максимальное количество недавно изменённых или открытых в FeatherPad файлов, если при запуске не выбраны другие. Start with recent files: Начать с последних файлов: Save changes to opened files every: Close Закрыть files файлы file файл No file Нет файла Warning: Ambiguous shortcut detected! Application restart is needed for changes to take effect. Чтобы изменения вступили в силу, необходим перезапуск приложения. Window reopening is needed for changes to take effect. Для того, чтобы изменения вступили в силу, требуется повторное открытие окна. &Recently Opened &Недавно открытые Recently &Modified &Недавно изменённые The typed shortcut was not valid. The typed shortcut was reserved. FeatherPad::SearchBar Search... Поиск... Match Case Учитывать регистр Whole Word Целое слово Next Следующий Previous Предыдущий FeatherPad::SessionDialog Session Manager Менеджер сеансов &Remove &Удалить <b>Save/Restore Session</b> <b>Сохранить/Восстановить сеанс</b> Filter... Open the selected session(s). Открыть выбранный сеанс(ы).. &Open &Открыть Del Ctrl+Del Remove &All &Убрать все By default, all files that are opened in all windows will be included in the saved session. Save only from this &window <b>Warning</b> <b>Предупреждение</b> &Yes &Да &No &Нет Re&name &Переименовать &Close &Закрыть Save the curent session under the given title. Сохранить текущий сеанс под данным заголовком. &Save &Сохранить Type a name to save session Введите имя для сохранения сеанса Nothing saved.<br>No file was opened. Ничего не сохранено.<br>Файл не был открыт. No file exists or can be opened. Файл не существует или не может быть открыт. Not all files exist or can be opened. Не все файлы существуют или могут быть открыты. &OK &OK Do you really want to remove all saved sessions? Вы действительно хотите удалить все сохраненные сеансы? Do you really want to remove the selected sessions? Вы действительно хотите удалить выбранные сеансы? Do you really want to remove the selected session? Вы действительно хотите удалить выбранный сеанс? A session with the same name exists.<br>Do you want to overwrite it? Существует сессия с тем же именем.<br>Вы хотите перезаписать её? FeatherPad::SidePane Filter... FeatherPad::TextEdit Open Link Copy Link Paste Date and Time FeatherPad::WarningBar Close Закрыть FeatherPad-0.8/featherpad/data/translations/featherpad_zh_CN.ts000066400000000000000000002423121325653242400246670ustar00rootroot00000000000000 FeatherPad::AboutDialog License FeatherPad::FPwin Untitled 无标题 Go to line: 转到行: Select text from cursor to this line (Ctrl+Shift+J) 选择从光标至此行的文本 (Ctrl+Shift+J) Select Text 选择文本 Ctrl+Shift+J &File 文件(&F) &Edit 编辑(&E) &Options 选项(&O) Encoding 编码 &Search 搜索(&S) &Help 帮助(&H) Find: 查找: To be replaced 要替换的文本 Replace with: 替换为: Replacing text 替换文本 F8 F7 F9 &New 新建(&N) New tab 新建标签 Ctrl+N &Open 打开(&O) Open a file 打开文件 Ctrl+O &Save 保存(&S) Save the current tab 保存当前标签 Ctrl+S &Undo 撤销(&U) Undo 撤销 Ctrl+Z &Redo 重做(&R) Redo 重做 Ctrl+Shift+Z Reload 刷新 Ctrl+Shift+R &Find 查找(&F) Ctrl+F Show/hide replacement dock 显示/隐藏替换工具栏 Ctrl+R Save &As 另存为(&A) Ctrl+Shift+S &Print 打印(&P) Ctrl+P Documen&t Properties 文档属性(&T) Ctrl+Shift+D &Close 关闭(&C) Ctrl+Shift+Q &Quit 退出(&Q) Ctrl+Q &Cut 剪切(&C) Ctrl+X C&opy 复制(&O) Ctrl+C &Paste 粘贴(&P) Ctrl+V &Delete 删除(&D) &Select All 全选(&S) Ctrl+A &Font 字体(&F) &Line Numbers 行数(&L) Ctrl+L &Wrap Lines 自动换行(&W) Ctrl+W &Auto-Indentation 自动缩进(&A) Ctrl+I &Syntax Highlighting 语法高亮(&S) Ctrl+Shift+H &Preferences 首选项(&P) Ctrl+Shift+P Ctrl+H &About 关于(&A) Enforce UTF-8 强制使用 UTF-8 Save with &Encoding 保存为编码(&E) &Jump to 跳转至(&J) Show/hide jump bar 显示/隐藏跳转栏 Ctrl+J Edit text 编辑文本 Ctrl+Shift+E &Run 运行(&R) Ctrl+E &Clear 清除(&C) Clear the list of recently modified files 清除最近更改的文件 Save/Restore Session 保存/恢复会话 Sa&ve/Restore Session 保存/恢复会话(&V) Ctrl+M Side-Pane 侧边栏 Ctrl+Alt+P Paste Date and Time 粘贴日期与时间 Ctrl+Shift+V &Detach Tab 分离标签(&D) Ctrl+T Close Ne&xt Tabs 关闭右侧标签(&X) Close &Previous Tabs 关闭左侧标签(&P) Ne&xt Tab 下一个标签(&X) Previous Ta&b 上一个标签(&B) Execute 执行 Close &All Tabs 关闭所有标签(&A) Recently &Modified 最近更改的文件(&M) &Encoding 编码(&E) &Unicode &Western European 西欧(&W) &East European 东欧(&E) Ea&st Asian 东亚(&S) Rep&lacement 替换(&L) Focus/hide search bar 显示/隐藏搜索栏 &Replace 替换(&R) Windows Arabic (&CP1256) Windows 阿拉伯语(&CP1256) &Other 其它(&O) &UTF-8 UTF-&16 &ISO-8859-1 &Windows-1252 &Cyrillic (CP1251) 西里尔文(CP1251)(&C) Cyrillic (&KOI8-U) 西里尔文(&KOI8-U) Cyrillic (&ISO-8859-5) 西里尔文(&ISO-8859-5) &Chineese (BIG5) 繁体中文(大五码)(&C) Chinese (&GB18030) 简体中文(&GB18030) &Japanese (ISO-2022-JP) 日文(ISO-2022-JP)(&J) Japanese (&ISO-2022-JP-2) 日文(&ISO-2022-JP-2) Japanese (ISO-&2022-KR) 日文(ISO-&2022-KR) Ja&panese (CP932) 日文(CP932)(&P) Japa&nese (EUC-JP) 日文(EUC-JP)(&N) &Korean (CP949) 韩文(CP949)(&K) K&orean (CP1361) 韩文(CP1361)(&O) Korean (&EUC-KR) 韩文(&EUC-KR) ISO-&8859-15 Close &Other Tabs 关闭其它标签(&O) &Copy File Name 复制文件名(&C) Copy File &Path 复制文件路径(&P) Alt+Right Alt+右方向键 Alt+Left Alt+左方向键 &First Tab 第一个标签(&F) Alt+Down Alt+下方向键 &Last Tab 最后一个标签(&L) Alt+Up Alt+上方向键 Menu 菜单 Calculate number of words (For huge texts, this may be CPU-intensive.) 统计字数 (对于大量文本,这可能导致 CPU 缓慢。) Next 下一个 Previous 上一个 Replace all 替换所有 &Recently Opened 最近打开的文件(&R) All 所有 Close 关闭 Save changes? 是否保存更改? Please attend to that window or just close its dialog! 请转到那个窗口或关闭其对话框! The document has been modified. 文档已更改。 The file has been removed. 文件已被删除。 Save 保存 Discard changes 抛弃更改 Cancel 取消 No to all 全不 Unsaved 未保存 Lines Sel. Chars 选区 Words Another process is running in this tab! 另外的进程正在此标签内运行! Script File 脚本文件 Another FeatherPad window has a modal dialog! 另外的 FeatherPad 窗口弹出了对话框! Ctrl+= Ctrl++ Ctrl+- Ctrl+0 F11 Ctrl+Shift+W Ctrl+Alt+E Shift+Ins Shift+Del Ctrl+Ins Ctrl+Left Ctrl+Right Ctrl+Up Ctrl+Down Ctrl+Home Ctrl+End F3 F4 F5 F6 Shift+Enter Ctrl+Tab Ctrl+Meta+Tab Ctrl+K Position: 位置: Normal 常规 Only one process is allowed per tab. 每个标签只允许一个进程。 Script Output 脚本输出 Clear 清除 FeatherPad does not open files larger than 100 MiB. FeatherPad 不能打开大于 100 MiB 的文件。 Some file(s) could not be opened! 无法打开某些文件! You may not have the permission to read. 您可能没有读取权限。 Uneditable file(s)! 文件不可编辑! Non-text files or files with huge lines cannot be edited. 无法编辑非文本文件或行数太多的文件。 A previous crash detected! 检测到前次崩溃! Preferably, close all FeatherPad windows and start again! 建议关闭所有 FeatherPad 窗口并重新启动本程序! All Files (*) 所有文件(*) All Files (*);;.%1 Files (*.%1) 所有文件(*);;.%1文件(*.%1) Open file... 打开文件... .%1 Files (*.%1);;All Files (*) .%1文件(*.%1);;所有文件(*) Save as... 另存为... Keep encoding and save as... 保持编码并另存为... Yes No Do you want to use <b>MS Windows</b> end-of-lines? 您是否想使用 <b>Windows</b> 结束行? This may be good for readability under MS Windows. 这可能使之适合在 Windows 下阅读。 Cannot be saved! 无法保存! Help 帮助 Syntax 语法 Huge file(s) not opened! 未打开大文件! This file has been modified elsewhere or in another way! 此文件已在他处或以其他方式被修改! Please be careful about reloading or saving this document! 请小心刷新或保存此文档! Print Document 打印文档 Copy Target Path 复制目标路径 Open Target Here 在此打开目标 Translators A lightweight, tabbed, plain-text editor 轻量级标签式纯文本编辑器 based on Qt5 基于 Qt5 Author 作者 aka. About FeatherPad 关于 FeatherPad No Replacement 无替换 One Replacement 一次替换 Replacements 次替换 FeatherPad::FileDialog Ctrl+H Toggle showing hidden files Alt+. Toggle showing hidden files FeatherPad::LineEdit Clear text (Ctrl+K) 清除文本(Ctrl+K) Ctrl+K Clear text FeatherPad::PrefDialog Preferences 首选项 Window 窗口 Window Settings 窗口设置 Remember window &size on closing 关闭时保存窗口大小(&S) Window frame is excluded. 不包括窗口框架。 Start with this size: 默认尺寸: px × Most suitable with sessions but without tab functionality. 最适合会话,但没有标签功能。 Uncheck for 1/5 of the width. 如取消勾选,则侧边栏宽度为窗口宽度的五分之一。 Remember splitter position 记住分割位置 No icons in the main window and its menus. KDE may have a bug that disables search and replace shortcuts with the iconless mode. Needs application restart to take effect. 主窗口与其菜单无图标。 如您在 KDE 下运行,则在无图标模式下 搜索与替换快捷方式可能被禁用。 需要重新启动应用程序以生效。 &Iconless mode 无图标模式(&I) Do not show &toolbar 隐藏工具栏(&T) If the menubar is hidden, a menu button appears on the toolbar. 如果菜单栏被隐藏,则工具栏上会出现一个菜单按钮。 Do not show &menubar 隐藏菜单栏(&M) Hide search &bar by default 默认隐藏搜索栏(&B) Always show st&atus bar 总是显示状态栏(&A) Show cursor position on status bar 在状态栏上显示光标位置 Will take effect after closing this dialog. 关闭此对话框后生效。 Tab position: 标签位置: North South West 西 East This means that, for LTR, Alt+Right goes to the first tab after the last tab is activated, and the same for Alt+Left. Tab navigation with mouse wheel is not affected. 意即对于从左到右的语言环境,Alt+Right 在最后一个 标签被激活后转至第一个标签,Alt+Left 同理。 此选项不影响鼠标滚轮控制的标签导航。 Tab navigation wraps &around 标签导航循环(&A) By default, if a FeatherPad window exists on the current desktop, files will be opened in its tabs. However, some desktop environments may not report that they have multiple desktops. 默认情况下,如当前桌面存在一个 FeatherPad 窗口,则文件会默认打开于其内。 但某些桌面环境可能不会报告其拥有多个桌面。 Always open in separate windows 总是在分离窗口打开文件 If this is checked, the file dialog provided by the current desktop environment will be used instead of the Qt file dialog. Some desktop environments, like KDE and LXQt, provide files dialogs. 如勾选,则将使用当前桌面环境提供的文件对话框,而不使用 Qt 的文件对话框。 某些桌面环境,如 KDE 与 LXQt,提供了自己的文件对话框。 Native file dialog 原生文件对话框 Uncheck for Monospace 9. 如不勾选,则字体默认为 Monospace 9。 Remember &font 记住字体(&F) This covers parentheses, braces, brackets and quotes. 包括圆括号、方括号、大括号与引号。 Auto-&bracket 自动补全括号(&B) Never highlight syntax for files > 禁用语法高亮于文件大小 > This creates a menu button on the status bar for changing the syntax. 如勾选,则将在状态栏上创建一个按钮,用于更改语法。 Support syntax override 支持语法重写 Show spaces, tabs and tab lines when the syntax is highlighted. 语法高亮时显示空格、缩进与缩进行。 Show whitespaces 用语法高亮显示空格 The vertical position lines will be drawn only if the editor font has a fixed pitch (like Monospace). 垂直位置行只会在编辑器字体点数固定时绘制。 Show vertical lines starting from this position: 显示从此位置开始的竖行: Also show line and document ends 也显示行与文档结尾 Date and time format: 日期与时间格式: Some text editors cannot open a document whose last line is not empty. 某些文本编辑器只能打开最后一行为空的文档。 Ensure an empty last line on saving 确保保存时在文档末尾留下一行空白 Remove trailing spaces on saving 保存时移除结尾空白 Inertial scrolling with mouse wheel 鼠标滚论滚动时带惯性 Files 文件 File Management 文件管理 This can be any starting command with arguments, for example, "xterm -hold" for running the process in XTerm. If the command is left empty, the file will be executed directly. If the script is not run in a terminal emulator, the output and error messages will be shown by a popup dialog. 此处可以是任何带参数的运行命令。如 “xterm -hold”为在 XTerm 中运行进程。 如留空,则文件会被直接执行。 如脚本不在终端模拟器中运行,则输出 与错误信息会在弹出的对话框中显示。 Start with this command: 以此命令运行脚本: Command + Arguments 命令 + 参数 Show recentl&y modified files 显示最近更改的文件(&Y) Show recentl&y opened files 显示最近打开的文件(&Y) Save changes to opened files every: 自动保存时间间隔: Help 帮助 Uncheck this if you want FeatherPad to use system icons! Needs application restart to take effect. 如要使用系统图标,则请取消勾选此选项! 需要重新启动本应用程序以生效。 Start with side-pane mode 以侧边栏模式启动 &Use own icons 使用系统图标(&U) If this is checked, not only you will lose the informative tooltip and context menu of a single tab but you could not merge a single tabbed window into another one by tab drag- and-drop either. 如勾选,您不仅会丢失单标签的工具提示与上下文菜单, 还会导致不能通过拖放将单标签窗口合并至另一个窗口。 &Do not show a single tab 不显示单个标签(&D) Text 文本 Text Editor 文本编辑器 &Wrap lines by default 默认自动换行(&W) Auto-&indent by default 默认自动缩进(&I) Always show line &numbers 总是显示行数(&N) Highlight s&yntax by default 默认语法高亮(&Y) Dark c&olor scheme 暗色主题(&O) Background color value: 背景颜色值: MiB This is not a complete fix but prevents annoying scroll jumps. 此修复并不完善,但避免了烦人的滚动跳转。 Workaround for &Qt5's scroll jump bug 修复 &Qt5 滚动时跳转错误 This only includes executable files written in script languages like Shell and Python. If it is checked and the opened file is executable, the file will be executed by clicking the Run button, that appears on the toolbar/filemenu when needed, or by its shortcut Ctrl+E. Then, the process could be killed by Ctrl+Alt+E. 仅适用于以脚本语言,如 Shell 或 Python 所写的可执行文件。 如勾选,且已打开的文件可以执行,则 可以通过单击“运行”按钮执行文件,此 按钮位于工具栏或文件菜单上。 您也可以通过按下 Ctrl+E 运行脚本, Ctrl+Alt+E 结束脚本运行。 Needs window reopening to take effect. 需要重新开启窗口以生效。 The color value of the background. 255 means white while 0 means black. For the light background, it can be between 230 and 255; for the dark background, between 0 and 50. Needs window reopening to take effect. 255 为白,0 为黑。 对于亮色背景,其值需在 230 与 255 之间。 对于暗色背景,其值需在 0 与 50 之间。 需要重新启动应用程序以生效。 Run executable scripts 运行可执行脚本 The maximum number of recently modified or opened files FeatherPad shows. The default is 10. Needs application restart to take effect. 指定 FeatherPad 显示最近更改 或最近打开的文件的最大数量。 默认为 10。 需要重新启动应用程序以生效。 Used for pasting the date and time. Takes effect after closing this dialog. Leave empty for: MMM dd, yyyy, hh:mm:ss 用于粘贴日期与时间,在关闭此对话框后生效。 默认格式为:MMM dd, yyyy, hh:mm:ss Should the mouse wheel scrolling be inertial if the cursor is inside the text view? 如光标在文本视图内,鼠标滚轮滚动是否应带惯性? Number of recent files: 最近文件的数量: The maximum number of recently modified or opened files FeatherPad should open with an empty startup of a session. 指定 FeatherPad 以空会话启动时应打开的 最近更改或最近打开的文件的数量。 Start with recent files: 启动时打开最近文件: Only for files that exist and can be saved. 仅对于存在且可被保存的文件。 min 分钟 Shortcuts 快捷方式 Action 动作 Shortcut 快捷方式 Restore default shortcuts. 恢复默认快捷方式。 Default 默认 Close 关闭 files 文件 file 文件 No file 无文件 Warning: Ambiguous shortcut detected! 警告:检测到不明确的快捷方式! Application restart is needed for changes to take effect. 需要重新启动应用程序以使更改生效。 Window reopening is needed for changes to take effect. 需要重新开启窗口以生效。 &Recently Opened 最近打开的文件(&R) Recently &Modified 最近更改的文件(&M) The typed shortcut was not valid. 输入的快捷方式无效。 The typed shortcut was reserved. 输入的快捷方式已被保留。 FeatherPad::SearchBar Search... 搜索... Match Case 区分大小写 Whole Word 全字匹配 Next 下一个 Previous 上一个 FeatherPad::SessionDialog Session Manager 会话管理器 &Remove 移除(&R) <b>Save/Restore Session</b> <b>保存/恢复会话</b> Filter... 过滤器... Open the selected session(s). 打开选中的会话。 &Open 打开(&O) Del Ctrl+Del By default, all files that are opened in all windows will be included in the saved session. 默认情况下,已保存的会话 包含所有已打开的文件。 Save only from this &window 只从本窗口保存(&W) <b>Warning</b> <b>警告</b> &Yes 是(&Y) &No 否(&N) Re&name 重命名(&N) Remove &All 移除所有(&A) &Close 关闭(&C) Save the curent session under the given title. 以指定标题保存会话。 &Save 保存(&S) Type a name to save session 输入名称以保存会话 Nothing saved.<br>No file was opened. 未保存任何文件。<br>未打开任何文件。 No file exists or can be opened. 没有可以打开的文件。 Not all files exist or can be opened. 某些文件不存在或无法打开。 &OK 确定(&O) Do you really want to remove all saved sessions? 您确定要移除所有已保存的会话吗? Do you really want to remove the selected sessions? 您确定要移除选中的会话吗? Do you really want to remove the selected session? 您确定要移除选中的会话吗? A session with the same name exists.<br>Do you want to overwrite it? 已有同名会话。<br>是否覆盖? FeatherPad::SidePane Filter... 过滤器... FeatherPad::TextEdit Open Link 打开链接 Copy Link 复制链接 Paste Date and Time 粘贴日期与时间 FeatherPad::WarningBar Close 关闭 FeatherPad-0.8/featherpad/encoding.cpp000066400000000000000000000517631325653242400200030ustar00rootroot00000000000000/* * Copyright (C) Pedram Pourang (aka Tsu Jan) 2014 * * FeatherPad 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 2 of the License, or * (at your option) any later version. * * FeatherPad is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * @license GPL-2.0+ */ /* This file was adapted from * Leafpad - GTK+ based simple text editor * File: src/encoding.c * Copyright: 2004-2005 Tarot Osuji * License: GPL-2.0+ * Homepage: http://tarot.freeshell.org/leafpad/ */ #include // getenv #include // CODESET, nl_langinfo #include // uint8_t, uint32_t #include // needed by FreeBSD for setlocale #include "encoding.h" namespace FeatherPad { #define MAX_COUNTRY_NUM 10 enum { IANA = 0, OPENI18N, CODEPAGE, ENCODING_MAX_ITEM_NUM }; /*------------------------*/ /* encoding numbers */ enum { LATIN1 = 0, LATIN2, LATIN3, LATIN4, LATINC, LATINC_UA, LATINC_TJ, LATINA, LATING, LATINH, LATIN5, CHINESE_CN, CHINESE_TW, CHINESE_HK, JAPANESE, KOREAN, VIETNAMESE, THAI, GEORGIAN, TOTAL_NUM // 19 }; /*------------------------*/ static const std::string countryTable[TOTAL_NUM][MAX_COUNTRY_NUM] = { /* list of countries using each encoding set */ /* LATIN1 */ {"" , "", "", "", "", "", "", "", "", "" }, /* LATIN2 */ {"cs", "hr", "hu", "pl", "ro", "sk", "sl", "sq", "sr", "uz"}, /* LATIN3 */ {"eo", "mt", "", "", "", "", "", "", "", "" }, /* LATIN4 */ {"et", "lt", "lv", "mi", "", "", "", "", "", "" }, /* LATINC */ {"be", "bg", "ky", "mk", "mn", "ru", "tt", "", "", "" }, /* LATINC_UA */ {"uk", "", "", "", "", "", "", "", "", "" }, /* LATINC_TJ */ {"tg", "", "", "", "", "", "", "", "", "" }, /* LATINA */ {"ar", "fa", "ur", "", "", "", "", "", "", "" }, /* LATING */ {"el", "", "", "", "", "", "", "", "", "" }, /* LATINH */ {"he", "yi", "", "", "", "", "", "", "", "" }, /* LATIN5 */ {"az", "tr", "", "", "", "", "", "", "", "" }, /* CHINESE_CN */ {"zh_CN", "", "", "", "", "", "", "", "", "" }, /* CHINESE_TW */ {"zh_TW", "", "", "", "", "", "", "", "", "" }, /* CHINESE_HK */ {"zh_HK", "", "", "", "", "", "", "", "", "" }, /* JAPANESE */ {"ja", "", "", "", "", "", "", "", "", "" }, /* KOREAN */ {"ko", "", "", "", "", "", "", "", "", "" }, /* VIETNAMESE */ {"vi", "", "", "", "", "", "", "", "", "" }, /* THAI */ {"th", "", "", "", "", "", "", "", "", "" }, /* GEORGIAN */ {"ka", "", "", "", "", "", "", "", "", "" } }; /*------------------------*/ static const std::string encodingTable[TOTAL_NUM][ENCODING_MAX_ITEM_NUM] = { /* IANA OpenI18N CODEPAGE */ /* LATIN1 */ { "ISO-8859-1", "ISO-8859-15", "CP1252" }, /* LATIN2 */ { "ISO-8859-2", "ISO-8859-16", "CP1250" }, /* LATIN3 */ { "ISO-8859-3", "", "" }, /* LATIN4 */ { "ISO-8859-4", "ISO-8859-13", "CP1257" }, /* LATINC */ { "ISO-8859-5", "KOI8-R", "CP1251" }, /* LATINC_UA */ { "ISO-8859-5", "KOI8-U", "CP1251" }, /* LATINC_TJ */ { "ISO-8859-5", "KOI8-T", "CP1251" }, /* LATINA */ { "ISO-8859-6", "", "CP1256" }, /* LATING */ { "ISO-8859-7", "", "CP1253" }, /* LATINH */ { "ISO-8859-8", "", "CP1255" }, /* LATIN5 */ { "ISO-8859-9", "", "CP1254" }, /* CHINESE_CN */ { "GB2312", "GB18030", "CP936" }, /* CHINESE_TW */ { "BIG5", "EUC-TW", "CP950" }, /* CHINESE_HK */ { "BIG5", "BIG5-HKSCS", "CP950" }, /* JAPANESE */ { "ISO-2022-JP", "EUC-JP", "CP932" }, /* KOREAN */ { "ISO-2022-KR", "EUC-KR", "CP949" }, /* VIETNAMESE */ { "", "VISCII", "CP1258" }, /* THAI */ { "", "TIS-620", "CP874" }, /* GEORGIAN */ { "", "GEORGIAN-PS", "" } }; /*************************/ static unsigned int getLocaleNum() { static unsigned int code = 0; // for me std::string env; const char *lcAll = getenv ("LC_ALL"); const char *lang = getenv ("LANG"); int j = 1; if (lcAll) env = lcAll; if (env.empty() && lang != NULL) /* if there's no value for all locales, get the native language, e.g. en_US.utf8*/ env = lang; if (!env.empty() && env.length() >= 2) while (code == 0 && j < TOTAL_NUM) { for (int i = 0; i < MAX_COUNTRY_NUM; i++) { if (countryTable[j][i].empty()) break; if (env.compare (0, countryTable[j][i].length(), countryTable[j][i]) == 0) { code = j; break; } } j++; } return code; } /*************************/ static unsigned int localeNum = getLocaleNum(); static const std::string encodingItem[ENCODING_MAX_ITEM_NUM] = {encodingTable[localeNum][0], encodingTable[localeNum][1], encodingTable[localeNum][2]}; /*************************/ static const std::string detectCharsetLatin (const char *text) { uint8_t c = *text; bool noniso = false; bool noniso15 = false; uint32_t xl = 0, xa = 0, xac = 0, xcC = 0, xcC1 = 0, xcS = 0, xcna = 0; /* the OpenI18N for the locale ("ISO-8859-15" for me) */ std::string charset = encodingItem[OPENI18N]; while (c != '\0') { if (c >= 0x41 && c <= 0x7A) /* ordinary Latin letters */ xl ++; else if (c >= 0x80 && c <= 0x9F) { noniso = true; } else if (c >= 0xC0) { /* Arabic or Cyrillic letters */ xac ++; if (c >= 0xC0 && c <= 0xCF) /* Cyrillic capital letters */ xcC++; else if (c >= 0xD0 && c <= 0xDF) { /* Cyrillic capital letters again */ xcC1++; if (c == 0xDE || c == 0xDF) /* not used in ISO-8859-15 (Icelandic or German) */ noniso15 = true; } else if (c >= 0xE0) { /* Cyrillic small letters */ xcS++; /* Cyrillic but not Arabic letters */ if (c == 0xE0 || c == 0xE2 || (c >= 0xE7 && c <= 0xEB) || c == 0xEE || c == 0xEF || c == 0xF4 || c == 0xF9 || c == 0xFB || c == 0xFC) xcna ++; /* Arabic LAM to HEH */ else if (c == 0xE1 || c == 0xE3 || c == 0xE4 || c == 0xE5 || c == 0xE6) xa++; } } c = *text++; } /* when there is a difference fom ISO-8859-1 and ISO-8859-15, and ordinary Latin letters are more than Arabic ones, and the text isn't Cyrillic KOI8-U */ if (noniso && xl >= xac && (xcC1 + xcS >= xcC || xcna == 0)) charset = "CP1252"; // Windows-1252 else // if (xl < xac) { if (!noniso && xcC + xcS < xcC1) charset = "ISO-8859-15"; // FIXME: ISO-8859-5 ? /* this is very tricky and I added it later */ else if (!noniso && xcC + xcC1 + xa >= xcS - xa && !(xcC1 + xcS < xcC && xcna > 0)) charset = "ISO-8859-1"; else if (xcC + xcC1 < xcS && xcna > 0) { if (noniso || noniso15) // FIXME: this is very inefficient charset = "CP1251"; // Cyrillic-1251 else charset = "ISO-8859-15"; } else if (xcC1 + xcS < xcC && xcna > 0) charset = "KOI8-U"; // Cyrillic-KOI /* this should cover most cases */ else if (noniso || xcC + xcC1 + xa >= xcS - xa) charset = "CP1256"; // MS Windows Arabic } return charset; } /*************************/ static const std::string detectCharsetCyrillic (const char *text) { uint8_t c = *text; bool noniso = false; uint32_t xl = 0, xa = 0, xac = 0, xcC = 0, xcC1 = 0, xcS = 0, xcna = 0; std::string charset = encodingItem[OPENI18N]; while (c != '\0') { if (c >= 0x41 && c <= 0x7A) xl ++; if (c >= 0x80 && c <= 0x9F) noniso = true; else if (c >= 0xC0) { xac ++; if (c >= 0xC0 && c <= 0xCF) xcC++; else if (c >= 0xD0 && c <= 0xDF) xcC1++; else if (c >= 0xE0) { xcS++; if (c == 0xE0 || c == 0xE2 || (c >= 0xE7 && c <= 0xEB) || c == 0xEE || c == 0xEF || c == 0xF4 || c == 0xF9 || c == 0xFB || c == 0xFC) xcna ++; else if (c == 0xE1 || c == 0xE3 || c == 0xE4 || c == 0xE5 || c == 0xE6) xa++; } } c = *text++; } if (xl < xac) { if (!noniso && (xcC + xcS < xcC1)) charset = "ISO-8859-5"; else if (xcC + xcC1 < xcS && xcna > 0) charset = "CP1251"; else if (xcC1 + xcS < xcC && xcna > 0) charset = "KOI8-U"; else if (noniso || xcC + xcC1 + xa >= xcS - xa) charset = "CP1256"; } return charset; } /*************************/ static const std::string detectCharsetWinArabic (const char *text) { uint8_t c = *text; uint32_t xl = 0, xa = 0; std::string charset = encodingItem[IANA]; while (c != '\0') { if (c >= 0x41 && c <= 0x7A) xl ++; else if (c >= 0xC0) xa ++; c = *text++; } if (xl < xa) charset = "CP1256"; return charset; } /*************************/ static const std::string detectCharsetChinese (const char *text) { uint8_t c = *text; std::string charset = encodingItem[IANA]; while ((c = *text++) != '\0') { if (c >= 0x81 && c <= 0x87) { charset = "GB18030"; break; } else if (c >= 0x88 && c <= 0xA0) { c = *text++; if ((c >= 0x30 && c <= 0x39) || (c >= 0x80 && c <= 0xA0)) { charset = "GB18030"; break; } //else GBK/Big5-HKSCS cannot determine } else if ((c >= 0xA1 && c <= 0xC6) || (c >= 0xC9 && c <= 0xF9)) { c = *text++; if (c >= 0x40 && c <= 0x7E) charset = "BIG5"; else if ((c >= 0x30 && c <= 0x39) || (c >= 0x80 && c <= 0xA0)) { charset = "GB18030"; break; } } else if (c >= 0xC7) { c = *text++; if ((c >= 0x30 && c <= 0x39) || (c >= 0x80 && c <= 0xA0)) { charset = "GB18030"; break; } } } return charset; } /*************************/ static const std::string detectCharsetJapanese (const char *text) { uint8_t c = *text; std::string charset = ""; while (charset.empty() && (c = *text++) != '\0') { if (c >= 0x81 && c <= 0x9F) { if (c == 0x8E) /* SS2 */ { c = *text++; if ((c >= 0x40 && c <= 0xA0) || (c >= 0xE0 && c <= 0xFC)) charset = "CP932"; } else if (c == 0x8F) /* SS3 */ { c = *text++; if (c >= 0x40 && c <= 0xA0) charset = "CP932"; else if (c >= 0xFD) break; } else charset = "CP932"; } else if (c >= 0xA1 && c <= 0xDF) { c = *text++; if (c <= 0x9F) charset = "CP932"; else if (c >= 0xFD) break; } else if (c >= 0xE0 && c <= 0xEF) { c = *text++; if (c >= 0x40 && c <= 0xA0) charset = "CP932"; else if (c >= 0xFD) break; } else if (c >= 0xF0) break; } if (charset.empty()) charset = "EUC-JP"; return charset; } /*************************/ static const std::string detectCharsetKorean (const char *text) { uint8_t c = *text; bool noneuc = false; bool nonjohab = false; std::string charset = ""; while (charset.empty() && (c = *text++) != '\0') { if (c >= 0x81 && c < 0x84) { charset = "CP949"; } else if (c >= 0x84 && c < 0xA1) { noneuc = true; c = *text++; if ((c > 0x5A && c < 0x61) || (c > 0x7A && c < 0x81)) charset = "CP1361"; else if (c == 0x52 || c == 0x72 || c == 0x92 || (c > 0x9D && c < 0xA1) || c == 0xB2 || (c > 0xBD && c < 0xC1) || c == 0xD2 || (c > 0xDD && c < 0xE1) || c == 0xF2 || c == 0xFE) charset = "CP949"; } else if (c >= 0xA1 && c <= 0xC6) { c = *text++; if (c < 0xA1) { noneuc = true; if ((c > 0x5A && c < 0x61) || (c > 0x7A && c < 0x81)) charset = "CP1361"; else if (c == 0x52 || c == 0x72 || c == 0x92 || (c > 0x9D && c < 0xA1)) charset = "CP949"; else if (c == 0xB2 || (c > 0xBD && c < 0xC1) || c == 0xD2 || (c > 0xDD && c < 0xE1) || c == 0xF2 || c == 0xFE) nonjohab = true; } } else if (c > 0xC6 && c <= 0xD3) { c = *text++; if (c < 0xA1) charset = "CP1361"; } else if (c > 0xD3 && c < 0xD8) { nonjohab = true; c = *text++; } else if (c >= 0xD8) { c = *text++; if (c < 0xA1) charset = "CP1361"; } if (noneuc && nonjohab) charset = "CP949"; } if (charset.empty()) { if (noneuc) charset = "CP949"; else charset = "EUC-KR"; } return charset; } /*************************/ // The character set of the locale // ("UTF-8" for me) static const std::string getDefaultCharset() { if (setlocale (LC_ALL, "") == NULL) return "UTF-8"; // something's wrong; fall back to UTF-8 const std::string charset = nl_langinfo (CODESET); return charset; } /*************************/ static bool detect_noniso (const char *text) { uint8_t c = *text; while ((c = *text++) != '\0') { if (c >= 0x80 && c <= 0x9F) return true; } return false; } /*************************/ /* In the GTK+ version, I used g_utf8_validate() but this function validates UTF-8 directly and seems faster than using QTextCodec::ConverterState with QTextCodec::toUnicode(), which may give incorrect results. */ bool validateUTF8 (const QByteArray byteArray) { const char *string = byteArray.constData(); if (!string) return true; const unsigned char *bytes = (const unsigned char*)string; unsigned int cp; // code point int bn; // bytes number while (*bytes != 0x00) { /* assuming that UTF-8 maps a sequence of 1-4 bytes, we find the code point and the number of bytes */ if ((*bytes & 0x80) == 0x00) { // 0xxxxxxx, all ASCII characters (0-127) cp = (*bytes & 0x7F); bn = 1; } else if ((*bytes & 0xE0) == 0xC0) { // 110xxxxx 10xxxxxx cp = (*bytes & 0x1F); bn = 2; } else if ((*bytes & 0xF0) == 0xE0) { // 1110xxxx 10xxxxxx 10xxxxxx cp = (*bytes & 0x0F); bn = 3; } else if ((*bytes & 0xF8) == 0xF0) { // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx cp = (*bytes & 0x07); bn = 4; } else return false; bytes += 1; for (int i = 1; i < bn; ++i) { /* all the other bytes should be of the form 10xxxxxx */ if ((*bytes & 0xC0) != 0x80) return false; cp = (cp << 6) | (*bytes & 0x3F); bytes += 1; } if (cp > 0x10FFFF // max code point by definition /* the range from 0xd800 to 0xdfff is reserved for use with UTF-16 and excluded from UTF-8 */ || (cp >= 0xD800 && cp <= 0xDFFF) /* logically impossible situations */ || (cp <= 0x007F && bn != 1) || (cp >= 0x0080 && cp <= 0x07FF && bn != 2) || (cp >= 0x0800 && cp <= 0xFFFF && bn != 3) || (cp >= 0x10000 && cp <= 0x1FFFFF && bn != 4)) { return false; } } return true; } /*************************/ const QString detectCharset (const QByteArray& byteArray) { const char* text = byteArray.constData(); uint8_t c = *text; std::string charset; if (validateUTF8 (byteArray)) { while ((c = *text++) != '\0') { if (c > 0x7F) { charset = "UTF-8"; break; } if (c == 0x1B) /* ESC */ { c = *text++; if (c == '$') { c = *text++; switch (c) { case 'B': // JIS X 0208-1983 case '@': // JIS X 0208-1978 charset = "ISO-2022-JP"; continue; case 'A': // GB2312-1980 charset = "ISO-2022-JP-2"; break; case '(': c = *text++; switch (c) { case 'C': // KSC5601-1987 case 'D': // JIS X 0212-1990 charset = "ISO-2022-JP-2"; } break; case ')': c = *text++; if (c == 'C') charset = "ISO-2022-KR"; // KSC5601-1987 } break; } } } if (charset.empty()) charset = getDefaultCharset(); } if (charset.empty()) { switch (localeNum) { case LATIN1: /* Windows-1252 */ charset = detectCharsetLatin (text); break; case LATINC: case LATINC_UA: case LATINC_TJ: /* Cyrillic */ charset = detectCharsetCyrillic (text); break; case LATINA: /* MS Windows Arabic */ charset = detectCharsetWinArabic (text); break; case CHINESE_CN: case CHINESE_TW: case CHINESE_HK: charset = detectCharsetChinese (text); break; case JAPANESE: charset = detectCharsetJapanese (text); break; case KOREAN: charset = detectCharsetKorean (text); break; case VIETNAMESE: case THAI: case GEORGIAN: charset = encodingItem[OPENI18N]; break; default: if (getDefaultCharset() != "UTF-8") charset = getDefaultCharset(); else if (detect_noniso (text)) charset = encodingItem[CODEPAGE]; else charset = encodingItem[OPENI18N]; if (charset.empty()) charset = encodingItem[IANA]; } } return QString::fromStdString (charset); } } FeatherPad-0.8/featherpad/encoding.h000066400000000000000000000002471325653242400174370ustar00rootroot00000000000000#ifndef ENCODING_H #define ENCODING_H #include namespace FeatherPad { const QString detectCharset (const QByteArray& byteArray); } #endif // ENCODING_H FeatherPad-0.8/featherpad/featherpad.pro000066400000000000000000000052721325653242400203300ustar00rootroot00000000000000QT += core gui \ widgets \ printsupport \ network \ x11extras \ svg TARGET = featherpad TEMPLATE = app CONFIG += c++11 SOURCES += main.cpp \ singleton.cpp \ fpwin.cpp \ encoding.cpp \ tabwidget.cpp \ lineedit.cpp \ textedit.cpp \ tabbar.cpp \ x11.cpp \ highlighter.cpp \ find.cpp \ replace.cpp \ pref.cpp \ config.cpp \ brackets.cpp \ syntax.cpp \ highlighter-sh.cpp \ highlighter-html.cpp \ highlighter-patterns.cpp \ highlighter-jsregex.cpp \ vscrollbar.cpp \ loading.cpp \ tabpage.cpp \ searchbar.cpp \ session.cpp \ sidepane.cpp \ svgicons.cpp HEADERS += singleton.h \ fpwin.h \ encoding.h \ tabwidget.h \ lineedit.h \ textedit.h \ tabbar.h \ x11.h \ highlighter.h \ vscrollbar.h \ filedialog.h \ config.h \ pref.h \ loading.h \ messagebox.h \ tabpage.h \ searchbar.h \ session.h \ warningbar.h \ utils.h \ sidepane.h \ svgicons.h FORMS += fp.ui \ predDialog.ui \ sessionDialog.ui \ about.ui RESOURCES += data/fp.qrc unix:!macx: LIBS += -lX11 unix { #TRANSLATIONS exists($$[QT_INSTALL_BINS]/lrelease) { TRANSLATIONS = $$system("find data/translations/ -name 'featherpad_*.ts'") updateqm.input = TRANSLATIONS updateqm.output = data/translations/translations/${QMAKE_FILE_BASE}.qm updateqm.commands = $$[QT_INSTALL_BINS]/lrelease ${QMAKE_FILE_IN} -qm data/translations/translations/${QMAKE_FILE_BASE}.qm updateqm.CONFIG += no_link target_predeps QMAKE_EXTRA_COMPILERS += updateqm } #VARIABLES isEmpty(PREFIX) { PREFIX = /usr } BINDIR = $$PREFIX/bin DATADIR =$$PREFIX/share DEFINES += DATADIR=\\\"$$DATADIR\\\" PKGDATADIR=\\\"$$PKGDATADIR\\\" #MAKE INSTALL target.path =$$BINDIR # add the fpad symlink slink.path = $$BINDIR slink.extra += ln -sf $${TARGET} fpad && cp --no-dereference fpad $(INSTALL_ROOT)$$BINDIR desktop.path = $$DATADIR/applications desktop.files += ./data/$${TARGET}.desktop iconsvg.path = $$DATADIR/icons/hicolor/scalable/apps iconsvg.files += ./data/$${TARGET}.svg help.path = $$DATADIR/featherpad help.files += ./data/help trans.path = $$DATADIR/featherpad trans.files += data/translations/translations INSTALLS += target slink desktop iconsvg help trans } FeatherPad-0.8/featherpad/filedialog.h000066400000000000000000000062471325653242400177560ustar00rootroot00000000000000/* * Copyright (C) Pedram Pourang (aka Tsu Jan) 2014 * * FeatherPad 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. * * FeatherPad is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * @license GPL-3.0+ */ #ifndef FILEDIALOG_H #define FILEDIALOG_H #include #include #include #include namespace FeatherPad { static bool showHidden = false; // remember /* We want to auto-scroll to selected file and remember whether hidden files should be shown. */ class FileDialog : public QFileDialog { Q_OBJECT public: FileDialog (QWidget *parent, bool isNative = false) : QFileDialog (parent) { tView = nullptr; p = parent; native = isNative; setWindowModality (Qt::WindowModal); setViewMode (QFileDialog::Detail); if (!native) { setOption (QFileDialog::DontUseNativeDialog); if (showHidden) setFilter (filter() | QDir::Hidden); QShortcut *toggleHidden0 = new QShortcut (QKeySequence (tr ("Ctrl+H", "Toggle showing hidden files")), this); QShortcut *toggleHidden1 = new QShortcut (QKeySequence (tr ("Alt+.", "Toggle showing hidden files")), this); connect (toggleHidden0, &QShortcut::activated, this, &FileDialog::toggleHidden); connect (toggleHidden1, &QShortcut::activated, this, &FileDialog::toggleHidden); } } ~FileDialog() { showHidden = (filter() & QDir::Hidden); } void autoScroll() { if (native) return; tView = findChild("treeView"); if (tView) connect (tView->model(), &QAbstractItemModel::layoutChanged, this, &FileDialog::scrollToSelection); } protected: void showEvent(QShowEvent * event) { if (p && !native) QTimer::singleShot (0, this, SLOT (center())); QFileDialog::showEvent (event); } private slots: void scrollToSelection() { if (tView) { QModelIndexList iList = tView->selectionModel()->selectedIndexes(); if (!iList.isEmpty()) tView->scrollTo (iList.at (0), QAbstractItemView::PositionAtCenter); } } void center() { move (p->x() + p->width()/2 - width()/2, p->y() + p->height()/2 - height()/ 2); } void toggleHidden() { showHidden = !(filter() & QDir::Hidden); if (showHidden) setFilter (filter() | QDir::Hidden); else setFilter (filter() & ~QDir::Hidden); } private: QTreeView *tView; QWidget *p; bool native; }; } #endif // FILEDIALOG_H FeatherPad-0.8/featherpad/find.cpp000066400000000000000000000466431325653242400171360ustar00rootroot00000000000000/* * Copyright (C) Pedram Pourang (aka Tsu Jan) 2014 * * FeatherPad 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. * * FeatherPad is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * @license GPL-3.0+ */ #include "fpwin.h" #include "ui_fp.h" #include namespace FeatherPad { /* This order is preserved everywhere for selections: current line -> replacement -> found matches -> bracket matches */ /************************************************************************ ***** Qt's backward search has some bugs. Therefore, we do our own ***** ***** backward search by using the following two static functions. ***** ************************************************************************/ static bool findBackwardInBlock (const QTextBlock &block, const QString &str, int offset, QTextCursor &cursor, QTextDocument::FindFlags flags) { Qt::CaseSensitivity cs = !(flags & QTextDocument::FindCaseSensitively) ? Qt::CaseInsensitive : Qt::CaseSensitive; QString text = block.text(); text.replace (QChar::Nbsp, QLatin1Char (' ')); int idx = -1; while (offset >= 0 && offset <= text.length()) { idx = text.lastIndexOf (str, offset, cs); if (idx == -1) return false; if (flags & QTextDocument::FindWholeWords) { const int start = idx; const int end = start + str.length(); if ((start != 0 && text.at (start - 1).isLetterOrNumber()) || (end != text.length() && text.at (end).isLetterOrNumber())) { // if this is not a whole word, continue the backward search offset = idx - 1; idx = -1; continue; } } cursor.setPosition (block.position() + idx); cursor.setPosition (cursor.position() + str.length(), QTextCursor::KeepAnchor); return true; } return false; } static bool findBackward (const QTextDocument *txtdoc, const QString str, QTextCursor &cursor, QTextDocument::FindFlags flags) { if (!str.isEmpty() && !cursor.isNull()) { int pos = cursor.anchor() - str.size(); // we don't want a match with the cursor inside it if (pos >= 0) { QTextBlock block = txtdoc->findBlock (pos); int blockOffset = pos - block.position(); while (block.isValid()) { if (findBackwardInBlock (block, str, blockOffset, cursor, flags)) return true; block = block.previous(); blockOffset = block.length() - 2; // newline is included in length() } } } cursor = QTextCursor(); return false; } /*************************/ // This method extends the searchable strings to those with line breaks. // It also corrects the behavior of Qt's backward search and can set an // end limit to the forward search. QTextCursor FPwin::finding (const QString& str, const QTextCursor& start, QTextDocument::FindFlags flags, const int end) const { /* let's be consistent first */ if (ui->tabWidget->currentIndex() == -1 || str.isEmpty()) return QTextCursor(); // null cursor QTextDocument *txtdoc = qobject_cast< TabPage *>(ui->tabWidget->currentWidget()) ->textEdit()->document(); QTextCursor res = start; if (str.contains ('\n')) { QTextCursor cursor = start; QTextCursor found; QStringList sl = str.split ("\n"); int i = 0; Qt::CaseSensitivity cs = !(flags & QTextDocument::FindCaseSensitively) ? Qt::CaseInsensitive : Qt::CaseSensitive; QString subStr; if (!(flags & QTextDocument::FindBackward)) { /* this loop searches for the consecutive occurrences of newline separated strings */ while (i < sl.count()) { if (i == 0) // the first string { subStr = sl.at (0); /* when the first string is empty... */ if (subStr.isEmpty()) { /* ... search anew from the next block */ cursor.movePosition (QTextCursor::EndOfBlock); if (end > 0 && cursor.anchor() > end) return QTextCursor(); res.setPosition (cursor.position()); if (!cursor.movePosition (QTextCursor::NextBlock)) return QTextCursor(); ++i; } else { if ((found = txtdoc->find (subStr, cursor, flags)).isNull()) return QTextCursor(); if (end > 0 && found.anchor() > end) return QTextCursor(); cursor.setPosition (found.position()); /* if the match doesn't end the block... */ while (!cursor.atBlockEnd()) { /* ... move the cursor to right and search until a match is found */ cursor.movePosition (QTextCursor::EndOfBlock); cursor.setPosition (cursor.position() - subStr.length()); if ((found = txtdoc->find (subStr, cursor, flags)).isNull()) return QTextCursor(); if (end > 0 && found.anchor() > end) return QTextCursor(); cursor.setPosition (found.position()); } res.setPosition (found.anchor()); if (!cursor.movePosition (QTextCursor::NextBlock)) return QTextCursor(); ++i; } } else if (i != sl.count() - 1) // middle strings { /* when the next block's test isn't the next string... */ if (QString::compare (cursor.block().text(), sl.at (i), cs) != 0) { /* ... reset the loop cautiously */ cursor.setPosition (res.position()); if (!cursor.movePosition (QTextCursor::NextBlock)) return QTextCursor(); i = 0; continue; } if (!cursor.movePosition (QTextCursor::NextBlock)) return QTextCursor(); ++i; } else // the last string (i == sl.count() - 1) { subStr = sl.at (i); if (subStr.isEmpty()) break; if (!(flags & QTextDocument::FindWholeWords)) { /* when the last string doesn't start the next block... */ if (!cursor.block().text().startsWith (subStr, cs)) { /* ... reset the loop cautiously */ cursor.setPosition (res.position()); if (!cursor.movePosition (QTextCursor::NextBlock)) return QTextCursor(); i = 0; continue; } cursor.setPosition (cursor.anchor() + subStr.count()); break; } else { if ((found = txtdoc->find (subStr, cursor, flags)).isNull() || found.anchor() != cursor.position()) { cursor.setPosition (res.position()); if (!cursor.movePosition (QTextCursor::NextBlock)) return QTextCursor(); i = 0; continue; } cursor.setPosition (found.position()); break; } } } res.setPosition (cursor.position(), QTextCursor::KeepAnchor); } else // backward search { cursor.setPosition (cursor.anchor()); int endPos = cursor.position(); while (i < sl.count()) { if (i == 0) // the last string { subStr = sl.at (sl.count() - 1); if (subStr.isEmpty()) { cursor.movePosition (QTextCursor::StartOfBlock); endPos = cursor.position(); if (!cursor.movePosition (QTextCursor::PreviousBlock)) return QTextCursor(); cursor.movePosition (QTextCursor::EndOfBlock); ++i; } else { if (!findBackward (txtdoc, subStr, cursor, flags)) return QTextCursor(); /* if the match doesn't start the block... */ while (cursor.anchor() > cursor.block().position()) { /* ... move the cursor to left and search backward until a match is found */ cursor.setPosition (cursor.block().position() + subStr.count()); if (!findBackward (txtdoc, subStr, cursor, flags)) return QTextCursor(); } endPos = cursor.position(); if (!cursor.movePosition (QTextCursor::PreviousBlock)) return QTextCursor(); cursor.movePosition (QTextCursor::EndOfBlock); ++i; } } else if (i != sl.count() - 1) // the middle strings { if (QString::compare (cursor.block().text(), sl.at (sl.count() - i - 1), cs) != 0) { // reset the loop if the block text doesn't match cursor.setPosition (endPos); if (!cursor.movePosition (QTextCursor::PreviousBlock)) return QTextCursor(); cursor.movePosition (QTextCursor::EndOfBlock); i = 0; continue; } if (!cursor.movePosition (QTextCursor::PreviousBlock)) return QTextCursor(); cursor.movePosition (QTextCursor::EndOfBlock); ++i; } else // the first string { subStr = sl.at (0); if (subStr.isEmpty()) break; if (!(flags & QTextDocument::FindWholeWords)) { /* when the first string doesn't end the previous block... */ if (!cursor.block().text().endsWith (subStr, cs)) { /* ... reset the loop */ cursor.setPosition (endPos); if (!cursor.movePosition (QTextCursor::PreviousBlock)) return QTextCursor(); cursor.movePosition (QTextCursor::EndOfBlock); i = 0; continue; } cursor.setPosition (cursor.anchor() - subStr.count()); break; } else { found = cursor; // block end if (!findBackward (txtdoc, subStr, found, flags) || found.position() != cursor.position()) { cursor.setPosition (endPos); if (!cursor.movePosition (QTextCursor::PreviousBlock)) return QTextCursor(); cursor.movePosition (QTextCursor::EndOfBlock); i = 0; continue; } cursor.setPosition (found.anchor()); break; } } } res.setPosition (cursor.anchor()); res.setPosition (endPos, QTextCursor::KeepAnchor); } } else // there's no line break { if (!(flags & QTextDocument::FindBackward)) { res = txtdoc->find (str, start, flags); if (end > 0 && res.anchor() > end) return QTextCursor(); } else findBackward (txtdoc, str, res, flags); } return res; } /*************************/ void FPwin::find (bool forward) { if (!isReady()) return; int index = ui->tabWidget->currentIndex(); if (index == -1) return; TabPage *tabPage = qobject_cast< TabPage *>(ui->tabWidget->widget (index)); TextEdit *textEdit = tabPage->textEdit(); QString txt = tabPage->searchEntry(); bool newSrch = false; if (textEdit->getSearchedText() != txt) { textEdit->setSearchedText (txt); newSrch = true; } disconnect (textEdit, &TextEdit::resized, this, &FPwin::hlight); disconnect (textEdit, &TextEdit::updateRect, this, &FPwin::hlighting); disconnect (textEdit, &QPlainTextEdit::textChanged, this, &FPwin::hlight); if (txt.isEmpty()) { /* remove all yellow and green highlights */ QList es; textEdit->setGreenSel (es); // not needed if (ui->actionLineNumbers->isChecked() || ui->spinBox->isVisible()) es.prepend (textEdit->currentLineSelection()); es.append (textEdit->getRedSel()); textEdit->setExtraSelections (es); return; } QTextDocument::FindFlags searchFlags = getSearchFlags(); QTextDocument::FindFlags newFlags = searchFlags; if (!forward) newFlags = searchFlags | QTextDocument::FindBackward; QTextCursor start = textEdit->textCursor(); QTextCursor found = finding (txt, start, newFlags); if (found.isNull()) { if (!forward) start.movePosition (QTextCursor::End, QTextCursor::MoveAnchor); else start.movePosition (QTextCursor::Start, QTextCursor::MoveAnchor); found = finding (txt, start, newFlags); } if (!found.isNull()) { start.setPosition (found.anchor()); /* this is needed for selectionChanged() to be emitted */ if (newSrch) textEdit->setTextCursor (start); start.setPosition (found.position(), QTextCursor::KeepAnchor); textEdit->setTextCursor (start); } /* matches highlights should come here, after the text area is scrolled and even when no match is found (it may be added later) */ hlight(); connect (textEdit, &QPlainTextEdit::textChanged, this, &FPwin::hlight); connect (textEdit, &TextEdit::updateRect, this, &FPwin::hlighting); connect (textEdit, &TextEdit::resized, this, &FPwin::hlight); } /*************************/ // Highlight found matches in the visible part of the text. void FPwin::hlight() const { int index = ui->tabWidget->currentIndex(); if (index == -1) return; TabPage *tabPage = qobject_cast< TabPage *>(ui->tabWidget->widget (index)); TextEdit *textEdit = tabPage->textEdit(); QString txt = textEdit->getSearchedText(); if (txt.isEmpty()) return; QTextDocument::FindFlags searchFlags = getSearchFlags(); /* prepend green highlights */ QList es = textEdit->getGreenSel(); QColor color = QColor (textEdit->hasDarkScheme() ? QColor (115, 115, 0) : Qt::yellow); QTextCursor found; /* first put a start cursor at the top left edge... */ QPoint Point (0, 0); QTextCursor start = textEdit->cursorForPosition (Point); /* ... then move it backward by the search text length */ int startPos = start.position() - txt.length(); if (startPos >= 0) start.setPosition (startPos); else start.setPosition (0); int w = textEdit->geometry().width(); int h = textEdit->geometry().height(); /* get the visible text to check if the search string is inside it */ Point = QPoint (w, h); QTextCursor end = textEdit->cursorForPosition (Point); int endLimit = end.anchor(); int endPos = end.position() + txt.length(); end.movePosition (QTextCursor::End); if (endPos <= end.position()) end.setPosition (endPos); QTextCursor visCur = start; visCur.setPosition (end.position(), QTextCursor::KeepAnchor); QString str = visCur.selection().toPlainText(); // '\n' is included in this way Qt::CaseSensitivity cs = Qt::CaseInsensitive; if (tabPage->matchCase()) cs = Qt::CaseSensitive; while (str.contains (txt, cs) // don't waste time if the searched text isn't visible && !(found = finding (txt, start, searchFlags, endLimit)).isNull()) { QTextEdit::ExtraSelection extra; extra.format.setBackground (color); extra.cursor = found; es.append (extra); start.setPosition (found.position()); } /* also prepend the current line highlight, so that it always comes first when it exists */ if (ui->actionLineNumbers->isChecked() || ui->spinBox->isVisible()) es.prepend (textEdit->currentLineSelection()); /* append red highlights */ es.append (textEdit->getRedSel()); textEdit->setExtraSelections (es); } /*************************/ void FPwin::hlighting (const QRect&, int dy) const { if (dy) hlight(); } /*************************/ void FPwin::searchFlagChanged() { if (!isReady()) return; int index = ui->tabWidget->currentIndex(); if (index == -1) return; /* deselect text for consistency */ TextEdit *textEdit = qobject_cast< TabPage *>(ui->tabWidget->widget (index))->textEdit(); QTextCursor start = textEdit->textCursor(); if (start.hasSelection()) { start.setPosition (start.anchor()); textEdit->setTextCursor (start); } hlight(); } /*************************/ QTextDocument::FindFlags FPwin::getSearchFlags() const { TabPage *tabPage = qobject_cast< TabPage *>(ui->tabWidget->currentWidget()); QTextDocument::FindFlags searchFlags = 0; if (tabPage->matchWhole()) searchFlags = QTextDocument::FindWholeWords; if (tabPage->matchCase()) searchFlags |= QTextDocument::FindCaseSensitively; return searchFlags; } } FeatherPad-0.8/featherpad/fp.ui000066400000000000000000000750711325653242400164530ustar00rootroot00000000000000 FeatherPad::FPwin 0 0 700 500 0 0 0 0 0 0 0 Qt::Horizontal -1 true true true 2 2 1 Go to line: 2 1 Qt::Horizontal QSizePolicy::Fixed 10 0 Qt::NoFocus Select text from cursor to this line (Ctrl+Shift+J) Select Text Ctrl+Shift+J Qt::Horizontal 40 0 0 0 700 20 Qt::PreventContextMenu &File Recently &Modified &Edit &Options &Encoding &Unicode &Western European &East European Ea&st Asian &Search &Help Qt::PreventContextMenu false TopToolBarArea false Qt::PreventContextMenu QDockWidget::DockWidgetClosable|QDockWidget::DockWidgetFloatable Qt::BottomDockWidgetArea|Qt::TopDockWidgetArea Rep&lacement 8 0 0 0 0 0 5 2 5 5 0 0 0 Find: Qt::AlignCenter 150 0 To be replaced 0 0 Replace with: Qt::AlignCenter 150 0 Replacing text 0 0 Qt::NoFocus true 0 0 Qt::NoFocus true 0 0 Qt::NoFocus true &New New tab Ctrl+N &Open Open a file Ctrl+O false &Save Save the current tab Ctrl+S false &Undo Undo Ctrl+Z false &Redo Redo Ctrl+Shift+Z false Reload Reload Ctrl+Shift+R &Find Focus/hide search bar Ctrl+F &Replace Show/hide replacement dock Ctrl+R Save &As Ctrl+Shift+S &Print Ctrl+P Documen&t Properties Ctrl+Shift+D &Close Ctrl+Shift+Q &Quit Ctrl+Q false &Cut Ctrl+X false C&opy Ctrl+C &Paste Ctrl+V false &Delete &Select All Ctrl+A &Font true &Line Numbers Ctrl+L true true &Wrap Lines Ctrl+W true true &Auto-Indentation Ctrl+I true true &Syntax Highlighting Ctrl+Shift+H &Preferences Ctrl+Shift+P &Help Ctrl+H &About true false Windows Arabic (&CP1256) true &Other true Enforce UTF-8 true false &UTF-8 true false UTF-&16 true false &ISO-8859-1 true false &Windows-1252 true false &Cyrillic (CP1251) true false Cyrillic (&KOI8-U) true false Cyrillic (&ISO-8859-5) true false &Chineese (BIG5) true false Chinese (&GB18030) true false &Japanese (ISO-2022-JP) true false Japanese (&ISO-2022-JP-2) true false Japanese (ISO-&2022-KR) true false Ja&panese (CP932) true false Japa&nese (EUC-JP) true &Korean (CP949) true K&orean (CP1361) true Korean (&EUC-KR) Save with &Encoding &Jump to Show/hide jump bar Ctrl+J &Edit Edit text Ctrl+Shift+E false &Detach Tab Ctrl+T true ISO-&8859-15 Close Ne&xt Tabs Close &Previous Tabs Close &All Tabs Close &Other Tabs &Copy File Name Copy File &Path false Ne&xt Tab Alt+Right false Previous Ta&b Alt+Left false &First Tab Alt+Down false &Last Tab Alt+Up Menu &Run Execute Ctrl+E &Clear Clear the list of recently modified files Save/Restore Session Sa&ve/Restore Session Ctrl+M Side-Pane Ctrl+Alt+P Paste Date and Time Ctrl+Shift+V FeatherPad::LineEdit QLineEdit
lineedit.h
FeatherPad::TabWidget QTabWidget
tabwidget.h
1
actionQuit triggered() FPwin close() -1 -1 346 246
FeatherPad-0.8/featherpad/fpwin.cpp000066400000000000000000005717261325653242400173460ustar00rootroot00000000000000/* * Copyright (C) Pedram Pourang (aka Tsu Jan) 2014 * * FeatherPad 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. * * FeatherPad is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * @license GPL-3.0+ */ #include "singleton.h" #include "ui_fp.h" #include "ui_about.h" #include "encoding.h" #include "filedialog.h" #include "messagebox.h" #include "pref.h" #include "session.h" #include "loading.h" #include "warningbar.h" #include "svgicons.h" #include #include #include #include #include #include // std::ofstream #include #include #include #include #include #include "x11.h" namespace FeatherPad { void BusyMaker::waiting() { QTimer::singleShot (timeout, this, SLOT (makeBusy())); } void BusyMaker::makeBusy() { if (QGuiApplication::overrideCursor() == nullptr) QGuiApplication::setOverrideCursor (Qt::WaitCursor); emit finished(); } FPwin::FPwin (QWidget *parent):QMainWindow (parent), dummyWidget (nullptr), ui (new Ui::FPwin) { ui->setupUi (this); loadingProcesses_ = 0; rightClicked_ = -1; busyThread_ = nullptr; autoSaver_ = nullptr; autoSaverRemainingTime_ = -1; sidePane_ = nullptr; /* "Jump to" bar */ ui->spinBox->hide(); ui->label->hide(); ui->checkBox->hide(); /* status bar */ QLabel *statusLabel = new QLabel(); statusLabel->setObjectName ("statusLabel"); statusLabel->setIndent (2); statusLabel->setMinimumWidth (100); statusLabel->setTextInteractionFlags (Qt::TextSelectableByMouse); QToolButton *wordButton = new QToolButton(); wordButton->setObjectName ("wordButton"); wordButton->setFocusPolicy (Qt::NoFocus); wordButton->setAutoRaise (true); wordButton->setToolButtonStyle (Qt::ToolButtonIconOnly); wordButton->setIconSize (QSize (16, 16)); wordButton->setIcon (symbolicIcon::icon (":icons/view-refresh.svg")); wordButton->setToolTip (tr ("Calculate number of words\n(For huge texts, this may be CPU-intensive.)")); connect (wordButton, &QAbstractButton::clicked, [=]{updateWordInfo();}); ui->statusBar->addWidget (statusLabel); ui->statusBar->addWidget (wordButton); /* text unlocking */ ui->actionEdit->setVisible (false); ui->actionRun->setVisible (false); /* replace dock */ ui->dockReplace->setTabOrder (ui->lineEditFind, ui->lineEditReplace); ui->dockReplace->setTabOrder (ui->lineEditReplace, ui->toolButtonNext); /* tooltips are set here for easier translation */ ui->toolButtonNext->setToolTip (tr ("Next") + " (" + tr ("F7") + ")"); ui->toolButtonPrv->setToolTip (tr ("Previous") + " (" + tr ("F8") + ")"); ui->toolButtonAll->setToolTip (tr ("Replace all") + " (" + tr ("F9") + ")"); ui->dockReplace->setVisible (false); applyConfigOnStarting(); QWidget* spacer = new QWidget(); spacer->setSizePolicy (QSizePolicy::Expanding, QSizePolicy::Preferred); ui->mainToolBar->insertWidget (ui->actionMenu, spacer); QMenu *menu = new QMenu (ui->mainToolBar); menu->addMenu (ui->menuFile); menu->addMenu (ui->menuEdit); menu->addMenu (ui->menuOptions); menu->addMenu (ui->menuSearch); menu->addMenu (ui->menuHelp); ui->actionMenu->setMenu(menu); QList tbList = ui->mainToolBar->findChildren(); if (!tbList.isEmpty()) tbList.at (tbList.count() - 1)->setPopupMode (QToolButton::InstantPopup); newTab(); aGroup_ = new QActionGroup (this); ui->actionUTF_8->setActionGroup (aGroup_); ui->actionUTF_16->setActionGroup (aGroup_); ui->actionWindows_Arabic->setActionGroup (aGroup_); ui->actionISO_8859_1->setActionGroup (aGroup_); ui->actionISO_8859_15->setActionGroup (aGroup_); ui->actionWindows_1252->setActionGroup (aGroup_); ui->actionCyrillic_CP1251->setActionGroup (aGroup_); ui->actionCyrillic_KOI8_U->setActionGroup (aGroup_); ui->actionCyrillic_ISO_8859_5->setActionGroup (aGroup_); ui->actionChineese_BIG5->setActionGroup (aGroup_); ui->actionChinese_GB18030->setActionGroup (aGroup_); ui->actionJapanese_ISO_2022_JP->setActionGroup (aGroup_); ui->actionJapanese_ISO_2022_JP_2->setActionGroup (aGroup_); ui->actionJapanese_ISO_2022_KR->setActionGroup (aGroup_); ui->actionJapanese_CP932->setActionGroup (aGroup_); ui->actionJapanese_EUC_JP->setActionGroup (aGroup_); ui->actionKorean_CP949->setActionGroup (aGroup_); ui->actionKorean_CP1361->setActionGroup (aGroup_); ui->actionKorean_EUC_KR->setActionGroup (aGroup_); ui->actionOther->setActionGroup (aGroup_); ui->actionUTF_8->setChecked (true); ui->actionOther->setDisabled (true); connect (ui->actionNew, &QAction::triggered, this, &FPwin::newTab); connect (ui->tabWidget->tabBar(), &TabBar::addEmptyTab, this, &FPwin::newTab); connect (ui->actionDetachTab, &QAction::triggered, this, &FPwin::detachTab); connect (ui->actionRightTab, &QAction::triggered, this, &FPwin::nextTab); connect (ui->actionLeftTab, &QAction::triggered, this, &FPwin::previousTab); if (sidePane_) { QString txt = ui->actionFirstTab->text(); ui->actionFirstTab->setText (ui->actionLastTab->text()); ui->actionLastTab->setText (txt); connect (ui->actionFirstTab, &QAction::triggered, this, &FPwin::lastTab); connect (ui->actionLastTab, &QAction::triggered, this, &FPwin::firstTab); } else { connect (ui->actionLastTab, &QAction::triggered, this, &FPwin::lastTab); connect (ui->actionFirstTab, &QAction::triggered, this, &FPwin::firstTab); } connect (ui->actionClose, &QAction::triggered, this, &FPwin::closeTab); connect (ui->tabWidget, &QTabWidget::tabCloseRequested, this, &FPwin::closeTabAtIndex); connect (ui->actionOpen, &QAction::triggered, this, &FPwin::fileOpen); connect (ui->actionReload, &QAction::triggered, this, &FPwin::reload); connect (aGroup_, &QActionGroup::triggered, this, &FPwin::enforceEncoding); connect (ui->actionSave, &QAction::triggered, [=]{saveFile (false);}); connect (ui->actionSaveAs, &QAction::triggered, this, [=]{saveFile (false);}); connect (ui->actionSaveCodec, &QAction::triggered, this, [=]{saveFile (false);}); connect (ui->actionCut, &QAction::triggered, this, &FPwin::cutText); connect (ui->actionCopy, &QAction::triggered, this, &FPwin::copyText); connect (ui->actionPaste, &QAction::triggered, this, &FPwin::pasteText); connect (ui->actionDate, &QAction::triggered, this, &FPwin::insertDate); connect (ui->actionDelete, &QAction::triggered, this, &FPwin::deleteText); connect (ui->actionSelectAll, &QAction::triggered, this, &FPwin::selectAllText); connect (ui->actionEdit, &QAction::triggered, this, &FPwin::makeEditable); connect (ui->actionSession, &QAction::triggered, this, &FPwin::manageSessions); connect (ui->actionRun, &QAction::triggered, this, &FPwin::executeProcess); connect (ui->actionUndo, &QAction::triggered, this, &FPwin::undoing); connect (ui->actionRedo, &QAction::triggered, this, &FPwin::redoing); connect (ui->tabWidget, &TabWidget::currentTabChanged, this, &FPwin::tabSwitch); connect (ui->tabWidget->tabBar(), &TabBar::tabDetached, this, &FPwin::detachTab); ui->tabWidget->tabBar()->setContextMenuPolicy (Qt::CustomContextMenu); connect (ui->tabWidget->tabBar(), &QWidget::customContextMenuRequested, this, &FPwin::tabContextMenu); connect (ui->actionCopyName, &QAction::triggered, this, &FPwin::copyTabFileName); connect (ui->actionCopyPath, &QAction::triggered, this, &FPwin::copyTabFilePath); connect (ui->actionCloseAll, &QAction::triggered, this, &FPwin::closeAllTabs); connect (ui->actionCloseRight, &QAction::triggered, this, &FPwin::closeNextTabs); connect (ui->actionCloseLeft, &QAction::triggered, this, &FPwin::closePreviousTabs); connect (ui->actionCloseOther, &QAction::triggered, this, &FPwin::closeOtherTabs); connect (ui->actionFont, &QAction::triggered, this, &FPwin::fontDialog); connect (ui->actionFind, &QAction::triggered, this, &FPwin::showHideSearch); connect (ui->actionJump, &QAction::triggered, this, &FPwin::jumpTo); connect (ui->spinBox, &QAbstractSpinBox::editingFinished, this, &FPwin::goTo); connect (ui->actionLineNumbers, &QAction::toggled, this, &FPwin::showLN); connect (ui->actionWrap, &QAction::triggered, this, &FPwin::toggleWrapping); connect (ui->actionSyntax, &QAction::triggered, this, &FPwin::toggleSyntaxHighlighting); connect (ui->actionIndent, &QAction::triggered, this, &FPwin::toggleIndent); connect (ui->actionPreferences, &QAction::triggered, this, &FPwin::prefDialog); connect (ui->actionReplace, &QAction::triggered, this, &FPwin::replaceDock); connect (ui->toolButtonNext, &QAbstractButton::clicked, this, &FPwin::replace); connect (ui->toolButtonPrv, &QAbstractButton::clicked, this, &FPwin::replace); connect (ui->toolButtonAll, &QAbstractButton::clicked, this, &FPwin::replaceAll); connect (ui->dockReplace, &QDockWidget::visibilityChanged, this, &FPwin::closeReplaceDock); connect (ui->dockReplace, &QDockWidget::topLevelChanged, this, &FPwin::resizeDock); connect (ui->actionDoc, &QAction::triggered, this, &FPwin::docProp); connect (ui->actionPrint, &QAction::triggered, this, &FPwin::filePrint); connect (ui->actionAbout, &QAction::triggered, this, &FPwin::aboutDialog); connect (ui->actionHelp, &QAction::triggered, this, &FPwin::helpDoc); connect (this, &FPwin::finishedLoading, [this] { if (sidePane_) sidePane_->listWidget()->scrollToItem (sidePane_->listWidget()->currentItem()); }); ui->actionSidePane->setAutoRepeat (false); // don't let UI change too rapidly connect (ui->actionSidePane, &QAction::triggered, [this] {toggleSidePane();}); /*************************************************************************** ***** KDE (KAcceleratorManager) has a nasty "feature" that ***** ***** "smartly" gives mnemonics to tab and tool button texts so ***** ***** that, sometimes, the same mnemonics are disabled in the GUI ***** ***** and, as a result, their corresponding action shortcuts ***** ***** become disabled too. As a workaround, we don't set text ***** ***** for tool buttons on the search bar and replacement dock. ***** ***** The toolbar buttons and menu items aren't affected by this bug. ***** ***************************************************************************/ ui->toolButtonNext->setShortcut (QKeySequence (tr ("F7"))); ui->toolButtonPrv->setShortcut (QKeySequence (tr ("F8"))); ui->toolButtonAll->setShortcut (QKeySequence (tr ("F9"))); QShortcut *zoomin = new QShortcut (QKeySequence (tr ("Ctrl+=")), this); QShortcut *zoominPlus = new QShortcut (QKeySequence (tr ("Ctrl++")), this); QShortcut *zoomout = new QShortcut (QKeySequence (tr ("Ctrl+-")), this); QShortcut *zoomzero = new QShortcut (QKeySequence (tr ("Ctrl+0")), this); connect (zoomin, &QShortcut::activated, this, &FPwin::zoomIn); connect (zoominPlus, &QShortcut::activated, this, &FPwin::zoomIn); connect (zoomout, &QShortcut::activated, this, &FPwin::zoomOut); connect (zoomzero, &QShortcut::activated, this, &FPwin::zoomZero); QShortcut *fullscreen = new QShortcut (QKeySequence (tr ("F11")), this); QShortcut *defaultsize = new QShortcut (QKeySequence (tr ("Ctrl+Shift+W")), this); connect (fullscreen, &QShortcut::activated, [this] {setWindowState (windowState() ^ Qt::WindowFullScreen);}); connect (defaultsize, &QShortcut::activated, this, &FPwin::defaultSize); /* this workaround, for the RTL bug in QPlainTextEdit, isn't needed because a better workaround is included in textedit.cpp */ /*QShortcut *align = new QShortcut (QKeySequence (tr ("Ctrl+Shift+A", "Alignment")), this); connect (align, &QShortcut::activated, this, &FPwin::align);*/ /* exiting a process */ QShortcut *kill = new QShortcut (QKeySequence (tr ("Ctrl+Alt+E")), this); connect (kill, &QShortcut::activated, this, &FPwin::exitProcess); dummyWidget = new QWidget(); setAcceptDrops (true); setAttribute (Qt::WA_AlwaysShowToolTips); setAttribute (Qt::WA_DeleteOnClose, false); // we delete windows in singleton } /*************************/ FPwin::~FPwin() { startAutoSaving (false); delete dummyWidget; dummyWidget = nullptr; delete aGroup_; aGroup_ = nullptr; delete ui; ui = nullptr; } /*************************/ void FPwin::closeEvent (QCloseEvent *event) { bool keep = closeTabs (-1, -1); if (keep) event->ignore(); else { FPsingleton *singleton = static_cast(qApp); Config& config = singleton->getConfig(); if (config.getRemSize() && windowState() == Qt::WindowNoState) config.setWinSize (size()); if (sidePane_ && config.getRemSplitterPos()) { QList sizes = ui->splitter->sizes(); config.setSplitterPos (qRound (100.0 * (qreal)sizes.at (0) / (qreal)(sizes.at (0) + sizes.at (1)))); } singleton->removeWin (this); event->accept(); } } /*************************/ void FPwin::toggleSidePane() { if (!sidePane_) { ui->tabWidget->tabBar()->hide(); ui->tabWidget->tabBar()->hideSingle (false); // prevent tabs from reappearing sidePane_ = new SidePane(); ui->splitter->insertWidget (0, sidePane_); sidePane_->listWidget()->setFocus(); int mult = size().width() / 100; // for more precision int sp = static_cast(qApp)->getConfig().getSplitterPos(); QList sizes; sizes << sp * mult << (100 - sp) * mult; ui->splitter->setSizes (sizes); connect (sidePane_->listWidget(), &QWidget::customContextMenuRequested, this, &FPwin::listContextMenu); connect (sidePane_->listWidget(), &QListWidget::currentItemChanged, this, &FPwin::changeTab); connect (sidePane_->listWidget(), &ListWidget::closItem, [this](QListWidgetItem* item) { if (!sideItems_.isEmpty()) closeTabAtIndex (ui->tabWidget->indexOf (sideItems_.value (item))); }); if (ui->tabWidget->count() > 0) { updateShortcuts (true); int curIndex = ui->tabWidget->currentIndex(); ListWidget *lw = sidePane_->listWidget(); for (int i = 0; i < ui->tabWidget->count(); ++i) { TabPage *tabPage = qobject_cast(ui->tabWidget->widget (i)); /* tab text can't be used because, on the one hand, it may be elided and, on the other hand, KDE's auto-mnemonics may interfere */ QString fname = tabPage->textEdit()->getFileName(); bool isLink (false); if (fname.isEmpty()) { if (tabPage->textEdit()->getProg() == "help") fname = "** " + tr ("Help") + " **"; else fname = tr ("Untitled"); } else { isLink = QFileInfo (fname).isSymLink(); fname = fname.section ('/', -1); } if (tabPage->textEdit()->document()->isModified()) fname.append ("*"); fname.replace ("\n", " "); QListWidgetItem *lwi = new QListWidgetItem (isLink ? QIcon (":icons/link.svg") : QIcon(), fname, lw); lwi->setToolTip (ui->tabWidget->tabToolTip (i)); sideItems_.insert (lwi, tabPage); lw->addItem (lwi); if (i == curIndex) lw->setCurrentItem (lwi); } sidePane_->listWidget()->scrollTo (sidePane_->listWidget()->currentIndex()); updateShortcuts (false); } } else { if (!sidePane_->listWidget()->hasFocus()) sidePane_->listWidget()->setFocus(); else { sideItems_.clear(); delete sidePane_; sidePane_ = nullptr; bool hideSingleTab = static_cast(qApp)-> getConfig().getHideSingleTab(); ui->tabWidget->tabBar()->hideSingle (hideSingleTab); if (!hideSingleTab || ui->tabWidget->count() > 1) ui->tabWidget->tabBar()->show(); /* return focus to the document */ if (TabPage *tabPage = qobject_cast(ui->tabWidget->currentWidget())) tabPage->textEdit()->setFocus(); } } } /*************************/ void FPwin::applyConfigOnStarting() { Config& config = static_cast(qApp)->getConfig(); if (config.getRemSize()) { resize (config.getWinSize()); if (config.getIsMaxed()) setWindowState (Qt::WindowMaximized); if (config.getIsFull() && config.getIsMaxed()) setWindowState (windowState() ^ Qt::WindowFullScreen); else if (config.getIsFull()) setWindowState (Qt::WindowFullScreen); } else { QSize startSize = config.getStartSize(); QSize ag = QApplication::desktop()->availableGeometry().size(); if (startSize.width() > ag.width() || startSize.height() > ag.height()) { startSize = startSize.boundedTo (ag); config.setStartSize (startSize); } else if (startSize.isEmpty()) { startSize = QSize (700, 500); config.setStartSize (startSize); } resize (startSize); } ui->mainToolBar->setVisible (!config.getNoToolbar()); ui->menuBar->setVisible (!config.getNoMenubar()); ui->actionMenu->setVisible (config.getNoMenubar()); ui->actionDoc->setVisible (!config.getShowStatusbar()); ui->actionWrap->setChecked (config.getWrapByDefault()); ui->actionIndent->setChecked (config.getIndentByDefault()); ui->actionLineNumbers->setChecked (config.getLineByDefault()); ui->actionLineNumbers->setDisabled (config.getLineByDefault()); ui->actionSyntax->setChecked (config.getSyntaxByDefault()); if (!config.getShowStatusbar()) ui->statusBar->hide(); else { if (config.getShowCursorPos()) addCursorPosLabel(); } if (config.getShowLangSelector() && config.getSyntaxByDefault()) { setupLangButton (true, config.getShowWhiteSpace() || config.getShowEndings() || config.getVLineDistance() > 0); } if (config.getTabPosition() != 0) ui->tabWidget->setTabPosition ((QTabWidget::TabPosition) config.getTabPosition()); if (!config.getSidePaneMode()) // hideSingle() shouldn't be set with the side-pane ui->tabWidget->tabBar()->hideSingle (config.getHideSingleTab()); else toggleSidePane(); if (config.getRecentOpened()) ui->menuOpenRecently->setTitle (tr ("&Recently Opened")); int recentNumber = config.getCurRecentFilesNumber(); QAction* recentAction = nullptr; for (int i = 0; i < recentNumber; ++i) { recentAction = new QAction (this); recentAction->setVisible (false); connect (recentAction, &QAction::triggered, this, &FPwin::newTabFromRecent); ui->menuOpenRecently->addAction (recentAction); } ui->menuOpenRecently->addAction (ui->actionClearRecent); connect (ui->menuOpenRecently, &QMenu::aboutToShow, this, &FPwin::updateRecenMenu); connect (ui->actionClearRecent, &QAction::triggered, this, &FPwin::clearRecentMenu); if (config.getIconless()) { iconMode_ = NONE; ui->toolButtonNext->setText (tr ("Next")); ui->toolButtonPrv->setText (tr ("Previous")); ui->toolButtonAll->setText (tr ("All")); } else { bool rtl (QApplication::layoutDirection() == Qt::RightToLeft); if (config.getSysIcon()) { iconMode_ = SYSTEM; ui->actionNew->setIcon (QIcon::fromTheme ("document-new")); ui->actionOpen->setIcon (QIcon::fromTheme ("document-open")); ui->menuOpenRecently->setIcon (QIcon::fromTheme ("document-open-recent")); ui->actionClearRecent->setIcon (QIcon::fromTheme ("edit-clear")); ui->actionSave->setIcon (QIcon::fromTheme ("document-save")); ui->actionSaveAs->setIcon (QIcon::fromTheme ("document-save-as")); ui->actionSaveCodec->setIcon (QIcon::fromTheme ("document-save-as")); ui->actionPrint->setIcon (QIcon::fromTheme ("document-print")); ui->actionDoc->setIcon (QIcon::fromTheme ("document-properties")); ui->actionUndo->setIcon (QIcon::fromTheme ("edit-undo")); ui->actionRedo->setIcon (QIcon::fromTheme ("edit-redo")); ui->actionCut->setIcon (QIcon::fromTheme ("edit-cut")); ui->actionCopy->setIcon (QIcon::fromTheme ("edit-copy")); ui->actionPaste->setIcon (QIcon::fromTheme ("edit-paste")); ui->actionDate->setIcon (QIcon::fromTheme ("clock")); ui->actionDelete->setIcon (QIcon::fromTheme ("edit-delete")); ui->actionSelectAll->setIcon (QIcon::fromTheme ("edit-select-all")); ui->actionReload->setIcon (QIcon::fromTheme ("view-refresh")); ui->actionFind->setIcon (QIcon::fromTheme ("edit-find")); ui->actionReplace->setIcon (QIcon::fromTheme ("edit-find-replace")); ui->actionClose->setIcon (QIcon::fromTheme ("window-close")); ui->actionQuit->setIcon (QIcon::fromTheme ("application-exit")); ui->actionFont->setIcon (QIcon::fromTheme ("preferences-desktop-font")); ui->actionPreferences->setIcon (QIcon::fromTheme ("preferences-system")); ui->actionHelp->setIcon (QIcon::fromTheme ("help-contents")); ui->actionAbout->setIcon (QIcon::fromTheme ("help-about")); ui->actionJump->setIcon (QIcon::fromTheme ("go-jump")); ui->actionEdit->setIcon (QIcon::fromTheme ("document-edit")); ui->actionRun->setIcon (QIcon::fromTheme ("system-run")); ui->actionCopyName->setIcon (QIcon::fromTheme ("edit-copy")); ui->actionCopyPath->setIcon (QIcon::fromTheme ("edit-copy")); /* these icons may not exist in some themes... */ QIcon icn = QIcon::fromTheme ("tab-close-other"); if (icn.isNull()) icn = symbolicIcon::icon (":icons/tab-close-other.svg"); ui->actionCloseOther->setIcon (icn); icn = QIcon::fromTheme ("application-menu"); if (icn.isNull()) icn = symbolicIcon::icon (":icons/application-menu.svg"); ui->actionMenu->setIcon (icn); /* ... and the following buttons don't have text, so we don't risk */ icn = QIcon::fromTheme ("go-down"); if (icn.isNull()) icn = QIcon (":icons/go-down.svg"); ui->toolButtonNext->setIcon (icn); icn = QIcon::fromTheme ("go-up"); if (icn.isNull()) icn = QIcon (":icons/go-up.svg"); ui->toolButtonPrv->setIcon (icn); icn = QIcon::fromTheme ("arrow-down-double"); if (icn.isNull()) icn = symbolicIcon::icon (":icons/arrow-down-double.svg"); ui->toolButtonAll->setIcon (icn); if (QToolButton *wordButton = ui->statusBar->findChild("wordButton")) { icn = QIcon::fromTheme ("view-refresh"); if (!icn.isNull()) wordButton->setIcon (icn); } if (rtl) { ui->actionCloseRight->setIcon (QIcon::fromTheme ("go-previous")); ui->actionCloseLeft->setIcon (QIcon::fromTheme ("go-next")); ui->actionRightTab->setIcon (QIcon::fromTheme ("go-previous")); ui->actionLeftTab->setIcon (QIcon::fromTheme ("go-next")); /* shortcuts should be reversed for rtl */ ui->actionRightTab->setShortcut (QKeySequence (tr ("Alt+Left"))); ui->actionLeftTab->setShortcut (QKeySequence (tr ("Alt+Right"))); } else { ui->actionCloseRight->setIcon (QIcon::fromTheme ("go-next")); ui->actionCloseLeft->setIcon (QIcon::fromTheme ("go-previous")); ui->actionRightTab->setIcon (QIcon::fromTheme ("go-next")); ui->actionLeftTab->setIcon (QIcon::fromTheme ("go-previous")); } icn = QIcon::fromTheme ("featherpad"); if (icn.isNull()) icn = QIcon (":icons/featherpad.svg"); setWindowIcon (icn); } else // own icons { iconMode_ = OWN; ui->actionNew->setIcon (symbolicIcon::icon (":icons/document-new.svg")); ui->actionOpen->setIcon (symbolicIcon::icon (":icons/document-open.svg")); ui->menuOpenRecently->setIcon (symbolicIcon::icon (":icons/document-open-recent.svg")); ui->actionClearRecent->setIcon (symbolicIcon::icon (":icons/edit-clear.svg")); ui->actionSave->setIcon (symbolicIcon::icon (":icons/document-save.svg")); ui->actionSaveAs->setIcon (symbolicIcon::icon (":icons/document-save-as.svg")); ui->actionSaveCodec->setIcon (symbolicIcon::icon (":icons/document-save-as.svg")); ui->actionPrint->setIcon (symbolicIcon::icon (":icons/document-print.svg")); ui->actionDoc->setIcon (symbolicIcon::icon (":icons/document-properties.svg")); ui->actionUndo->setIcon (symbolicIcon::icon (":icons/edit-undo.svg")); ui->actionRedo->setIcon (symbolicIcon::icon (":icons/edit-redo.svg")); ui->actionCut->setIcon (symbolicIcon::icon (":icons/edit-cut.svg")); ui->actionCopy->setIcon (symbolicIcon::icon (":icons/edit-copy.svg")); ui->actionPaste->setIcon (symbolicIcon::icon (":icons/edit-paste.svg")); ui->actionDate->setIcon (symbolicIcon::icon (":icons/document-open-recent.svg")); ui->actionDelete->setIcon (QIcon (":icons/edit-delete.svg")); ui->actionSelectAll->setIcon (symbolicIcon::icon (":icons/edit-select-all.svg")); ui->actionReload->setIcon (symbolicIcon::icon (":icons/view-refresh.svg")); ui->actionFind->setIcon (symbolicIcon::icon (":icons/edit-find.svg")); ui->actionReplace->setIcon (symbolicIcon::icon (":icons/edit-find-replace.svg")); ui->actionClose->setIcon (QIcon (":icons/window-close.svg")); ui->actionQuit->setIcon (QIcon (":icons/application-exit.svg")); ui->actionFont->setIcon (symbolicIcon::icon (":icons/preferences-desktop-font.svg")); ui->actionPreferences->setIcon (symbolicIcon::icon (":icons/preferences-system.svg")); ui->actionHelp->setIcon (symbolicIcon::icon (":icons/help-contents.svg")); ui->actionAbout->setIcon (symbolicIcon::icon (":icons/help-about.svg")); ui->actionJump->setIcon (symbolicIcon::icon (":icons/go-jump.svg")); ui->actionEdit->setIcon (symbolicIcon::icon (":icons/document-edit.svg")); ui->actionRun->setIcon (symbolicIcon::icon (":icons/system-run.svg")); ui->actionCopyName->setIcon (symbolicIcon::icon (":icons/edit-copy.svg")); ui->actionCopyPath->setIcon (symbolicIcon::icon (":icons/edit-copy.svg")); ui->actionCloseOther->setIcon (symbolicIcon::icon (":icons/tab-close-other.svg")); ui->actionMenu->setIcon (symbolicIcon::icon (":icons/application-menu.svg")); ui->toolButtonNext->setIcon (symbolicIcon::icon (":icons/go-down.svg")); ui->toolButtonPrv->setIcon (symbolicIcon::icon (":icons/go-up.svg")); ui->toolButtonAll->setIcon (symbolicIcon::icon (":icons/arrow-down-double.svg")); if (rtl) { ui->actionCloseRight->setIcon (symbolicIcon::icon (":icons/go-previous.svg")); ui->actionCloseLeft->setIcon (symbolicIcon::icon (":icons/go-next.svg")); ui->actionRightTab->setIcon (symbolicIcon::icon (":icons/go-previous.svg")); ui->actionLeftTab->setIcon (symbolicIcon::icon (":icons/go-next.svg")); ui->actionRightTab->setShortcut (QKeySequence (tr ("Alt+Left"))); ui->actionLeftTab->setShortcut (QKeySequence (tr ("Alt+Right"))); } else { ui->actionCloseRight->setIcon (symbolicIcon::icon (":icons/go-next.svg")); ui->actionCloseLeft->setIcon (symbolicIcon::icon (":icons/go-previous.svg")); ui->actionRightTab->setIcon (symbolicIcon::icon (":icons/go-next.svg")); ui->actionLeftTab->setIcon (symbolicIcon::icon (":icons/go-previous.svg")); } setWindowIcon (QIcon (":icons/featherpad.svg")); } } if (!config.hasReservedShortcuts()) { // this is here, and not in "singleton.cpp", just to simplify translation QStringList reserved; /* QPLainTextEdit */ reserved << tr ("Ctrl+Shift+Z") << tr ("Ctrl+Z") << tr ("Ctrl+X") << tr ("Ctrl+C") << tr ("Ctrl+V") << tr ("Ctrl+A") << tr ("Shift+Ins") << tr ("Shift+Del") << tr ("Ctrl+Ins") << tr ("Ctrl+Left") << tr ("Ctrl+Right") << tr ("Ctrl+Up") << tr ("Ctrl+Down") << tr ("Ctrl+Home") << tr ("Ctrl+End") /* search and replacement */ << tr ("F3") << tr ("F4") << tr ("F5") << tr ("F6") << tr ("F7") << tr ("F8") << tr ("F9") << tr ("F11") << tr ("Ctrl+Shift+W") << tr ("Ctrl+=") << tr ("Ctrl++") << tr ("Ctrl+-") << tr ("Ctrl+0") // zooming << tr ("Ctrl+Alt+E") // exiting a process << tr ("Shift+Enter") << tr ("Ctrl+Tab") << tr ("Ctrl+Meta+Tab") // text tabulation << tr ("Alt+Right") << tr ("Alt+Left") << tr ("Alt+Down") << tr ("Alt+Up") // tab switching << tr ("Ctrl+Shift+J") // select text on jumping (not an action) << tr ("Ctrl+K"); // used by LineEdit as well as QPlainTextEdit config.setReservedShortcuts (reserved); config.readShortcuts(); } QHash ca = config.customShortcutActions(); QHash::const_iterator it = ca.constBegin(); while (it != ca.constEnd()) { if (QAction *action = findChild(it.key())) action->setShortcut (it.value()); ++it; } if (config.getAutoSave()) startAutoSaving (true, config.getAutoSaveInterval()); } /*************************/ void FPwin::addCursorPosLabel() { if (ui->statusBar->findChild("posLabel")) return; QLabel *posLabel = new QLabel(); posLabel->setObjectName ("posLabel"); posLabel->setText ("" + tr ("Position:") + ""); posLabel->setIndent (2); posLabel->setTextInteractionFlags (Qt::TextSelectableByMouse); ui->statusBar->addPermanentWidget (posLabel); } /*************************/ void FPwin::setupLangButton (bool add, bool normalAsUrl) { static QStringList langList; if (langList.isEmpty()) { langList << "c" << "changelog" << "cmake" << "config" << "cpp" << "css" << "deb" << "desktop" << "diff" << "gtkrc" << "html" << "javascript" << "log" << "lua" << "m3u" << "markdown" << "makefile" << "perl" << "php" << "python" << "qmake" << "qml" << "ruby" << "scss" << "sh" << "troff" << "theme" << "xml"; if (!normalAsUrl) langList.append ("url"); langList.sort(); } if (!add) { // remove the language button (normalAsUrl plays no role) langs.clear(); langList.clear(); if (QToolButton *langButton = ui->statusBar->findChild("langButton")) delete langButton; for (int i = 0; i < ui->tabWidget->count(); ++i) { TextEdit *textEdit = qobject_cast(ui->tabWidget->widget (i))->textEdit(); if (!textEdit->getLang().isEmpty()) { textEdit->setLang (QString()); // remove the enforced syntax if (ui->actionSyntax->isChecked()) { syntaxHighlighting (textEdit, false); syntaxHighlighting (textEdit); } } textEdit->setNormalAsUrl (normalAsUrl); handleNormalAsUrl (textEdit); } } else { QToolButton *langButton = ui->statusBar->findChild("langButton"); if (langButton) { // just add or remove the url action if (normalAsUrl && langs.contains ("url")) { if (QAction *urlAction = langs.take ("url")) { if (QMenu *menu = langButton->findChild()) menu->removeAction (urlAction); delete urlAction; if (!langList.isEmpty()) langList.removeAll ("url"); } } else if (!normalAsUrl && !langs.contains ("url")) { QMenu *menu = langButton->findChild(); QActionGroup *aGroup = langButton->findChild(); if (menu && aGroup) { QAction *urlAction = new QAction ("url", menu); QList allActions = menu->actions(); menu->insertAction (allActions.size() <= 1 ? nullptr /* before the separator and "Normal" */ : allActions.at (allActions.size() - 2), urlAction); urlAction->setCheckable (true); urlAction->setActionGroup (aGroup); langs.insert ("url", urlAction); if (!langList.isEmpty()) { langList.append ("url"); langList.sort(); } } } } else { // add the language button QString normal = tr ("Normal"); langButton = new QToolButton(); langButton->setObjectName ("langButton"); langButton->setFocusPolicy (Qt::NoFocus); langButton->setAutoRaise (true); langButton->setToolButtonStyle (Qt::ToolButtonTextOnly); langButton->setText (normal); langButton->setPopupMode (QToolButton::InstantPopup); QMenu *menu = new QMenu (langButton); QActionGroup *aGroup = new QActionGroup (langButton); QAction *action; for (int i = 0; i < langList.count(); ++i) { QString lang = langList.at (i); action = menu->addAction (lang); action->setCheckable (true); action->setActionGroup (aGroup); langs.insert (lang, action); } menu->addSeparator(); action = menu->addAction (normal); action->setCheckable (true); action->setActionGroup (aGroup); langs.insert (normal, action); langButton->setMenu (menu); ui->statusBar->insertPermanentWidget (2, langButton); connect (aGroup, &QActionGroup::triggered, this, &FPwin::setLang); } for (int i = 0; i < ui->tabWidget->count(); ++i) { // in case this is called from outside c-tor TextEdit *textEdit = qobject_cast(ui->tabWidget->widget (i))->textEdit(); textEdit->setNormalAsUrl (normalAsUrl); handleNormalAsUrl (textEdit); } } /* correct the language button and statusbar message (if this is called from outside c-tor) */ if (TabPage *tabPage = qobject_cast< TabPage *>(ui->tabWidget->currentWidget())) { TextEdit *textEdit = tabPage->textEdit(); showLang (textEdit); /* the statusbar message should be changed only for url texts */ if (ui->statusBar->isVisible() && ((normalAsUrl && textEdit->getProg().isEmpty()) || textEdit->getProg() == "url")) { statusMsgWithLineCount (textEdit->document()->blockCount()); if (textEdit->getWordNumber() == -1) { if (QToolButton *wordButton = ui->statusBar->findChild("wordButton")) wordButton->setVisible (true); } else { if (QToolButton *wordButton = ui->statusBar->findChild("wordButton")) wordButton->setVisible (false); QLabel *statusLabel = ui->statusBar->findChild("statusLabel"); statusLabel->setText (QString ("%1 %2") .arg (statusLabel->text()) .arg (textEdit->getWordNumber())); } } } } /*************************/ void FPwin::handleNormalAsUrl (TextEdit *textEdit) { if (!ui->actionSyntax->isChecked() || !textEdit->getProg().isEmpty()) return; if (textEdit->getNormalAsUrl()) { if (!textEdit->getHighlighter()) syntaxHighlighting (textEdit); else if (textEdit->getLang() == "url") textEdit->setLang (QString()); // "url" may have been enforced } else if (!textEdit->getNormalAsUrl() && textEdit->getHighlighter() && textEdit->getLang().isEmpty()) syntaxHighlighting (textEdit, false); } /*************************/ // We want all dialogs to be window-modal as far as possible. However there is a problem: // If a dialog is opened in a FeatherPad window and is closed after another dialog is // opened in another window, the second dialog will be seen as a child of the first window. // This could cause a crash if the dialog is closed after closing the first window. // As a workaround, we keep window-modality but don't let the user open two window-modal dialogs. bool FPwin::hasAnotherDialog() { closeWarningBar(); bool res = false; FPsingleton *singleton = static_cast(qApp); for (int i = 0; i < singleton->Wins.count(); ++i) { FPwin *win = singleton->Wins.at (i); if (win != this) { QList dialogs = win->findChildren(); for (int j = 0; j < dialogs.count(); ++j) { if (dialogs.at (j)->objectName() != "processDialog" && dialogs.at (j)->objectName() != "sessionDialog") { res = true; break; } } if (res) break; } } if (res) { showWarningBar("
" + tr ("Another FeatherPad window has a modal dialog!") + "
" + "
" +tr ("Please attend to that window or just close its dialog!") + "
"); } return res; } /*************************/ void FPwin::deleteTabPage (int tabIndex) { TabPage *tabPage = qobject_cast< TabPage *>(ui->tabWidget->widget (tabIndex)); if (sidePane_ && !sideItems_.isEmpty()) { if (QListWidgetItem *wi = sideItems_.key (tabPage)) { sideItems_.remove (wi); delete sidePane_->listWidget()->takeItem (sidePane_->listWidget()->row (wi)); } } TextEdit *textEdit = tabPage->textEdit(); if (textEdit->getSaveCursor()) { QString fileName = textEdit->getFileName(); if (!fileName.isEmpty()) { Config& config = static_cast(qApp)->getConfig(); config.saveCursorPos (fileName, textEdit->textCursor().position()); } } /* because deleting the syntax highlighter changes the text, it is better to disconnect contentsChange() here to prevent a crash */ disconnect (textEdit, &QPlainTextEdit::textChanged, this, &FPwin::hlight); disconnect (textEdit->document(), &QTextDocument::contentsChange, this, &FPwin::updateWordInfo); syntaxHighlighting (textEdit, false); ui->tabWidget->removeTab (tabIndex); delete tabPage; tabPage = nullptr; } /*************************/ // Here, "first" is the index/row, to whose right/bottom all tabs/rows are to be closed. // Similarly, "last" is the index/row, to whose left/top all tabs/rows are to be closed. // A negative value means including the start for "first" and the end for "last". // If both "first" and "last" are negative, all tabs will be closed. // The case, when they're both greater than -1, is covered but not used anywhere. // Tabs/rows are always closed from right/bottom to left/top. bool FPwin::closeTabs (int first, int last) { if (!isReady()) return true; pauseAutoSaving (true); bool hasSideList (sidePane_ && !sideItems_.isEmpty()); TabPage *curPage = nullptr; QListWidgetItem *curItem = nullptr; if (hasSideList) { int cur = sidePane_->listWidget()->currentRow(); if (!(first < cur && (cur < last || last == -1))) curItem = sidePane_->listWidget()->currentItem(); } else { int cur = ui->tabWidget->currentIndex(); if (!(first < cur && (cur < last || last == -1))) curPage = qobject_cast(ui->tabWidget->currentWidget()); } bool keep = false; int index, count; DOCSTATE state = SAVED; while (state == SAVED && ui->tabWidget->count() > 0) { if (QGuiApplication::overrideCursor() == nullptr) waitToMakeBusy(); if (last == 0) break; // no tab on the left if (last < 0) // close from the end index = ui->tabWidget->count() - 1; // the last tab/row else // if (last > 0) index = last - 1; if (first >= index) break; int tabIndex = hasSideList ? ui->tabWidget->indexOf (sideItems_.value (sidePane_->listWidget()->item (index))) : index; if (first == index - 1) // only one tab to be closed state = savePrompt (tabIndex, false); else state = savePrompt (tabIndex, true); // with a "No to all" button switch (state) { case SAVED: // close this tab and go to the next one on the left keep = false; deleteTabPage (tabIndex); if (last > -1) // also last > 0 --last; // a left tab is removed /* final changes */ count = ui->tabWidget->count(); if (count == 0) { ui->actionReload->setDisabled (true); ui->actionSave->setDisabled (true); enableWidgets (false); } else if (count == 1) { ui->actionDetachTab->setDisabled (true); ui->actionRightTab->setDisabled (true); ui->actionLeftTab->setDisabled (true); ui->actionLastTab->setDisabled (true); ui->actionFirstTab->setDisabled (true); } break; case UNDECIDED: // stop quitting (cancel or can't save) keep = true; break; case DISCARDED: // no to all: close all tabs (and quit) keep = false; while (index > first) { if (last == 0) break; deleteTabPage (tabIndex); if (last < 0) index = ui->tabWidget->count() - 1; else // if (last > 0) { --last; // a left tab is removed index = last - 1; } tabIndex = hasSideList ? ui->tabWidget->indexOf (sideItems_.value (sidePane_->listWidget()->item (index))) : index; count = ui->tabWidget->count(); if (count == 0) { ui->actionReload->setDisabled (true); ui->actionSave->setDisabled (true); enableWidgets (false); } else if (count == 1) { ui->actionDetachTab->setDisabled (true); ui->actionRightTab->setDisabled (true); ui->actionLeftTab->setDisabled (true); ui->actionLastTab->setDisabled (true); ui->actionFirstTab->setDisabled (true); } } break; default: break; } } unbusy(); if (!keep) { // restore the current page/item if (curPage) ui->tabWidget->setCurrentWidget (curPage); else if (curItem) sidePane_->listWidget()->setCurrentItem (curItem); } pauseAutoSaving (false); return keep; } /*************************/ void FPwin::copyTabFileName() { if (rightClicked_ < 0) return; TabPage *tabPage; if (sidePane_) tabPage = sideItems_.value (sidePane_->listWidget()->item (rightClicked_)); else tabPage = qobject_cast(ui->tabWidget->widget (rightClicked_)); QString fname = tabPage->textEdit()->getFileName(); QApplication::clipboard()->setText (fname.section ('/', -1)); } /*************************/ void FPwin::copyTabFilePath() { if (rightClicked_ < 0) return; TabPage *tabPage; if (sidePane_) tabPage = sideItems_.value (sidePane_->listWidget()->item (rightClicked_)); else tabPage = qobject_cast(ui->tabWidget->widget (rightClicked_)); QString str = tabPage->textEdit()->getFileName(); str.chop (str.section ('/', -1).count()); QApplication::clipboard()->setText (str); } /*************************/ void FPwin::closeAllTabs() { closeTabs (-1, -1); } /*************************/ void FPwin::closeNextTabs() { closeTabs (rightClicked_, -1); } /*************************/ void FPwin::closePreviousTabs() { closeTabs (-1, rightClicked_); } /*************************/ void FPwin::closeOtherTabs() { if (!closeTabs (rightClicked_, -1)) closeTabs (-1, rightClicked_); } /*************************/ void FPwin::dragEnterEvent (QDragEnterEvent *event) { if (findChildren().count() == 0 && (event->mimeData()->hasUrls() || event->mimeData()->hasFormat ("application/featherpad-tab"))) { event->acceptProposedAction(); } } /*************************/ void FPwin::dropEvent (QDropEvent *event) { if (event->mimeData()->hasFormat ("application/featherpad-tab")) dropTab (event->mimeData()->data("application/featherpad-tab")); else { const QList urlList = event->mimeData()->urls(); bool multiple (urlList.count() > 1 || isLoading()); for (const QUrl &url : urlList) newTabFromName (url.adjusted (QUrl::NormalizePathSegments) // KDE may give a double slash .toLocalFile(), false, multiple); } event->acceptProposedAction(); } /*************************/ // This method checks if there's any text that isn't saved under a tab and, // if there is, it activates the tab and shows an appropriate prompt dialog. // "tabIndex" is always the tab index and not the item row (in the side-pane). FPwin::DOCSTATE FPwin::savePrompt (int tabIndex, bool noToAll) { DOCSTATE state = SAVED; TabPage *tabPage = qobject_cast(ui->tabWidget->widget (tabIndex)); TextEdit *textEdit = tabPage->textEdit(); QString fname = textEdit->getFileName(); bool isRemoved (!fname.isEmpty() && (!QFile::exists (fname) || !QFileInfo (fname).isFile())); if (textEdit->document()->isModified() || isRemoved) { unbusy(); // made busy at closeTabs() if (hasAnotherDialog()) return UNDECIDED; // cancel if (tabIndex != ui->tabWidget->currentIndex()) { // switch to the page that needs attention if (sidePane_ && !sideItems_.isEmpty()) sidePane_->listWidget()->setCurrentItem (sideItems_.key (tabPage)); // sets the current widget at changeTab() else ui->tabWidget->setCurrentIndex (tabIndex); } updateShortcuts (true); MessageBox msgBox (this); msgBox.setIcon (QMessageBox::Question); msgBox.setText ("
" + tr ("Save changes?") + "
"); if (isRemoved) msgBox.setInformativeText ("
" + tr ("The file has been removed.") + "
"); else msgBox.setInformativeText ("
" + tr ("The document has been modified.") + "
"); if (noToAll && ui->tabWidget->count() > 1) msgBox.setStandardButtons (QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel | QMessageBox::NoToAll); else msgBox.setStandardButtons (QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); msgBox.changeButtonText (QMessageBox::Save, tr ("Save")); msgBox.changeButtonText (QMessageBox::Discard, tr ("Discard changes")); msgBox.changeButtonText (QMessageBox::Cancel, tr ("Cancel")); if (noToAll) msgBox.changeButtonText (QMessageBox::NoToAll, tr ("No to all")); msgBox.setDefaultButton (QMessageBox::Save); msgBox.setWindowModality (Qt::WindowModal); /* enforce a central position */ /*msgBox.show(); msgBox.move (x() + width()/2 - msgBox.width()/2, y() + height()/2 - msgBox.height()/ 2);*/ switch (msgBox.exec()) { case QMessageBox::Save: if (!saveFile (true)) state = UNDECIDED; break; case QMessageBox::Discard: break; case QMessageBox::Cancel: state = UNDECIDED; break; case QMessageBox::NoToAll: state = DISCARDED; break; default: state = UNDECIDED; break; } updateShortcuts (false); } return state; } /*************************/ // Enable or disable some widgets. void FPwin::enableWidgets (bool enable) const { if (!enable && ui->dockReplace->isVisible()) ui->dockReplace->setVisible (false); if (!enable && ui->spinBox->isVisible()) { ui->spinBox->setVisible (false); ui->label->setVisible (false); ui->checkBox->setVisible (false); } if ((!enable && ui->statusBar->isVisible()) || (enable && static_cast(qApp)->getConfig() .getShowStatusbar())) // starting from no tab { ui->statusBar->setVisible (enable); } ui->actionSelectAll->setEnabled (enable); ui->actionFind->setEnabled (enable); ui->actionJump->setEnabled (enable); ui->actionReplace->setEnabled (enable); ui->actionClose->setEnabled (enable); ui->actionSaveAs->setEnabled (enable); ui->menuEncoding->setEnabled (enable); ui->actionSaveCodec->setEnabled (enable); ui->actionFont->setEnabled (enable); ui->actionDoc->setEnabled (enable); ui->actionPrint->setEnabled (enable); if (!enable) { ui->actionUndo->setEnabled (false); ui->actionRedo->setEnabled (false); ui->actionEdit->setVisible (false); ui->actionRun->setVisible (false); ui->actionCut->setEnabled (false); ui->actionCopy->setEnabled (false); ui->actionPaste->setEnabled (false); ui->actionDate->setEnabled (false); ui->actionDelete->setEnabled (false); } } /*************************/ void FPwin::updateCustomizableShortcuts (bool disable) { if (disable) { ui->actionLineNumbers->setShortcut (QKeySequence()); ui->actionWrap->setShortcut (QKeySequence()); ui->actionIndent->setShortcut (QKeySequence()); ui->actionSyntax->setShortcut (QKeySequence()); ui->actionNew->setShortcut (QKeySequence()); ui->actionOpen->setShortcut (QKeySequence()); ui->actionSave->setShortcut (QKeySequence()); ui->actionFind->setShortcut (QKeySequence()); ui->actionReplace->setShortcut (QKeySequence()); ui->actionSaveAs->setShortcut (QKeySequence()); ui->actionPrint->setShortcut (QKeySequence()); ui->actionDoc->setShortcut (QKeySequence()); ui->actionClose->setShortcut (QKeySequence()); ui->actionQuit->setShortcut (QKeySequence()); ui->actionPreferences->setShortcut (QKeySequence()); ui->actionHelp->setShortcut (QKeySequence()); ui->actionEdit->setShortcut (QKeySequence()); ui->actionDetachTab->setShortcut (QKeySequence()); ui->actionReload->setShortcut (QKeySequence()); /* the shortcuts of these 3 actions don't need to be unset but they may need to be reset with Preferences dialog */ ui->actionJump->setShortcut (QKeySequence()); ui->actionRun->setShortcut (QKeySequence()); ui->actionSession->setShortcut (QKeySequence()); ui->actionSidePane->setShortcut (QKeySequence()); ui->actionUndo->setShortcut (QKeySequence()); ui->actionRedo->setShortcut (QKeySequence()); ui->actionDate->setShortcut (QKeySequence()); } else { QHash ca = static_cast(qApp)-> getConfig().customShortcutActions(); QList keys = ca.keys(); ui->actionLineNumbers->setShortcut (keys.contains ("actionLineNumbers") ? ca.value ("actionLineNumbers") : QKeySequence (tr ("Ctrl+L"))); ui->actionWrap->setShortcut (keys.contains ("actionWrap") ? ca.value ("actionWrap") : QKeySequence (tr ("Ctrl+W"))); ui->actionIndent->setShortcut (keys.contains ("actionIndent") ? ca.value ("actionIndent") : QKeySequence (tr ("Ctrl+I"))); ui->actionSyntax->setShortcut (keys.contains ("actionSyntax") ? ca.value ("actionSyntax") : QKeySequence (tr ("Ctrl+Shift+H"))); ui->actionNew->setShortcut (keys.contains ("actionNew") ? ca.value ("actionNew") : QKeySequence (tr ("Ctrl+N"))); ui->actionOpen->setShortcut (keys.contains ("actionOpen") ? ca.value ("actionOpen") : QKeySequence (tr ("Ctrl+O"))); ui->actionSave->setShortcut (keys.contains ("actionSave") ? ca.value ("actionSave") : QKeySequence (tr ("Ctrl+S"))); ui->actionFind->setShortcut (keys.contains ("actionFind") ? ca.value ("actionFind") : QKeySequence (tr ("Ctrl+F"))); ui->actionReplace->setShortcut (keys.contains ("actionReplace") ? ca.value ("actionReplace") : QKeySequence (tr ("Ctrl+R"))); ui->actionSaveAs->setShortcut (keys.contains ("actionSaveAs") ? ca.value ("actionSaveAs") : QKeySequence (tr ("Ctrl+Shift+S"))); ui->actionPrint->setShortcut (keys.contains ("actionPrint") ? ca.value ("actionPrint") : QKeySequence (tr ("Ctrl+P"))); ui->actionDoc->setShortcut (keys.contains ("actionDoc") ? ca.value ("actionDoc") : QKeySequence (tr ("Ctrl+Shift+D"))); ui->actionClose->setShortcut (keys.contains ("actionClose") ? ca.value ("actionClose") : QKeySequence (tr ("Ctrl+Shift+Q"))); ui->actionQuit->setShortcut (keys.contains ("actionQuit") ? ca.value ("actionQuit") : QKeySequence (tr ("Ctrl+Q"))); ui->actionPreferences->setShortcut (keys.contains ("actionPreferences") ? ca.value ("actionPreferences") : QKeySequence (tr ("Ctrl+Shift+P"))); ui->actionHelp->setShortcut (keys.contains ("actionHelp") ? ca.value ("actionHelp") : QKeySequence (tr ("Ctrl+H"))); ui->actionEdit->setShortcut (keys.contains ("actionEdit") ? ca.value ("actionEdit") : QKeySequence (tr ("Ctrl+E"))); ui->actionDetachTab->setShortcut (keys.contains ("actionDetachTab") ? ca.value ("actionDetachTab") : QKeySequence (tr ("Ctrl+T"))); ui->actionReload->setShortcut (keys.contains ("actionReload") ? ca.value ("actionReload") : QKeySequence (tr ("Ctrl+Shift+R"))); ui->actionJump->setShortcut (keys.contains ("actionJump") ? ca.value ("actionJump") : QKeySequence (tr ("Ctrl+J"))); ui->actionRun->setShortcut (keys.contains ("actionRun") ? ca.value ("actionRun") : QKeySequence (tr ("Ctrl+E"))); ui->actionSession->setShortcut (keys.contains ("actionSession") ? ca.value ("actionSession") : QKeySequence (tr ("Ctrl+M"))); ui->actionSidePane->setShortcut (keys.contains ("actionSidePane") ? ca.value ("actionSidePane") : QKeySequence (tr ("Ctrl+Alt+P"))); ui->actionUndo->setShortcut (keys.contains ("actionUndo") ? ca.value ("actionUndo") : QKeySequence (tr ("Ctrl+Z"))); ui->actionRedo->setShortcut (keys.contains ("actionRedo") ? ca.value ("actionRedo") : QKeySequence (tr ("Ctrl+Shift+Z"))); ui->actionDate->setShortcut (keys.contains ("actionDate") ? ca.value ("actionDate") : QKeySequence (tr ("Ctrl+Shift+V"))); } } /*************************/ // When a window-modal dialog is shown, Qt doesn't disable the main window shortcuts. // This is definitely a bug in Qt. As a workaround, we use this function to disable // all shortcuts on showing a dialog and to enable them again on hiding it. // The searchbar shortcuts of the current tab page are handled separately. // // This function also updates shortcuts after they're customized in the Preferences dialog. void FPwin::updateShortcuts (bool disable, bool page) { if (disable) { ui->actionCut->setShortcut (QKeySequence()); ui->actionCopy->setShortcut (QKeySequence()); ui->actionPaste->setShortcut (QKeySequence()); ui->actionSelectAll->setShortcut (QKeySequence()); ui->toolButtonNext->setShortcut (QKeySequence()); ui->toolButtonPrv->setShortcut (QKeySequence()); ui->toolButtonAll->setShortcut (QKeySequence()); ui->actionRightTab->setShortcut (QKeySequence()); ui->actionLeftTab->setShortcut (QKeySequence()); ui->actionLastTab->setShortcut (QKeySequence()); ui->actionFirstTab->setShortcut (QKeySequence()); } else { ui->actionCut->setShortcut (QKeySequence (tr ("Ctrl+X"))); ui->actionCopy->setShortcut (QKeySequence (tr ("Ctrl+C"))); ui->actionPaste->setShortcut (QKeySequence (tr ("Ctrl+V"))); ui->actionSelectAll->setShortcut (QKeySequence (tr ("Ctrl+A"))); ui->toolButtonNext->setShortcut (QKeySequence (tr ("F7"))); ui->toolButtonPrv->setShortcut (QKeySequence (tr ("F8"))); ui->toolButtonAll->setShortcut (QKeySequence (tr ("F9"))); if (QApplication::layoutDirection() == Qt::RightToLeft) { ui->actionRightTab->setShortcut (QKeySequence (tr ("Alt+Left"))); ui->actionLeftTab->setShortcut (QKeySequence (tr ("Alt+Right"))); } else { ui->actionRightTab->setShortcut (QKeySequence (tr ("Alt+Right"))); ui->actionLeftTab->setShortcut (QKeySequence (tr ("Alt+Left"))); } ui->actionLastTab->setShortcut (QKeySequence (tr ("Alt+Up"))); ui->actionFirstTab->setShortcut (QKeySequence (tr ("Alt+Down"))); } updateCustomizableShortcuts (disable); if (page) // disable/enable searchbar shortcuts of the current page too { if (TabPage *tabPage = qobject_cast< TabPage *>(ui->tabWidget->currentWidget())) tabPage->updateShortcuts (disable); } } /*************************/ void FPwin::newTab() { createEmptyTab (!isLoading()); } /*************************/ TabPage* FPwin::createEmptyTab (bool setCurrent, bool allowNormalHighlighter) { Config config = static_cast(qApp)->getConfig(); static const QStringList searchShortcuts = {tr ("F3"), tr ("F4"), tr ("F5"), tr ("F6")}; TabPage *tabPage = new TabPage (iconMode_, config.getDarkColScheme() ? config.getDarkBgColorValue() : config.getLightBgColorValue(), searchShortcuts, nullptr); TextEdit *textEdit = tabPage->textEdit(); textEdit->setAutoBracket (config.getAutoBracket()); textEdit->setScrollJumpWorkaround (config.getScrollJumpWorkaround()); textEdit->setEditorFont (config.getFont()); textEdit->setInertialScrolling (config.getInertialScrolling()); textEdit->setDateFormat (config.getDateFormat()); /* the (url) syntax highlighter will be created at tabSwitch() */ if (config.getShowWhiteSpace() || config.getShowEndings() || config.getVLineDistance() > 0) { textEdit->setNormalAsUrl (true); if (allowNormalHighlighter) syntaxHighlighting (textEdit); } int index = ui->tabWidget->currentIndex(); if (index == -1) enableWidgets (true); /* hide the searchbar consistently */ if ((index == -1 && config.getHideSearchbar()) || (index > -1 && !qobject_cast< TabPage *>(ui->tabWidget->widget (index))->isSearchBarVisible())) { tabPage->setSearchBarVisible (false); } ui->tabWidget->insertTab (index + 1, tabPage, tr ("Untitled")); /* set all preliminary properties */ if (index >= 0) { ui->actionDetachTab->setEnabled (true); ui->actionRightTab->setEnabled (true); ui->actionLeftTab->setEnabled (true); ui->actionLastTab->setEnabled (true); ui->actionFirstTab->setEnabled (true); } ui->tabWidget->setTabToolTip (index + 1, tr ("Unsaved")); if (!ui->actionWrap->isChecked()) textEdit->setLineWrapMode (QPlainTextEdit::NoWrap); if (!ui->actionIndent->isChecked()) textEdit->setAutoIndentation (false); if (ui->actionLineNumbers->isChecked() || ui->spinBox->isVisible()) textEdit->showLineNumbers (true); if (ui->spinBox->isVisible()) connect (textEdit->document(), &QTextDocument::blockCountChanged, this, &FPwin::setMax); if (ui->statusBar->isVisible() || config.getShowStatusbar()) // when the main window is being created, isVisible() isn't set yet { int showCurPos = config.getShowCursorPos(); if (setCurrent) { if (QToolButton *wordButton = ui->statusBar->findChild("wordButton")) wordButton->setVisible (false); QLabel *statusLabel = ui->statusBar->findChild("statusLabel"); statusLabel->setText ("" + tr ("Encoding") + ": UTF-8    " + tr ("Lines") + ": 1    " + tr ("Sel. Chars") + ": 0    " + tr ("Words") + ": 0"); if (showCurPos) showCursorPos(); } connect (textEdit, &QPlainTextEdit::blockCountChanged, this, &FPwin::statusMsgWithLineCount); connect (textEdit, &QPlainTextEdit::selectionChanged, this, &FPwin::statusMsg); if (showCurPos) connect (textEdit, &QPlainTextEdit::cursorPositionChanged, this, &FPwin::showCursorPos); } connect (textEdit->document(), &QTextDocument::undoAvailable, ui->actionUndo, &QAction::setEnabled); connect (textEdit->document(), &QTextDocument::redoAvailable, ui->actionRedo, &QAction::setEnabled); connect (textEdit->document(), &QTextDocument::modificationChanged, ui->actionSave, &QAction::setEnabled); connect (textEdit->document(), &QTextDocument::modificationChanged, this, &FPwin::asterisk); connect (textEdit, &QPlainTextEdit::copyAvailable, ui->actionCut, &QAction::setEnabled); connect (textEdit, &QPlainTextEdit::copyAvailable, ui->actionDelete, &QAction::setEnabled); connect (textEdit, &QPlainTextEdit::copyAvailable, ui->actionCopy, &QAction::setEnabled); connect (textEdit, &TextEdit::fileDropped, this, &FPwin::newTabFromName); connect (textEdit, &TextEdit::zoomedOut, this, &FPwin::reformat); connect (tabPage, &TabPage::find, this, &FPwin::find); connect (tabPage, &TabPage::searchFlagChanged, this, &FPwin::searchFlagChanged); /* I don't know why, under KDE, when text is selected for the first time, it isn't copied to the selection clipboard. Perhaps it has something to do with Klipper. I neither know why this s a workaround: */ QApplication::clipboard()->text (QClipboard::Selection); if (sidePane_) { ListWidget *lw = sidePane_->listWidget(); QListWidgetItem *lwi = new QListWidgetItem (tr ("Untitled"), lw); lwi->setToolTip (tr ("Unsaved")); sideItems_.insert (lwi, tabPage); lw->addItem (lwi); if (setCurrent || index == -1) // for tabs, it's done automatically { lw->setCurrentItem (lwi); } } if (setCurrent) { ui->tabWidget->setCurrentWidget (tabPage); textEdit->setFocus(); } /* this isn't enough for unshading under all WMs */ /*if (isMinimized()) setWindowState (windowState() & (~Qt::WindowMinimized | Qt::WindowActive));*/ if (static_cast(qApp)->isX11() && isWindowShaded (winId())) unshadeWindow (winId()); if (setCurrent) { activateWindow(); raise(); } return tabPage; } /*************************/ void FPwin::updateRecenMenu() { Config config = static_cast(qApp)->getConfig(); QStringList recentFiles = config.getRecentFiles(); int recentNumber = config.getCurRecentFilesNumber(); QList actions = ui->menuOpenRecently->actions(); int recentSize = recentFiles.count(); QFontMetrics metrics (ui->menuOpenRecently->font()); int w = 150 * metrics.width (' '); for (int i = 0; i < recentNumber; ++i) { if (i < recentSize) { actions.at (i)->setText (metrics.elidedText (recentFiles.at (i), Qt::ElideMiddle, w)); actions.at (i)->setData (recentFiles.at (i)); actions.at (i)->setVisible (true); } else { actions.at (i)->setText (QString()); actions.at (i)->setData (QVariant()); actions.at (i)->setVisible (false); } } ui->actionClearRecent->setEnabled (recentSize != 0); } /*************************/ void FPwin::clearRecentMenu() { Config& config = static_cast(qApp)->getConfig(); config.clearRecentFiles(); updateRecenMenu(); } /*************************/ void FPwin::reformat (TextEdit *textEdit) { formatTextRect (textEdit->rect()); // in "syntax.cpp" if (!textEdit->getSearchedText().isEmpty()) hlight(); // in "find.cpp" } /*************************/ void FPwin::zoomIn() { if (TabPage *tabPage = qobject_cast(ui->tabWidget->currentWidget())) tabPage->textEdit()->zooming (1.f); } /*************************/ void FPwin::zoomOut() { if (TabPage *tabPage = qobject_cast(ui->tabWidget->currentWidget())) { TextEdit *textEdit = tabPage->textEdit(); textEdit->zooming (-1.f); } } /*************************/ void FPwin::zoomZero() { if (TabPage *tabPage = qobject_cast(ui->tabWidget->currentWidget())) { TextEdit *textEdit = tabPage->textEdit(); textEdit->zooming (0.f); } } /*************************/ void FPwin::defaultSize() { QSize s = static_cast(qApp)->getConfig().getStartSize(); if (size() == s) return; if (isMaximized() || isFullScreen()) showNormal(); /*if (isMaximized() && isFullScreen()) showMaximized(); if (isMaximized()) showNormal();*/ /* instead of hiding, reparent with the dummy widget to guarantee resizing under all DEs */ /*Qt::WindowFlags flags = windowFlags(); setParent (dummyWidget, Qt::SubWindow);*/ hide(); resize (s); /*if (parent() != nullptr) setParent (nullptr, flags);*/ QTimer::singleShot (0, this, SLOT (show())); } /*************************/ /*void FPwin::align() { int index = ui->tabWidget->currentIndex(); if (index == -1) return; TextEdit *textEdit = qobject_cast< TabPage *>(ui->tabWidget->widget (index))->textEdit(); QTextOption opt = textEdit->document()->defaultTextOption(); if (opt.alignment() == (Qt::AlignLeft)) { opt = QTextOption (Qt::AlignRight); opt.setTextDirection (Qt::LayoutDirectionAuto); textEdit->document()->setDefaultTextOption (opt); } else if (opt.alignment() == (Qt::AlignRight)) { opt = QTextOption (Qt::AlignLeft); opt.setTextDirection (Qt::LayoutDirectionAuto); textEdit->document()->setDefaultTextOption (opt); } }*/ /*************************/ void FPwin::executeProcess() { QList dialogs = findChildren(); for (int i = 0; i < dialogs.count(); ++i) { if (dialogs.at (i)->objectName() != "processDialog" && dialogs.at (i)->objectName() != "sessionDialog") { return; // shortcut may work when there's a modal dialog } } closeWarningBar(); Config config = static_cast(qApp)->getConfig(); if (!config.getExecuteScripts()) return; if (TabPage *tabPage = qobject_cast(ui->tabWidget->currentWidget())) { if (tabPage->findChild(QString(), Qt::FindDirectChildrenOnly)) { showWarningBar ("
" + tr ("Another process is running in this tab!") + "
" + "
" + tr ("Only one process is allowed per tab.") + "
"); return; } QString fName = tabPage->textEdit()->getFileName(); if (!isScriptLang (tabPage->textEdit()->getProg()) || !QFileInfo (fName).isExecutable()) return; QProcess *process = new QProcess (tabPage); process->setObjectName (fName); // to put it into the message dialog connect(process, &QProcess::readyReadStandardOutput,this, &FPwin::displayOutput); connect(process, &QProcess::readyReadStandardError,this, &FPwin::displayError); QString command = config.getExecuteCommand(); if (!command.isEmpty()) command += " "; fName.replace ("\"", "\"\"\""); // literal quotes in the command are shown by triple quotes process->start (command + "\"" + fName + "\""); connect(process, static_cast(&QProcess::finished), [=](int /*exitCode*/, QProcess::ExitStatus /*exitStatus*/){ process->deleteLater(); }); } } /*************************/ bool FPwin::isScriptLang (QString lang) { return (lang == "sh" || lang == "python" || lang == "ruby" || lang == "lua" || lang == "perl"); } /*************************/ void FPwin::exitProcess() { if (TabPage *tabPage = qobject_cast(ui->tabWidget->currentWidget())) { if (QProcess *process = tabPage->findChild(QString(), Qt::FindDirectChildrenOnly)) process->kill(); } } /*************************/ void FPwin::displayMessage (bool error) { QProcess *process = static_cast(QObject::sender()); if (!process) return; // impossible QByteArray msg; if (error) { process->setReadChannel(QProcess::StandardError); msg = process->readAllStandardError(); } else { process->setReadChannel(QProcess::StandardOutput); msg = process->readAllStandardOutput(); } if (msg.isEmpty()) return; QPointer msgDlg = nullptr; QList dialogs = findChildren(); for (int i = 0; i < dialogs.count(); ++i) { if (dialogs.at (i)->parent() == process->parent()) { msgDlg = dialogs.at (i); break; } } if (msgDlg) { // append to the existing message if (QPlainTextEdit *tEdit = msgDlg->findChild()) { tEdit->setPlainText (tEdit->toPlainText() + "\n" + msg.constData()); QTextCursor cur = tEdit->textCursor(); cur.movePosition (QTextCursor::End); tEdit->setTextCursor (cur); } } else { msgDlg = new QDialog (qobject_cast(process->parent())); msgDlg->setObjectName ("processDialog"); msgDlg->setWindowTitle (tr ("Script Output")); msgDlg->setSizeGripEnabled (true); QGridLayout *grid = new QGridLayout; QLabel *label = new QLabel (msgDlg); label->setText ("
" + tr ("Script File") + ":
" + process->objectName() + ""); label->setTextInteractionFlags (Qt::TextSelectableByMouse); label->setWordWrap (true); label->setMargin (5); grid->addWidget (label, 0, 0, 1, 2); QPlainTextEdit *tEdit = new QPlainTextEdit (msgDlg); tEdit->setTextInteractionFlags (Qt::TextSelectableByMouse); tEdit->ensureCursorVisible(); grid->addWidget (tEdit, 1, 0, 1, 2); QPushButton *closeButton = new QPushButton (QIcon::fromTheme ("edit-delete"), tr ("Close")); connect (closeButton, &QAbstractButton::clicked, msgDlg, &QDialog::reject); grid->addWidget (closeButton, 2, 1, Qt::AlignRight); QPushButton *clearButton = new QPushButton (QIcon::fromTheme ("edit-clear"), tr ("Clear")); connect (clearButton, &QAbstractButton::clicked, tEdit, &QPlainTextEdit::clear); grid->addWidget (clearButton, 2, 0, Qt::AlignLeft); msgDlg->setLayout (grid); tEdit->setPlainText (msg.constData()); QTextCursor cur = tEdit->textCursor(); cur.movePosition (QTextCursor::End); tEdit->setTextCursor (cur); msgDlg->setAttribute (Qt::WA_DeleteOnClose); msgDlg->show(); msgDlg->raise(); msgDlg->activateWindow(); } } /*************************/ void FPwin::displayOutput() { displayMessage (false); } /*************************/ void FPwin::displayError() { displayMessage (true); } /*************************/ // This closes either the current page or the right-clicked side-pane item but // never the right-clicked tab because the tab context menu has no closing item. void FPwin::closeTab() { if (!isReady()) return; pauseAutoSaving (true); QListWidgetItem *curItem = nullptr; int index = -1; if (sidePane_ && rightClicked_ >= 0) // close the right-clicked item { index = ui->tabWidget->indexOf (sideItems_.value (sidePane_->listWidget()->item (rightClicked_))); if (index != ui->tabWidget->currentIndex()) curItem = sidePane_->listWidget()->currentItem(); } else // close the current page { index = ui->tabWidget->currentIndex(); if (index == -1) // not needed { pauseAutoSaving (false); return; } } if (savePrompt (index, false) != SAVED) { pauseAutoSaving (false); return; } deleteTabPage (index); int count = ui->tabWidget->count(); if (count == 0) { ui->actionReload->setDisabled (true); ui->actionSave->setDisabled (true); enableWidgets (false); } else // set focus to text-edit { if (count == 1) { ui->actionDetachTab->setDisabled (true); ui->actionRightTab->setDisabled (true); ui->actionLeftTab->setDisabled (true); ui->actionLastTab->setDisabled (true); ui->actionFirstTab->setDisabled (true); } if (curItem) // restore the current item sidePane_->listWidget()->setCurrentItem (curItem); if (TabPage *tabPage = qobject_cast< TabPage *>(ui->tabWidget->currentWidget())) tabPage->textEdit()->setFocus(); } pauseAutoSaving (false); } /*************************/ void FPwin::closeTabAtIndex (int index) { pauseAutoSaving (true); TabPage *curPage = nullptr; if (index != ui->tabWidget->currentIndex()) curPage = qobject_cast(ui->tabWidget->currentWidget()); if (savePrompt (index, false) != SAVED) { pauseAutoSaving (false); return; } closeWarningBar(); deleteTabPage (index); int count = ui->tabWidget->count(); if (count == 0) { ui->actionReload->setDisabled (true); ui->actionSave->setDisabled (true); enableWidgets (false); } else { if (count == 1) { ui->actionDetachTab->setDisabled (true); ui->actionRightTab->setDisabled (true); ui->actionLeftTab->setDisabled (true); ui->actionLastTab->setDisabled (true); ui->actionFirstTab->setDisabled (true); } if (curPage) // restore the current page ui->tabWidget->setCurrentWidget (curPage); if (TabPage *tabPage = qobject_cast< TabPage *>(ui->tabWidget->currentWidget())) tabPage->textEdit()->setFocus(); } pauseAutoSaving (false); } /*************************/ void FPwin::setTitle (const QString& fileName, int tabIndex) { int index = tabIndex; if (index < 0) index = ui->tabWidget->currentIndex(); // is never -1 bool isLink (false); QString shownName; if (fileName.isEmpty()) shownName = tr ("Untitled"); else { isLink = QFileInfo (fileName).isSymLink(); shownName = fileName.section ('/', -1); shownName.replace ("\n", " "); // no multi-line tab text } if (tabIndex < 0) setWindowTitle (shownName); shownName.replace ("&", "&&"); // single ampersand is for mnemonic ui->tabWidget->setTabText (index, shownName); if (isLink) ui->tabWidget->setTabIcon (index, QIcon (":icons/link.svg")); else ui->tabWidget->setTabIcon (index, QIcon()); if (sidePane_ && !sideItems_.isEmpty()) { if (QListWidgetItem *wi = sideItems_.key (qobject_cast(ui->tabWidget->widget (index)))) { wi->setText (shownName); if (isLink) wi->setIcon (QIcon (":icons/link.svg")); else wi->setIcon (QIcon()); } } } /*************************/ void FPwin::asterisk (bool modified) { int index = ui->tabWidget->currentIndex(); QString fname = qobject_cast< TabPage *>(ui->tabWidget->widget (index)) ->textEdit()->getFileName(); QString shownName; if (fname.isEmpty()) shownName = tr ("Untitled"); else shownName = fname.section ('/', -1); if (modified) shownName.prepend ("*"); shownName.replace ("\n", " "); setWindowTitle (shownName); shownName.replace ("&", "&&"); ui->tabWidget->setTabText (index, shownName); if (sidePane_) { if (modified) { shownName.remove (0, 1); shownName.append ("*"); } sidePane_->listWidget()->currentItem()->setText (shownName); } } /*************************/ void FPwin::waitToMakeBusy() { if (busyThread_ != nullptr) return; busyThread_ = new QThread; BusyMaker *makeBusy = new BusyMaker(); makeBusy->moveToThread (busyThread_); connect (busyThread_, &QThread::started, makeBusy, &BusyMaker::waiting); connect (busyThread_, &QThread::finished, busyThread_, &QObject::deleteLater); connect (busyThread_, &QThread::finished, makeBusy, &QObject::deleteLater); connect (makeBusy, &BusyMaker::finished, busyThread_, &QThread::quit); busyThread_->start(); } /*************************/ void FPwin::unbusy() { if (busyThread_ && !busyThread_->isFinished()) { busyThread_->quit(); busyThread_->wait(); } if (QGuiApplication::overrideCursor() != nullptr) QGuiApplication::restoreOverrideCursor(); } /*************************/ void FPwin::loadText (const QString& fileName, bool enforceEncod, bool reload, bool saveCursor, bool enforceUneditable, bool multiple) { if (loadingProcesses_ == 0) closeWarningBar(); ++ loadingProcesses_; QString charset; if (enforceEncod) charset = checkToEncoding(); Loading *thread = new Loading (fileName, charset, reload, saveCursor, enforceUneditable, multiple); connect (thread, &Loading::completed, this, &FPwin::addText); connect (thread, &Loading::finished, thread, &QObject::deleteLater); thread->start(); if (QGuiApplication::overrideCursor() == nullptr) waitToMakeBusy(); ui->tabWidget->tabBar()->lockTabs (true); updateShortcuts (true, false); } /*************************/ // When multiple files are being loaded, we don't change the current tab. void FPwin::addText (const QString& text, const QString& fileName, const QString& charset, bool enforceEncod, bool reload, bool saveCursor, bool uneditable, bool multiple) { if (fileName.isEmpty() || charset.isEmpty()) { if (!fileName.isEmpty() && charset.isEmpty()) // means a very large file connect (this, &FPwin::finishedLoading, this, &FPwin::onOpeningHugeFiles, Qt::UniqueConnection); else connect (this, &FPwin::finishedLoading, this, &FPwin::onPermissionDenied, Qt::UniqueConnection); -- loadingProcesses_; // can never become negative if (!isLoading()) { unbusy(); ui->tabWidget->tabBar()->lockTabs (false); updateShortcuts (false, false); emit finishedLoading(); } return; } if (enforceEncod || reload) multiple = false; // respect the logic /* only for the side-pane mode */ static bool scrollToFirstItem (false); static TabPage *firstPage = nullptr; TextEdit *textEdit; TabPage *tabPage; if (ui->tabWidget->currentIndex() == -1) tabPage = createEmptyTab (!multiple, false); else tabPage = qobject_cast(ui->tabWidget->currentWidget()); textEdit = tabPage->textEdit(); bool openInCurrentTab (true); if (!reload && !enforceEncod && (!textEdit->document()->isEmpty() || textEdit->document()->isModified() || !textEdit->getFileName().isEmpty())) { tabPage = createEmptyTab (!multiple, false); textEdit = tabPage->textEdit(); openInCurrentTab = false; } else { if (sidePane_ && !reload && !enforceEncod) // an unused empty tab scrollToFirstItem = true; /*if (isMinimized()) setWindowState (windowState() & (~Qt::WindowMinimized | Qt::WindowActive));*/ if (static_cast(qApp)->isX11() && isWindowShaded (winId())) unshadeWindow (winId()); activateWindow(); raise(); } textEdit->setSaveCursor (saveCursor); /* uninstall the syntax highlgihter to reinstall it below (when the text is reloaded, its encoding is enforced, or a new tab with normal as url was opened here) */ if (textEdit->getHighlighter()) { textEdit->setGreenSel (QList()); // they'll have no meaning later syntaxHighlighting (textEdit, false); } QFileInfo fInfo (fileName); if (scrollToFirstItem && (!firstPage || firstPage->textEdit()->getFileName().section ('/', -1). compare (fInfo.fileName(), Qt::CaseInsensitive) > 0)) { firstPage = tabPage; } /* this workaround, for the RTL bug in QPlainTextEdit, isn't needed because a better workaround is included in textedit.cpp */ /*QTextOption opt = textEdit->document()->defaultTextOption(); if (text.isRightToLeft()) { if (opt.alignment() == (Qt::AlignLeft)) { opt = QTextOption (Qt::AlignRight); opt.setTextDirection (Qt::LayoutDirectionAuto); textEdit->document()->setDefaultTextOption (opt); } } else if (opt.alignment() == (Qt::AlignRight)) { opt = QTextOption (Qt::AlignLeft); opt.setTextDirection (Qt::LayoutDirectionAuto); textEdit->document()->setDefaultTextOption (opt); }*/ /* we want to restore the cursor later */ int pos = 0, anchor = 0; int scrollbarValue = -1; if (reload) { pos = textEdit->textCursor().position(); anchor = textEdit->textCursor().anchor(); if (QScrollBar *scrollbar = textEdit->verticalScrollBar()) { if (scrollbar->isVisible()) scrollbarValue = scrollbar->value(); } } /* set the text */ disconnect (textEdit->document(), &QTextDocument::modificationChanged, ui->actionSave, &QAction::setEnabled); disconnect (textEdit->document(), &QTextDocument::modificationChanged, this, &FPwin::asterisk); textEdit->setPlainText (text); connect (textEdit->document(), &QTextDocument::modificationChanged, ui->actionSave, &QAction::setEnabled); connect (textEdit->document(), &QTextDocument::modificationChanged, this, &FPwin::asterisk); Config& config = static_cast(qApp)->getConfig(); /* now, restore the cursor */ if (reload) { QTextCursor cur = textEdit->textCursor(); cur.movePosition (QTextCursor::End, QTextCursor::MoveAnchor); int curPos = cur.position(); if (anchor <= curPos && pos <= curPos) { cur.setPosition (anchor); cur.setPosition (pos, QTextCursor::KeepAnchor); } textEdit->setTextCursor (cur); } else if (saveCursor) { QHash cursorPos = config.savedCursorPos(); if (cursorPos.contains (fileName)) { QTextCursor cur = textEdit->textCursor(); cur.movePosition (QTextCursor::End, QTextCursor::MoveAnchor); int pos = qMin (qMax (cursorPos.value (fileName, 0).toInt(), 0), cur.position()); cur.setPosition (pos); textEdit->setTextCursor (cur); } } textEdit->setFileName (fileName); textEdit->setSize (fInfo.size()); textEdit->setLastModified (fInfo.lastModified()); lastFile_ = fileName; if (config.getRecentOpened()) config.addRecentFile (lastFile_); textEdit->setEncoding (charset); textEdit->setWordNumber (-1); if (uneditable) { connect (this, &FPwin::finishedLoading, this, &FPwin::onOpeningUneditable, Qt::UniqueConnection); textEdit->makeUneditable (uneditable); } setProgLang (textEdit); if (ui->actionSyntax->isChecked()) syntaxHighlighting (textEdit); setTitle (fileName, (multiple && !openInCurrentTab) ? /* the index may have changed because syntaxHighlighting() waits for all events to be processed (but it won't change from here on) */ ui->tabWidget->indexOf (tabPage) : -1); QString tip (fInfo.absolutePath() + "/"); QFontMetrics metrics (QToolTip::font()); int w = QApplication::desktop()->screenGeometry().width(); if (w > 200 * metrics.width (' ')) w = 200 * metrics.width (' '); QString elidedTip = metrics.elidedText (tip, Qt::ElideMiddle, w); ui->tabWidget->setTabToolTip (ui->tabWidget->indexOf (tabPage), elidedTip); if (!sideItems_.isEmpty()) { if (QListWidgetItem *wi = sideItems_.key (tabPage)) wi->setToolTip (elidedTip); } if (uneditable || alreadyOpen (tabPage)) { textEdit->setReadOnly (true); if (!textEdit->hasDarkScheme()) { if (uneditable) // as with Help textEdit->viewport()->setStyleSheet (".QWidget {" "color: black;" "background-color: rgb(225, 238, 255);}"); else textEdit->viewport()->setStyleSheet (".QWidget {" "color: black;" "background-color: rgb(236, 236, 208);}"); } else { if (uneditable) textEdit->viewport()->setStyleSheet (".QWidget {" "color: white;" "background-color: rgb(0, 60, 110);}"); else textEdit->viewport()->setStyleSheet (".QWidget {" "color: white;" "background-color: rgb(60, 0, 0);}"); } if (!multiple || openInCurrentTab) { if (!uneditable) ui->actionEdit->setVisible (true); else ui->actionSaveAs->setDisabled (true); ui->actionCut->setDisabled (true); ui->actionPaste->setDisabled (true); ui->actionDate->setDisabled (true); ui->actionDelete->setDisabled (true); } disconnect (textEdit, &QPlainTextEdit::copyAvailable, ui->actionCut, &QAction::setEnabled); disconnect (textEdit, &QPlainTextEdit::copyAvailable, ui->actionDelete, &QAction::setEnabled); } else if (textEdit->isReadOnly()) QTimer::singleShot (0, this, SLOT (makeEditable())); if (!multiple || openInCurrentTab) { if (ui->statusBar->isVisible()) { statusMsgWithLineCount (textEdit->document()->blockCount()); if (QToolButton *wordButton = ui->statusBar->findChild("wordButton")) wordButton->setVisible (true); if (text.isEmpty()) updateWordInfo(); } if (config.getShowLangSelector() && config.getSyntaxByDefault()) showLang (textEdit); encodingToCheck (charset); ui->actionReload->setEnabled (true); textEdit->setFocus(); // the text may have been opened in this (empty) tab if (openInCurrentTab) { if (isScriptLang (textEdit->getProg()) && fInfo.isExecutable()) ui->actionRun->setVisible (config.getExecuteScripts()); else ui->actionRun->setVisible (false); } } /* a file is completely loaded */ -- loadingProcesses_; if (!isLoading()) { unbusy(); ui->tabWidget->tabBar()->lockTabs (false); updateShortcuts (false, false); if (reload && scrollbarValue > -1) { // restore the scrollbar position lambdaConnection_ = QObject::connect (this, &FPwin::finishedLoading, textEdit, [this, textEdit, scrollbarValue]() { if (QScrollBar *scrollbar = textEdit->verticalScrollBar()) { if (scrollbar->isVisible()) scrollbar->setValue (scrollbarValue); } disconnectLambda(); }); } /* select the first item (sidePane_ exists) */ else if (firstPage && !sideItems_.isEmpty()) { if (QListWidgetItem *wi = sideItems_.key (firstPage)) sidePane_->listWidget()->setCurrentItem (wi); } /* reset the static variables */ scrollToFirstItem = false; firstPage = nullptr; emit finishedLoading(); } } /*************************/ void FPwin::disconnectLambda() { QObject::disconnect (lambdaConnection_); } /*************************/ void FPwin::onOpeningHugeFiles() { disconnect (this, &FPwin::finishedLoading, this, &FPwin::onOpeningHugeFiles); QTimer::singleShot (100, this, [=]() { // TabWidget has a 50-ms timer showWarningBar ("
" + tr ("Huge file(s) not opened!") + "
\n" + "
" + tr ("FeatherPad does not open files larger than 100 MiB.") + "
"); }); } /*************************/ void FPwin::onPermissionDenied() { disconnect (this, &FPwin::finishedLoading, this, &FPwin::onPermissionDenied); QTimer::singleShot (100, this, [=]() { showWarningBar ("
" + tr ("Some file(s) could not be opened!") + "
\n" + "
" + tr ("You may not have the permission to read.") + "
"); }); } /*************************/ void FPwin::onOpeningUneditable() { disconnect (this, &FPwin::finishedLoading, this, &FPwin::onOpeningUneditable); QTimer::singleShot (100, this, [=]() { this->showWarningBar ("
" + tr ("Uneditable file(s)!") + "
\n" + "
" + tr ("Non-text files or files with huge lines cannot be edited.") + "
"); }); } /*************************/ void FPwin::showWarningBar (const QString& message) { /* don't close and show the same warning bar */ if (QLayoutItem *item = ui->verticalLayout->itemAt (ui->verticalLayout->count() - 1)) { if (WarningBar *wb = qobject_cast(item->widget())) { if (wb->getMessage() == message) return; ui->verticalLayout->removeWidget (item->widget()); delete wb; } } WarningBar *bar = new WarningBar (message, iconMode_); ui->verticalLayout->insertWidget (ui->verticalLayout->count(), bar); // at end connect (bar, &WarningBar::closeButtonPressed, [=] { ui->verticalLayout->removeWidget(bar); bar->deleteLater(); }); /* close the bar when the text is scrolled */ if (TabPage *tabPage = qobject_cast(ui->tabWidget->currentWidget())) { connect (tabPage->textEdit(), &QPlainTextEdit::updateRequest, bar, [=](const QRect&, int dy) { if (dy != 0) closeWarningBar(); }); } } /*************************/ void FPwin::showCrashWarning() { QTimer::singleShot (0, this, [=]() { this->showWarningBar ("
" + tr ("A previous crash detected!") + "
" + "
" +tr ("Preferably, close all FeatherPad windows and start again!") + "
"); }); } /*************************/ void FPwin::closeWarningBar() { if (QLayoutItem *item = ui->verticalLayout->itemAt (ui->verticalLayout->count() - 1)) { if (WarningBar *wb = qobject_cast(item->widget())) { ui->verticalLayout->removeWidget (item->widget()); delete wb; // delete it immediately because a modal dialog might pop up } } } /*************************/ void FPwin::newTabFromName (const QString& fileName, bool saveCursor, bool multiple) { if (!fileName.isEmpty() /* although loadText() takes care of folders, we don't want to open (a symlink to) /dev/null and then, get a prompt dialog on closing */ && QFileInfo (fileName).isFile()) { loadText (fileName, false, false, saveCursor, false, multiple); } } /*************************/ void FPwin::newTabFromRecent() { QAction *action = qobject_cast(QObject::sender()); if (!action) return; loadText (action->data().toString(), false, false); } /*************************/ void FPwin::fileOpen() { if (isLoading()) return; /* find a suitable directory */ QString fname; if (TabPage *tabPage = qobject_cast(ui->tabWidget->currentWidget())) fname = tabPage->textEdit()->getFileName(); QString path; if (!fname.isEmpty()) { if (QFile::exists (fname)) path = fname; else { QDir dir = QFileInfo (fname).absoluteDir(); if (!dir.exists()) dir = QDir::home(); path = dir.path(); } } else { /* I like the last opened file to be remembered */ fname = lastFile_; if (!fname.isEmpty()) { QDir dir = QFileInfo (fname).absoluteDir(); if (!dir.exists()) dir = QDir::home(); path = dir.path(); } else { QDir dir = QDir::home(); path = dir.path(); } } if (hasAnotherDialog()) return; updateShortcuts (true); QString filter = tr ("All Files (*)"); if (!fname.isEmpty() && QFileInfo (fname).fileName().contains ('.')) { /* if relevant, do filtering to make opening of similar files easier */ filter = tr ("All Files (*);;.%1 Files (*.%1)").arg (fname.section ('.', -1, -1)); } FileDialog dialog (this, static_cast(qApp)->getConfig().getNativeDialog()); dialog.setAcceptMode (QFileDialog::AcceptOpen); dialog.setWindowTitle (tr ("Open file...")); dialog.setFileMode (QFileDialog::ExistingFiles); dialog.setNameFilter (filter); /*dialog.setLabelText (QFileDialog::Accept, tr ("Open")); dialog.setLabelText (QFileDialog::Reject, tr ("Cancel"));*/ if (QFileInfo (path).isDir()) dialog.setDirectory (path); else { dialog.selectFile (path); dialog.autoScroll(); } if (dialog.exec()) { const QStringList files = dialog.selectedFiles(); bool multiple (files.count() > 1 || isLoading()); for (const QString &file : files) newTabFromName (file, false, multiple); } updateShortcuts (false); } /*************************/ // Check if the file is already opened for editing somewhere else. bool FPwin::alreadyOpen (TabPage *tabPage) const { bool res = false; QString fileName = tabPage->textEdit()->getFileName(); QFileInfo info (fileName); QString target = info.isSymLink() ? info.symLinkTarget() // consider symlinks too : fileName; FPsingleton *singleton = static_cast(qApp); for (int i = 0; i < singleton->Wins.count(); ++i) { FPwin *thisOne = singleton->Wins.at (i); for (int j = 0; j < thisOne->ui->tabWidget->count(); ++j) { TabPage *thisTabPage = qobject_cast(thisOne->ui->tabWidget->widget (j)); if (thisOne == this && thisTabPage == tabPage) continue; TextEdit *thisTextEdit = thisTabPage->textEdit(); if (thisTextEdit->isReadOnly()) continue; QFileInfo thisInfo (thisTextEdit->getFileName()); QString thisTarget = thisInfo.isSymLink() ? thisInfo.symLinkTarget() : thisTextEdit->getFileName(); if (thisTarget == target) { res = true; break; } } if (res) break; } return res; } /*************************/ void FPwin::enforceEncoding (QAction*) { /* here, we don't need to check if some files are loading because encoding has no keyboard shortcut or tool button */ int index = ui->tabWidget->currentIndex(); if (index == -1) return; TextEdit *textEdit = qobject_cast< TabPage *>(ui->tabWidget->widget (index))->textEdit(); QString fname = textEdit->getFileName(); if (!fname.isEmpty()) { if (savePrompt (index, false) != SAVED) { // back to the previous encoding encodingToCheck (textEdit->getEncoding()); return; } textEdit->setLang (QString()); // remove the enforced syntax loadText (fname, true, true, false, textEdit->isUneditable(), false); } else { /* just change the statusbar text; the doc might be saved later with the new encoding */ textEdit->setEncoding (checkToEncoding()); if (ui->statusBar->isVisible()) { QLabel *statusLabel = ui->statusBar->findChild("statusLabel"); QString str = statusLabel->text(); QString encodStr = tr ("Encoding"); // the next info is about lines; there's no syntax info QString lineStr = "    " + tr ("Lines"); int i = str.indexOf (encodStr); int j = str.indexOf (lineStr); int offset = encodStr.count() + 9; // size of ": " str.replace (i + offset, j - i - offset, checkToEncoding()); statusLabel->setText (str); } } } /*************************/ void FPwin::reload() { if (isLoading()) return; int index = ui->tabWidget->currentIndex(); if (index == -1) return; if (savePrompt (index, false) != SAVED) return; TextEdit *textEdit = qobject_cast< TabPage *>(ui->tabWidget->widget (index))->textEdit(); textEdit->setLang (QString()); // remove the enforced syntax QString fname = textEdit->getFileName(); if (!fname.isEmpty()) { loadText (fname, false, true, textEdit->getSaveCursor()); } } /*************************/ static inline int trailingSpaces (const QString &str) { int i = 0; while (i < str.length()) { if (!str.at (str.length() - 1 - i).isSpace()) return i; ++i; } return i; } /*************************/ // This is for both "Save" and "Save As" bool FPwin::saveFile (bool keepSyntax) { if (!isReady()) return false; int index = ui->tabWidget->currentIndex(); if (index == -1) return false; TabPage *tabPage = qobject_cast< TabPage *>(ui->tabWidget->widget (index)); TextEdit *textEdit = tabPage->textEdit(); QString fname = textEdit->getFileName(); if (fname.isEmpty()) fname = lastFile_; QString filter = tr ("All Files (*)"); if (!fname.isEmpty() && QFileInfo (fname).fileName().contains ('.')) { /* if relevant, do filtering to prevent disastrous overwritings */ filter = tr (".%1 Files (*.%1);;All Files (*)").arg (fname.section ('.', -1, -1)); } Config& config = static_cast(qApp)->getConfig(); if (fname.isEmpty() || !QFile::exists (fname) || textEdit->getFileName().isEmpty()) { bool restorable = false; if (fname.isEmpty()) { QDir dir = QDir::home(); fname = dir.filePath (tr ("Untitled")); } else if (!QFile::exists (fname)) { QDir dir = QFileInfo (fname).absoluteDir(); if (!dir.exists()) { dir = QDir::home(); if (textEdit->getFileName().isEmpty()) filter = tr ("All Files (*)"); } /* if the removed file is opened in this tab and its containing folder still exists, it's restorable */ else if (!textEdit->getFileName().isEmpty()) restorable = true; /* add the file name */ if (!textEdit->getFileName().isEmpty()) fname = dir.filePath (QFileInfo (fname).fileName()); else fname = dir.filePath (tr ("Untitled")); } else fname = QFileInfo (fname).absoluteDir().filePath (tr ("Untitled")); /* use Save-As for Save or saving */ if (!restorable && QObject::sender() != ui->actionSaveAs && QObject::sender() != ui->actionSaveCodec) { if (hasAnotherDialog()) return false; updateShortcuts (true); FileDialog dialog (this, config.getNativeDialog()); dialog.setAcceptMode (QFileDialog::AcceptSave); dialog.setWindowTitle (tr ("Save as...")); dialog.setFileMode (QFileDialog::AnyFile); dialog.setNameFilter (filter); dialog.selectFile (fname); dialog.autoScroll(); /*dialog.setLabelText (QFileDialog::Accept, tr ("Save")); dialog.setLabelText (QFileDialog::Reject, tr ("Cancel"));*/ if (dialog.exec()) { fname = dialog.selectedFiles().at (0); if (fname.isEmpty() || QFileInfo (fname).isDir()) { updateShortcuts (false); return false; } } else { updateShortcuts (false); return false; } updateShortcuts (false); } } if (QObject::sender() == ui->actionSaveAs) { if (hasAnotherDialog()) return false; updateShortcuts (true); FileDialog dialog (this, config.getNativeDialog()); dialog.setAcceptMode (QFileDialog::AcceptSave); dialog.setWindowTitle (tr ("Save as...")); dialog.setFileMode (QFileDialog::AnyFile); dialog.setNameFilter (filter); dialog.selectFile (fname); dialog.autoScroll(); /*dialog.setLabelText (QFileDialog::Accept, tr ("Save")); dialog.setLabelText (QFileDialog::Reject, tr ("Cancel"));*/ if (dialog.exec()) { fname = dialog.selectedFiles().at (0); if (fname.isEmpty() || QFileInfo (fname).isDir()) { updateShortcuts (false); return false; } } else { updateShortcuts (false); return false; } updateShortcuts (false); } else if (QObject::sender() == ui->actionSaveCodec) { if (hasAnotherDialog()) return false; updateShortcuts (true); FileDialog dialog (this, config.getNativeDialog()); dialog.setAcceptMode (QFileDialog::AcceptSave); dialog.setWindowTitle (tr ("Keep encoding and save as...")); dialog.setFileMode (QFileDialog::AnyFile); dialog.setNameFilter (filter); dialog.selectFile (fname); dialog.autoScroll(); /*dialog.setLabelText (QFileDialog::Accept, tr ("Save")); dialog.setLabelText (QFileDialog::Reject, tr ("Cancel"));*/ if (dialog.exec()) { fname = dialog.selectedFiles().at (0); if (fname.isEmpty() || QFileInfo (fname).isDir()) { updateShortcuts (false); return false; } } else { updateShortcuts (false); return false; } updateShortcuts (false); } if (config.getRemoveTrailingSpaces()) { /* using text blocks directly is the fastest and lightest way of removing trailing spaces */ if (QGuiApplication::overrideCursor() == nullptr) waitToMakeBusy(); QTextBlock block = textEdit->document()->firstBlock(); QTextCursor tmpCur = textEdit->textCursor(); tmpCur.beginEditBlock(); while (block.isValid()) { if (const int num = trailingSpaces (block.text())) { tmpCur.setPosition (block.position() + block.text().length()); if (num > 1 && textEdit->getProg() == "markdown") // md sees two trailing spaces as a new line tmpCur.movePosition (QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, num - 2); else tmpCur.movePosition (QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, num); tmpCur.removeSelectedText(); } block = block.next(); } tmpCur.endEditBlock(); unbusy(); } if (config.getAppendEmptyLine() && !textEdit->document()->lastBlock().text().isEmpty()) { QTextCursor tmpCur = textEdit->textCursor(); tmpCur.beginEditBlock(); tmpCur.movePosition (QTextCursor::End); tmpCur.insertBlock(); tmpCur.endEditBlock(); } /* now, try to write */ QTextDocumentWriter writer (fname, "plaintext"); bool success = false; if (QObject::sender() == ui->actionSaveCodec) { QString encoding = checkToEncoding(); if (hasAnotherDialog()) return false; updateShortcuts (true); MessageBox msgBox (this); msgBox.setIcon (QMessageBox::Question); msgBox.addButton (QMessageBox::Yes); msgBox.addButton (QMessageBox::No); msgBox.addButton (QMessageBox::Cancel); msgBox.changeButtonText (QMessageBox::Yes, tr ("Yes")); msgBox.changeButtonText (QMessageBox::No, tr ("No")); msgBox.changeButtonText (QMessageBox::Cancel, tr ("Cancel")); msgBox.setText ("
" + tr ("Do you want to use MS Windows end-of-lines?") + "
"); msgBox.setInformativeText ("
" + tr ("This may be good for readability under MS Windows.") + "
"); msgBox.setWindowModality (Qt::WindowModal); QString contents; int ln; QTextCodec *codec; QByteArray encodedString; const char *txt; switch (msgBox.exec()) { case QMessageBox::Yes: contents = textEdit->document()->toPlainText(); contents.replace ("\n", "\r\n"); ln = contents.length(); // for fwrite(); codec = QTextCodec::codecForName (checkToEncoding().toUtf8()); encodedString = codec->fromUnicode (contents); txt = encodedString.constData(); if (encoding != "UTF-16") { std::ofstream file; file.open (fname.toUtf8().constData()); if (file.is_open()) { file << txt; file.close(); success = true; } } else { FILE * file; file = fopen (fname.toUtf8().constData(), "wb"); if (file != nullptr) { /* this worked correctly as I far as I tested */ fwrite (txt , 2 , ln + 1 , file); fclose (file); success = true; } } break; case QMessageBox::No: writer.setCodec (QTextCodec::codecForName (encoding.toUtf8())); break; default: updateShortcuts (false); return false; break; } updateShortcuts (false); } if (!success) success = writer.write (textEdit->document()); if (success) { QFileInfo fInfo (fname); textEdit->document()->setModified (false); textEdit->setFileName (fname); textEdit->setSize (fInfo.size()); textEdit->setLastModified (fInfo.lastModified()); ui->actionReload->setDisabled (false); setTitle (fname); QString tip (fInfo.absolutePath() + "/"); QFontMetrics metrics (QToolTip::font()); int w = QApplication::desktop()->screenGeometry().width(); if (w > 200 * metrics.width (' ')) w = 200 * metrics.width (' '); QString elidedTip = metrics.elidedText (tip, Qt::ElideMiddle, w); ui->tabWidget->setTabToolTip (index, elidedTip); if (!sideItems_.isEmpty()) { if (QListWidgetItem *wi = sideItems_.key (tabPage)) wi->setToolTip (elidedTip); } lastFile_ = fname; config.addRecentFile (lastFile_); if (!keepSyntax) { // uninstall and reinstall the syntax highlgihter if the programming language is changed QString prevLan = textEdit->getProg(); setProgLang (textEdit); if (prevLan != textEdit->getProg()) { if (config.getShowLangSelector() && config.getSyntaxByDefault()) { if (textEdit->getLang() == textEdit->getProg()) textEdit->setLang (QString()); // not enforced because it's the real syntax showLang (textEdit); } if (ui->statusBar->isVisible() && textEdit->getWordNumber() != -1) { // we want to change the statusbar text below disconnect (textEdit->document(), &QTextDocument::contentsChange, this, &FPwin::updateWordInfo); } if (textEdit->getLang().isEmpty()) { // restart the syntax highlighting only when the language isn't forced syntaxHighlighting (textEdit, false); if (ui->actionSyntax->isChecked()) syntaxHighlighting (textEdit); } if (ui->statusBar->isVisible()) { // correct the statusbar text just by replacing the old syntax info QLabel *statusLabel = ui->statusBar->findChild("statusLabel"); QString str = statusLabel->text(); QString syntaxStr = tr ("Syntax"); int i = str.indexOf (syntaxStr); if (i == -1) // there was no language before saving (prevLan.isEmpty()) { QString lineStr = "    " + tr ("Lines"); int j = str.indexOf (lineStr); syntaxStr = "    " + tr ("Syntax") + QString (": %1") .arg (textEdit->getProg()); str.insert (j, syntaxStr); } else { if (textEdit->getProg().isEmpty()) // there's no language after saving { syntaxStr = "    " + tr ("Syntax"); QString lineStr = "    " + tr ("Lines"); int j = str.indexOf (syntaxStr); int k = str.indexOf (lineStr); str.remove (j, k - j); } else // the language is changed by saving { QString lineStr = "
    " + tr ("Lines"); int j = str.indexOf (lineStr); int offset = syntaxStr.count() + 9; // size of ": " str.replace (i + offset, j - i - offset, textEdit->getProg()); } } statusLabel->setText (str); if (textEdit->getWordNumber() != -1) connect (textEdit->document(), &QTextDocument::contentsChange, this, &FPwin::updateWordInfo); } } } } else { QString str = writer.device()->errorString(); showWarningBar ("
" + tr ("Cannot be saved!") + "
\n" + "
" + QString ("
%1.
").arg (str) + "
"); } if (success && textEdit->isReadOnly() && !alreadyOpen (tabPage)) QTimer::singleShot (0, this, SLOT (makeEditable())); return success; } /*************************/ void FPwin::cutText() { if (TabPage *tabPage = qobject_cast(ui->tabWidget->currentWidget())) tabPage->textEdit()->cut(); } /*************************/ void FPwin::copyText() { if (TabPage *tabPage = qobject_cast(ui->tabWidget->currentWidget())) tabPage->textEdit()->copy(); } /*************************/ void FPwin::pasteText() { if (TabPage *tabPage = qobject_cast(ui->tabWidget->currentWidget())) tabPage->textEdit()->paste(); } /*************************/ void FPwin::insertDate() { if (TabPage *tabPage = qobject_cast(ui->tabWidget->currentWidget())) { Config config = static_cast(qApp)->getConfig(); QString format = config.getDateFormat(); tabPage->textEdit()->insertPlainText (QDateTime::currentDateTime().toString (format.isEmpty() ? "MMM dd, yyyy, hh:mm:ss" : format)); } } /*************************/ void FPwin::deleteText() { if (TabPage *tabPage = qobject_cast(ui->tabWidget->currentWidget())) { TextEdit *textEdit = tabPage->textEdit(); if (!textEdit->isReadOnly()) textEdit->insertPlainText (""); } } /*************************/ void FPwin::selectAllText() { if (TabPage *tabPage = qobject_cast(ui->tabWidget->currentWidget())) tabPage->textEdit()->selectAll(); } /*************************/ void FPwin::makeEditable() { if (!isReady()) return; int index = ui->tabWidget->currentIndex(); if (index == -1) return; TextEdit *textEdit = qobject_cast< TabPage *>(ui->tabWidget->widget (index))->textEdit(); bool textIsSelected = textEdit->textCursor().hasSelection(); textEdit->setReadOnly (false); Config config = static_cast(qApp)->getConfig(); if (!textEdit->hasDarkScheme()) { textEdit->viewport()->setStyleSheet (QString (".QWidget {" "color: black;" "background-color: rgb(%1, %1, %1);}") .arg (config.getLightBgColorValue())); } else { textEdit->viewport()->setStyleSheet (QString (".QWidget {" "color: white;" "background-color: rgb(%1, %1, %1);}") .arg (config.getDarkBgColorValue())); } ui->actionEdit->setVisible (false); ui->actionPaste->setEnabled (true); ui->actionDate->setEnabled (true); ui->actionCopy->setEnabled (textIsSelected); ui->actionCut->setEnabled (textIsSelected); ui->actionDelete->setEnabled (textIsSelected); connect (textEdit, &QPlainTextEdit::copyAvailable, ui->actionCut, &QAction::setEnabled); connect (textEdit, &QPlainTextEdit::copyAvailable, ui->actionDelete, &QAction::setEnabled); } /*************************/ void FPwin::undoing() { int index = ui->tabWidget->currentIndex(); if (index == -1) return; TextEdit *textEdit = qobject_cast< TabPage *>(ui->tabWidget->widget (index))->textEdit(); textEdit->removeGreenHighlights(); // always remove replacing highlights before undoing textEdit->undo(); } /*************************/ void FPwin::redoing() { if (TabPage *tabPage = qobject_cast(ui->tabWidget->currentWidget())) tabPage->textEdit()->redo(); } /*************************/ void FPwin::changeTab (QListWidgetItem *current, QListWidgetItem* /*previous*/) { if (!sidePane_ || sideItems_.isEmpty()) return; ui->tabWidget->setCurrentWidget (sideItems_.value (current)); } /*************************/ // Change the window title and the search entry when switching tabs and... void FPwin::tabSwitch (int index) { if (index == -1) { setWindowTitle ("FeatherPad[*]"); setWindowModified (false); return; } closeWarningBar(); TabPage *tabPage = qobject_cast< TabPage *>(ui->tabWidget->widget (index)); TextEdit *textEdit = tabPage->textEdit(); if (!tabPage->isSearchBarVisible()) textEdit->setFocus(); QString fname = textEdit->getFileName(); bool modified (textEdit->document()->isModified()); QFileInfo info; QString shownName; if (fname.isEmpty()) { if (textEdit->getProg() == "help") shownName = "** " + tr ("Help") + " **"; else shownName = tr ("Untitled"); } else { info.setFile (fname); shownName = fname.section ('/', -1); if (!QFile::exists (fname)) showWarningBar ("
" + tr ("The file has been removed.") + "
"); else if (textEdit->getLastModified() != info.lastModified()) showWarningBar ("
" + tr ("This file has been modified elsewhere or in another way!") + "
\n" + "
" + tr ("Please be careful about reloading or saving this document!") + "
"); } if (modified) shownName.prepend ("*"); shownName.replace ("\n", " "); setWindowTitle (shownName); /* although the window size, wrapping state or replacing text may have changed or the replace dock may have been closed, hlight() will be called automatically */ //if (!textEdit->getSearchedText().isEmpty()) hlight(); /* correct the encoding menu */ encodingToCheck (textEdit->getEncoding()); /* correct the states of some buttons */ ui->actionUndo->setEnabled (textEdit->document()->isUndoAvailable()); ui->actionRedo->setEnabled (textEdit->document()->isRedoAvailable()); ui->actionSave->setEnabled (modified); ui->actionReload->setEnabled (!fname.isEmpty()); bool readOnly = textEdit->isReadOnly(); if (fname.isEmpty() && !modified && !textEdit->document()->isEmpty()) // 'Help' is an exception { ui->actionEdit->setVisible (false); ui->actionSaveAs->setEnabled (true); } else { ui->actionEdit->setVisible (readOnly && !textEdit->isUneditable()); ui->actionSaveAs->setEnabled (!textEdit->isUneditable()); } ui->actionPaste->setEnabled (!readOnly); ui->actionDate->setEnabled (!readOnly); bool textIsSelected = textEdit->textCursor().hasSelection(); ui->actionCopy->setEnabled (textIsSelected); ui->actionCut->setEnabled (!readOnly && textIsSelected); ui->actionDelete->setEnabled (!readOnly && textIsSelected); Config config = static_cast(qApp)->getConfig(); if (isScriptLang (textEdit->getProg()) && info.isExecutable()) ui->actionRun->setVisible (config.getExecuteScripts()); else ui->actionRun->setVisible (false); /* handle the spinbox */ if (ui->spinBox->isVisible()) ui->spinBox->setMaximum (textEdit->document()->blockCount()); /* handle the statusbar */ if (ui->statusBar->isVisible()) { statusMsgWithLineCount (textEdit->document()->blockCount()); QToolButton *wordButton = ui->statusBar->findChild("wordButton"); if (textEdit->getWordNumber() == -1) { if (wordButton) wordButton->setVisible (true); if (textEdit->document()->isEmpty()) // make an exception updateWordInfo(); } else { if (wordButton) wordButton->setVisible (false); QLabel *statusLabel = ui->statusBar->findChild("statusLabel"); statusLabel->setText (QString ("%1 %2") .arg (statusLabel->text()) .arg (textEdit->getWordNumber())); } showCursorPos(); } if (config.getShowLangSelector() && config.getSyntaxByDefault()) showLang (textEdit); /* al last, set the title of Replacment dock */ if (ui->dockReplace->isVisible()) { QString title = textEdit->getReplaceTitle(); if (!title.isEmpty()) ui->dockReplace->setWindowTitle (title); else ui->dockReplace->setWindowTitle (tr ("Rep&lacement")); } else textEdit->setReplaceTitle (QString()); } /*************************/ void FPwin::fontDialog() { if (isLoading()) return; int index = ui->tabWidget->currentIndex(); if (index == -1) return; if (hasAnotherDialog()) return; updateShortcuts (true); TextEdit *textEdit = qobject_cast< TabPage *>(ui->tabWidget->widget (index))->textEdit(); QFont currentFont = textEdit->getDefaultFont(); QFontDialog fd (currentFont, this); //fd.setOption (QFontDialog::DontUseNativeDialog); fd.setWindowModality (Qt::WindowModal); fd.move (x() + width()/2 - fd.width()/2, y() + height()/2 - fd.height()/ 2); if (fd.exec()) { QFont newFont = fd.selectedFont(); FPsingleton *singleton = static_cast(qApp); for (int i = 0; i < singleton->Wins.count(); ++i) { FPwin *thisWin = singleton->Wins.at (i); for (int j = 0; j < thisWin->ui->tabWidget->count(); ++j) { TextEdit *thisTextEdit = qobject_cast< TabPage *>(thisWin->ui->tabWidget->widget (j))->textEdit(); thisTextEdit->setEditorFont (newFont); } } Config& config = static_cast(qApp)->getConfig(); if (config.getRemFont()) config.setFont (newFont); /* the font can become larger... */ textEdit->adjustScrollbars(); /* ... or smaller */ reformat (textEdit); } updateShortcuts (false); } /*************************/ void FPwin::changeEvent (QEvent *event) { Config& config = static_cast(qApp)->getConfig(); if (config.getRemSize() && event->type() == QEvent::WindowStateChange) { if (windowState() == Qt::WindowFullScreen) { config.setIsFull (true); config.setIsMaxed (false); } else if (windowState() == (Qt::WindowFullScreen ^ Qt::WindowMaximized)) { config.setIsFull (true); config.setIsMaxed (true); } else { config.setIsFull (false); if (windowState() == Qt::WindowMaximized) config.setIsMaxed (true); else config.setIsMaxed (false); } } QWidget::changeEvent (event); } /*************************/ bool FPwin::event (QEvent *event) { if (event->type() == QEvent::ActivationChange && isActiveWindow()) { if (TabPage *tabPage = qobject_cast< TabPage *>(ui->tabWidget->currentWidget())) { TextEdit *textEdit = tabPage->textEdit(); QString fname = textEdit->getFileName(); if (!fname.isEmpty()) { if (!QFile::exists (fname)) showWarningBar ("
" + tr ("The file has been removed.") + "
"); else if (textEdit->getLastModified() != QFileInfo (fname).lastModified()) showWarningBar ("
" + tr ("This file has been modified elsewhere or in another way!") + "
\n" + "
" + tr ("Please be careful about reloading or saving this document!") + "
"); } } } return QMainWindow::event (event); } /*************************/ void FPwin::showHideSearch() { if (!isReady()) return; int index = ui->tabWidget->currentIndex(); if (index == -1) return; TabPage *tabPage = qobject_cast< TabPage *>(ui->tabWidget->widget (index)); bool isFocused = tabPage->isSearchBarVisible() && tabPage->searchBarHasFocus(); if (!isFocused) tabPage->focusSearchBar(); else { ui->dockReplace->setVisible (false); // searchbar is needed by replace dock /* return focus to the document,... */ tabPage->textEdit()->setFocus(); } int count = ui->tabWidget->count(); for (int indx = 0; indx < count; ++indx) { TabPage *page = qobject_cast< TabPage *>(ui->tabWidget->widget (indx)); if (isFocused) { /* ... remove all yellow and green highlights... */ TextEdit *textEdit = page->textEdit(); textEdit->setSearchedText (QString()); QList es; textEdit->setGreenSel (es); // not needed if (ui->actionLineNumbers->isChecked() || ui->spinBox->isVisible()) es.prepend (textEdit->currentLineSelection()); es.append (textEdit->getRedSel()); textEdit->setExtraSelections (es); /* ... and empty all search entries */ page->clearSearchEntry(); } page->setSearchBarVisible (!isFocused); } } /*************************/ void FPwin::jumpTo() { if (!isReady()) return; bool visibility = ui->spinBox->isVisible(); for (int i = 0; i < ui->tabWidget->count(); ++i) { TextEdit *thisTextEdit = qobject_cast< TabPage *>(ui->tabWidget->widget (i))->textEdit(); if (!ui->actionLineNumbers->isChecked()) thisTextEdit->showLineNumbers (!visibility); if (!visibility) { /* setMaximum() isn't a slot */ connect (thisTextEdit->document(), &QTextDocument::blockCountChanged, this, &FPwin::setMax); } else disconnect (thisTextEdit->document(), &QTextDocument::blockCountChanged, this, &FPwin::setMax); } TabPage *tabPage = qobject_cast< TabPage *>(ui->tabWidget->currentWidget()); if (tabPage) { if (!visibility && ui->tabWidget->count() > 0) ui->spinBox->setMaximum (tabPage->textEdit() ->document() ->blockCount()); } ui->spinBox->setVisible (!visibility); ui->label->setVisible (!visibility); ui->checkBox->setVisible (!visibility); if (!visibility) { ui->spinBox->setFocus(); ui->spinBox->selectAll(); } else if (tabPage)/* return focus to doc */ tabPage->textEdit()->setFocus(); } /*************************/ void FPwin::setMax (const int max) { ui->spinBox->setMaximum (max); } /*************************/ void FPwin::goTo() { /* workaround for not being able to use returnPressed() because of protectedness of spinbox's QLineEdit */ if (!ui->spinBox->hasFocus()) return; if (TabPage *tabPage = qobject_cast< TabPage *>(ui->tabWidget->currentWidget())) { TextEdit *textEdit = tabPage->textEdit(); QTextBlock block = textEdit->document()->findBlockByNumber (ui->spinBox->value() - 1); int pos = block.position(); QTextCursor start = textEdit->textCursor(); if (ui->checkBox->isChecked()) start.setPosition (pos, QTextCursor::KeepAnchor); else start.setPosition (pos); textEdit->setTextCursor (start); } } /*************************/ void FPwin::showLN (bool checked) { int count = ui->tabWidget->count(); if (count == 0) return; if (checked) { for (int i = 0; i < count; ++i) qobject_cast< TabPage *>(ui->tabWidget->widget (i))->textEdit()->showLineNumbers (true); } else if (!ui->spinBox->isVisible()) // also the spinBox affects line numbers visibility { for (int i = 0; i < count; ++i) qobject_cast< TabPage *>(ui->tabWidget->widget (i))->textEdit()->showLineNumbers (false); } } /*************************/ void FPwin::toggleWrapping() { int count = ui->tabWidget->count(); if (count == 0) return; if (ui->actionWrap->isChecked()) { for (int i = 0; i < count; ++i) qobject_cast< TabPage *>(ui->tabWidget->widget (i))->textEdit()->setLineWrapMode (QPlainTextEdit::WidgetWidth); } else { for (int i = 0; i < count; ++i) qobject_cast< TabPage *>(ui->tabWidget->widget (i))->textEdit()->setLineWrapMode (QPlainTextEdit::NoWrap); } } /*************************/ void FPwin::toggleIndent() { int count = ui->tabWidget->count(); if (count == 0) return; if (ui->actionIndent->isChecked()) { for (int i = 0; i < count; ++i) qobject_cast< TabPage *>(ui->tabWidget->widget (i))->textEdit()->setAutoIndentation (true); } else { for (int i = 0; i < count; ++i) qobject_cast< TabPage *>(ui->tabWidget->widget (i))->textEdit()->setAutoIndentation (false); } } /*************************/ void FPwin::encodingToCheck (const QString& encoding) { if (encoding != "UTF-8") ui->actionOther->setDisabled (true); if (encoding == "UTF-8") ui->actionUTF_8->setChecked (true); else if (encoding == "UTF-16") ui->actionUTF_16->setChecked (true); else if (encoding == "CP1256") ui->actionWindows_Arabic->setChecked (true); else if (encoding == "ISO-8859-1") ui->actionISO_8859_1->setChecked (true); else if (encoding == "ISO-8859-15") ui->actionISO_8859_15->setChecked (true); else if (encoding == "CP1252") ui->actionWindows_1252->setChecked (true); else if (encoding == "CP1251") ui->actionCyrillic_CP1251->setChecked (true); else if (encoding == "KOI8-U") ui->actionCyrillic_KOI8_U->setChecked (true); else if (encoding == "ISO-8859-5") ui->actionCyrillic_ISO_8859_5->setChecked (true); else if (encoding == "BIG5") ui->actionChineese_BIG5->setChecked (true); else if (encoding == "B18030") ui->actionChinese_GB18030->setChecked (true); else if (encoding == "ISO-2022-JP") ui->actionJapanese_ISO_2022_JP->setChecked (true); else if (encoding == "ISO-2022-JP-2") ui->actionJapanese_ISO_2022_JP_2->setChecked (true); else if (encoding == "ISO-2022-KR") ui->actionJapanese_ISO_2022_KR->setChecked (true); else if (encoding == "CP932") ui->actionJapanese_CP932->setChecked (true); else if (encoding == "EUC-JP") ui->actionJapanese_EUC_JP->setChecked (true); else if (encoding == "CP949") ui->actionKorean_CP949->setChecked (true); else if (encoding == "CP1361") ui->actionKorean_CP1361->setChecked (true); else if (encoding == "EUC-KR") ui->actionKorean_EUC_KR->setChecked (true); else { ui->actionOther->setDisabled (false); ui->actionOther->setChecked (true); } } /*************************/ const QString FPwin::checkToEncoding() const { QString encoding; if (ui->actionUTF_8->isChecked()) encoding = "UTF-8"; else if (ui->actionUTF_16->isChecked()) encoding = "UTF-16"; else if (ui->actionWindows_Arabic->isChecked()) encoding = "CP1256"; else if (ui->actionISO_8859_1->isChecked()) encoding = "ISO-8859-1"; else if (ui->actionISO_8859_15->isChecked()) encoding = "ISO-8859-15"; else if (ui->actionWindows_1252->isChecked()) encoding = "CP1252"; else if (ui->actionCyrillic_CP1251->isChecked()) encoding = "CP1251"; else if (ui->actionCyrillic_KOI8_U->isChecked()) encoding = "KOI8-U"; else if (ui->actionCyrillic_ISO_8859_5->isChecked()) encoding = "ISO-8859-5"; else if (ui->actionChineese_BIG5->isChecked()) encoding = "BIG5"; else if (ui->actionChinese_GB18030->isChecked()) encoding = "B18030"; else if (ui->actionJapanese_ISO_2022_JP->isChecked()) encoding = "ISO-2022-JP"; else if (ui->actionJapanese_ISO_2022_JP_2->isChecked()) encoding = "ISO-2022-JP-2"; else if (ui->actionJapanese_ISO_2022_KR->isChecked()) encoding = "ISO-2022-KR"; else if (ui->actionJapanese_CP932->isChecked()) encoding = "CP932"; else if (ui->actionJapanese_EUC_JP->isChecked()) encoding = "EUC-JP"; else if (ui->actionKorean_CP949->isChecked()) encoding = "CP949"; else if (ui->actionKorean_CP1361->isChecked()) encoding = "CP1361"; else if (ui->actionKorean_EUC_KR->isChecked()) encoding = "EUC-KR"; else encoding = "UTF-8"; return encoding; } /*************************/ void FPwin::docProp() { bool showCurPos = static_cast(qApp)->getConfig().getShowCursorPos(); if (ui->statusBar->isVisible()) { for (int i = 0; i < ui->tabWidget->count(); ++i) { TextEdit *thisTextEdit = qobject_cast< TabPage *>(ui->tabWidget->widget (i))->textEdit(); disconnect (thisTextEdit, &QPlainTextEdit::blockCountChanged, this, &FPwin::statusMsgWithLineCount); disconnect (thisTextEdit, &QPlainTextEdit::selectionChanged, this, &FPwin::statusMsg); if (showCurPos) disconnect (thisTextEdit, &QPlainTextEdit::cursorPositionChanged, this, &FPwin::showCursorPos); /* don't delete the cursor position label because the statusbar might be shown later */ } ui->statusBar->setVisible (false); return; } int index = ui->tabWidget->currentIndex(); if (index == -1) return; statusMsgWithLineCount (qobject_cast< TabPage *>(ui->tabWidget->widget (index)) ->textEdit()->document()->blockCount()); for (int i = 0; i < ui->tabWidget->count(); ++i) { TextEdit *thisTextEdit = qobject_cast< TabPage *>(ui->tabWidget->widget (i))->textEdit(); connect (thisTextEdit, &QPlainTextEdit::blockCountChanged, this, &FPwin::statusMsgWithLineCount); connect (thisTextEdit, &QPlainTextEdit::selectionChanged, this, &FPwin::statusMsg); if (showCurPos) connect (thisTextEdit, &QPlainTextEdit::cursorPositionChanged, this, &FPwin::showCursorPos); } ui->statusBar->setVisible (true); if (showCurPos) { addCursorPosLabel(); showCursorPos(); } if (QToolButton *wordButton = ui->statusBar->findChild("wordButton")) wordButton->setVisible (true); updateWordInfo(); } /*************************/ // Set the status bar text according to the block count. void FPwin::statusMsgWithLineCount (const int lines) { TextEdit *textEdit = qobject_cast< TabPage *>(ui->tabWidget->currentWidget())->textEdit(); /* ensure that the signal comes from the active tab if this is about a tab a signal */ if (qobject_cast(QObject::sender()) && QObject::sender() != textEdit) return; QLabel *statusLabel = ui->statusBar->findChild("statusLabel"); /* the order: Encoding -> Syntax -> Lines -> Sel. Chars -> Words */ QString encodStr = "" + tr ("Encoding") + QString (": %1").arg (textEdit->getEncoding()); QString syntaxStr; if (!textEdit->getProg().isEmpty() && textEdit->getProg() != "help") syntaxStr = "    " + tr ("Syntax") + QString (": %1").arg (textEdit->getProg()); QString lineStr = "    " + tr ("Lines") + QString (": %1").arg (lines); QString selStr = "    " + tr ("Sel. Chars") + QString (": %1").arg (textEdit->textCursor().selectedText().size()); QString wordStr = "    " + tr ("Words") + ":"; statusLabel->setText (encodStr + syntaxStr + lineStr + selStr + wordStr); } /*************************/ // Change the status bar text when the selection changes. void FPwin::statusMsg() { QLabel *statusLabel = ui->statusBar->findChild("statusLabel"); int sel = qobject_cast< TabPage *>(ui->tabWidget->currentWidget())->textEdit() ->textCursor().selectedText().size(); QString str = statusLabel->text(); QString selStr = tr ("Sel. Chars"); QString wordStr = "    " + tr ("Words"); int i = str.indexOf (selStr) + selStr.count(); int j = str.indexOf (wordStr); if (sel == 0) { QString prevSel = str.mid (i + 9, j - i - 13); // j - i - 13 --> j - (i + 9[": ]") - 4[""] if (prevSel.toInt() == 0) return; } QString charN; charN.setNum (sel); str.replace (i + 9, j - i - 13, charN); statusLabel->setText (str); } /*************************/ void FPwin::showCursorPos() { QLabel *posLabel = ui->statusBar->findChild("posLabel"); if (!posLabel) return; TabPage *tabPage = qobject_cast< TabPage *>(ui->tabWidget->currentWidget()); if (!tabPage) return; int pos = tabPage->textEdit()->textCursor().positionInBlock(); QString charN; charN.setNum (pos); charN = " " + charN + ""; QString str = posLabel->text(); QString scursorStr = "" + tr ("Position:") + ""; int i = scursorStr.count(); str.replace (i, str.count() - i, charN); posLabel->setText (str); } /*************************/ void FPwin::showLang (TextEdit *textEdit) { QToolButton *langButton = ui->statusBar->findChild("langButton"); if (!langButton) return; langButton->setEnabled (textEdit->getProg() != "help"); QString lang = textEdit->getLang().isEmpty() ? textEdit->getProg() : textEdit->getLang(); if (lang.isEmpty() || lang == "normal" || lang == "help") lang = tr ("Normal"); langButton->setText (lang); if (QAction *action = langs.value (lang)) action->setChecked (true); } /*************************/ void FPwin::setLang (QAction *action) { QToolButton *langButton = ui->statusBar->findChild("langButton"); if (!langButton) return; TabPage *tabPage = qobject_cast< TabPage *>(ui->tabWidget->currentWidget()); if (!tabPage) return; TextEdit *textEdit = tabPage->textEdit(); QString lang = action->text(); lang.remove ('&'); // because of KAcceleratorManager langButton->setText (lang); if (lang == tr ("Normal")) { lang = "normal"; if (textEdit->getProg().isEmpty()) textEdit->setLang (QString()); else textEdit->setLang ("normal"); } else if (textEdit->getProg() == lang) textEdit->setLang (QString()); // not enforced because it's the real syntax else textEdit->setLang (lang); if (ui->actionSyntax->isChecked()) { syntaxHighlighting (textEdit, false); syntaxHighlighting (textEdit, true, lang); } } /*************************/ void FPwin::updateWordInfo (int /*position*/, int charsRemoved, int charsAdded) { QToolButton *wordButton = ui->statusBar->findChild("wordButton"); if (!wordButton) return; int index = ui->tabWidget->currentIndex(); if (index == -1) return; TextEdit *textEdit = qobject_cast< TabPage *>(ui->tabWidget->widget (index))->textEdit(); /* ensure that the signal comes from the active tab (when the info is going to be removed) */ if (qobject_cast(QObject::sender()) && QObject::sender() != textEdit->document()) return; if (wordButton->isVisible()) { QLabel *statusLabel = ui->statusBar->findChild("statusLabel"); int words = textEdit->getWordNumber(); if (words == -1) { words = textEdit->toPlainText() .split (QRegularExpression ("(\\s|\\n|\\r)+"), QString::SkipEmptyParts) .count(); textEdit->setWordNumber (words); } wordButton->setVisible (false); statusLabel->setText (QString ("%1 %2") .arg (statusLabel->text()) .arg (words)); connect (textEdit->document(), &QTextDocument::contentsChange, this, &FPwin::updateWordInfo); } else if (charsRemoved > 0 || charsAdded > 0) // not if only the format is changed { disconnect (textEdit->document(), &QTextDocument::contentsChange, this, &FPwin::updateWordInfo); textEdit->setWordNumber (-1); wordButton->setVisible (true); statusMsgWithLineCount (textEdit->document()->blockCount()); } } /*************************/ void FPwin::filePrint() { if (isLoading()) return; int index = ui->tabWidget->currentIndex(); if (index == -1) return; if (hasAnotherDialog()) return; updateShortcuts (true); TextEdit *textEdit = qobject_cast< TabPage *>(ui->tabWidget->widget (index))->textEdit(); QPrinter printer (QPrinter::HighResolution); /* choose an appropriate name and directory */ QString fileName = textEdit->getFileName(); if (fileName.isEmpty()) { QDir dir = QDir::home(); fileName= dir.filePath (tr ("Untitled")); } if (printer.outputFormat() == QPrinter::PdfFormat) printer.setOutputFileName (fileName.append (".pdf")); /*else if (printer.outputFormat() == QPrinter::PostScriptFormat) printer.setOutputFileName (fileName.append (".ps"));*/ QPrintDialog dlg (&printer, this); dlg.setWindowModality (Qt::WindowModal); if (textEdit->textCursor().hasSelection()) dlg.setOption (QAbstractPrintDialog::PrintSelection); dlg.setWindowTitle (tr ("Print Document")); if (dlg.exec() == QDialog::Accepted) textEdit->print (&printer); updateShortcuts (false); } /*************************/ void FPwin::nextTab() { if (isLoading()) return; int index = ui->tabWidget->currentIndex(); if (index == -1) return; if (sidePane_) { int curRow = sidePane_->listWidget()->currentRow(); if (curRow == sidePane_->listWidget()->count() - 1) { if (static_cast(qApp)->getConfig().getTabWrapAround()) sidePane_->listWidget()->setCurrentRow (0); } else sidePane_->listWidget()->setCurrentRow (curRow + 1); } else { if (QWidget *widget = ui->tabWidget->widget (index + 1)) ui->tabWidget->setCurrentWidget (widget); else if (static_cast(qApp)->getConfig().getTabWrapAround()) ui->tabWidget->setCurrentIndex (0); } } /*************************/ void FPwin::previousTab() { if (isLoading()) return; int index = ui->tabWidget->currentIndex(); if (index == -1) return; if (sidePane_) { int curRow = sidePane_->listWidget()->currentRow(); if (curRow == 0) { if (static_cast(qApp)->getConfig().getTabWrapAround()) sidePane_->listWidget()->setCurrentRow (sidePane_->listWidget()->count() - 1); } else sidePane_->listWidget()->setCurrentRow (curRow - 1); } else { if (QWidget *widget = ui->tabWidget->widget (index - 1)) ui->tabWidget->setCurrentWidget (widget); else if (static_cast(qApp)->getConfig().getTabWrapAround()) { int count = ui->tabWidget->count(); if (count > 0) ui->tabWidget->setCurrentIndex (count - 1); } } } /*************************/ void FPwin::lastTab() { if (isLoading()) return; if (sidePane_) { int count = sidePane_->listWidget()->count(); if (count > 0) sidePane_->listWidget()->setCurrentRow (count - 1); } else { int count = ui->tabWidget->count(); if (count > 0) ui->tabWidget->setCurrentIndex (count - 1); } } /*************************/ void FPwin::firstTab() { if (isLoading()) return; if (sidePane_) { if (sidePane_->listWidget()->count() > 0) sidePane_->listWidget()->setCurrentRow (0); } else if (ui->tabWidget->count() > 0) ui->tabWidget->setCurrentIndex (0); } /*************************/ void FPwin::detachTab() { if (!isReady()) return; int index = -1; if (sidePane_ && rightClicked_ >= 0) index = ui->tabWidget->indexOf (sideItems_.value (sidePane_->listWidget()->item (rightClicked_))); else index = ui->tabWidget->currentIndex(); if (index == -1 || ui->tabWidget->count() == 1) { ui->tabWidget->tabBar()->finishMouseMoveEvent(); return; } /***************************************************** ***** Get all necessary info. ***** ***** Then, remove the tab but keep its widget. ***** *****************************************************/ QString tooltip = ui->tabWidget->tabToolTip (index); QString tabText = ui->tabWidget->tabText (index); QString title = windowTitle(); bool hl = true; bool spin = false; bool ln = false; bool status = false; bool statusCurPos = false; if (!ui->actionSyntax->isChecked()) hl = false; if (ui->spinBox->isVisible()) spin = true; if (ui->actionLineNumbers->isChecked()) ln = true; if (ui->statusBar->isVisible()) { status = true; if (ui->statusBar->findChild("posLabel")) statusCurPos = true; } TabPage *tabPage = qobject_cast< TabPage *>(ui->tabWidget->widget (index)); TextEdit *textEdit = tabPage->textEdit(); disconnect (textEdit, &TextEdit::updateRect, this ,&FPwin::hlighting); disconnect (textEdit, &QPlainTextEdit::textChanged, this ,&FPwin::hlight); if (status) { disconnect (textEdit, &QPlainTextEdit::blockCountChanged, this, &FPwin::statusMsgWithLineCount); disconnect (textEdit, &QPlainTextEdit::selectionChanged, this, &FPwin::statusMsg); if (statusCurPos) disconnect (textEdit, &QPlainTextEdit::cursorPositionChanged, this, &FPwin::showCursorPos); } disconnect (textEdit, &QPlainTextEdit::copyAvailable, ui->actionCut, &QAction::setEnabled); disconnect (textEdit, &QPlainTextEdit::copyAvailable, ui->actionDelete, &QAction::setEnabled); disconnect (textEdit, &QPlainTextEdit::copyAvailable, ui->actionCopy, &QAction::setEnabled); disconnect (textEdit, &TextEdit::zoomedOut, this, &FPwin::reformat); disconnect (textEdit, &TextEdit::fileDropped, this, &FPwin::newTabFromName); disconnect (textEdit, &TextEdit::updateBracketMatching, this, &FPwin::matchBrackets); disconnect (textEdit, &QPlainTextEdit::blockCountChanged, this, &FPwin::formatOnBlockChange); disconnect (textEdit, &TextEdit::updateRect, this, &FPwin::formatVisibleText); disconnect (textEdit, &TextEdit::resized, this, &FPwin::formatOnResizing); disconnect (textEdit->document(), &QTextDocument::contentsChange, this, &FPwin::updateWordInfo); disconnect (textEdit->document(), &QTextDocument::contentsChange, this, &FPwin::formatOnTextChange); disconnect (textEdit->document(), &QTextDocument::blockCountChanged, this, &FPwin::setMax); disconnect (textEdit->document(), &QTextDocument::modificationChanged, this, &FPwin::asterisk); disconnect (textEdit->document(), &QTextDocument::undoAvailable, ui->actionUndo, &QAction::setEnabled); disconnect (textEdit->document(), &QTextDocument::redoAvailable, ui->actionRedo, &QAction::setEnabled); disconnect (textEdit->document(), &QTextDocument::modificationChanged, ui->actionSave, &QAction::setEnabled); disconnect (tabPage, &TabPage::find, this, &FPwin::find); disconnect (tabPage, &TabPage::searchFlagChanged, this, &FPwin::searchFlagChanged); /* for tabbar to be updated peoperly with tab reordering during a fast drag-and-drop, mouse should be released before tab removal */ ui->tabWidget->tabBar()->releaseMouse(); ui->tabWidget->removeTab (index); if (ui->tabWidget->count() == 1) { ui->actionDetachTab->setDisabled (true); ui->actionRightTab->setDisabled (true); ui->actionLeftTab->setDisabled (true); ui->actionLastTab->setDisabled (true); ui->actionFirstTab->setDisabled (true); } if (sidePane_ && !sideItems_.isEmpty()) { if (QListWidgetItem *wi = sideItems_.key (tabPage)) { sideItems_.remove (wi); delete sidePane_->listWidget()->takeItem (sidePane_->listWidget()->row (wi)); } } /******************************************************************* ***** create a new window and replace its tab by this widget. ***** *******************************************************************/ FPsingleton *singleton = static_cast(qApp); FPwin * dropTarget = singleton->newWin (""); dropTarget->closeTabAtIndex (0); /* first, set the new info... */ dropTarget->lastFile_ = textEdit->getFileName(); textEdit->setGreenSel (QList()); textEdit->setRedSel (QList()); /* ... then insert the detached widget... */ dropTarget->enableWidgets (true); // the tab will be inserted and switched to below bool isLink = dropTarget->lastFile_.isEmpty() ? false : QFileInfo (dropTarget->lastFile_).isSymLink(); dropTarget->ui->tabWidget->insertTab (0, tabPage, isLink ? QIcon (":icons/link.svg") : QIcon(), tabText); if (dropTarget->sidePane_) { ListWidget *lw = dropTarget->sidePane_->listWidget(); if (textEdit->document()->isModified()) { tabText.remove (0, 1); tabText.append ("*"); } QListWidgetItem *lwi = new QListWidgetItem (isLink ? QIcon (":icons/link.svg") : QIcon(), tabText, lw); lw->setToolTip (tooltip); dropTarget->sideItems_.insert (lwi, tabPage); lw->addItem (lwi); lw->setCurrentItem (lwi); } /* ... and remove all yellow and green highlights (the yellow ones will be recreated later if needed) */ QList es; if (ln || spin) es.prepend (textEdit->currentLineSelection()); textEdit->setExtraSelections (es); /* at last, set all properties correctly */ dropTarget->setWindowTitle (title); dropTarget->ui->tabWidget->setTabToolTip (0, tooltip); /* reload buttons, syntax highlighting, jump bar, line numbers */ dropTarget->encodingToCheck (textEdit->getEncoding()); if (!textEdit->getFileName().isEmpty()) dropTarget->ui->actionReload->setEnabled (true); if (!hl) dropTarget->ui->actionSyntax->setChecked (false); else dropTarget->syntaxHighlighting (textEdit, true, textEdit->getLang()); if (spin) { dropTarget->ui->spinBox->setVisible (true); dropTarget->ui->label->setVisible (true); dropTarget->ui->spinBox->setMaximum (textEdit->document()->blockCount()); connect (textEdit->document(), &QTextDocument::blockCountChanged, dropTarget, &FPwin::setMax); } if (ln) dropTarget->ui->actionLineNumbers->setChecked (true); /* searching */ if (!textEdit->getSearchedText().isEmpty()) { connect (textEdit, &QPlainTextEdit::textChanged, dropTarget, &FPwin::hlight); connect (textEdit, &TextEdit::updateRect, dropTarget, &FPwin::hlighting); /* restore yellow highlights, which will automatically set the current line highlight if needed because the spin button and line number menuitem are set above */ dropTarget->hlight(); } /* status bar */ if (status) { dropTarget->ui->statusBar->setVisible (true); dropTarget->statusMsgWithLineCount (textEdit->document()->blockCount()); if (textEdit->getWordNumber() == -1) { if (QToolButton *wordButton = dropTarget->ui->statusBar->findChild("wordButton")) wordButton->setVisible (true); } else { if (QToolButton *wordButton = dropTarget->ui->statusBar->findChild("wordButton")) wordButton->setVisible (false); QLabel *statusLabel = dropTarget->ui->statusBar->findChild("statusLabel"); statusLabel->setText (QString ("%1 %2") .arg (statusLabel->text()) .arg (textEdit->getWordNumber())); connect (textEdit->document(), &QTextDocument::contentsChange, dropTarget, &FPwin::updateWordInfo); } connect (textEdit, &QPlainTextEdit::blockCountChanged, dropTarget, &FPwin::statusMsgWithLineCount); connect (textEdit, &QPlainTextEdit::selectionChanged, dropTarget, &FPwin::statusMsg); if (statusCurPos) { dropTarget->addCursorPosLabel(); dropTarget->showCursorPos(); connect (textEdit, &QPlainTextEdit::cursorPositionChanged, dropTarget, &FPwin::showCursorPos); } } if (textEdit->lineWrapMode() == QPlainTextEdit::NoWrap) dropTarget->ui->actionWrap->setChecked (false); /* auto indentation */ if (textEdit->getAutoIndentation() == false) dropTarget->ui->actionIndent->setChecked (false); /* the remaining signals */ connect (textEdit->document(), &QTextDocument::undoAvailable, dropTarget->ui->actionUndo, &QAction::setEnabled); connect (textEdit->document(), &QTextDocument::redoAvailable, dropTarget->ui->actionRedo, &QAction::setEnabled); connect (textEdit->document(), &QTextDocument::modificationChanged, dropTarget->ui->actionSave, &QAction::setEnabled); connect (textEdit->document(), &QTextDocument::modificationChanged, dropTarget, &FPwin::asterisk); connect (textEdit, &QPlainTextEdit::copyAvailable, dropTarget->ui->actionCopy, &QAction::setEnabled); connect (tabPage, &TabPage::find, dropTarget, &FPwin::find); connect (tabPage, &TabPage::searchFlagChanged, dropTarget, &FPwin::searchFlagChanged); if (!textEdit->isReadOnly()) { connect (textEdit, &QPlainTextEdit::copyAvailable, dropTarget->ui->actionCut, &QAction::setEnabled); connect (textEdit, &QPlainTextEdit::copyAvailable, dropTarget->ui->actionDelete, &QAction::setEnabled); } connect (textEdit, &TextEdit::fileDropped, dropTarget, &FPwin::newTabFromName); connect (textEdit, &TextEdit::zoomedOut, dropTarget, &FPwin::reformat); textEdit->setFocus(); dropTarget->activateWindow(); dropTarget->raise(); } /*************************/ void FPwin::dropTab (const QString& str) { QStringList list = str.split ("+", QString::SkipEmptyParts); if (list.count() != 2) { ui->tabWidget->tabBar()->finishMouseMoveEvent(); return; } int index = list.at (1).toInt(); if (index <= -1) // impossible { ui->tabWidget->tabBar()->finishMouseMoveEvent(); return; } FPsingleton *singleton = static_cast(qApp); FPwin *dragSource = nullptr; for (int i = 0; i < singleton->Wins.count(); ++i) { if (singleton->Wins.at (i)->winId() == (uint) list.at (0).toInt()) { dragSource = singleton->Wins.at (i); break; } } if (dragSource == this || dragSource == nullptr) // impossible { ui->tabWidget->tabBar()->finishMouseMoveEvent(); return; } closeWarningBar(); dragSource->closeWarningBar(); QString tooltip = dragSource->ui->tabWidget->tabToolTip (index); QString tabText = dragSource->ui->tabWidget->tabText (index); bool spin = false; bool ln = false; if (dragSource->ui->spinBox->isVisible()) spin = true; if (dragSource->ui->actionLineNumbers->isChecked()) ln = true; TabPage *tabPage = qobject_cast< TabPage *>(dragSource->ui->tabWidget->widget (index)); TextEdit *textEdit = tabPage->textEdit(); disconnect (textEdit, &TextEdit::updateRect, dragSource ,&FPwin::hlighting); disconnect (textEdit, &QPlainTextEdit::textChanged, dragSource ,&FPwin::hlight); if (dragSource->ui->statusBar->isVisible()) { disconnect (textEdit, &QPlainTextEdit::blockCountChanged, dragSource, &FPwin::statusMsgWithLineCount); disconnect (textEdit, &QPlainTextEdit::selectionChanged, dragSource, &FPwin::statusMsg); if (dragSource->ui->statusBar->findChild("posLabel")) disconnect (textEdit, &QPlainTextEdit::cursorPositionChanged, dragSource, &FPwin::showCursorPos); } disconnect (textEdit, &QPlainTextEdit::copyAvailable, dragSource->ui->actionCut, &QAction::setEnabled); disconnect (textEdit, &QPlainTextEdit::copyAvailable, dragSource->ui->actionDelete, &QAction::setEnabled); disconnect (textEdit, &QPlainTextEdit::copyAvailable, dragSource->ui->actionCopy, &QAction::setEnabled); disconnect (textEdit, &TextEdit::zoomedOut, dragSource, &FPwin::reformat); disconnect (textEdit, &TextEdit::fileDropped, dragSource, &FPwin::newTabFromName); disconnect (textEdit, &TextEdit::updateBracketMatching, dragSource, &FPwin::matchBrackets); disconnect (textEdit, &QPlainTextEdit::blockCountChanged, dragSource, &FPwin::formatOnBlockChange); disconnect (textEdit, &TextEdit::updateRect, dragSource, &FPwin::formatVisibleText); disconnect (textEdit, &TextEdit::resized, dragSource, &FPwin::formatOnResizing); disconnect (textEdit->document(), &QTextDocument::contentsChange, dragSource, &FPwin::updateWordInfo); disconnect (textEdit->document(), &QTextDocument::contentsChange, dragSource, &FPwin::formatOnTextChange); disconnect (textEdit->document(), &QTextDocument::blockCountChanged, dragSource, &FPwin::setMax); disconnect (textEdit->document(), &QTextDocument::modificationChanged, dragSource, &FPwin::asterisk); disconnect (textEdit->document(), &QTextDocument::undoAvailable, dragSource->ui->actionUndo, &QAction::setEnabled); disconnect (textEdit->document(), &QTextDocument::redoAvailable, dragSource->ui->actionRedo, &QAction::setEnabled); disconnect (textEdit->document(), &QTextDocument::modificationChanged, dragSource->ui->actionSave, &QAction::setEnabled); disconnect (tabPage, &TabPage::find, dragSource, &FPwin::find); disconnect (tabPage, &TabPage::searchFlagChanged, dragSource, &FPwin::searchFlagChanged); /* it's important to release mouse before tab removal because otherwise, the source tabbar might not be updated properly with tab reordering during a fast drag-and-drop */ dragSource->ui->tabWidget->tabBar()->releaseMouse(); dragSource->ui->tabWidget->removeTab (index); // there can't be a side-pane here int count = dragSource->ui->tabWidget->count(); if (count == 1) { dragSource->ui->actionDetachTab->setDisabled (true); dragSource->ui->actionRightTab->setDisabled (true); dragSource->ui->actionLeftTab->setDisabled (true); dragSource->ui->actionLastTab->setDisabled (true); dragSource->ui->actionFirstTab->setDisabled (true); } /*************************************************************************** ***** The tab is dropped into this window; so insert it as a new tab. ***** ***************************************************************************/ int insertIndex = ui->tabWidget->currentIndex() + 1; /* first, set the new info... */ lastFile_ = textEdit->getFileName(); textEdit->setGreenSel (QList()); textEdit->setRedSel (QList()); /* ... then insert the detached widget, considering whether the searchbar should be shown... */ if (!textEdit->getSearchedText().isEmpty()) { if (insertIndex == 0 // the window has no tab yet || !qobject_cast< TabPage *>(ui->tabWidget->widget (insertIndex - 1))->isSearchBarVisible()) { for (int i = 0; i < ui->tabWidget->count(); ++i) qobject_cast< TabPage *>(ui->tabWidget->widget (i))->setSearchBarVisible (true); } } else if (insertIndex > 0) { tabPage->setSearchBarVisible (qobject_cast< TabPage *>(ui->tabWidget->widget (insertIndex - 1)) ->isSearchBarVisible()); } if (ui->tabWidget->count() == 0) // the tab will be inserted and switched to below enableWidgets (true); else if (ui->tabWidget->count() == 1) { // tab detach and switch actions ui->actionDetachTab->setEnabled (true); ui->actionRightTab->setEnabled (true); ui->actionLeftTab->setEnabled (true); ui->actionLastTab->setEnabled (true); ui->actionFirstTab->setEnabled (true); } bool isLink = lastFile_.isEmpty() ? false : QFileInfo (lastFile_).isSymLink(); ui->tabWidget->insertTab (insertIndex, tabPage, isLink ? QIcon (":icons/link.svg") : QIcon(), tabText); if (sidePane_) { ListWidget *lw = sidePane_->listWidget(); if (textEdit->document()->isModified()) { tabText.remove (0, 1); tabText.append ("*"); } QListWidgetItem *lwi = new QListWidgetItem (isLink ? QIcon (":icons/link.svg") : QIcon(), tabText, lw); lw->setToolTip (tooltip); sideItems_.insert (lwi, tabPage); lw->addItem (lwi); lw->setCurrentItem (lwi); } ui->tabWidget->setCurrentIndex (insertIndex); /* ... and remove all yellow and green highlights (the yellow ones will be recreated later if needed) */ QList es; if ((ln || spin) && (ui->actionLineNumbers->isChecked() || ui->spinBox->isVisible())) { es.prepend (textEdit->currentLineSelection()); } textEdit->setExtraSelections (es); /* at last, set all properties correctly */ ui->tabWidget->setTabToolTip (insertIndex, tooltip); /* reload buttons, syntax highlighting, jump bar, line numbers */ if (ui->actionSyntax->isChecked()) syntaxHighlighting (textEdit, true, textEdit->getLang()); else if (!ui->actionSyntax->isChecked() && textEdit->getHighlighter()) { // there's no connction to the drag target yet textEdit->setDrawIndetLines (false); Highlighter *highlighter = qobject_cast< Highlighter *>(textEdit->getHighlighter()); textEdit->setHighlighter (nullptr); delete highlighter; highlighter = nullptr; } if (ui->spinBox->isVisible()) connect (textEdit->document(), &QTextDocument::blockCountChanged, this, &FPwin::setMax); if (ui->actionLineNumbers->isChecked() || ui->spinBox->isVisible()) textEdit->showLineNumbers (true); else textEdit->showLineNumbers (false); /* searching */ if (!textEdit->getSearchedText().isEmpty()) { connect (textEdit, &QPlainTextEdit::textChanged, this, &FPwin::hlight); connect (textEdit, &TextEdit::updateRect, this, &FPwin::hlighting); /* restore yellow highlights, which will automatically set the current line highlight if needed because the spin button and line number menuitem are set above */ hlight(); } /* status bar */ if (ui->statusBar->isVisible()) { connect (textEdit, &QPlainTextEdit::blockCountChanged, this, &FPwin::statusMsgWithLineCount); connect (textEdit, &QPlainTextEdit::selectionChanged, this, &FPwin::statusMsg); if (ui->statusBar->findChild("posLabel")) { showCursorPos(); connect (textEdit, &QPlainTextEdit::cursorPositionChanged, this, &FPwin::showCursorPos); } if (textEdit->getWordNumber() != -1) connect (textEdit->document(), &QTextDocument::contentsChange, this, &FPwin::updateWordInfo); } if (ui->actionWrap->isChecked() && textEdit->lineWrapMode() == QPlainTextEdit::NoWrap) textEdit->setLineWrapMode (QPlainTextEdit::WidgetWidth); else if (!ui->actionWrap->isChecked() && textEdit->lineWrapMode() == QPlainTextEdit::WidgetWidth) textEdit->setLineWrapMode (QPlainTextEdit::NoWrap); /* auto indentation */ if (ui->actionIndent->isChecked() && textEdit->getAutoIndentation() == false) textEdit->setAutoIndentation (true); else if (!ui->actionIndent->isChecked() && textEdit->getAutoIndentation() == true) textEdit->setAutoIndentation (false); /* the remaining signals */ connect (textEdit->document(), &QTextDocument::undoAvailable, ui->actionUndo, &QAction::setEnabled); connect (textEdit->document(), &QTextDocument::redoAvailable, ui->actionRedo, &QAction::setEnabled); connect (textEdit->document(), &QTextDocument::modificationChanged, ui->actionSave, &QAction::setEnabled); connect (textEdit->document(), &QTextDocument::modificationChanged, this, &FPwin::asterisk); connect (textEdit, &QPlainTextEdit::copyAvailable, ui->actionCopy, &QAction::setEnabled); connect (tabPage, &TabPage::find, this, &FPwin::find); connect (tabPage, &TabPage::searchFlagChanged, this, &FPwin::searchFlagChanged); if (!textEdit->isReadOnly()) { connect (textEdit, &QPlainTextEdit::copyAvailable, ui->actionCut, &QAction::setEnabled); connect (textEdit, &QPlainTextEdit::copyAvailable, ui->actionDelete, &QAction::setEnabled); } connect (textEdit, &TextEdit::fileDropped, this, &FPwin::newTabFromName); connect (textEdit, &TextEdit::zoomedOut, this, &FPwin::reformat); textEdit->setFocus(); activateWindow(); raise(); if (count == 0) QTimer::singleShot (0, dragSource, SLOT (close())); } /*************************/ void FPwin::tabContextMenu (const QPoint& p) { int tabNum = ui->tabWidget->count(); QTabBar *tbar = ui->tabWidget->tabBar(); rightClicked_ = tbar->tabAt (p); if (rightClicked_ < 0) return; QString fname = qobject_cast< TabPage *>(ui->tabWidget->widget (rightClicked_)) ->textEdit()->getFileName(); QMenu menu; bool showMenu = false; if (tabNum > 1) { showMenu = true; if (rightClicked_ < tabNum - 1) menu.addAction (ui->actionCloseRight); if (rightClicked_ > 0) menu.addAction (ui->actionCloseLeft); menu.addSeparator(); if (rightClicked_ < tabNum - 1 && rightClicked_ > 0) menu.addAction (ui->actionCloseOther); menu.addAction (ui->actionCloseAll); if (!fname.isEmpty()) menu.addSeparator(); } if (!fname.isEmpty()) { showMenu = true; menu.addAction (ui->actionCopyName); menu.addAction (ui->actionCopyPath); QFileInfo info (fname); if (info.isSymLink()) { menu.addSeparator(); QAction *action = menu.addAction (QIcon (":icons/link.svg"), tr ("Copy Target Path")); connect (action, &QAction::triggered, action, [info] { QApplication::clipboard()->setText (info.symLinkTarget()); }); action = menu.addAction (QIcon (":icons/link.svg"), tr ("Open Target Here")); connect (action, &QAction::triggered, action, [this, info] { QString targetName = info.symLinkTarget(); for (int i = 0; i < ui->tabWidget->count(); ++i) { TabPage *thisTabPage = qobject_cast(ui->tabWidget->widget (i)); if (targetName == thisTabPage->textEdit()->getFileName()) { ui->tabWidget->setCurrentWidget (thisTabPage); return; } } newTabFromName (targetName, false); }); } } if (showMenu) // we don't want an empty menu menu.exec (tbar->mapToGlobal (p)); rightClicked_ = -1; // reset } /*************************/ void FPwin::listContextMenu (const QPoint& p) { if (!sidePane_ || sideItems_.isEmpty()) return; ListWidget *lw = sidePane_->listWidget(); QModelIndex index = lw->indexAt (p); if (!index.isValid()) return; QListWidgetItem *item = lw->getItemFromIndex (index); rightClicked_ = lw->row (item); QString fname = sideItems_.value (item)->textEdit()->getFileName(); QMenu menu; menu.addAction (ui->actionClose); if (lw->count() > 1) { menu.addSeparator(); if (rightClicked_ < lw->count() - 1) menu.addAction (ui->actionCloseRight); if (rightClicked_ > 0) menu.addAction (ui->actionCloseLeft); if (rightClicked_ < lw->count() - 1 && rightClicked_ > 0) { menu.addSeparator(); menu.addAction (ui->actionCloseOther); } menu.addAction (ui->actionCloseAll); menu.addSeparator(); menu.addAction (ui->actionDetachTab); } if (!fname.isEmpty()) { menu.addSeparator(); menu.addAction (ui->actionCopyName); menu.addAction (ui->actionCopyPath); QFileInfo info (fname); if (info.isSymLink()) { menu.addSeparator(); QAction *action = menu.addAction (QIcon (":icons/link.svg"), tr ("Copy Target Path")); connect (action, &QAction::triggered, action, [info] { QApplication::clipboard()->setText (info.symLinkTarget()); }); action = menu.addAction (QIcon (":icons/link.svg"), tr ("Open Target Here")); connect (action, &QAction::triggered, action, [this, info] { QString targetName = info.symLinkTarget(); for (int i = 0; i < ui->tabWidget->count(); ++i) { TabPage *thisTabPage = qobject_cast(ui->tabWidget->widget (i)); if (targetName == thisTabPage->textEdit()->getFileName()) { if (QListWidgetItem *wi = sideItems_.key (thisTabPage)) sidePane_->listWidget()->setCurrentItem (wi); // sets the current widget at changeTab() return; } } newTabFromName (targetName, false); }); } } menu.exec (lw->mapToGlobal (p)); rightClicked_ = -1; // reset } /*************************/ void FPwin::prefDialog() { if (isLoading()) return; if (hasAnotherDialog()) return; static QHash defaultShortcuts; // FIXME: use C++11 initializer if (defaultShortcuts.isEmpty()) { defaultShortcuts.insert ("actionNew", tr ("Ctrl+N")); defaultShortcuts.insert ("actionOpen", tr ("Ctrl+O")); defaultShortcuts.insert ("actionSave", tr ("Ctrl+S")); defaultShortcuts.insert ("actionReload", tr ("Ctrl+Shift+R")); defaultShortcuts.insert ("actionFind", tr ("Ctrl+F")); defaultShortcuts.insert ("actionReplace", tr ("Ctrl+R")); defaultShortcuts.insert ("actionSaveAs", tr ("Ctrl+Shift+S")); defaultShortcuts.insert ("actionPrint", tr ("Ctrl+P")); defaultShortcuts.insert ("actionDoc", tr ("Ctrl+Shift+D")); defaultShortcuts.insert ("actionClose", tr ("Ctrl+Shift+Q")); defaultShortcuts.insert ("actionQuit", tr ("Ctrl+Q")); defaultShortcuts.insert ("actionLineNumbers", tr ("Ctrl+L")); defaultShortcuts.insert ("actionWrap", tr ("Ctrl+W")); defaultShortcuts.insert ("actionIndent", tr ("Ctrl+I")); defaultShortcuts.insert ("actionSyntax", tr ("Ctrl+Shift+H")); defaultShortcuts.insert ("actionPreferences", tr ("Ctrl+Shift+P")); defaultShortcuts.insert ("actionHelp", tr ("Ctrl+H")); defaultShortcuts.insert ("actionJump", tr ("Ctrl+J")); defaultShortcuts.insert ("actionEdit", tr ("Ctrl+Shift+E")); defaultShortcuts.insert ("actionDetachTab", tr ("Ctrl+T")); defaultShortcuts.insert ("actionRun", tr ("Ctrl+E")); defaultShortcuts.insert ("actionSession", tr ("Ctrl+M")); defaultShortcuts.insert ("actionSidePane", tr ("Ctrl+Alt+P")); defaultShortcuts.insert ("actionUndo", tr ("Ctrl+Z")); defaultShortcuts.insert ("actionRedo", tr ("Ctrl+Shift+Z")); defaultShortcuts.insert ("actionDate", tr ("Ctrl+Shift+V")); } updateShortcuts (true); PrefDialog dlg (defaultShortcuts, this); /*dlg.show(); move (x() + width()/2 - dlg.width()/2, y() + height()/2 - dlg.height()/ 2);*/ dlg.exec(); updateShortcuts (false); } /*************************/ void FPwin::manageSessions() { if (!isReady()) return; /* first see whether the Sessions dialog is already open... */ FPsingleton *singleton = static_cast(qApp); for (int i = 0; i < singleton->Wins.count(); ++i) { QList dialogs = singleton->Wins.at (i)->findChildren(); for (int j = 0; j < dialogs.count(); ++j) { if (dialogs.at (j)->objectName() == "sessionDialog") { dialogs.at (j)->raise(); dialogs.at (j)->activateWindow(); return; } } } /* ... and if not, create a non-modal Sessions dialog */ SessionDialog *dlg = new SessionDialog (this); dlg->setAttribute (Qt::WA_DeleteOnClose); dlg->show(); /*move (x() + width()/2 - dlg.width()/2, y() + height()/2 - dlg.height()/ 2);*/ dlg->raise(); dlg->activateWindow(); } /*************************/ // Pauses or resumes auto-saving. void FPwin::pauseAutoSaving (bool pause) { if (!autoSaver_) return; if (pause) { autoSaverPause_.start(); autoSaverRemainingTime_ = autoSaver_->remainingTime(); } else if (autoSaverPause_.isValid()) { if (autoSaverPause_.hasExpired (autoSaverRemainingTime_)) { autoSaverPause_.invalidate(); autoSave(); } else autoSaverPause_.invalidate(); } } /*************************/ void FPwin::startAutoSaving (bool start, int interval) { if (start) { if (!autoSaver_) { autoSaver_ = new QTimer (this); connect (autoSaver_, &QTimer::timeout, this, &FPwin::autoSave); } autoSaver_->setInterval (interval * 1000 * 60); autoSaver_->start(); } else if (autoSaver_) { if (autoSaver_->isActive()) autoSaver_->stop(); delete autoSaver_; autoSaver_ = nullptr; } } /*************************/ void FPwin::autoSave() { /* since there are important differences between this and saveFile(), we can't use the latter here. We especially don't show any prompt or warning here. */ if (autoSaverPause_.isValid()) return; QTimer::singleShot (0, this, [=]() { if (!autoSaver_ || !autoSaver_->isActive()) return; int index = ui->tabWidget->currentIndex(); if (index == -1) return; Config& config = static_cast(qApp)->getConfig(); for (int indx = 0; indx < ui->tabWidget->count(); ++indx) { TabPage *thisTabPage = qobject_cast< TabPage *>(ui->tabWidget->widget (indx)); TextEdit *thisTextEdit = thisTabPage->textEdit(); if (thisTextEdit->isUneditable() || !thisTextEdit->document()->isModified()) continue; QString fname = thisTextEdit->getFileName(); if (fname.isEmpty() || !QFile::exists (fname)) continue; /* make changes to the document if needed */ if (config.getRemoveTrailingSpaces()) { if (QGuiApplication::overrideCursor() == nullptr) waitToMakeBusy(); QTextBlock block = thisTextEdit->document()->firstBlock(); QTextCursor tmpCur = thisTextEdit->textCursor(); tmpCur.beginEditBlock(); while (block.isValid()) { if (const int num = trailingSpaces (block.text())) { tmpCur.setPosition (block.position() + block.text().length()); if (num > 1 && thisTextEdit->getProg() == "markdown") tmpCur.movePosition (QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, num - 2); else tmpCur.movePosition (QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, num); tmpCur.removeSelectedText(); } block = block.next(); } tmpCur.endEditBlock(); unbusy(); } if (config.getAppendEmptyLine() && !thisTextEdit->document()->lastBlock().text().isEmpty()) { QTextCursor tmpCur = thisTextEdit->textCursor(); tmpCur.beginEditBlock(); tmpCur.movePosition (QTextCursor::End); tmpCur.insertBlock(); tmpCur.endEditBlock(); } QTextDocumentWriter writer (fname, "plaintext"); if (writer.write (thisTextEdit->document())) { thisTextEdit->document()->setModified (false); QFileInfo fInfo (fname); thisTextEdit->setSize (fInfo.size()); thisTextEdit->setLastModified (fInfo.lastModified()); setTitle (fname, (indx == index ? -1 : indx)); config.addRecentFile (fname); // recently saved also means recently opened /* uninstall and reinstall the syntax highlgihter if the programming language is changed */ QString prevLan = thisTextEdit->getProg(); setProgLang (thisTextEdit); if (prevLan != thisTextEdit->getProg()) { if (config.getShowLangSelector() && config.getSyntaxByDefault()) { if (thisTextEdit->getLang() == thisTextEdit->getProg()) thisTextEdit->setLang (QString()); // not enforced because it's the real syntax showLang (thisTextEdit); } if (indx == index && ui->statusBar->isVisible() && thisTextEdit->getWordNumber() != -1) { // we want to change the statusbar text below disconnect (thisTextEdit->document(), &QTextDocument::contentsChange, this, &FPwin::updateWordInfo); } if (thisTextEdit->getLang().isEmpty()) { // restart the syntax highlighting only when the language isn't forced syntaxHighlighting (thisTextEdit, false); if (ui->actionSyntax->isChecked()) syntaxHighlighting (thisTextEdit); } if (indx == index && ui->statusBar->isVisible()) { // correct the statusbar text just by replacing the old syntax info QLabel *statusLabel = ui->statusBar->findChild("statusLabel"); QString str = statusLabel->text(); QString syntaxStr = tr ("Syntax"); int i = str.indexOf (syntaxStr); if (i == -1) // there was no language before saving (prevLan.isEmpty()) { QString lineStr = "    " + tr ("Lines"); int j = str.indexOf (lineStr); syntaxStr = "    " + tr ("Syntax") + QString (": %1") .arg (thisTextEdit->getProg()); str.insert (j, syntaxStr); } else { if (thisTextEdit->getProg().isEmpty()) // there's no language after saving { syntaxStr = "    " + tr ("Syntax"); QString lineStr = "    " + tr ("Lines"); int j = str.indexOf (syntaxStr); int k = str.indexOf (lineStr); str.remove (j, k - j); } else // the language is changed by saving { QString lineStr = "
    " + tr ("Lines"); int j = str.indexOf (lineStr); int offset = syntaxStr.count() + 9; // size of ": " str.replace (i + offset, j - i - offset, thisTextEdit->getProg()); } } statusLabel->setText (str); if (thisTextEdit->getWordNumber() != -1) connect (thisTextEdit->document(), &QTextDocument::contentsChange, this, &FPwin::updateWordInfo); } } } } }); } /*************************/ void FPwin::aboutDialog() { if (isLoading()) return; if (hasAnotherDialog()) return; updateShortcuts (true); class AboutDialog : public QDialog { public: explicit AboutDialog (QWidget* parent = nullptr, Qt::WindowFlags f = 0) : QDialog (parent, f) { aboutUi.setupUi (this); aboutUi.textLabel->setOpenExternalLinks (true); } void setTabTexts (const QString& first, const QString& sec) { aboutUi.tabWidget->setTabText (0, first); aboutUi.tabWidget->setTabText (1, sec); } void setMainIcon (const QIcon& icn) { aboutUi.iconLabel->setPixmap (icn.pixmap (64, 64)); } void settMainTitle (const QString& title) { aboutUi.titleLabel->setText (title); } void setMainText (const QString& txt) { aboutUi.textLabel->setText (txt); } private: Ui::AboutDialog aboutUi; }; AboutDialog dialog (this); Config config = static_cast(qApp)->getConfig(); QIcon FPIcon; if (config.getSysIcon()) { FPIcon = QIcon::fromTheme ("featherpad"); if (FPIcon.isNull()) FPIcon = QIcon (":icons/featherpad.svg"); } else FPIcon = QIcon (":icons/featherpad.svg"); dialog.setMainIcon (FPIcon); dialog.settMainTitle (QString ("
%1 %2

").arg (qApp->applicationName()).arg (qApp->applicationVersion())); dialog.setMainText ("
" + tr ("A lightweight, tabbed, plain-text editor") + "
\n
" + tr ("based on Qt5") + "

" + tr ("Author")+": Pedram Pourang (" + tr ("aka.") + " Tsu Jan)

"); dialog.setTabTexts (tr ("About FeatherPad"), tr ("Translators")); dialog.setWindowTitle (tr ("About FeatherPad")); dialog.setWindowModality (Qt::WindowModal); dialog.exec(); updateShortcuts (false); } /*************************/ void FPwin::helpDoc() { int index = ui->tabWidget->currentIndex(); if (index == -1) newTab(); else { for (int i = 0; i < ui->tabWidget->count(); ++i) { TabPage *thisTabPage = qobject_cast< TabPage *>(ui->tabWidget->widget (i)); TextEdit *thisTextEdit = thisTabPage->textEdit(); if (thisTextEdit->getFileName().isEmpty() && !thisTextEdit->document()->isModified() && !thisTextEdit->document()->isEmpty()) { if (sidePane_ && !sideItems_.isEmpty()) { if (QListWidgetItem *wi = sideItems_.key (thisTabPage)) sidePane_->listWidget()->setCurrentItem (wi); // sets the current widget at changeTab() } else ui->tabWidget->setCurrentWidget (thisTabPage); return; } } } QFile helpFile (DATADIR "/featherpad/help"); if (!helpFile.exists()) return; if (!helpFile.open (QFile::ReadOnly)) return; TextEdit *textEdit = qobject_cast(ui->tabWidget->currentWidget())->textEdit(); if (!textEdit->document()->isEmpty() || textEdit->document()->isModified() || !textEdit->getFileName().isEmpty()) // an empty file is just opened { createEmptyTab (!isLoading(), false); textEdit = qobject_cast(ui->tabWidget->currentWidget())->textEdit(); } else if (textEdit->getHighlighter()) // in case normal is highlighted as urrl syntaxHighlighting (textEdit, false); QByteArray data = helpFile.readAll(); helpFile.close(); QTextCodec *codec = QTextCodec::codecForName ("UTF-8"); QString str = codec->toUnicode (data); textEdit->setPlainText (str); textEdit->setReadOnly (true); if (!textEdit->hasDarkScheme()) textEdit->viewport()->setStyleSheet (".QWidget {" "color: black;" "background-color: rgb(225, 238, 255);}"); else textEdit->viewport()->setStyleSheet (".QWidget {" "color: white;" "background-color: rgb(0, 60, 110);}"); ui->actionCut->setDisabled (true); ui->actionPaste->setDisabled (true); ui->actionDate->setDisabled (true); ui->actionDelete->setDisabled (true); disconnect (textEdit, &QPlainTextEdit::copyAvailable, ui->actionCut, &QAction::setEnabled); disconnect (textEdit, &QPlainTextEdit::copyAvailable, ui->actionDelete, &QAction::setEnabled); index = ui->tabWidget->currentIndex(); textEdit->setEncoding ("UTF-8"); textEdit->setWordNumber (-1); textEdit->setProg ("help"); // just for marking if (ui->statusBar->isVisible()) { statusMsgWithLineCount (textEdit->document()->blockCount()); if (QToolButton *wordButton = ui->statusBar->findChild("wordButton")) wordButton->setVisible (true); } if (QToolButton *langButton = ui->statusBar->findChild("langButton")) langButton->setEnabled (false); encodingToCheck ("UTF-8"); QString title = "** " + tr ("Help") + " **"; ui->tabWidget->setTabText (index, title); setWindowTitle (title + "[*]"); setWindowModified (false); ui->tabWidget->setTabToolTip (index, title); if (sidePane_) { QListWidgetItem *cur = sidePane_->listWidget()->currentItem(); cur->setText (title); cur->setToolTip (title); } } } FeatherPad-0.8/featherpad/fpwin.h000066400000000000000000000202701325653242400167720ustar00rootroot00000000000000/* * Copyright (C) Pedram Pourang (aka Tsu Jan) 2014 * * FeatherPad 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. * * FeatherPad is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * @license GPL-3.0+ */ #ifndef FPWIN_H #define FPWIN_H #include #include #include #include "highlighter.h" #include "textedit.h" #include "tabpage.h" #include "sidepane.h" #include "config.h" namespace FeatherPad { namespace Ui { class FPwin; } class BusyMaker : public QObject { Q_OBJECT public: BusyMaker(){} ~BusyMaker(){} public slots: void waiting(); private slots : void makeBusy(); signals: void finished(); private: static const int timeout = 1000; }; // A FeatherPad window. class FPwin : public QMainWindow { Q_OBJECT public: explicit FPwin (QWidget *parent = 0); ~FPwin(); bool isScriptLang (QString lang); bool isLoading() { return (loadingProcesses_ > 0); } bool isReady() { if (loadingProcesses_ <= 0) { closeWarningBar(); return true; } return false; } bool hasSidePane()const { return (sidePane_ != nullptr); } void addCursorPosLabel(); void setupLangButton (bool add, bool normalAsUrl); void showCrashWarning(); void updateCustomizableShortcuts (bool disable = false); void startAutoSaving (bool start, int interval = 1); signals: void finishedLoading(); public slots: void newTabFromName (const QString& fileName, bool saveCursor, bool multiple = false); void newTab(); void statusMsg(); void statusMsgWithLineCount (const int lines); void showCursorPos(); void updateWordInfo (int position = -1, int charsRemoved = 0, int charsAdded = 0); private slots: void newTabFromRecent(); void clearRecentMenu(); void updateRecenMenu(); void closeTab(); void closeTabAtIndex (int index); void copyTabFileName(); void copyTabFilePath(); void closeAllTabs(); void closeNextTabs(); void closePreviousTabs(); void closeOtherTabs(); void fileOpen(); void reload(); void enforceEncoding (QAction*); void cutText(); void copyText(); void pasteText(); void insertDate(); void deleteText(); void selectAllText(); void makeEditable(); void undoing(); void redoing(); void tabSwitch (int index); void fontDialog(); void find (bool forward); void hlight() const; void hlighting (const QRect&, int dy) const; void searchFlagChanged(); void showHideSearch(); void showLN (bool checked); void toggleSyntaxHighlighting(); void formatOnBlockChange (int) const; void formatOnTextChange (int, int charsRemoved, int charsAdded) const; void formatVisibleText (const QRect &rect, int dy) const; void formatOnResizing() const; void toggleWrapping(); void toggleIndent(); void replace(); void replaceAll(); void closeReplaceDock (bool visible); void replaceDock(); void resizeDock (bool topLevel); void jumpTo(); void setMax (const int max); void goTo(); void asterisk (bool modified); void reformat (TextEdit *textEdit); void zoomIn(); void zoomOut(); void zoomZero(); void defaultSize(); //void align(); void manageSessions(); void executeProcess(); void exitProcess(); void displayError(); void displayOutput(); void docProp(); void filePrint(); void detachTab(); void nextTab(); void previousTab(); void lastTab(); void firstTab(); void tabContextMenu (const QPoint& p); void listContextMenu (const QPoint& p); void prefDialog(); void aboutDialog(); void helpDoc(); void matchBrackets(); void addText (const QString& text, const QString& fileName, const QString& charset, bool enforceEncod, bool reload, bool saveCursor, bool uneditable, // This doc should be uneditable? bool multiple); // Multiple files are being loaded? void onOpeningHugeFiles(); void onPermissionDenied(); void onOpeningUneditable(); void autoSave(); void pauseAutoSaving (bool pause); void setLang (QAction *action); public: QWidget *dummyWidget; // Bypasses KDE's demand for a new window. Ui::FPwin *ui; private: enum DOCSTATE { SAVED, UNDECIDED, DISCARDED }; TabPage *createEmptyTab(bool setCurrent, bool allowNormalHighlighter = true); bool hasAnotherDialog(); void deleteTabPage (int tabIndex); void loadText (const QString& fileName, bool enforceEncod, bool reload, bool saveCursor = false, bool enforceUneditable = false, bool multiple = false); bool alreadyOpen (TabPage *tabPage) const; void setTitle (const QString& fileName, int tabIndex = -1); DOCSTATE savePrompt (int tabIndex, bool noToAll); bool saveFile (bool keepSyntax); void closeEvent (QCloseEvent *event); bool closeTabs (int first, int last); void dragEnterEvent (QDragEnterEvent *event); void dropEvent (QDropEvent *event); void dropTab (const QString& str); void changeEvent (QEvent *event); bool event (QEvent *event); QTextDocument::FindFlags getSearchFlags() const; void enableWidgets (bool enable) const; void updateShortcuts (bool disable, bool page = true); QTextCursor finding (const QString& str, const QTextCursor& start, QTextDocument::FindFlags flags = 0, const int end = 0) const; void setProgLang (TextEdit *textEdit); void syntaxHighlighting (TextEdit *textEdit, bool highlight = true, const QString& lang = QString()); void encodingToCheck (const QString& encoding); const QString checkToEncoding() const; void applyConfigOnStarting(); bool matchLeftParenthesis (QTextBlock currentBlock, int index, int numRightParentheses); bool matchRightParenthesis (QTextBlock currentBlock, int index, int numLeftParentheses); bool matchLeftBrace (QTextBlock currentBlock, int index, int numRightBraces); bool matchRightBrace (QTextBlock currentBlock, int index, int numLeftBraces); bool matchLeftBracket (QTextBlock currentBlock, int index, int numRightBrackets); bool matchRightBracket (QTextBlock currentBlock, int index, int numLeftBrackets); void createSelection (int pos); void formatTextRect (QRect rect) const; void removeGreenSel(); void waitToMakeBusy(); void unbusy(); void displayMessage (bool error); void showWarningBar (const QString& message); void closeWarningBar(); void disconnectLambda(); void changeTab (QListWidgetItem *current, QListWidgetItem*); void toggleSidePane(); void showLang (TextEdit *textEdit); void handleNormalAsUrl (TextEdit *textEdit); QActionGroup *aGroup_; QString lastFile_; // The last opened or saved file (for file dialogs). QString txtReplace_; // The replacing text. int rightClicked_; // The index/row of the right-clicked tab/item. int loadingProcesses_; // The number of loading processes (used to prevent early closing). QPointer busyThread_; // Used to wait one second for making the cursor busy. ICONMODE iconMode_; // Used only internally. QMetaObject::Connection lambdaConnection_; // Captures a lambda connection to disconnect it later. SidePane *sidePane_; QHash sideItems_; // For fast tab switching. QHash langs; // All programming languages (to be enforced by the user). // Auto-saving: QTimer *autoSaver_; QElapsedTimer autoSaverPause_; int autoSaverRemainingTime_; }; } #endif // FPWIN_H FeatherPad-0.8/featherpad/highlighter-html.cpp000066400000000000000000000615021325653242400214450ustar00rootroot00000000000000/* * Copyright (C) Pedram Pourang (aka Tsu Jan) 2014 * * FeatherPad 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. * * FeatherPad is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program. If not, see . * * @license GPL-3.0+ */ #include "highlighter.h" namespace FeatherPad { // This should be called before "htmlCSSHighlighter()" and "htmlJavascript()". void Highlighter::htmlBrackets (const QString &text, const int start) { if (progLan != "html") return; /***************************** * (Multiline) HTML Brackets * *****************************/ int braIndex = start; int indx = 0; QRegularExpressionMatch startMatch; QRegularExpression braStartExp ("<(?!\\!)/{0,1}[A-Za-z0-9_\\-]+"); QRegularExpressionMatch endMatch; QRegularExpression braEndExp (">"); QRegularExpression styleExp ("<(style|STYLE)$|<(style|STYLE)\\s+[^>]*"); bool isStyle (false); QTextCharFormat htmlBraFormat; htmlBraFormat.setFontWeight (QFont::Bold); htmlBraFormat.setForeground (Violet); int prevState = previousBlockState(); if (braIndex > 0 || (prevState != singleQuoteState && prevState != doubleQuoteState && (prevState < htmlBracketState || prevState > htmlStyleSingleQuoteState))) { braIndex = text.indexOf (braStartExp, start, &startMatch); while (format (braIndex) == commentFormat || format (braIndex) == urlFormat) braIndex = text.indexOf (braStartExp, braIndex + 1, &startMatch); if (braIndex > -1) { indx = text.indexOf (styleExp, start); while (format (indx) == commentFormat || format (indx) == urlFormat) indx = text.indexOf (styleExp, indx + 1); isStyle = indx > -1 && braIndex == indx; } } else if (braIndex == 0 && (prevState == htmlStyleState /* these quote states are set only with styles */ || prevState == htmlStyleSingleQuoteState || prevState == htmlStyleDoubleQuoteState)) { isStyle = true; } int bn = currentBlock().blockNumber(); bool mainFormatting (bn >= startCursor.blockNumber() && bn <= endCursor.blockNumber()); bool hugeLine (text.length() > 50000); int firstBraIndex = braIndex; // to check progress in the following loop while (braIndex >= 0) { if (hugeLine) { setFormat (braIndex, text.length() - braIndex, translucentFormat); break; } int braEndIndex; int matched = 0; if (braIndex == 0 && (prevState == singleQuoteState || prevState == doubleQuoteState || (prevState >= htmlBracketState && prevState <= htmlStyleSingleQuoteState))) { braEndIndex = text.indexOf (braEndExp, 0, &endMatch); } else { matched = startMatch.capturedLength(); braEndIndex = text.indexOf (braEndExp, braIndex + matched, &endMatch); } int len; if (braEndIndex == -1) { len = text.length() - braIndex; if (isStyle) setCurrentBlockState (htmlStyleState); else setCurrentBlockState (htmlBracketState); } else { len = braEndIndex - braIndex + endMatch.capturedLength(); } if (matched > 0) setFormat (braIndex, matched, htmlBraFormat); if (braEndIndex > -1) setFormat (braEndIndex, endMatch.capturedLength(), htmlBraFormat); int endLimit; if (braEndIndex == -1) endLimit = text.length(); else endLimit = braEndIndex; /*************************** * (Multiline) HTML Quotes * ***************************/ int quoteIndex = braIndex; QRegularExpressionMatch quoteMatch; QRegularExpression quoteExpression ("\"|\'"); int quote = doubleQuoteState; /* find the start quote */ if (braIndex > firstBraIndex // when we're in another bracket || (prevState != doubleQuoteState && prevState != singleQuoteState && prevState != htmlStyleSingleQuoteState && prevState != htmlStyleDoubleQuoteState)) { quoteIndex = text.indexOf (quoteExpression, braIndex); /* if the start quote is found... */ if (quoteIndex >= braIndex && quoteIndex <= endLimit) { /* ... distinguish between double and single quotes */ if (quoteIndex == text.indexOf (quoteMark, quoteIndex)) { quoteExpression = quoteMark; quote = currentBlockState() == htmlStyleState ? htmlStyleDoubleQuoteState : doubleQuoteState; } else { quoteExpression.setPattern ("\'"); quote = currentBlockState() == htmlStyleState ? htmlStyleSingleQuoteState : singleQuoteState; } } } else // but if we're inside a quotation... { /* ... distinguish between the two quote kinds by checking the previous line */ quote = prevState; if (quote == doubleQuoteState || quote == htmlStyleDoubleQuoteState) quoteExpression = quoteMark; else quoteExpression.setPattern ("\'"); } while (quoteIndex >= braIndex && quoteIndex <= endLimit) { /* if the search is continued... */ if (quoteExpression.pattern() == "\"|\'") { /* ... distinguish between double and single quotes again because the quote mark may have changed */ if (quoteIndex == text.indexOf (quoteMark, quoteIndex)) { quoteExpression = quoteMark; quote = currentBlockState() == htmlStyleState ? htmlStyleDoubleQuoteState : doubleQuoteState; } else { quoteExpression.setPattern ("\'"); quote = currentBlockState() == htmlStyleState ? htmlStyleSingleQuoteState : singleQuoteState; } } int quoteEndIndex = text.indexOf (quoteExpression, quoteIndex + 1, "eMatch); if (quoteIndex == braIndex && (prevState == doubleQuoteState || prevState == singleQuoteState || prevState == htmlStyleSingleQuoteState || prevState == htmlStyleDoubleQuoteState)) { quoteEndIndex = text.indexOf (quoteExpression, braIndex, "eMatch); } int Matched = 0; if (quoteEndIndex == -1) { if (braEndIndex > -1) quoteEndIndex = braEndIndex; } else { if (quoteEndIndex > endLimit) quoteEndIndex = endLimit; else Matched = quoteMatch.capturedLength(); } int quoteLength; if (quoteEndIndex == -1) { if (currentBlockState() == htmlBracketState || currentBlockState() == htmlStyleState) { setCurrentBlockState (quote); } quoteLength = text.length() - quoteIndex; } else quoteLength = quoteEndIndex - quoteIndex + Matched; setFormat (quoteIndex, quoteLength, quoteExpression == quoteMark ? quoteFormat : altQuoteFormat); /* the next quote may be different */ quoteExpression.setPattern ("\"|\'"); quoteIndex = text.indexOf (quoteExpression, quoteIndex + quoteLength); } /******************************* * (Multiline) HTML Attributes * *******************************/ if (mainFormatting) { QTextCharFormat htmlAttributeFormat; htmlAttributeFormat.setFontItalic (true); htmlAttributeFormat.setForeground (Brown); QRegularExpressionMatch attMatch; QRegularExpression attExp ("[A-Za-z0-9_\\-]+(?=\\s*\\=)"); int attIndex = text.indexOf (attExp, braIndex, &attMatch); QTextCharFormat fi = format (attIndex); while (fi == quoteFormat || fi == altQuoteFormat || fi == urlInsideQuoteFormat) { attIndex += attMatch.capturedLength(); fi = format (attIndex); while (fi == quoteFormat || fi == altQuoteFormat || fi == urlInsideQuoteFormat) { ++ attIndex; fi = format (attIndex); } attIndex = text.indexOf (attExp, attIndex, &attMatch); fi = format (attIndex); } while (attIndex >= braIndex && attIndex < endLimit) { setFormat (attIndex, attMatch.capturedLength(), htmlAttributeFormat); attIndex = text.indexOf(attExp, attIndex + attMatch.capturedLength(), &attMatch); fi = format (attIndex); while (fi == quoteFormat || fi == altQuoteFormat || fi == urlInsideQuoteFormat) { attIndex += attMatch.capturedLength(); fi = format (attIndex); while (fi == quoteFormat || fi == altQuoteFormat || fi == urlInsideQuoteFormat) { ++ attIndex; fi = format (attIndex); } attIndex = text.indexOf (attExp, attIndex, &attMatch); fi = format (attIndex); } } } indx = braIndex + len; braIndex = text.indexOf (braStartExp, braIndex + len, &startMatch); while (format (braIndex) == commentFormat || format (braIndex) == urlFormat) braIndex = text.indexOf (braStartExp, braIndex + 1, &startMatch); if (braIndex > -1) { indx = text.indexOf (styleExp, indx); while (format (indx) == commentFormat || format (indx) == urlFormat) indx = text.indexOf (styleExp, indx + 1); isStyle = indx > -1 && braIndex == indx; } else isStyle = false; } /* at last, format whitespaces */ if (mainFormatting) { static_cast(currentBlock().userData())->insertHighlightInfo (true); // completely highlighted for (const HighlightingRule &rule : static_cast&>(highlightingRules)) { if (rule.format == whiteSpaceFormat) { QRegularExpressionMatch match; int index = text.indexOf (rule.pattern, start, &match); while (index >= 0) { setFormat (index, match.capturedLength(), rule.format); index = text.indexOf (rule.pattern, index + match.capturedLength(), &match); } } } } } /*************************/ void Highlighter::htmlCSSHighlighter (const QString &text, const int start) { if (progLan != "html") return; int cssIndex = start; QRegularExpressionMatch startMatch; QRegularExpression cssStartExp ("<(style|STYLE)>|<(style|STYLE)\\s+[^>]*>"); QRegularExpressionMatch endMatch; QRegularExpression cssEndExp (""); QRegularExpressionMatch braMatch; QRegularExpression braEndExp (">"); /* switch to css temporarily */ commentStartExpression.setPattern ("/\\*"); commentEndExpression.setPattern ("\\*/"); progLan = "css"; bool wasCSS (false); int prevState = previousBlockState(); bool wasStyle (prevState == htmlStyleState || prevState == htmlStyleSingleQuoteState || prevState == htmlStyleDoubleQuoteState); QTextBlock prevBlock = currentBlock().previous(); if (prevBlock.isValid()) { if (TextBlockData *prevData = static_cast(prevBlock.userData())) wasCSS = prevData->labelInfo() == "CSS"; // it's labeled below } QTextCharFormat fi; int matched = 0; if ((!wasCSS || start > 0) && !wasStyle) { cssIndex = text.indexOf (cssStartExp, start, &startMatch); fi = format (cssIndex); while (cssIndex >= 0 && (fi == commentFormat || fi == quoteFormat || fi == altQuoteFormat || fi == urlInsideQuoteFormat)) { cssIndex = text.indexOf (cssStartExp, cssIndex + startMatch.capturedLength(), &startMatch); fi = format (cssIndex); } } else if (wasStyle) { cssIndex = text.indexOf (braEndExp, start, &braMatch); fi = format (cssIndex); while (cssIndex >= 0 && (fi == commentFormat || fi == urlFormat || fi == quoteFormat || fi == altQuoteFormat || fi == urlInsideQuoteFormat)) { cssIndex = text.indexOf (braEndExp, cssIndex + 1, &braMatch); fi = format (cssIndex); } if (cssIndex > -1) matched = braMatch.capturedLength(); // 1 } TextBlockData *curData = static_cast(currentBlock().userData()); int bn = currentBlock().blockNumber(); bool mainFormatting (bn >= startCursor.blockNumber() && bn <= endCursor.blockNumber()); while (cssIndex >= 0) { /* single-line style bracket (